From a92bfa476467822e5723edb689cb1e80c13da52d Mon Sep 17 00:00:00 2001 From: "Felipe M. Vieira" Date: Sat, 5 May 2018 12:52:29 -0300 Subject: [PATCH 001/185] set pymode's default python to python3 --- plugin/pymode.vim | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugin/pymode.vim b/plugin/pymode.vim index 969ade7b..01b734e5 100644 --- a/plugin/pymode.vim +++ b/plugin/pymode.vim @@ -19,7 +19,7 @@ filetype plugin on " OPTIONS: {{{ " Vim Python interpreter. Set to 'disable' for remove python features. -call pymode#default('g:pymode_python', '') +call pymode#default('g:pymode_python', 'python3') " Disable pymode warnings call pymode#default('g:pymode_warning', 1) @@ -274,10 +274,6 @@ if &compatible endif filetype plugin on -" Disable python-related functionality -" let g:pymode_python = 'disable' -" let g:pymode_python = 'python3' - " UltiSnips Fixes if !len(g:pymode_python) if exists('g:_uspy') && g:_uspy == ':py' From 5f78649aa58e86cf7ee62278c590f020c2b3c050 Mon Sep 17 00:00:00 2001 From: Diego Rabatone Oliveira Date: Fri, 1 Jun 2018 18:10:17 -0300 Subject: [PATCH 002/185] Set python3 as default, if it exists. If there is a python3 installed on the system, then it is going to be the default to be used. Otherwise, we use just the python command. It can even be a python3 version, in systems that only have python3 or the python command is linked to the python3 version. This commit fixes #896. --- plugin/pymode.vim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugin/pymode.vim b/plugin/pymode.vim index 01b734e5..218d4d83 100644 --- a/plugin/pymode.vim +++ b/plugin/pymode.vim @@ -19,7 +19,11 @@ filetype plugin on " OPTIONS: {{{ " Vim Python interpreter. Set to 'disable' for remove python features. -call pymode#default('g:pymode_python', 'python3') +if executable('python3') + call pymode#default('g:pymode_python', 'python3') +else + call pymode#default('g:pymode_python', 'python') +endif " Disable pymode warnings call pymode#default('g:pymode_warning', 1) From 232801848221f5a8e437e960929b6acab9475a53 Mon Sep 17 00:00:00 2001 From: Diego Rabatone Oliveira Date: Fri, 1 Jun 2018 18:53:58 -0300 Subject: [PATCH 003/185] Improve travis-ci configuration --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9be7bb46..8a689acb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,15 @@ # Build vim from source with python3 support and execute tests. +dist: trusty +sudo: required branches: only: - develop - dev_unstable before_install: - export ORIGINAL_FOLDER=$PWD - - sudo apt install libncurses5-dev libgnome2-dev libgnomeui-dev libgtk2.0-dev libatk1.0-dev libbonoboui2-dev libcairo2-dev libx11-dev libxpm-dev libxt-dev python-dev python3-dev lua5.1 lua5.1-dev libperl-dev git - - sudo apt remove vim vim-runtime gvim + - sudo apt update + - sudo apt install -yqq libncurses5-dev libgnome2-dev libgnomeui-dev libgtk2.0-dev libatk1.0-dev libbonoboui2-dev libcairo2-dev libx11-dev libxpm-dev libxt-dev python-dev python3-dev lua5.1 lua5.1-dev libperl-dev git + - sudo apt remove --purge vim vim-runtime gvim - cd /tmp - git clone https://github.com/vim/vim.git - cd vim From 521b4980433776f50e44fe37cb0dc0c21fd12050 Mon Sep 17 00:00:00 2001 From: Xiangyu Xu Date: Tue, 12 Jun 2018 02:53:27 -0500 Subject: [PATCH 004/185] Update submodules Move astroid from pymode/libs to submodules and update Move pylint from pymode/libs to submodules and update Move rope from pymode/libs to submodules and update Update logilab to 1.4.1 from PyPI Update submodules recursively --- .gitmodules | 9 + pymode/libs/astroid | 1 + pymode/libs/astroid/__pkginfo__.py | 61 - pymode/libs/astroid/astpeephole.py | 74 - pymode/libs/astroid/brain/brain_numpy.py | 50 - pymode/libs/astroid/brain/brain_six.py | 276 - pymode/libs/astroid/brain/brain_typing.py | 89 - pymode/libs/astroid/node_classes.py | 2082 -------- pymode/libs/logilab | 1 + pymode/libs/logilab-common-1.4.1/COPYING | 339 ++ .../libs/logilab-common-1.4.1/COPYING.LESSER | 510 ++ pymode/libs/logilab-common-1.4.1/ChangeLog | 1613 ++++++ pymode/libs/logilab-common-1.4.1/MANIFEST.in | 14 + pymode/libs/logilab-common-1.4.1/PKG-INFO | 164 + .../README} | 3 - .../libs/logilab-common-1.4.1/__pkginfo__.py | 61 + .../logilab-common-1.4.1/bin/logilab-pytest | 7 + .../bin/logilab-pytest.bat | 17 + .../logilab-common-1.4.1/doc/logilab-pytest.1 | 54 + pymode/libs/logilab-common-1.4.1/doc/makefile | 8 + .../logilab-common-1.4.1/logilab/__init__.py | 1 + .../logilab/common/__init__.py | 2 +- .../logilab/common/cache.py | 0 .../logilab/common/changelog.py | 65 +- .../logilab/common/clcommands.py | 0 .../logilab/common/compat.py | 0 .../logilab/common/configuration.py | 19 +- .../logilab/common/daemon.py | 0 .../logilab/common/date.py | 2 +- .../logilab/common/debugger.py | 0 .../logilab/common/decorators.py | 0 .../logilab/common/deprecation.py | 0 .../logilab/common/fileutils.py | 15 +- .../logilab/common/graph.py | 0 .../logilab/common/interface.py | 0 .../logilab/common/logging_ext.py | 0 .../logilab/common/modutils.py | 64 +- .../logilab/common/optik_ext.py | 6 +- .../logilab/common/optparser.py | 0 .../logilab/common/proc.py | 0 .../logilab/common/pytest.py | 254 +- .../logilab/common/registry.py | 67 +- .../logilab/common/shellutils.py | 58 +- .../logilab/common/sphinx_ext.py | 0 .../logilab/common/sphinxutils.py | 0 .../logilab/common/table.py | 0 .../logilab/common/tasksqueue.py | 0 .../logilab/common/testlib.py | 708 +++ .../logilab/common/textutils.py | 2 + .../logilab/common/tree.py | 0 .../logilab/common/umessage.py | 107 +- .../logilab/common/ureports/__init__.py | 0 .../logilab/common/ureports/docbook_writer.py | 0 .../logilab/common/ureports/html_writer.py | 0 .../logilab/common/ureports/nodes.py | 0 .../logilab/common/ureports/text_writer.py | 0 .../logilab/common/urllib2ext.py | 0 .../logilab/common/vcgutils.py | 0 .../logilab/common/visitor.py | 0 .../logilab/common/xmlutils.py | 0 pymode/libs/logilab-common-1.4.1/setup.cfg | 9 + pymode/libs/logilab-common-1.4.1/setup.py | 54 + .../logilab-common-1.4.1/test/data/ChangeLog | 184 + .../test/data/MyPyPa-0.1.0.zip | Bin 0 -> 206 bytes .../test/data}/__init__.py | 0 .../test/data/__pkginfo__.py | 57 + .../test/data/content_differ_dir/NOTHING} | 0 .../test/data/content_differ_dir/README | 1 + .../test/data/content_differ_dir/subdir/coin | 1 + .../data/content_differ_dir/subdir/toto.txt | 53 + .../test/data/deprecation.py | 4 + .../test/data/file_differ_dir/NOTHING} | 0 .../test/data/file_differ_dir/README | 1 + .../test/data/file_differ_dir/subdir/toto.txt | 53 + .../data/file_differ_dir/subdirtwo/Hello} | 0 .../test/data/find_test}/__init__.py | 0 .../test/data/find_test/foo.txt} | 0 .../test/data/find_test/module.py | 0 .../test/data/find_test/module2.py | 0 .../test/data/find_test/newlines.txt | 0 .../test/data/find_test/noendingnewline.py | 0 .../test/data/find_test/nonregr.py | 0 .../test/data/find_test/normal_file.txt | 0 .../test/data/find_test/spam.txt | 0 .../test/data/find_test/sub/doc.txt | 0 .../test/data/find_test/sub/momo.py | 0 .../test/data/find_test/test.ini | 0 .../test/data/find_test/test1.msg | 0 .../test/data/find_test/test2.msg | 0 .../data/find_test/write_protected_file.txt | 0 .../logilab-common-1.4.1/test/data/foo.txt | 9 + .../test/data/lmfp/__init__.py | 2 + .../test/data/lmfp/foo.py | 6 + .../logilab-common-1.4.1/test/data/module.py | 69 + .../logilab-common-1.4.1/test/data/module2.py | 77 + .../test/data/newlines.txt | 3 + .../test/data/noendingnewline.py | 36 + .../logilab-common-1.4.1/test/data/nonregr.py | 16 + .../test/data/normal_file.txt | 0 .../test/data/reference_dir/NOTHING | 0 .../test/data/reference_dir/README | 1 + .../test/data/reference_dir/subdir/coin | 1 + .../test/data/reference_dir/subdir/toto.txt | 53 + .../test/data/regobjects.py | 22 + .../test/data/regobjects2.py | 8 + .../test/data/same_dir/NOTHING | 0 .../test/data/same_dir/README | 1 + .../test/data/same_dir/subdir/coin | 1 + .../test/data/same_dir/subdir/toto.txt | 53 + .../logilab-common-1.4.1/test/data/spam.txt | 9 + .../test/data/sub/doc.txt | 1 + .../test/data/sub/momo.py | 3 + .../test/data/subdir_differ_dir/NOTHING | 0 .../test/data/subdir_differ_dir/README | 1 + .../test/data/subdir_differ_dir/subdir/coin | 1 + .../data/subdir_differ_dir/subdir/toto.txt | 53 + .../logilab-common-1.4.1/test/data/test.ini | 20 + .../logilab-common-1.4.1/test/data/test1.msg | 30 + .../logilab-common-1.4.1/test/data/test2.msg | 42 + .../test/data/write_protected_file.txt | 0 .../test/unittest_cache.py | 129 + .../test/unittest_changelog.py | 40 + .../test/unittest_configuration.py | 509 ++ .../test/unittest_date.py | 206 + .../test/unittest_decorators.py | 208 + .../test/unittest_deprecation.py | 147 + .../test/unittest_fileutils.py | 146 + .../test/unittest_graph.py | 89 + .../test/unittest_interface.py | 87 + .../test/unittest_modutils.py | 296 ++ .../test/unittest_pytest.py | 86 + .../test/unittest_registry.py | 220 + .../test/unittest_shellutils.py | 235 + .../test/unittest_table.py | 448 ++ .../test/unittest_taskqueue.py | 71 + .../test/unittest_testlib.py | 790 +++ .../test/unittest_textutils.py | 268 + .../test/unittest_tree.py | 247 + .../test/unittest_umessage.py | 94 + .../test/unittest_ureports_html.py | 63 + .../test/unittest_ureports_text.py | 104 + .../test/unittest_xmlutils.py | 75 + .../libs/logilab-common-1.4.1/test/utils.py | 96 + pymode/libs/logilab/__init__.py | 1 - pymode/libs/logilab/common/testlib.py | 1338 ----- .../libs/logilab_common-1.0.2-py2.7-nspkg.pth | 1 - .../logilab_common-1.0.2.dist-info/METADATA | 169 - .../logilab_common-1.0.2.dist-info/RECORD | 87 - .../libs/logilab_common-1.0.2.dist-info/WHEEL | 5 - .../metadata.json | 1 - .../namespace_packages.txt | 1 - .../top_level.txt | 1 - pymode/libs/pylint | 1 + pymode/libs/pylint/__init__.py | 29 - pymode/libs/pylint/__main__.py | 7 - pymode/libs/pylint/__pkginfo__.py | 98 - pymode/libs/pylint/checkers/__init__.py | 116 - pymode/libs/pylint/checkers/async.py | 75 - pymode/libs/pylint/checkers/base.py | 1660 ------ pymode/libs/pylint/checkers/classes.py | 1402 ------ .../libs/pylint/checkers/design_analysis.py | 334 -- pymode/libs/pylint/checkers/exceptions.py | 389 -- pymode/libs/pylint/checkers/format.py | 1069 ---- pymode/libs/pylint/checkers/imports.py | 768 --- pymode/libs/pylint/checkers/logging.py | 271 - pymode/libs/pylint/checkers/misc.py | 99 - pymode/libs/pylint/checkers/newstyle.py | 179 - pymode/libs/pylint/checkers/python3.py | 861 ---- pymode/libs/pylint/checkers/raw_metrics.py | 115 - pymode/libs/pylint/checkers/refactoring.py | 715 --- pymode/libs/pylint/checkers/similar.py | 363 -- pymode/libs/pylint/checkers/spelling.py | 265 - pymode/libs/pylint/checkers/stdlib.py | 278 - pymode/libs/pylint/checkers/strings.py | 621 --- pymode/libs/pylint/checkers/typecheck.py | 1289 ----- pymode/libs/pylint/checkers/utils.py | 860 ---- pymode/libs/pylint/checkers/variables.py | 1324 ----- pymode/libs/pylint/config.py | 831 --- pymode/libs/pylint/epylint.py | 175 - pymode/libs/pylint/exceptions.py | 15 - .../pylint/extensions/_check_docs_utils.py | 580 --- pymode/libs/pylint/extensions/bad_builtin.py | 67 - pymode/libs/pylint/extensions/check_docs.py | 21 - pymode/libs/pylint/extensions/check_elif.py | 67 - .../libs/pylint/extensions/comparetozero.py | 71 - pymode/libs/pylint/extensions/docparams.py | 419 -- pymode/libs/pylint/extensions/docstyle.py | 75 - pymode/libs/pylint/extensions/emptystring.py | 71 - pymode/libs/pylint/extensions/mccabe.py | 170 - .../extensions/overlapping_exceptions.py | 81 - .../extensions/redefined_variable_type.py | 104 - pymode/libs/pylint/graph.py | 170 - pymode/libs/pylint/interfaces.py | 94 - pymode/libs/pylint/lint.py | 1365 ----- pymode/libs/pylint/pyreverse/__init__.py | 9 - pymode/libs/pylint/pyreverse/diadefslib.py | 228 - pymode/libs/pylint/pyreverse/diagrams.py | 249 - pymode/libs/pylint/pyreverse/inspector.py | 361 -- pymode/libs/pylint/pyreverse/main.py | 137 - pymode/libs/pylint/pyreverse/utils.py | 201 - pymode/libs/pylint/pyreverse/vcgutils.py | 185 - pymode/libs/pylint/pyreverse/writer.py | 188 - pymode/libs/pylint/reporters/__init__.py | 126 - pymode/libs/pylint/reporters/json.py | 56 - pymode/libs/pylint/reporters/text.py | 235 - .../pylint/reporters/ureports/__init__.py | 94 - .../libs/pylint/reporters/ureports/nodes.py | 182 - .../pylint/reporters/ureports/text_writer.py | 94 - pymode/libs/pylint/testutils.py | 382 -- pymode/libs/pylint/utils.py | 1182 ----- pymode/libs/rope | 1 + pymode/libs/rope/__init__.py | 19 - pymode/libs/rope/base/__init__.py | 8 - pymode/libs/rope/base/arguments.py | 111 - pymode/libs/rope/base/ast.py | 76 - pymode/libs/rope/base/astutils.py | 64 - pymode/libs/rope/base/builtins.py | 812 --- pymode/libs/rope/base/change.py | 450 -- pymode/libs/rope/base/codeanalyze.py | 362 -- pymode/libs/rope/base/default_config.py | 115 - pymode/libs/rope/base/evaluate.py | 332 -- pymode/libs/rope/base/exceptions.py | 61 - pymode/libs/rope/base/fscommands.py | 288 -- pymode/libs/rope/base/history.py | 235 - pymode/libs/rope/base/libutils.py | 122 - pymode/libs/rope/base/oi/__init__.py | 38 - pymode/libs/rope/base/oi/doa.py | 166 - pymode/libs/rope/base/oi/memorydb.py | 127 - pymode/libs/rope/base/oi/objectdb.py | 179 - pymode/libs/rope/base/oi/objectinfo.py | 232 - pymode/libs/rope/base/oi/runmod.py | 222 - pymode/libs/rope/base/oi/soa.py | 139 - pymode/libs/rope/base/oi/soi.py | 222 - pymode/libs/rope/base/oi/transform.py | 285 -- .../rope/base/oi/type_hinting/evaluate.py | 353 -- .../libs/rope/base/oi/type_hinting/factory.py | 70 - .../rope/base/oi/type_hinting/interfaces.py | 25 - .../oi/type_hinting/providers/composite.py | 59 - .../oi/type_hinting/providers/docstrings.py | 193 - .../oi/type_hinting/providers/inheritance.py | 66 - .../oi/type_hinting/providers/interfaces.py | 37 - .../type_hinting/providers/numpydocstrings.py | 41 - .../providers/pep0484_type_comments.py | 42 - .../oi/type_hinting/resolvers/composite.py | 22 - .../oi/type_hinting/resolvers/interfaces.py | 10 - .../base/oi/type_hinting/resolvers/types.py | 16 - .../libs/rope/base/oi/type_hinting/utils.py | 136 - pymode/libs/rope/base/prefs.py | 41 - pymode/libs/rope/base/project.py | 491 -- pymode/libs/rope/base/pycore.py | 346 -- pymode/libs/rope/base/pynames.py | 201 - pymode/libs/rope/base/pynamesdef.py | 55 - pymode/libs/rope/base/pyobjects.py | 311 -- pymode/libs/rope/base/pyobjectsdef.py | 562 --- pymode/libs/rope/base/pyscopes.py | 314 -- pymode/libs/rope/base/resourceobserver.py | 272 - pymode/libs/rope/base/resources.py | 243 - pymode/libs/rope/base/simplify.py | 55 - pymode/libs/rope/base/stdmods.py | 61 - pymode/libs/rope/base/taskhandle.py | 131 - pymode/libs/rope/base/utils/__init__.py | 98 - pymode/libs/rope/base/utils/datastructures.py | 67 - pymode/libs/rope/base/utils/pycompat.py | 45 - pymode/libs/rope/base/worder.py | 525 -- pymode/libs/rope/contrib/__init__.py | 7 - pymode/libs/rope/contrib/autoimport.py | 222 - pymode/libs/rope/contrib/changestack.py | 52 - pymode/libs/rope/contrib/codeassist.py | 695 --- pymode/libs/rope/contrib/finderrors.py | 91 - pymode/libs/rope/contrib/findit.py | 114 - pymode/libs/rope/contrib/fixmodnames.py | 69 - pymode/libs/rope/contrib/fixsyntax.py | 181 - pymode/libs/rope/contrib/generate.py | 362 -- pymode/libs/rope/refactor/__init__.py | 55 - pymode/libs/rope/refactor/change_signature.py | 352 -- .../libs/rope/refactor/encapsulate_field.py | 209 - pymode/libs/rope/refactor/extract.py | 810 --- pymode/libs/rope/refactor/functionutils.py | 222 - .../rope/refactor/importutils/__init__.py | 316 -- .../libs/rope/refactor/importutils/actions.py | 361 -- .../rope/refactor/importutils/importinfo.py | 201 - .../refactor/importutils/module_imports.py | 506 -- pymode/libs/rope/refactor/inline.py | 625 --- .../libs/rope/refactor/introduce_factory.py | 135 - .../libs/rope/refactor/introduce_parameter.py | 96 - pymode/libs/rope/refactor/localtofield.py | 49 - pymode/libs/rope/refactor/method_object.py | 90 - pymode/libs/rope/refactor/move.py | 784 --- pymode/libs/rope/refactor/multiproject.py | 78 - pymode/libs/rope/refactor/occurrences.py | 402 -- pymode/libs/rope/refactor/patchedast.py | 829 --- pymode/libs/rope/refactor/rename.py | 220 - pymode/libs/rope/refactor/restructure.py | 307 -- pymode/libs/rope/refactor/similarfinder.py | 370 -- pymode/libs/rope/refactor/sourceutils.py | 91 - pymode/libs/rope/refactor/suites.py | 158 - pymode/libs/rope/refactor/topackage.py | 32 - pymode/libs/rope/refactor/usefunction.py | 174 - pymode/libs/rope/refactor/wildcards.py | 178 - submodules/astroid/.coveragerc | 9 + submodules/astroid/.github/ISSUE_TEMPLATE.md | 13 + .../astroid/.github/PULL_REQUEST_TEMPLATE.md | 2 + submodules/astroid/.gitignore | 15 + submodules/astroid/.travis.yml | 43 + submodules/astroid/COPYING | 339 ++ submodules/astroid/COPYING.LESSER | 510 ++ submodules/astroid/ChangeLog | 1778 +++++++ submodules/astroid/MANIFEST.in | 7 + submodules/astroid/README.rst | 66 + submodules/astroid/appveyor.yml | 32 + submodules/astroid/appveyor/install.ps1 | 27 + .../astroid}/astroid/__init__.py | 64 +- submodules/astroid/astroid/__pkginfo__.py | 48 + submodules/astroid/astroid/_ast.py | 43 + .../astroid}/astroid/arguments.py | 7 +- .../astroid}/astroid/as_string.py | 8 +- .../astroid}/astroid/bases.py | 107 +- .../astroid/astroid/brain/brain_attrs.py | 60 + .../astroid/brain/brain_builtin_inference.py | 328 +- .../astroid/brain/brain_collections.py | 39 +- .../astroid/astroid/brain/brain_curses.py | 177 + .../astroid}/astroid/brain/brain_dateutil.py | 0 .../astroid}/astroid/brain/brain_fstrings.py | 3 +- .../astroid}/astroid/brain/brain_functools.py | 2 +- .../astroid}/astroid/brain/brain_gi.py | 0 .../astroid}/astroid/brain/brain_hashlib.py | 10 +- .../astroid}/astroid/brain/brain_io.py | 0 .../astroid}/astroid/brain/brain_mechanize.py | 0 .../astroid/brain/brain_multiprocessing.py | 0 .../astroid/brain/brain_namedtuple_enum.py | 228 +- .../astroid}/astroid/brain/brain_nose.py | 0 .../astroid/astroid/brain/brain_numpy.py | 326 ++ .../astroid/brain/brain_pkg_resources.py | 1 + .../astroid}/astroid/brain/brain_pytest.py | 0 .../astroid}/astroid/brain/brain_qt.py | 22 +- .../astroid/astroid/brain/brain_random.py | 98 + .../astroid}/astroid/brain/brain_re.py | 0 submodules/astroid/astroid/brain/brain_six.py | 191 + .../astroid}/astroid/brain/brain_ssl.py | 0 .../astroid/brain/brain_subprocess.py | 13 +- .../astroid}/astroid/brain/brain_threading.py | 0 .../astroid/astroid/brain/brain_typing.py | 93 + .../astroid/astroid/brain/brain_uuid.py | 22 + .../astroid}/astroid/builder.py | 77 +- .../astroid}/astroid/context.py | 63 +- .../astroid}/astroid/decorators.py | 18 +- .../astroid}/astroid/exceptions.py | 18 +- .../astroid}/astroid/helpers.py | 121 +- .../astroid}/astroid/inference.py | 158 +- .../astroid/astroid/interpreter/__init__.py | 0 .../astroid/interpreter/_import/__init__.py | 0 .../astroid/interpreter/_import/spec.py | 13 +- .../astroid/interpreter/_import/util.py | 1 - .../astroid/interpreter/dunder_lookup.py | 0 .../astroid/interpreter/objectmodel.py | 131 +- .../astroid}/astroid/manager.py | 53 +- .../astroid}/astroid/mixins.py | 64 +- .../astroid}/astroid/modutils.py | 75 +- submodules/astroid/astroid/node_classes.py | 4465 ++++++++++++++++ .../astroid}/astroid/nodes.py | 24 +- .../astroid}/astroid/objects.py | 23 +- .../astroid}/astroid/protocols.py | 97 +- .../astroid}/astroid/raw_building.py | 20 +- .../astroid}/astroid/rebuilder.py | 305 +- .../astroid}/astroid/scoped_nodes.py | 1432 +++++- .../astroid}/astroid/test_utils.py | 8 +- submodules/astroid/astroid/tests/__init__.py | 0 submodules/astroid/astroid/tests/resources.py | 63 + .../python2/data/MyPyPa-0.1.0-py2.5.zip | Bin 0 -> 1222 bytes .../testdata/python2/data/SSL1/Connection1.py | 14 + .../testdata/python2/data/SSL1/__init__.py | 1 + .../tests/testdata/python2/data/__init__.py | 1 + .../testdata/python2/data/absimp/__init__.py | 5 + .../data/absimp/sidepackage/__init__.py | 3 + .../testdata/python2/data/absimp/string.py | 3 + .../tests/testdata/python2/data/absimport.py | 3 + .../tests/testdata/python2/data/all.py | 9 + .../testdata/python2/data/appl/__init__.py | 3 + .../python2/data/appl/myConnection.py | 12 + .../namespace_pep_420/submodule.py | 1 + .../testdata/python2/data/descriptor_crash.py | 11 + .../tests/testdata/python2/data/email.py | 1 + .../python2/data/find_test/__init__.py | 0 .../testdata/python2/data/find_test/module.py | 0 .../python2/data/find_test/module2.py | 0 .../python2/data/find_test/noendingnewline.py | 0 .../python2/data/find_test/nonregr.py | 0 .../python2/data/foogle/fax/__init__.py | 0 .../testdata/python2/data/foogle/fax/a.py | 1 + .../data/foogle_fax-0.12.5-py2.7-nspkg.pth | 2 + .../tests/testdata/python2/data/format.py | 34 + .../testdata/python2/data/invalid_encoding.py | 1 + .../testdata/python2/data/lmfp/__init__.py | 2 + .../tests/testdata/python2/data/lmfp/foo.py | 6 + .../tests/testdata/python2/data/module.py | 89 + .../python2/data/module1abs/__init__.py | 4 + .../testdata/python2/data/module1abs/core.py | 1 + .../tests/testdata/python2/data/module2.py | 143 + .../python2/data/namespace_pep_420/module.py | 1 + .../testdata/python2/data/noendingnewline.py | 36 + .../tests/testdata/python2/data/nonregr.py | 57 + .../tests/testdata/python2/data/notall.py | 7 + .../testdata/python2/data/notamodule/file.py | 0 .../testdata/python2/data/package/__init__.py | 4 + .../python2/data/package/absimport.py | 6 + .../testdata/python2/data/package/hello.py | 2 + .../import_package_subpackage_module.py | 49 + .../data/package/subpackage/__init__.py | 1 + .../python2/data/package/subpackage/module.py | 1 + .../path_pkg_resources_1/package/__init__.py | 1 + .../data/path_pkg_resources_1/package/foo.py | 0 .../path_pkg_resources_2/package/__init__.py | 1 + .../data/path_pkg_resources_2/package/bar.py | 0 .../path_pkg_resources_3/package/__init__.py | 1 + .../data/path_pkg_resources_3/package/baz.py | 0 .../data/path_pkgutil_1/package/__init__.py | 2 + .../data/path_pkgutil_1/package/foo.py | 0 .../data/path_pkgutil_2/package/__init__.py | 2 + .../data/path_pkgutil_2/package/bar.py | 0 .../data/path_pkgutil_3/package/__init__.py | 2 + .../data/path_pkgutil_3/package/baz.py | 0 .../tests/testdata/python2/data/recursion.py | 3 + .../testdata/python2/data/tmp__init__.py | 0 .../python2/data/unicode_package/__init__.py | 1 + .../data/unicode_package/core/__init__.py | 0 .../python3/data/MyPyPa-0.1.0-py2.5.zip | Bin 0 -> 1222 bytes .../testdata/python3/data/SSL1/Connection1.py | 14 + .../testdata/python3/data/SSL1/__init__.py | 1 + .../tests/testdata/python3/data/__init__.py | 1 + .../testdata/python3/data/absimp/__init__.py | 5 + .../data/absimp/sidepackage/__init__.py | 3 + .../testdata/python3/data/absimp/string.py | 3 + .../tests/testdata/python3/data/absimport.py | 3 + .../tests/testdata/python3/data/all.py | 9 + .../testdata/python3/data/appl/__init__.py | 3 + .../python3/data/appl/myConnection.py | 11 + .../namespace_pep_420/submodule.py | 1 + .../testdata/python3/data/descriptor_crash.py | 11 + .../tests/testdata/python3/data/email.py | 1 + .../python3/data/find_test/__init__.py | 0 .../testdata/python3/data/find_test/module.py | 0 .../python3/data/find_test/module2.py | 0 .../python3/data/find_test/noendingnewline.py | 0 .../python3/data/find_test/nonregr.py | 0 .../python3/data/foogle/fax/__init__.py | 0 .../testdata/python3/data/foogle/fax/a.py | 1 + .../data/foogle_fax-0.12.5-py2.7-nspkg.pth | 2 + .../tests/testdata/python3/data/format.py | 34 + .../testdata/python3/data/invalid_encoding.py | 1 + .../testdata/python3/data/lmfp/__init__.py | 2 + .../tests/testdata/python3/data/lmfp/foo.py | 6 + .../tests/testdata/python3/data/module.py | 88 + .../python3/data/module1abs/__init__.py | 4 + .../testdata/python3/data/module1abs/core.py | 1 + .../tests/testdata/python3/data/module2.py | 144 + .../python3/data/namespace_pep_420/module.py | 1 + .../testdata/python3/data/noendingnewline.py | 36 + .../tests/testdata/python3/data/nonregr.py | 57 + .../tests/testdata/python3/data/notall.py | 8 + .../testdata/python3/data/notamodule/file.py | 0 .../testdata/python3/data/package/__init__.py | 4 + .../python3/data/package/absimport.py | 6 + .../testdata/python3/data/package/hello.py | 2 + .../import_package_subpackage_module.py | 49 + .../data/package/subpackage/__init__.py | 1 + .../python3/data/package/subpackage/module.py | 1 + .../path_pkg_resources_1/package/__init__.py | 1 + .../data/path_pkg_resources_1/package/foo.py | 0 .../path_pkg_resources_2/package/__init__.py | 1 + .../data/path_pkg_resources_2/package/bar.py | 0 .../path_pkg_resources_3/package/__init__.py | 1 + .../data/path_pkg_resources_3/package/baz.py | 0 .../data/path_pkgutil_1/package/__init__.py | 2 + .../data/path_pkgutil_1/package/foo.py | 0 .../data/path_pkgutil_2/package/__init__.py | 2 + .../data/path_pkgutil_2/package/bar.py | 0 .../data/path_pkgutil_3/package/__init__.py | 2 + .../data/path_pkgutil_3/package/baz.py | 0 .../tests/testdata/python3/data/recursion.py | 3 + .../testdata/python3/data/tmp__init__.py | 0 .../python3/data/unicode_package/__init__.py | 1 + .../data/unicode_package/core/__init__.py | 0 .../astroid/astroid/tests/unittest_brain.py | 1415 ++++++ .../astroid/tests/unittest_brain_numpy.py | 391 ++ .../astroid/astroid/tests/unittest_builder.py | 713 +++ .../astroid/astroid/tests/unittest_helpers.py | 254 + .../astroid/tests/unittest_inference.py | 4481 +++++++++++++++++ .../astroid/astroid/tests/unittest_lookup.py | 341 ++ .../astroid/astroid/tests/unittest_manager.py | 286 ++ .../astroid/tests/unittest_modutils.py | 286 ++ .../astroid/astroid/tests/unittest_nodes.py | 927 ++++ .../astroid/tests/unittest_object_model.py | 577 +++ .../astroid/astroid/tests/unittest_objects.py | 520 ++ .../astroid/tests/unittest_protocols.py | 206 + .../astroid/astroid/tests/unittest_python3.py | 354 ++ .../astroid/tests/unittest_raw_building.py | 91 + .../astroid/tests/unittest_regrtest.py | 328 ++ .../astroid/tests/unittest_scoped_nodes.py | 1793 +++++++ .../astroid/tests/unittest_transforms.py | 235 + .../astroid/astroid/tests/unittest_utils.py | 112 + .../astroid}/astroid/transforms.py | 9 +- .../astroid}/astroid/util.py | 32 +- submodules/astroid/debian.sid/control | 42 + submodules/astroid/debian.sid/rules | 41 + submodules/astroid/debian.sid/source/format | 1 + submodules/astroid/debian/changelog | 30 + submodules/astroid/debian/compat | 1 + submodules/astroid/debian/control | 31 + submodules/astroid/debian/copyright | 36 + submodules/astroid/debian/python-astroid.dirs | 1 + submodules/astroid/debian/rules | 66 + submodules/astroid/debian/watch | 2 + submodules/astroid/doc/Makefile | 130 + .../astroid/doc/api/astroid.exceptions.rst | 38 + submodules/astroid/doc/api/astroid.nodes.rst | 226 + submodules/astroid/doc/api/base_nodes.rst | 44 + submodules/astroid/doc/api/general.rst | 5 + submodules/astroid/doc/api/index.rst | 12 + submodules/astroid/doc/ast_objects.inv | 11 + submodules/astroid/doc/changelog.rst | 1 + submodules/astroid/doc/conf.py | 237 + submodules/astroid/doc/extending.rst | 250 + submodules/astroid/doc/index.rst | 35 + submodules/astroid/doc/inference.rst | 31 + submodules/astroid/doc/make.bat | 170 + submodules/astroid/doc/release.txt | 30 + submodules/astroid/doc/whatsnew.rst | 11 + submodules/astroid/pylintrc | 386 ++ submodules/astroid/pytest.ini | 3 + submodules/astroid/setup.cfg | 8 + submodules/astroid/setup.py | 49 + submodules/astroid/tox.ini | 75 + submodules/autopep8 | 2 +- submodules/mccabe | 2 +- submodules/pycodestyle | 2 +- submodules/pydocstyle | 2 +- submodules/pyflakes | 2 +- submodules/pylint | 1 + submodules/rope | 1 + submodules/snowball_py | 2 +- 540 files changed, 36963 insertions(+), 45885 deletions(-) create mode 120000 pymode/libs/astroid delete mode 100644 pymode/libs/astroid/__pkginfo__.py delete mode 100644 pymode/libs/astroid/astpeephole.py delete mode 100644 pymode/libs/astroid/brain/brain_numpy.py delete mode 100644 pymode/libs/astroid/brain/brain_six.py delete mode 100644 pymode/libs/astroid/brain/brain_typing.py delete mode 100644 pymode/libs/astroid/node_classes.py create mode 120000 pymode/libs/logilab create mode 100644 pymode/libs/logilab-common-1.4.1/COPYING create mode 100644 pymode/libs/logilab-common-1.4.1/COPYING.LESSER create mode 100644 pymode/libs/logilab-common-1.4.1/ChangeLog create mode 100644 pymode/libs/logilab-common-1.4.1/MANIFEST.in create mode 100644 pymode/libs/logilab-common-1.4.1/PKG-INFO rename pymode/libs/{logilab_common-1.0.2.dist-info/DESCRIPTION.rst => logilab-common-1.4.1/README} (99%) create mode 100644 pymode/libs/logilab-common-1.4.1/__pkginfo__.py create mode 100755 pymode/libs/logilab-common-1.4.1/bin/logilab-pytest create mode 100644 pymode/libs/logilab-common-1.4.1/bin/logilab-pytest.bat create mode 100644 pymode/libs/logilab-common-1.4.1/doc/logilab-pytest.1 create mode 100644 pymode/libs/logilab-common-1.4.1/doc/makefile create mode 100644 pymode/libs/logilab-common-1.4.1/logilab/__init__.py rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/__init__.py (98%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/cache.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/changelog.py (82%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/clcommands.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/compat.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/configuration.py (98%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/daemon.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/date.py (99%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/debugger.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/decorators.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/deprecation.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/fileutils.py (97%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/graph.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/interface.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/logging_ext.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/modutils.py (93%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/optik_ext.py (98%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/optparser.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/proc.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/pytest.py (85%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/registry.py (95%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/shellutils.py (85%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/sphinx_ext.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/sphinxutils.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/table.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/tasksqueue.py (100%) create mode 100644 pymode/libs/logilab-common-1.4.1/logilab/common/testlib.py rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/textutils.py (99%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/tree.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/umessage.py (64%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/ureports/__init__.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/ureports/docbook_writer.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/ureports/html_writer.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/ureports/nodes.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/ureports/text_writer.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/urllib2ext.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/vcgutils.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/visitor.py (100%) rename pymode/libs/{ => logilab-common-1.4.1}/logilab/common/xmlutils.py (100%) create mode 100644 pymode/libs/logilab-common-1.4.1/setup.cfg create mode 100644 pymode/libs/logilab-common-1.4.1/setup.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/ChangeLog create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/MyPyPa-0.1.0.zip rename pymode/libs/{astroid/interpreter => logilab-common-1.4.1/test/data}/__init__.py (100%) create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/__pkginfo__.py rename pymode/libs/{astroid/interpreter/_import/__init__.py => logilab-common-1.4.1/test/data/content_differ_dir/NOTHING} (100%) create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/README create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/coin create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/toto.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/deprecation.py rename pymode/libs/{pylint/extensions/__init__.py => logilab-common-1.4.1/test/data/file_differ_dir/NOTHING} (100%) create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/README create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/subdir/toto.txt rename pymode/libs/{rope/base/oi/type_hinting/__init__.py => logilab-common-1.4.1/test/data/file_differ_dir/subdirtwo/Hello} (100%) rename pymode/libs/{rope/base/oi/type_hinting/providers => logilab-common-1.4.1/test/data/find_test}/__init__.py (100%) rename pymode/libs/{rope/base/oi/type_hinting/resolvers/__init__.py => logilab-common-1.4.1/test/data/find_test/foo.txt} (100%) create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/module.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/module2.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/newlines.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/noendingnewline.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/nonregr.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/normal_file.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/spam.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/sub/doc.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/sub/momo.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/test.ini create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/test1.msg create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/test2.msg create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/find_test/write_protected_file.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/foo.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/lmfp/__init__.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/lmfp/foo.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/module.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/module2.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/newlines.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/noendingnewline.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/nonregr.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/normal_file.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/reference_dir/NOTHING create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/reference_dir/README create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/coin create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/toto.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/regobjects.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/regobjects2.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/same_dir/NOTHING create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/same_dir/README create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/coin create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/toto.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/spam.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/sub/doc.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/sub/momo.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/NOTHING create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/README create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/coin create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/toto.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/test.ini create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/test1.msg create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/test2.msg create mode 100644 pymode/libs/logilab-common-1.4.1/test/data/write_protected_file.txt create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_cache.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_changelog.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_configuration.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_date.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_decorators.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_deprecation.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_fileutils.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_graph.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_interface.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_modutils.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_pytest.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_registry.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_shellutils.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_table.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_taskqueue.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_testlib.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_textutils.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_tree.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_umessage.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_ureports_html.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_ureports_text.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/unittest_xmlutils.py create mode 100644 pymode/libs/logilab-common-1.4.1/test/utils.py delete mode 100644 pymode/libs/logilab/__init__.py delete mode 100644 pymode/libs/logilab/common/testlib.py delete mode 100644 pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/METADATA delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/RECORD delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/WHEEL delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/metadata.json delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt create mode 120000 pymode/libs/pylint delete mode 100644 pymode/libs/pylint/__init__.py delete mode 100644 pymode/libs/pylint/__main__.py delete mode 100644 pymode/libs/pylint/__pkginfo__.py delete mode 100644 pymode/libs/pylint/checkers/__init__.py delete mode 100644 pymode/libs/pylint/checkers/async.py delete mode 100644 pymode/libs/pylint/checkers/base.py delete mode 100644 pymode/libs/pylint/checkers/classes.py delete mode 100644 pymode/libs/pylint/checkers/design_analysis.py delete mode 100644 pymode/libs/pylint/checkers/exceptions.py delete mode 100644 pymode/libs/pylint/checkers/format.py delete mode 100644 pymode/libs/pylint/checkers/imports.py delete mode 100644 pymode/libs/pylint/checkers/logging.py delete mode 100644 pymode/libs/pylint/checkers/misc.py delete mode 100644 pymode/libs/pylint/checkers/newstyle.py delete mode 100644 pymode/libs/pylint/checkers/python3.py delete mode 100644 pymode/libs/pylint/checkers/raw_metrics.py delete mode 100644 pymode/libs/pylint/checkers/refactoring.py delete mode 100644 pymode/libs/pylint/checkers/similar.py delete mode 100644 pymode/libs/pylint/checkers/spelling.py delete mode 100644 pymode/libs/pylint/checkers/stdlib.py delete mode 100644 pymode/libs/pylint/checkers/strings.py delete mode 100644 pymode/libs/pylint/checkers/typecheck.py delete mode 100644 pymode/libs/pylint/checkers/utils.py delete mode 100644 pymode/libs/pylint/checkers/variables.py delete mode 100644 pymode/libs/pylint/config.py delete mode 100644 pymode/libs/pylint/epylint.py delete mode 100644 pymode/libs/pylint/exceptions.py delete mode 100644 pymode/libs/pylint/extensions/_check_docs_utils.py delete mode 100644 pymode/libs/pylint/extensions/bad_builtin.py delete mode 100644 pymode/libs/pylint/extensions/check_docs.py delete mode 100644 pymode/libs/pylint/extensions/check_elif.py delete mode 100644 pymode/libs/pylint/extensions/comparetozero.py delete mode 100644 pymode/libs/pylint/extensions/docparams.py delete mode 100644 pymode/libs/pylint/extensions/docstyle.py delete mode 100644 pymode/libs/pylint/extensions/emptystring.py delete mode 100644 pymode/libs/pylint/extensions/mccabe.py delete mode 100644 pymode/libs/pylint/extensions/overlapping_exceptions.py delete mode 100644 pymode/libs/pylint/extensions/redefined_variable_type.py delete mode 100644 pymode/libs/pylint/graph.py delete mode 100644 pymode/libs/pylint/interfaces.py delete mode 100644 pymode/libs/pylint/lint.py delete mode 100644 pymode/libs/pylint/pyreverse/__init__.py delete mode 100644 pymode/libs/pylint/pyreverse/diadefslib.py delete mode 100644 pymode/libs/pylint/pyreverse/diagrams.py delete mode 100644 pymode/libs/pylint/pyreverse/inspector.py delete mode 100644 pymode/libs/pylint/pyreverse/main.py delete mode 100644 pymode/libs/pylint/pyreverse/utils.py delete mode 100644 pymode/libs/pylint/pyreverse/vcgutils.py delete mode 100644 pymode/libs/pylint/pyreverse/writer.py delete mode 100644 pymode/libs/pylint/reporters/__init__.py delete mode 100644 pymode/libs/pylint/reporters/json.py delete mode 100644 pymode/libs/pylint/reporters/text.py delete mode 100644 pymode/libs/pylint/reporters/ureports/__init__.py delete mode 100644 pymode/libs/pylint/reporters/ureports/nodes.py delete mode 100644 pymode/libs/pylint/reporters/ureports/text_writer.py delete mode 100644 pymode/libs/pylint/testutils.py delete mode 100644 pymode/libs/pylint/utils.py create mode 120000 pymode/libs/rope delete mode 100644 pymode/libs/rope/__init__.py delete mode 100644 pymode/libs/rope/base/__init__.py delete mode 100644 pymode/libs/rope/base/arguments.py delete mode 100644 pymode/libs/rope/base/ast.py delete mode 100644 pymode/libs/rope/base/astutils.py delete mode 100644 pymode/libs/rope/base/builtins.py delete mode 100644 pymode/libs/rope/base/change.py delete mode 100644 pymode/libs/rope/base/codeanalyze.py delete mode 100644 pymode/libs/rope/base/default_config.py delete mode 100644 pymode/libs/rope/base/evaluate.py delete mode 100644 pymode/libs/rope/base/exceptions.py delete mode 100644 pymode/libs/rope/base/fscommands.py delete mode 100644 pymode/libs/rope/base/history.py delete mode 100644 pymode/libs/rope/base/libutils.py delete mode 100644 pymode/libs/rope/base/oi/__init__.py delete mode 100644 pymode/libs/rope/base/oi/doa.py delete mode 100644 pymode/libs/rope/base/oi/memorydb.py delete mode 100644 pymode/libs/rope/base/oi/objectdb.py delete mode 100644 pymode/libs/rope/base/oi/objectinfo.py delete mode 100644 pymode/libs/rope/base/oi/runmod.py delete mode 100644 pymode/libs/rope/base/oi/soa.py delete mode 100644 pymode/libs/rope/base/oi/soi.py delete mode 100644 pymode/libs/rope/base/oi/transform.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/evaluate.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/factory.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/interfaces.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/providers/composite.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/providers/docstrings.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/providers/inheritance.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/providers/interfaces.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/providers/numpydocstrings.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/providers/pep0484_type_comments.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/resolvers/composite.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/resolvers/interfaces.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/resolvers/types.py delete mode 100644 pymode/libs/rope/base/oi/type_hinting/utils.py delete mode 100644 pymode/libs/rope/base/prefs.py delete mode 100644 pymode/libs/rope/base/project.py delete mode 100644 pymode/libs/rope/base/pycore.py delete mode 100644 pymode/libs/rope/base/pynames.py delete mode 100644 pymode/libs/rope/base/pynamesdef.py delete mode 100644 pymode/libs/rope/base/pyobjects.py delete mode 100644 pymode/libs/rope/base/pyobjectsdef.py delete mode 100644 pymode/libs/rope/base/pyscopes.py delete mode 100644 pymode/libs/rope/base/resourceobserver.py delete mode 100644 pymode/libs/rope/base/resources.py delete mode 100644 pymode/libs/rope/base/simplify.py delete mode 100644 pymode/libs/rope/base/stdmods.py delete mode 100644 pymode/libs/rope/base/taskhandle.py delete mode 100644 pymode/libs/rope/base/utils/__init__.py delete mode 100644 pymode/libs/rope/base/utils/datastructures.py delete mode 100644 pymode/libs/rope/base/utils/pycompat.py delete mode 100644 pymode/libs/rope/base/worder.py delete mode 100644 pymode/libs/rope/contrib/__init__.py delete mode 100644 pymode/libs/rope/contrib/autoimport.py delete mode 100644 pymode/libs/rope/contrib/changestack.py delete mode 100644 pymode/libs/rope/contrib/codeassist.py delete mode 100644 pymode/libs/rope/contrib/finderrors.py delete mode 100644 pymode/libs/rope/contrib/findit.py delete mode 100644 pymode/libs/rope/contrib/fixmodnames.py delete mode 100644 pymode/libs/rope/contrib/fixsyntax.py delete mode 100644 pymode/libs/rope/contrib/generate.py delete mode 100644 pymode/libs/rope/refactor/__init__.py delete mode 100644 pymode/libs/rope/refactor/change_signature.py delete mode 100644 pymode/libs/rope/refactor/encapsulate_field.py delete mode 100644 pymode/libs/rope/refactor/extract.py delete mode 100644 pymode/libs/rope/refactor/functionutils.py delete mode 100644 pymode/libs/rope/refactor/importutils/__init__.py delete mode 100644 pymode/libs/rope/refactor/importutils/actions.py delete mode 100644 pymode/libs/rope/refactor/importutils/importinfo.py delete mode 100644 pymode/libs/rope/refactor/importutils/module_imports.py delete mode 100644 pymode/libs/rope/refactor/inline.py delete mode 100644 pymode/libs/rope/refactor/introduce_factory.py delete mode 100644 pymode/libs/rope/refactor/introduce_parameter.py delete mode 100644 pymode/libs/rope/refactor/localtofield.py delete mode 100644 pymode/libs/rope/refactor/method_object.py delete mode 100644 pymode/libs/rope/refactor/move.py delete mode 100644 pymode/libs/rope/refactor/multiproject.py delete mode 100644 pymode/libs/rope/refactor/occurrences.py delete mode 100644 pymode/libs/rope/refactor/patchedast.py delete mode 100644 pymode/libs/rope/refactor/rename.py delete mode 100644 pymode/libs/rope/refactor/restructure.py delete mode 100644 pymode/libs/rope/refactor/similarfinder.py delete mode 100644 pymode/libs/rope/refactor/sourceutils.py delete mode 100644 pymode/libs/rope/refactor/suites.py delete mode 100644 pymode/libs/rope/refactor/topackage.py delete mode 100644 pymode/libs/rope/refactor/usefunction.py delete mode 100644 pymode/libs/rope/refactor/wildcards.py create mode 100644 submodules/astroid/.coveragerc create mode 100644 submodules/astroid/.github/ISSUE_TEMPLATE.md create mode 100644 submodules/astroid/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 submodules/astroid/.gitignore create mode 100644 submodules/astroid/.travis.yml create mode 100644 submodules/astroid/COPYING create mode 100644 submodules/astroid/COPYING.LESSER create mode 100644 submodules/astroid/ChangeLog create mode 100644 submodules/astroid/MANIFEST.in create mode 100644 submodules/astroid/README.rst create mode 100644 submodules/astroid/appveyor.yml create mode 100644 submodules/astroid/appveyor/install.ps1 rename {pymode/libs => submodules/astroid}/astroid/__init__.py (70%) create mode 100644 submodules/astroid/astroid/__pkginfo__.py create mode 100644 submodules/astroid/astroid/_ast.py rename {pymode/libs => submodules/astroid}/astroid/arguments.py (98%) rename {pymode/libs => submodules/astroid}/astroid/as_string.py (99%) rename {pymode/libs => submodules/astroid}/astroid/bases.py (83%) create mode 100644 submodules/astroid/astroid/brain/brain_attrs.py rename {pymode/libs => submodules/astroid}/astroid/brain/brain_builtin_inference.py (58%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_collections.py (51%) create mode 100644 submodules/astroid/astroid/brain/brain_curses.py rename {pymode/libs => submodules/astroid}/astroid/brain/brain_dateutil.py (100%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_fstrings.py (97%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_functools.py (96%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_gi.py (100%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_hashlib.py (78%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_io.py (100%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_mechanize.py (100%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_multiprocessing.py (100%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_namedtuple_enum.py (55%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_nose.py (100%) create mode 100644 submodules/astroid/astroid/brain/brain_numpy.py rename {pymode/libs => submodules/astroid}/astroid/brain/brain_pkg_resources.py (98%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_pytest.py (100%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_qt.py (68%) create mode 100644 submodules/astroid/astroid/brain/brain_random.py rename {pymode/libs => submodules/astroid}/astroid/brain/brain_re.py (100%) create mode 100644 submodules/astroid/astroid/brain/brain_six.py rename {pymode/libs => submodules/astroid}/astroid/brain/brain_ssl.py (100%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_subprocess.py (93%) rename {pymode/libs => submodules/astroid}/astroid/brain/brain_threading.py (100%) create mode 100644 submodules/astroid/astroid/brain/brain_typing.py create mode 100644 submodules/astroid/astroid/brain/brain_uuid.py rename {pymode/libs => submodules/astroid}/astroid/builder.py (88%) rename {pymode/libs => submodules/astroid}/astroid/context.py (53%) rename {pymode/libs => submodules/astroid}/astroid/decorators.py (93%) rename {pymode/libs => submodules/astroid}/astroid/exceptions.py (90%) rename {pymode/libs => submodules/astroid}/astroid/helpers.py (55%) rename {pymode/libs => submodules/astroid}/astroid/inference.py (87%) create mode 100644 submodules/astroid/astroid/interpreter/__init__.py create mode 100644 submodules/astroid/astroid/interpreter/_import/__init__.py rename {pymode/libs => submodules/astroid}/astroid/interpreter/_import/spec.py (94%) rename {pymode/libs => submodules/astroid}/astroid/interpreter/_import/util.py (76%) rename {pymode/libs => submodules/astroid}/astroid/interpreter/dunder_lookup.py (100%) rename {pymode/libs => submodules/astroid}/astroid/interpreter/objectmodel.py (86%) rename {pymode/libs => submodules/astroid}/astroid/manager.py (88%) rename {pymode/libs => submodules/astroid}/astroid/mixins.py (73%) rename {pymode/libs => submodules/astroid}/astroid/modutils.py (91%) create mode 100644 submodules/astroid/astroid/node_classes.py rename {pymode/libs => submodules/astroid}/astroid/nodes.py (70%) rename {pymode/libs => submodules/astroid}/astroid/objects.py (94%) rename {pymode/libs => submodules/astroid}/astroid/protocols.py (89%) rename {pymode/libs => submodules/astroid}/astroid/raw_building.py (97%) rename {pymode/libs => submodules/astroid}/astroid/rebuilder.py (84%) rename {pymode/libs => submodules/astroid}/astroid/scoped_nodes.py (59%) rename {pymode/libs => submodules/astroid}/astroid/test_utils.py (91%) create mode 100644 submodules/astroid/astroid/tests/__init__.py create mode 100644 submodules/astroid/astroid/tests/resources.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/MyPyPa-0.1.0-py2.5.zip create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/SSL1/Connection1.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/SSL1/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/absimp/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/absimp/sidepackage/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/absimp/string.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/absimport.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/all.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/appl/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/appl/myConnection.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/contribute_to_namespace/namespace_pep_420/submodule.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/descriptor_crash.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/email.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/find_test/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/find_test/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/find_test/module2.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/find_test/noendingnewline.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/find_test/nonregr.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/foogle/fax/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/foogle/fax/a.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/foogle_fax-0.12.5-py2.7-nspkg.pth create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/format.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/invalid_encoding.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/lmfp/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/lmfp/foo.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/module1abs/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/module1abs/core.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/module2.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/namespace_pep_420/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/noendingnewline.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/nonregr.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/notall.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/notamodule/file.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/package/absimport.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/package/hello.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/package/import_package_subpackage_module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/package/subpackage/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/package/subpackage/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkg_resources_1/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkg_resources_1/package/foo.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkg_resources_2/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkg_resources_2/package/bar.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkg_resources_3/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkg_resources_3/package/baz.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkgutil_1/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkgutil_1/package/foo.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkgutil_2/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkgutil_2/package/bar.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkgutil_3/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/path_pkgutil_3/package/baz.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/recursion.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/tmp__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/unicode_package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python2/data/unicode_package/core/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/MyPyPa-0.1.0-py2.5.zip create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/SSL1/Connection1.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/SSL1/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/absimp/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/absimp/sidepackage/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/absimp/string.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/absimport.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/all.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/appl/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/appl/myConnection.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/contribute_to_namespace/namespace_pep_420/submodule.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/descriptor_crash.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/email.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/find_test/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/find_test/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/find_test/module2.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/find_test/noendingnewline.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/find_test/nonregr.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/foogle/fax/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/foogle/fax/a.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/foogle_fax-0.12.5-py2.7-nspkg.pth create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/format.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/invalid_encoding.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/lmfp/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/lmfp/foo.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/module1abs/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/module1abs/core.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/module2.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/namespace_pep_420/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/noendingnewline.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/nonregr.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/notall.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/notamodule/file.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/package/absimport.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/package/hello.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/package/import_package_subpackage_module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/package/subpackage/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/package/subpackage/module.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkg_resources_1/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkg_resources_1/package/foo.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkg_resources_2/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkg_resources_2/package/bar.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkg_resources_3/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkg_resources_3/package/baz.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkgutil_1/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkgutil_1/package/foo.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkgutil_2/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkgutil_2/package/bar.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkgutil_3/package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/path_pkgutil_3/package/baz.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/recursion.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/tmp__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/unicode_package/__init__.py create mode 100644 submodules/astroid/astroid/tests/testdata/python3/data/unicode_package/core/__init__.py create mode 100644 submodules/astroid/astroid/tests/unittest_brain.py create mode 100644 submodules/astroid/astroid/tests/unittest_brain_numpy.py create mode 100644 submodules/astroid/astroid/tests/unittest_builder.py create mode 100644 submodules/astroid/astroid/tests/unittest_helpers.py create mode 100644 submodules/astroid/astroid/tests/unittest_inference.py create mode 100644 submodules/astroid/astroid/tests/unittest_lookup.py create mode 100644 submodules/astroid/astroid/tests/unittest_manager.py create mode 100644 submodules/astroid/astroid/tests/unittest_modutils.py create mode 100644 submodules/astroid/astroid/tests/unittest_nodes.py create mode 100644 submodules/astroid/astroid/tests/unittest_object_model.py create mode 100644 submodules/astroid/astroid/tests/unittest_objects.py create mode 100644 submodules/astroid/astroid/tests/unittest_protocols.py create mode 100644 submodules/astroid/astroid/tests/unittest_python3.py create mode 100644 submodules/astroid/astroid/tests/unittest_raw_building.py create mode 100644 submodules/astroid/astroid/tests/unittest_regrtest.py create mode 100644 submodules/astroid/astroid/tests/unittest_scoped_nodes.py create mode 100644 submodules/astroid/astroid/tests/unittest_transforms.py create mode 100644 submodules/astroid/astroid/tests/unittest_utils.py rename {pymode/libs => submodules/astroid}/astroid/transforms.py (90%) rename {pymode/libs => submodules/astroid}/astroid/util.py (82%) create mode 100644 submodules/astroid/debian.sid/control create mode 100755 submodules/astroid/debian.sid/rules create mode 100644 submodules/astroid/debian.sid/source/format create mode 100644 submodules/astroid/debian/changelog create mode 100644 submodules/astroid/debian/compat create mode 100644 submodules/astroid/debian/control create mode 100644 submodules/astroid/debian/copyright create mode 100644 submodules/astroid/debian/python-astroid.dirs create mode 100755 submodules/astroid/debian/rules create mode 100644 submodules/astroid/debian/watch create mode 100644 submodules/astroid/doc/Makefile create mode 100644 submodules/astroid/doc/api/astroid.exceptions.rst create mode 100644 submodules/astroid/doc/api/astroid.nodes.rst create mode 100644 submodules/astroid/doc/api/base_nodes.rst create mode 100644 submodules/astroid/doc/api/general.rst create mode 100644 submodules/astroid/doc/api/index.rst create mode 100644 submodules/astroid/doc/ast_objects.inv create mode 100644 submodules/astroid/doc/changelog.rst create mode 100644 submodules/astroid/doc/conf.py create mode 100644 submodules/astroid/doc/extending.rst create mode 100644 submodules/astroid/doc/index.rst create mode 100644 submodules/astroid/doc/inference.rst create mode 100644 submodules/astroid/doc/make.bat create mode 100644 submodules/astroid/doc/release.txt create mode 100644 submodules/astroid/doc/whatsnew.rst create mode 100644 submodules/astroid/pylintrc create mode 100644 submodules/astroid/pytest.ini create mode 100644 submodules/astroid/setup.cfg create mode 100644 submodules/astroid/setup.py create mode 100644 submodules/astroid/tox.ini create mode 160000 submodules/pylint create mode 160000 submodules/rope diff --git a/.gitmodules b/.gitmodules index 5a82bbf1..5963b5d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,3 +26,12 @@ path = submodules/pylama url = https://github.com/fmv1992/pylama ignore = dirty +[submodule "submodules/pylint"] + path = submodules/pylint + url = https://github.com/PyCQA/pylint +[submodule "submodules/rope"] + path = submodules/rope + url = https://github.com/python-rope/rope +[submodule "pymode/submodules/astroid"] + path = pymode/submodules/astroid + url = https://github.com/PyCQA/astroid diff --git a/pymode/libs/astroid b/pymode/libs/astroid new file mode 120000 index 00000000..492d8fbc --- /dev/null +++ b/pymode/libs/astroid @@ -0,0 +1 @@ +../../submodules/astroid/astroid \ No newline at end of file diff --git a/pymode/libs/astroid/__pkginfo__.py b/pymode/libs/astroid/__pkginfo__.py deleted file mode 100644 index 96c2dcba..00000000 --- a/pymode/libs/astroid/__pkginfo__.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""astroid packaging information""" - -from sys import version_info as py_version - -from pkg_resources import parse_version -from setuptools import __version__ as setuptools_version - -distname = 'astroid' - -modname = 'astroid' - -version = '1.5.3' -numversion = tuple(map(int, version.split('.'))) - -extras_require = {} -install_requires = ['lazy_object_proxy', 'six', 'wrapt'] - - -def has_environment_marker_range_operators_support(): - """Code extracted from 'pytest/setup.py' - https://github.com/pytest-dev/pytest/blob/7538680c/setup.py#L31 - - The first known release to support environment marker with range operators - it is 17.1, see: https://setuptools.readthedocs.io/en/latest/history.html#id113 - """ - return parse_version(setuptools_version) >= parse_version('17.1') - - -if has_environment_marker_range_operators_support(): - extras_require[':python_version<"3.4"'] = ['enum34>=1.1.3', 'singledispatch'] - extras_require[':python_version<"3.3"'] = ['backports.functools_lru_cache'] -else: - if py_version < (3, 4): - install_requires.extend(['enum34', 'singledispatch']) - if py_version < (3, 3): - install_requires.append('backports.functools_lru_cache') - - -# pylint: disable=redefined-builtin; why license is a builtin anyway? -license = 'LGPL' - -author = 'Python Code Quality Authority' -author_email = 'code-quality@python.org' -mailinglist = "mailto://%s" % author_email -web = 'https://github.com/PyCQA/astroid' - -description = "A abstract syntax tree for Python with inference support." - -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/pymode/libs/astroid/astpeephole.py b/pymode/libs/astroid/astpeephole.py deleted file mode 100644 index dde5dca1..00000000 --- a/pymode/libs/astroid/astpeephole.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Small AST optimizations.""" - -import _ast - -from astroid import nodes - - -__all__ = ('ASTPeepholeOptimizer', ) - - -try: - _TYPES = (_ast.Str, _ast.Bytes) -except AttributeError: - _TYPES = (_ast.Str, ) - - -class ASTPeepholeOptimizer(object): - """Class for applying small optimizations to generate new AST.""" - - def optimize_binop(self, node, parent=None): - """Optimize BinOps with string Const nodes on the lhs. - - This fixes an infinite recursion crash, where multiple - strings are joined using the addition operator. With a - sufficient number of such strings, astroid will fail - with a maximum recursion limit exceeded. The - function will return a Const node with all the strings - already joined. - Return ``None`` if no AST node can be obtained - through optimization. - """ - ast_nodes = [] - current = node - while isinstance(current, _ast.BinOp): - # lhs must be a BinOp with the addition operand. - if not isinstance(current.left, _ast.BinOp): - return - if (not isinstance(current.left.op, _ast.Add) - or not isinstance(current.op, _ast.Add)): - return - - # rhs must a str / bytes. - if not isinstance(current.right, _TYPES): - return - - ast_nodes.append(current.right.s) - current = current.left - - if (isinstance(current, _ast.BinOp) - and isinstance(current.left, _TYPES) - and isinstance(current.right, _TYPES)): - # Stop early if we are at the last BinOp in - # the operation - ast_nodes.append(current.right.s) - ast_nodes.append(current.left.s) - break - - if not ast_nodes: - return - - # If we have inconsistent types, bail out. - known = type(ast_nodes[0]) - if any(not isinstance(element, known) - for element in ast_nodes[1:]): - return - - value = known().join(reversed(ast_nodes)) - newnode = nodes.Const(value, node.lineno, node.col_offset, parent) - return newnode diff --git a/pymode/libs/astroid/brain/brain_numpy.py b/pymode/libs/astroid/brain/brain_numpy.py deleted file mode 100644 index 8acfe053..00000000 --- a/pymode/libs/astroid/brain/brain_numpy.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -"""Astroid hooks for numpy.""" - -import astroid - - -# TODO(cpopa): drop when understanding augmented assignments - -def numpy_core_transform(): - return astroid.parse(''' - from numpy.core import numeric - from numpy.core import fromnumeric - from numpy.core import defchararray - from numpy.core import records - from numpy.core import function_base - from numpy.core import machar - from numpy.core import getlimits - from numpy.core import shape_base - __all__ = (['char', 'rec', 'memmap', 'chararray'] + numeric.__all__ + - fromnumeric.__all__ + - records.__all__ + - function_base.__all__ + - machar.__all__ + - getlimits.__all__ + - shape_base.__all__) - ''') - - -def numpy_transform(): - return astroid.parse(''' - from numpy import core - from numpy import matrixlib as _mat - from numpy import lib - __all__ = ['add_newdocs', - 'ModuleDeprecationWarning', - 'VisibleDeprecationWarning', 'linalg', 'fft', 'random', - 'ctypeslib', 'ma', - '__version__', 'pkgload', 'PackageLoader', - 'show_config'] + core.__all__ + _mat.__all__ + lib.__all__ - - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'numpy.core', numpy_core_transform) -astroid.register_module_extender(astroid.MANAGER, 'numpy', numpy_transform) diff --git a/pymode/libs/astroid/brain/brain_six.py b/pymode/libs/astroid/brain/brain_six.py deleted file mode 100644 index f16a2938..00000000 --- a/pymode/libs/astroid/brain/brain_six.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -"""Astroid hooks for six module.""" - -import sys -from textwrap import dedent - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid.exceptions import AstroidBuildingError, InferenceError -from astroid import nodes - - -SIX_ADD_METACLASS = 'six.add_metaclass' - - -def _indent(text, prefix, predicate=None): - """Adds 'prefix' to the beginning of selected lines in 'text'. - - If 'predicate' is provided, 'prefix' will only be added to the lines - where 'predicate(line)' is True. If 'predicate' is not provided, - it will default to adding 'prefix' to all non-empty lines that do not - consist solely of whitespace characters. - """ - if predicate is None: - predicate = lambda line: line.strip() - - def prefixed_lines(): - for line in text.splitlines(True): - yield prefix + line if predicate(line) else line - return ''.join(prefixed_lines()) - - -if sys.version_info[0] == 2: - _IMPORTS_2 = """ - import BaseHTTPServer - import CGIHTTPServer - import SimpleHTTPServer - - from StringIO import StringIO - from cStringIO import StringIO as cStringIO - from UserDict import UserDict - from UserList import UserList - from UserString import UserString - - import __builtin__ as builtins - import thread as _thread - import dummy_thread as _dummy_thread - import ConfigParser as configparser - import copy_reg as copyreg - from itertools import (imap as map, - ifilter as filter, - ifilterfalse as filterfalse, - izip_longest as zip_longest, - izip as zip) - import htmlentitydefs as html_entities - import HTMLParser as html_parser - import httplib as http_client - import cookielib as http_cookiejar - import Cookie as http_cookies - import Queue as queue - import repr as reprlib - from pipes import quote as shlex_quote - import SocketServer as socketserver - import SimpleXMLRPCServer as xmlrpc_server - import xmlrpclib as xmlrpc_client - import _winreg as winreg - import robotparser as urllib_robotparser - import Tkinter as tkinter - import tkFileDialog as tkinter_tkfiledialog - - input = raw_input - intern = intern - range = xrange - xrange = xrange - reduce = reduce - reload_module = reload - - class UrllibParse(object): - import urlparse as _urlparse - import urllib as _urllib - ParseResult = _urlparse.ParseResult - SplitResult = _urlparse.SplitResult - parse_qs = _urlparse.parse_qs - parse_qsl = _urlparse.parse_qsl - urldefrag = _urlparse.urldefrag - urljoin = _urlparse.urljoin - urlparse = _urlparse.urlparse - urlsplit = _urlparse.urlsplit - urlunparse = _urlparse.urlunparse - urlunsplit = _urlparse.urlunsplit - quote = _urllib.quote - quote_plus = _urllib.quote_plus - unquote = _urllib.unquote - unquote_plus = _urllib.unquote_plus - urlencode = _urllib.urlencode - splitquery = _urllib.splitquery - splittag = _urllib.splittag - splituser = _urllib.splituser - uses_fragment = _urlparse.uses_fragment - uses_netloc = _urlparse.uses_netloc - uses_params = _urlparse.uses_params - uses_query = _urlparse.uses_query - uses_relative = _urlparse.uses_relative - - class UrllibError(object): - import urllib2 as _urllib2 - import urllib as _urllib - URLError = _urllib2.URLError - HTTPError = _urllib2.HTTPError - ContentTooShortError = _urllib.ContentTooShortError - - class DummyModule(object): - pass - - class UrllibRequest(object): - import urlparse as _urlparse - import urllib2 as _urllib2 - import urllib as _urllib - urlopen = _urllib2.urlopen - install_opener = _urllib2.install_opener - build_opener = _urllib2.build_opener - pathname2url = _urllib.pathname2url - url2pathname = _urllib.url2pathname - getproxies = _urllib.getproxies - Request = _urllib2.Request - OpenerDirector = _urllib2.OpenerDirector - HTTPDefaultErrorHandler = _urllib2.HTTPDefaultErrorHandler - HTTPRedirectHandler = _urllib2.HTTPRedirectHandler - HTTPCookieProcessor = _urllib2.HTTPCookieProcessor - ProxyHandler = _urllib2.ProxyHandler - BaseHandler = _urllib2.BaseHandler - HTTPPasswordMgr = _urllib2.HTTPPasswordMgr - HTTPPasswordMgrWithDefaultRealm = _urllib2.HTTPPasswordMgrWithDefaultRealm - AbstractBasicAuthHandler = _urllib2.AbstractBasicAuthHandler - HTTPBasicAuthHandler = _urllib2.HTTPBasicAuthHandler - ProxyBasicAuthHandler = _urllib2.ProxyBasicAuthHandler - AbstractDigestAuthHandler = _urllib2.AbstractDigestAuthHandler - HTTPDigestAuthHandler = _urllib2.HTTPDigestAuthHandler - ProxyDigestAuthHandler = _urllib2.ProxyDigestAuthHandler - HTTPHandler = _urllib2.HTTPHandler - HTTPSHandler = _urllib2.HTTPSHandler - FileHandler = _urllib2.FileHandler - FTPHandler = _urllib2.FTPHandler - CacheFTPHandler = _urllib2.CacheFTPHandler - UnknownHandler = _urllib2.UnknownHandler - HTTPErrorProcessor = _urllib2.HTTPErrorProcessor - urlretrieve = _urllib.urlretrieve - urlcleanup = _urllib.urlcleanup - proxy_bypass = _urllib.proxy_bypass - - urllib_parse = UrllibParse() - urllib_error = UrllibError() - urllib = DummyModule() - urllib.request = UrllibRequest() - urllib.parse = UrllibParse() - urllib.error = UrllibError() - """ -else: - _IMPORTS_3 = """ - import _io - cStringIO = _io.StringIO - filter = filter - from itertools import filterfalse - input = input - from sys import intern - map = map - range = range - from imp import reload as reload_module - from functools import reduce - from shlex import quote as shlex_quote - from io import StringIO - from collections import UserDict, UserList, UserString - xrange = range - zip = zip - from itertools import zip_longest - import builtins - import configparser - import copyreg - import _dummy_thread - import http.cookiejar as http_cookiejar - import http.cookies as http_cookies - import html.entities as html_entities - import html.parser as html_parser - import http.client as http_client - import http.server - BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server - import pickle as cPickle - import queue - import reprlib - import socketserver - import _thread - import winreg - import xmlrpc.server as xmlrpc_server - import xmlrpc.client as xmlrpc_client - import urllib.robotparser as urllib_robotparser - import email.mime.multipart as email_mime_multipart - import email.mime.nonmultipart as email_mime_nonmultipart - import email.mime.text as email_mime_text - import email.mime.base as email_mime_base - import urllib.parse as urllib_parse - import urllib.error as urllib_error - import tkinter - import tkinter.dialog as tkinter_dialog - import tkinter.filedialog as tkinter_filedialog - import tkinter.scrolledtext as tkinter_scrolledtext - import tkinter.simpledialog as tkinder_simpledialog - import tkinter.tix as tkinter_tix - import tkinter.ttk as tkinter_ttk - import tkinter.constants as tkinter_constants - import tkinter.dnd as tkinter_dnd - import tkinter.colorchooser as tkinter_colorchooser - import tkinter.commondialog as tkinter_commondialog - import tkinter.filedialog as tkinter_tkfiledialog - import tkinter.font as tkinter_font - import tkinter.messagebox as tkinter_messagebox - import urllib.request - import urllib.robotparser as urllib_robotparser - import urllib.parse as urllib_parse - import urllib.error as urllib_error - """ -if sys.version_info[0] == 2: - _IMPORTS = dedent(_IMPORTS_2) -else: - _IMPORTS = dedent(_IMPORTS_3) - - -def six_moves_transform(): - code = dedent(''' - class Moves(object): - {} - moves = Moves() - ''').format(_indent(_IMPORTS, " ")) - module = AstroidBuilder(MANAGER).string_build(code) - module.name = 'six.moves' - return module - - -def _six_fail_hook(modname): - if modname != 'six.moves': - raise AstroidBuildingError(modname=modname) - module = AstroidBuilder(MANAGER).string_build(_IMPORTS) - module.name = 'six.moves' - return module - -def transform_six_add_metaclass(node): - """Check if the given class node is decorated with *six.add_metaclass* - - If so, inject its argument as the metaclass of the underlying class. - """ - if not node.decorators: - return - - for decorator in node.decorators.nodes: - if not isinstance(decorator, nodes.Call): - continue - - try: - func = next(decorator.func.infer()) - except InferenceError: - continue - if func.qname() == SIX_ADD_METACLASS and decorator.args: - metaclass = decorator.args[0] - node._metaclass = metaclass - return node - - -register_module_extender(MANAGER, 'six', six_moves_transform) -register_module_extender(MANAGER, 'requests.packages.urllib3.packages.six', - six_moves_transform) -MANAGER.register_failed_import_hook(_six_fail_hook) -MANAGER.register_transform(nodes.ClassDef, transform_six_add_metaclass) diff --git a/pymode/libs/astroid/brain/brain_typing.py b/pymode/libs/astroid/brain/brain_typing.py deleted file mode 100644 index 711066cb..00000000 --- a/pymode/libs/astroid/brain/brain_typing.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) 2016 David Euresti - -"""Astroid hooks for typing.py support.""" -import textwrap - -from astroid import ( - MANAGER, UseInferenceDefault, extract_node, inference_tip, - nodes, InferenceError) -from astroid.nodes import List, Tuple - - -TYPING_NAMEDTUPLE_BASENAMES = { - 'NamedTuple', - 'typing.NamedTuple' -} - - -def infer_typing_namedtuple(node, context=None): - """Infer a typing.NamedTuple(...) call.""" - # This is essentially a namedtuple with different arguments - # so we extract the args and infer a named tuple. - try: - func = next(node.func.infer()) - except InferenceError: - raise UseInferenceDefault - - if func.qname() != 'typing.NamedTuple': - raise UseInferenceDefault - - if len(node.args) != 2: - raise UseInferenceDefault - - if not isinstance(node.args[1], (List, Tuple)): - raise UseInferenceDefault - - names = [] - for elt in node.args[1].elts: - if not isinstance(elt, (List, Tuple)): - raise UseInferenceDefault - if len(elt.elts) != 2: - raise UseInferenceDefault - names.append(elt.elts[0].as_string()) - - typename = node.args[0].as_string() - node = extract_node('namedtuple(%(typename)s, (%(fields)s,)) ' % - {'typename': typename, 'fields': ",".join(names)}) - return node.infer(context=context) - - -def infer_typing_namedtuple_class(node, context=None): - """Infer a subclass of typing.NamedTuple""" - - # Check if it has the corresponding bases - if not set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES: - raise UseInferenceDefault - - annassigns_fields = [ - annassign.target.name for annassign in node.body - if isinstance(annassign, nodes.AnnAssign) - ] - code = textwrap.dedent(''' - from collections import namedtuple - namedtuple({typename!r}, {fields!r}) - ''').format( - typename=node.name, - fields=",".join(annassigns_fields) - ) - node = extract_node(code) - return node.infer(context=context) - - -def looks_like_typing_namedtuple(node): - func = node.func - if isinstance(func, nodes.Attribute): - return func.attrname == 'NamedTuple' - if isinstance(func, nodes.Name): - return func.name == 'NamedTuple' - return False - - -MANAGER.register_transform( - nodes.Call, - inference_tip(infer_typing_namedtuple), - looks_like_typing_namedtuple -) -MANAGER.register_transform( - nodes.ClassDef, - inference_tip(infer_typing_namedtuple_class) -) diff --git a/pymode/libs/astroid/node_classes.py b/pymode/libs/astroid/node_classes.py deleted file mode 100644 index b4e71874..00000000 --- a/pymode/libs/astroid/node_classes.py +++ /dev/null @@ -1,2082 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014, 2016 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2016 Jakub Wilk - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Module for some node classes. More nodes in scoped_nodes.py -""" - -import abc -import pprint -import warnings -try: - from functools import singledispatch as _singledispatch -except ImportError: - from singledispatch import singledispatch as _singledispatch - -import six - -from astroid import as_string -from astroid import bases -from astroid import context as contextmod -from astroid import decorators -from astroid import exceptions -from astroid import manager -from astroid import mixins -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ -MANAGER = manager.AstroidManager() - - -@decorators.raise_if_nothing_inferred -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: - if elt is util.Uninferable: - yield elt - continue - for inferred_elt in unpack_infer(elt, context): - yield inferred_elt - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=stmt, context=context)) - # if inferred is a final node, return it and stop - inferred = next(stmt.infer(context)) - if inferred is stmt: - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=stmt, context=context)) - # else, infer recursively, except Uninferable object that should be returned as is - for inferred in stmt.infer(context): - if inferred is util.Uninferable: - yield inferred - else: - for inf_inf in unpack_infer(inferred, context): - yield inf_inf - raise StopIteration(dict(node=stmt, context=context)) - - -def are_exclusive(stmt1, stmt2, exceptions=None): # pylint: disable=redefined-outer-name - """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: - first_in_body_caught_by_handlers = ( - c2attr == 'handlers' - and c1attr == 'body' - and previous.catch(exceptions)) - second_in_body_caught_by_handlers = ( - c2attr == 'body' - and c1attr == 'handlers' - and children[node].catch(exceptions)) - first_in_else_other_in_handlers = ( - c2attr == 'handlers' and c1attr == 'orelse') - second_in_else_other_in_handlers = ( - c2attr == 'orelse' and c1attr == 'handlers') - if any((first_in_body_caught_by_handlers, - second_in_body_caught_by_handlers, - first_in_else_other_in_handlers, - second_in_else_other_in_handlers)): - return True - elif c2attr == 'handlers' and c1attr == 'handlers': - return previous is not children[node] - return False - previous = node - node = node.parent - return False - - -# getitem() helpers. - -_SLICE_SENTINEL = object() - - -def _slice_value(index, context=None): - """Get the value of the given slice index.""" - - if isinstance(index, Const): - if isinstance(index.value, (int, type(None))): - return index.value - elif index is None: - return None - else: - # Try to infer what the index actually is. - # Since we can't return all the possible values, - # we'll stop at the first possible value. - try: - inferred = next(index.infer(context=context)) - except exceptions.InferenceError: - pass - else: - if isinstance(inferred, Const): - if isinstance(inferred.value, (int, type(None))): - return inferred.value - - # Use a sentinel, because None can be a valid - # value that this function can return, - # as it is the case for unspecified bounds. - return _SLICE_SENTINEL - - -def _infer_slice(node, context=None): - lower = _slice_value(node.lower, context) - upper = _slice_value(node.upper, context) - step = _slice_value(node.step, context) - if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)): - return slice(lower, upper, step) - - raise exceptions.AstroidTypeError( - message='Could not infer slice used in subscript', - node=node, index=node.parent, context=context) - - -def _container_getitem(instance, elts, index, context=None): - """Get a slice or an item, using the given *index*, for the given sequence.""" - try: - if isinstance(index, Slice): - index_slice = _infer_slice(index, context=context) - new_cls = instance.__class__() - new_cls.elts = elts[index_slice] - new_cls.parent = instance.parent - return new_cls - elif isinstance(index, Const): - return elts[index.value] - except IndexError: - util.reraise(exceptions.AstroidIndexError( - message='Index {index!s} out of range', - node=instance, index=index, context=context)) - except TypeError as exc: - util.reraise(exceptions.AstroidTypeError( - message='Type error {error!r}', error=exc, - node=instance, index=index, context=context)) - - raise exceptions.AstroidTypeError( - 'Could not use %s as subscript index' % index - ) - - -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 FunctionDef nodes - # attributes below are set by the builder module or by raw factories - lineno = None - col_offset = None - # parent node in the tree - parent = None - # attributes containing child node(s) redefined in most concrete classes: - _astroid_fields = () - # attributes containing non-nodes: - _other_fields = () - # attributes containing AST-dependent fields: - _other_other_fields = () - # instance specific inference function infer(node, context) - _explicit_inference = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.lineno = lineno - self.col_offset = col_offset - self.parent = parent - - def infer(self, context=None, **kwargs): - """main interface to the interface system, return a generator on inferred - 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: - # pylint: disable=not-callable - return self._explicit_inference(self, context, **kwargs) - except exceptions.UseInferenceDefault: - pass - - if not context: - return self._infer(context, **kwargs) - - key = (self, context.lookupname, - context.callcontext, context.boundnode) - if key in context.inferred: - return iter(context.inferred[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): - rname = self._repr_name() - cname = type(self).__name__ - if rname: - string = '%(cname)s.%(rname)s(%(fields)s)' - alignment = len(cname) + len(rname) + 2 - else: - string = '%(cname)s(%(fields)s)' - alignment = len(cname) + 1 - result = [] - for field in self._other_fields + self._astroid_fields: - value = getattr(self, field) - width = 80 - len(field) - alignment - lines = pprint.pformat(value, indent=2, - width=width).splitlines(True) - - inner = [lines[0]] - for line in lines[1:]: - inner.append(' ' * alignment + line) - result.append('%s=%s' % (field, ''.join(inner))) - - return string % {'cname': cname, - 'rname': rname, - 'fields': (',\n' + ' ' * alignment).join(result)} - - def __repr__(self): - rname = self._repr_name() - if rname: - string = '<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>' - else: - string = '<%(cname)s l.%(lineno)s at 0x%(id)x>' - return string % {'cname': type(self).__name__, - 'rname': rname, - 'lineno': self.fromlineno, - 'id': 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 isinstance(attr, (list, tuple)): - return attr[-1] - - 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, FunctionDef or - ClassDef) - - """ - return self.parent.frame() - - def scope(self): - """return the first node defining a new scope (i.e. Module, - FunctionDef, ClassDef, 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 - - msg = 'Could not find %s in %s\'s children' - raise exceptions.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 exceptions.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 - - @decorators.cachedproperty - def fromlineno(self): - if self.lineno is None: - return self._fixed_source_line() - - return self.lineno - - @decorators.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 - - return lastchild.tolineno - - 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 ImportFrom, 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 exceptions.InferenceError('No inference function for {node!r}.', - node=self, context=context) - - def inferred(self): - '''return list of inferred values for a more simple inference usage''' - return list(self.infer()) - - def infered(self): - warnings.warn('%s.infered() is deprecated and slated for removal ' - 'in astroid 2.0, use %s.inferred() instead.' - % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.inferred() - - def instantiate_class(self): - """instantiate a node if it is a ClassDef 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): - return as_string.to_code(self) - - def repr_tree(self, ids=False, include_linenos=False, - ast_state=False, indent=' ', max_depth=0, max_width=80): - """Returns a string representation of the AST from this node. - - :param ids: If true, includes the ids with the node type names. - - :param include_linenos: If true, includes the line numbers and - column offsets. - - :param ast_state: If true, includes information derived from - the whole AST like local and global variables. - - :param indent: A string to use to indent the output string. - - :param max_depth: If set to a positive integer, won't return - nodes deeper than max_depth in the string. - - :param max_width: Only positive integer values are valid, the - default is 80. Attempts to format the output string to stay - within max_width characters, but can exceed it under some - circumstances. - """ - @_singledispatch - def _repr_tree(node, result, done, cur_indent='', depth=1): - """Outputs a representation of a non-tuple/list, non-node that's - contained within an AST, including strings. - """ - lines = pprint.pformat(node, - width=max(max_width - len(cur_indent), - 1)).splitlines(True) - result.append(lines[0]) - result.extend([cur_indent + line for line in lines[1:]]) - return len(lines) != 1 - - # pylint: disable=unused-variable; doesn't understand singledispatch - @_repr_tree.register(tuple) - @_repr_tree.register(list) - def _repr_seq(node, result, done, cur_indent='', depth=1): - """Outputs a representation of a sequence that's contained within an AST.""" - cur_indent += indent - result.append('[') - if not node: - broken = False - elif len(node) == 1: - broken = _repr_tree(node[0], result, done, cur_indent, depth) - elif len(node) == 2: - broken = _repr_tree(node[0], result, done, cur_indent, depth) - if not broken: - result.append(', ') - else: - result.append(',\n') - result.append(cur_indent) - broken = (_repr_tree(node[1], result, done, cur_indent, depth) - or broken) - else: - result.append('\n') - result.append(cur_indent) - for child in node[:-1]: - _repr_tree(child, result, done, cur_indent, depth) - result.append(',\n') - result.append(cur_indent) - _repr_tree(node[-1], result, done, cur_indent, depth) - broken = True - result.append(']') - return broken - - # pylint: disable=unused-variable; doesn't understand singledispatch - @_repr_tree.register(NodeNG) - def _repr_node(node, result, done, cur_indent='', depth=1): - """Outputs a strings representation of an astroid node.""" - if node in done: - result.append(indent + ' max_depth: - result.append('...') - return False - depth += 1 - cur_indent += indent - if ids: - result.append('%s<0x%x>(\n' % (type(node).__name__, id(node))) - else: - result.append('%s(' % type(node).__name__) - fields = [] - if include_linenos: - fields.extend(('lineno', 'col_offset')) - fields.extend(node._other_fields) - fields.extend(node._astroid_fields) - if ast_state: - fields.extend(node._other_other_fields) - if not fields: - broken = False - elif len(fields) == 1: - result.append('%s=' % fields[0]) - broken = _repr_tree(getattr(node, fields[0]), result, done, - cur_indent, depth) - else: - result.append('\n') - result.append(cur_indent) - for field in fields[:-1]: - result.append('%s=' % field) - _repr_tree(getattr(node, field), result, done, cur_indent, - depth) - result.append(',\n') - result.append(cur_indent) - result.append('%s=' % fields[-1]) - _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, - depth) - broken = True - result.append(')') - return broken - - result = [] - _repr_tree(self, result, set()) - return ''.join(result) - - def bool_value(self): - """Determine the bool value of this node - - The boolean value of a node can have three - possible values: - - * False. For instance, empty data structures, - False, empty strings, instances which return - explicitly False from the __nonzero__ / __bool__ - method. - * True. Most of constructs are True by default: - classes, functions, modules etc - * Uninferable: the inference engine is uncertain of the - node's value. - """ - return util.Uninferable - - -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] - - - -@six.add_metaclass(abc.ABCMeta) -class _BaseContainer(mixins.ParentAssignTypeMixin, - NodeNG, bases.Instance): - """Base class for Set, FrozenSet, Tuple and List.""" - - _astroid_fields = ('elts',) - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.elts = [] - super(_BaseContainer, self).__init__(lineno, col_offset, parent) - - def postinit(self, elts): - self.elts = elts - - @classmethod - def from_constants(cls, elts=None): - node = cls() - if elts is None: - node.elts = [] - else: - node.elts = [const_factory(e) for e in elts] - return node - - def itered(self): - return self.elts - - def bool_value(self): - return bool(self.elts) - - @abc.abstractmethod - def pytype(self): - pass - - -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): - """inferred lookup - - return an iterator on inferred values of the statements returned by - the lookup method - """ - frame, stmts = self.lookup(name) - context = contextmod.InferenceContext() - return bases._infer_stmts(stmts, context, 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() - 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, 'assign_type'), (node, node.scope(), - node.scope().locals) - assign_type = node.assign_type() - if node.has_base(self): - break - - _stmts, done = assign_type._get_filtered_stmts(self, node, _stmts, mystmt) - if done: - break - - optional_assign = assign_type.optional_assign - if optional_assign and assign_type.parent_of(self): - # we are inside a loop, loop var assignment is hiding previous - # assignment - _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].assign_type().parent_of(assign_type): - # both statements are not at the same block level - continue - # if currently visited node is following previously considered - # assignment 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 assignments 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, AssignName): - 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 AssignName(LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG): - """class representing an AssignName node""" - _other_fields = ('name',) - - def __init__(self, name=None, lineno=None, col_offset=None, parent=None): - self.name = name - super(AssignName, self).__init__(lineno, col_offset, parent) - - -class DelName(LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG): - """class representing a DelName node""" - _other_fields = ('name',) - - def __init__(self, name=None, lineno=None, col_offset=None, parent=None): - self.name = name - super(DelName, self).__init__(lineno, col_offset, parent) - - -class Name(LookupMixIn, NodeNG): - """class representing a Name node""" - _other_fields = ('name',) - - def __init__(self, name=None, lineno=None, col_offset=None, parent=None): - self.name = name - super(Name, self).__init__(lineno, col_offset, parent) - - -class Arguments(mixins.AssignTypeMixin, NodeNG): - """class representing an Arguments node""" - if six.PY3: - # 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', 'kwonlyargs_annotations') - varargannotation = None - kwargannotation = None - else: - _astroid_fields = ('args', 'defaults', 'kwonlyargs', 'kw_defaults') - _other_fields = ('vararg', 'kwarg') - - def __init__(self, vararg=None, kwarg=None, parent=None): - super(Arguments, self).__init__(parent=parent) - self.vararg = vararg - self.kwarg = kwarg - self.args = [] - self.defaults = [] - self.kwonlyargs = [] - self.kw_defaults = [] - self.annotations = [] - self.kwonlyargs_annotations = [] - - def postinit(self, args, defaults, kwonlyargs, kw_defaults, - annotations, - kwonlyargs_annotations=None, - varargannotation=None, - kwargannotation=None): - self.args = args - self.defaults = defaults - self.kwonlyargs = kwonlyargs - self.kw_defaults = kw_defaults - self.annotations = annotations - self.kwonlyargs_annotations = kwonlyargs_annotations - self.varargannotation = varargannotation - self.kwargannotation = kwargannotation - - def _infer_name(self, frame, name): - if self.parent is frame: - return name - return None - - @decorators.cachedproperty - def fromlineno(self): - lineno = super(Arguments, self).fromlineno - return max(lineno, self.parent.fromlineno or 0) - - def format_args(self): - """return arguments formatted as string""" - result = [] - if self.args: - result.append( - _format_args(self.args, self.defaults, - getattr(self, 'annotations', None)) - ) - if self.vararg: - result.append('*%s' % self.vararg) - if self.kwonlyargs: - if not self.vararg: - result.append('*') - result.append(_format_args( - self.kwonlyargs, - self.kw_defaults, - self.kwonlyargs_annotations - )) - if self.kwarg: - result.append('**%s' % self.kwarg) - 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 exceptions.NoDefault(func=self.parent, name=argname) - - 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 or - self.kwonlyargs and _find_arg(name, self.kwonlyargs, 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, annotations=None): - values = [] - if args is None: - return '' - if annotations is None: - annotations = [] - if defaults is not None: - default_offset = len(args) - len(defaults) - packed = six.moves.zip_longest(args, annotations) - for i, (arg, annotation) in enumerate(packed): - if isinstance(arg, Tuple): - values.append('(%s)' % _format_args(arg.elts)) - else: - argname = arg.name - if annotation is not None: - argname += ':' + annotation.as_string() - values.append(argname) - - 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 AssignAttr(mixins.ParentAssignTypeMixin, NodeNG): - """class representing an AssignAttr node""" - _astroid_fields = ('expr',) - _other_fields = ('attrname',) - expr = None - - def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): - self.attrname = attrname - super(AssignAttr, self).__init__(lineno, col_offset, parent) - - def postinit(self, expr=None): - self.expr = expr - - -class Assert(Statement): - """class representing an Assert node""" - _astroid_fields = ('test', 'fail',) - test = None - fail = None - - def postinit(self, test=None, fail=None): - self.fail = fail - self.test = test - - -class Assign(mixins.AssignTypeMixin, Statement): - """class representing an Assign node""" - _astroid_fields = ('targets', 'value',) - targets = None - value = None - - def postinit(self, targets=None, value=None): - self.targets = targets - self.value = value - - -class AnnAssign(mixins.AssignTypeMixin, Statement): - """Class representing an AnnAssign node""" - - _astroid_fields = ('target', 'annotation', 'value',) - _other_fields = ('simple',) - target = None - annotation = None - value = None - simple = None - - def postinit(self, target, annotation, simple, value=None): - self.target = target - self.annotation = annotation - self.value = value - self.simple = simple - - -class AugAssign(mixins.AssignTypeMixin, Statement): - """class representing an AugAssign node""" - _astroid_fields = ('target', 'value') - _other_fields = ('op',) - target = None - value = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(AugAssign, self).__init__(lineno, col_offset, parent) - - def postinit(self, target=None, value=None): - self.target = target - self.value = value - - # This is set by inference.py - def _infer_augassign(self, context=None): - raise NotImplementedError - - def type_errors(self, context=None): - """Return a list of TypeErrors which can occur during inference. - - Each TypeError is represented by a :class:`BadBinaryOperationMessage`, - which holds the original exception. - """ - try: - results = self._infer_augassign(context=context) - return [result for result in results - if isinstance(result, util.BadBinaryOperationMessage)] - except exceptions.InferenceError: - return [] - - -class Repr(NodeNG): - """class representing a Repr node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class BinOp(NodeNG): - """class representing a BinOp node""" - _astroid_fields = ('left', 'right') - _other_fields = ('op',) - left = None - right = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(BinOp, self).__init__(lineno, col_offset, parent) - - def postinit(self, left=None, right=None): - self.left = left - self.right = right - - # This is set by inference.py - def _infer_binop(self, context=None): - raise NotImplementedError - - def type_errors(self, context=None): - """Return a list of TypeErrors which can occur during inference. - - Each TypeError is represented by a :class:`BadBinaryOperationMessage`, - which holds the original exception. - """ - try: - results = self._infer_binop(context=context) - return [result for result in results - if isinstance(result, util.BadBinaryOperationMessage)] - except exceptions.InferenceError: - return [] - - -class BoolOp(NodeNG): - """class representing a BoolOp node""" - _astroid_fields = ('values',) - _other_fields = ('op',) - values = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(BoolOp, self).__init__(lineno, col_offset, parent) - - def postinit(self, values=None): - self.values = values - - -class Break(Statement): - """class representing a Break node""" - - -class Call(NodeNG): - """class representing a Call node""" - _astroid_fields = ('func', 'args', 'keywords') - func = None - args = None - keywords = None - - def postinit(self, func=None, args=None, keywords=None): - self.func = func - self.args = args - self.keywords = keywords - - @property - def starargs(self): - args = self.args or [] - return [arg for arg in args if isinstance(arg, Starred)] - - @property - def kwargs(self): - keywords = self.keywords or [] - return [keyword for keyword in keywords if keyword.arg is None] - - -class Compare(NodeNG): - """class representing a Compare node""" - _astroid_fields = ('left', 'ops',) - left = None - ops = None - - def postinit(self, left=None, ops=None): - self.left = left - self.ops = ops - - 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') - _other_fields = ('is_async',) - target = None - iter = None - ifs = None - is_async = None - - def __init__(self, parent=None): - super(Comprehension, self).__init__() - self.parent = parent - - # pylint: disable=redefined-builtin; same name as builtin ast module. - def postinit(self, target=None, iter=None, ifs=None, is_async=None): - self.target = target - self.iter = iter - self.ifs = ifs - self.is_async = is_async - - optional_assign = True - def assign_type(self): - return self - - def ass_type(self): - warnings.warn('%s.ass_type() is deprecated and slated for removal' - 'in astroid 2.0, use %s.assign_type() instead.' - % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.assign_type() - - 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, bases.Instance): - """represent a constant node like num, str, bool, None, bytes""" - _other_fields = ('value',) - - def __init__(self, value, lineno=None, col_offset=None, parent=None): - self.value = value - super(Const, self).__init__(lineno, col_offset, parent) - - def getitem(self, index, context=None): - if isinstance(index, Const): - index_value = index.value - elif isinstance(index, Slice): - index_value = _infer_slice(index, context=context) - - else: - raise exceptions.AstroidTypeError( - 'Could not use type {} as subscript index'.format(type(index)) - ) - - try: - if isinstance(self.value, six.string_types): - return Const(self.value[index_value]) - if isinstance(self.value, bytes) and six.PY3: - # Bytes aren't instances of six.string_types - # on Python 3. Also, indexing them should return - # integers. - return Const(self.value[index_value]) - except IndexError as exc: - util.reraise(exceptions.AstroidIndexError( - message='Index {index!r} out of range', error=exc, - node=self, index=index, context=context)) - except TypeError as exc: - util.reraise(exceptions.AstroidTypeError( - message='Type error {error!r}', error=exc, - node=self, index=index, context=context)) - - raise exceptions.AstroidTypeError( - '%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() - - def bool_value(self): - return bool(self.value) - - -class Continue(Statement): - """class representing a Continue node""" - - -class Decorators(NodeNG): - """class representing a Decorators node""" - _astroid_fields = ('nodes',) - nodes = None - - def postinit(self, nodes): - 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(mixins.ParentAssignTypeMixin, NodeNG): - """class representing a DelAttr node""" - _astroid_fields = ('expr',) - _other_fields = ('attrname',) - expr = None - - def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): - self.attrname = attrname - super(DelAttr, self).__init__(lineno, col_offset, parent) - - def postinit(self, expr=None): - self.expr = expr - - -class Delete(mixins.AssignTypeMixin, Statement): - """class representing a Delete node""" - _astroid_fields = ('targets',) - targets = None - - def postinit(self, targets=None): - self.targets = targets - - -class Dict(NodeNG, bases.Instance): - """class representing a Dict node""" - _astroid_fields = ('items',) - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.items = [] - super(Dict, self).__init__(lineno, col_offset, parent) - - def postinit(self, items): - self.items = items - - @classmethod - def from_constants(cls, items=None): - node = cls() - if items is None: - node.items = [] - else: - node.items = [(const_factory(k), const_factory(v)) - for k, v in items.items()] - return node - - 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, index, context=None): - for key, value in self.items: - # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}. - if isinstance(key, DictUnpack): - try: - return value.getitem(index, context) - except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): - continue - for inferredkey in key.infer(context): - if inferredkey is util.Uninferable: - continue - if isinstance(inferredkey, Const) and isinstance(index, Const): - if inferredkey.value == index.value: - return value - - raise exceptions.AstroidIndexError(index) - - def bool_value(self): - return bool(self.items) - - -class Expr(Statement): - """class representing a Expr node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class Ellipsis(NodeNG): # pylint: disable=redefined-builtin - """class representing an Ellipsis node""" - - def bool_value(self): - return True - - -class EmptyNode(NodeNG): - """class representing an EmptyNode node""" - - object = None - - -class ExceptHandler(mixins.AssignTypeMixin, Statement): - """class representing an ExceptHandler node""" - _astroid_fields = ('type', 'name', 'body',) - type = None - name = None - body = None - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, type=None, name=None, body=None): - self.type = type - self.name = name - self.body = body - - @decorators.cachedproperty - def blockstart_tolineno(self): - if self.name: - return self.name.tolineno - elif self.type: - return self.type.tolineno - - return self.lineno - - def catch(self, exceptions): # pylint: disable=redefined-outer-name - 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 - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, expr=None, globals=None, locals=None): - self.expr = expr - self.globals = globals - self.locals = locals - - -class ExtSlice(NodeNG): - """class representing an ExtSlice node""" - _astroid_fields = ('dims',) - dims = None - - def postinit(self, dims=None): - self.dims = dims - - -class For(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, Statement): - """class representing a For node""" - _astroid_fields = ('target', 'iter', 'body', 'orelse',) - target = None - iter = None - body = None - orelse = None - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, target=None, iter=None, body=None, orelse=None): - self.target = target - self.iter = iter - self.body = body - self.orelse = orelse - - optional_assign = True - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.iter.tolineno - - -class AsyncFor(For): - """Asynchronous For built with `async` keyword.""" - - -class Await(NodeNG): - """Await node for the `await` keyword.""" - - _astroid_fields = ('value', ) - value = None - - def postinit(self, value=None): - self.value = value - - -class ImportFrom(mixins.ImportFromMixin, Statement): - """class representing a ImportFrom node""" - _other_fields = ('modname', 'names', 'level') - - def __init__(self, fromname, names, level=0, lineno=None, - col_offset=None, parent=None): - self.modname = fromname - self.names = names - self.level = level - super(ImportFrom, self).__init__(lineno, col_offset, parent) - - -class Attribute(NodeNG): - """class representing a Attribute node""" - _astroid_fields = ('expr',) - _other_fields = ('attrname',) - expr = None - - def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): - self.attrname = attrname - super(Attribute, self).__init__(lineno, col_offset, parent) - - def postinit(self, expr=None): - self.expr = expr - - -class Global(Statement): - """class representing a Global node""" - _other_fields = ('names',) - - def __init__(self, names, lineno=None, col_offset=None, parent=None): - self.names = names - super(Global, self).__init__(lineno, col_offset, parent) - - def _infer_name(self, frame, name): - return name - - -class If(mixins.BlockRangeMixIn, Statement): - """class representing an If node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - def postinit(self, test=None, body=None, orelse=None): - self.test = test - self.body = body - self.orelse = orelse - - @decorators.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 - - def postinit(self, test=None, body=None, orelse=None): - self.test = test - self.body = body - self.orelse = orelse - - -class Import(mixins.ImportFromMixin, Statement): - """class representing an Import node""" - _other_fields = ('names',) - - def __init__(self, names=None, lineno=None, col_offset=None, parent=None): - self.names = names - super(Import, self).__init__(lineno, col_offset, parent) - - -class Index(NodeNG): - """class representing an Index node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class Keyword(NodeNG): - """class representing a Keyword node""" - _astroid_fields = ('value',) - _other_fields = ('arg',) - value = None - - def __init__(self, arg=None, lineno=None, col_offset=None, parent=None): - self.arg = arg - super(Keyword, self).__init__(lineno, col_offset, parent) - - def postinit(self, value=None): - self.value = value - - -class List(_BaseContainer): - """class representing a List node""" - _other_fields = ('ctx',) - - def __init__(self, ctx=None, lineno=None, - col_offset=None, parent=None): - self.ctx = ctx - super(List, self).__init__(lineno, col_offset, parent) - - def pytype(self): - return '%s.list' % BUILTINS - - def getitem(self, index, context=None): - return _container_getitem(self, self.elts, index, context=context) - - -class Nonlocal(Statement): - """class representing a Nonlocal node""" - _other_fields = ('names',) - - def __init__(self, names, lineno=None, col_offset=None, parent=None): - self.names = names - super(Nonlocal, self).__init__(lineno, col_offset, parent) - - 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 - - def __init__(self, nl=None, lineno=None, col_offset=None, parent=None): - self.nl = nl - super(Print, self).__init__(lineno, col_offset, parent) - - def postinit(self, dest=None, values=None): - self.dest = dest - self.values = values - - -class Raise(Statement): - """class representing a Raise node""" - exc = None - if six.PY2: - _astroid_fields = ('exc', 'inst', 'tback') - inst = None - tback = None - - def postinit(self, exc=None, inst=None, tback=None): - self.exc = exc - self.inst = inst - self.tback = tback - else: - _astroid_fields = ('exc', 'cause') - exc = None - cause = None - - def postinit(self, exc=None, cause=None): - self.exc = exc - self.cause = cause - - 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 - - def postinit(self, value=None): - self.value = value - - -class Set(_BaseContainer): - """class representing a Set node""" - - def pytype(self): - return '%s.set' % BUILTINS - - -class Slice(NodeNG): - """class representing a Slice node""" - _astroid_fields = ('lower', 'upper', 'step') - lower = None - upper = None - step = None - - def postinit(self, lower=None, upper=None, step=None): - self.lower = lower - self.upper = upper - self.step = step - - def _wrap_attribute(self, attr): - """Wrap the empty attributes of the Slice in a Const node.""" - if not attr: - const = const_factory(attr) - const.parent = self - return const - return attr - - @decorators.cachedproperty - def _proxied(self): - builtins = MANAGER.astroid_cache[BUILTINS] - return builtins.getattr('slice')[0] - - def pytype(self): - return '%s.slice' % BUILTINS - - def igetattr(self, attrname, context=None): - if attrname == 'start': - yield self._wrap_attribute(self.lower) - elif attrname == 'stop': - yield self._wrap_attribute(self.upper) - elif attrname == 'step': - yield self._wrap_attribute(self.step) - else: - for value in self.getattr(attrname, context=context): - yield value - - def getattr(self, attrname, context=None): - return self._proxied.getattr(attrname, context) - - -class Starred(mixins.ParentAssignTypeMixin, NodeNG): - """class representing a Starred node""" - _astroid_fields = ('value',) - _other_fields = ('ctx', ) - value = None - - def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): - self.ctx = ctx - super(Starred, self).__init__(lineno=lineno, - col_offset=col_offset, parent=parent) - - def postinit(self, value=None): - self.value = value - - -class Subscript(NodeNG): - """class representing a Subscript node""" - _astroid_fields = ('value', 'slice') - _other_fields = ('ctx', ) - value = None - slice = None - - def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): - self.ctx = ctx - super(Subscript, self).__init__(lineno=lineno, - col_offset=col_offset, parent=parent) - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, value=None, slice=None): - self.value = value - self.slice = slice - - -class TryExcept(mixins.BlockRangeMixIn, Statement): - """class representing a TryExcept node""" - _astroid_fields = ('body', 'handlers', 'orelse',) - body = None - handlers = None - orelse = None - - def postinit(self, body=None, handlers=None, orelse=None): - self.body = body - self.handlers = handlers - self.orelse = orelse - - 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(mixins.BlockRangeMixIn, Statement): - """class representing a TryFinally node""" - _astroid_fields = ('body', 'finalbody',) - body = None - finalbody = None - - def postinit(self, body=None, finalbody=None): - self.body = body - self.finalbody = finalbody - - 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(_BaseContainer): - """class representing a Tuple node""" - - _other_fields = ('ctx',) - - def __init__(self, ctx=None, lineno=None, - col_offset=None, parent=None): - self.ctx = ctx - super(Tuple, self).__init__(lineno, col_offset, parent) - - def pytype(self): - return '%s.tuple' % BUILTINS - - def getitem(self, index, context=None): - return _container_getitem(self, self.elts, index, context=context) - - -class UnaryOp(NodeNG): - """class representing an UnaryOp node""" - _astroid_fields = ('operand',) - _other_fields = ('op',) - operand = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(UnaryOp, self).__init__(lineno, col_offset, parent) - - def postinit(self, operand=None): - self.operand = operand - - # This is set by inference.py - def _infer_unaryop(self, context=None): - raise NotImplementedError - - def type_errors(self, context=None): - """Return a list of TypeErrors which can occur during inference. - - Each TypeError is represented by a :class:`BadUnaryOperationMessage`, - which holds the original exception. - """ - try: - results = self._infer_unaryop(context=context) - return [result for result in results - if isinstance(result, util.BadUnaryOperationMessage)] - except exceptions.InferenceError: - return [] - - -class While(mixins.BlockRangeMixIn, Statement): - """class representing a While node""" - _astroid_fields = ('test', 'body', 'orelse',) - test = None - body = None - orelse = None - - def postinit(self, test=None, body=None, orelse=None): - self.test = test - self.body = body - self.orelse = orelse - - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for and while statements""" - return self. _elsed_block_range(lineno, self.orelse) - - -class With(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, Statement): - """class representing a With node""" - _astroid_fields = ('items', 'body') - items = None - body = None - - def postinit(self, items=None, body=None): - self.items = items - self.body = body - - @decorators.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 AsyncWith(With): - """Asynchronous `with` built with the `async` keyword.""" - - -class Yield(NodeNG): - """class representing a Yield node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class YieldFrom(Yield): - """ Class representing a YieldFrom node. """ - - -class DictUnpack(NodeNG): - """Represents the unpacking of dicts into dicts using PEP 448.""" - - -class FormattedValue(NodeNG): - """Represents a PEP 498 format string.""" - _astroid_fields = ('value', 'format_spec') - value = None - conversion = None - format_spec = None - - def postinit(self, value, conversion=None, format_spec=None): - self.value = value - self.conversion = conversion - self.format_spec = format_spec - - -class JoinedStr(NodeNG): - """Represents a list of string expressions to be joined.""" - _astroid_fields = ('values',) - values = None - - def postinit(self, values=None): - self.values = values - - -class Unknown(NodeNG): - '''This node represents a node in a constructed AST where - introspection is not possible. At the moment, it's only used in - the args attribute of FunctionDef nodes where function signature - introspection failed. - ''' - def infer(self, context=None, **kwargs): - '''Inference on an Unknown node immediately terminates.''' - yield util.Uninferable - - -# constants ############################################################## - -CONST_CLS = { - list: List, - tuple: Tuple, - dict: Dict, - set: Set, - type(None): Const, - type(NotImplemented): Const, - } - -def _update_const_classes(): - """update constant classes, so the keys of CONST_CLS can be reused""" - klasses = (bool, int, float, complex, str) - if six.PY2: - # pylint: disable=undefined-variable - klasses += (unicode, long) - klasses += (bytes,) - for kls in klasses: - CONST_CLS[kls] = Const -_update_const_classes() - - -def _two_step_initialization(cls, value): - instance = cls() - instance.postinit(value) - return instance - - -def _dict_initialization(cls, value): - if isinstance(value, dict): - value = tuple(value.items()) - return _two_step_initialization(cls, value) - - -_CONST_CLS_CONSTRUCTORS = { - List: _two_step_initialization, - Tuple: _two_step_initialization, - Dict: _dict_initialization, - Set: _two_step_initialization, - Const: lambda cls, value: cls(value) -} - - -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) - - # Hack for ignoring elements of a sequence - # or a mapping, in order to avoid transforming - # each element to an AST. This is fixed in 2.0 - # and this approach is a temporary hack. - if isinstance(value, (list, set, tuple, dict)): - elts = [] - else: - elts = value - - try: - initializer_cls = CONST_CLS[value.__class__] - initializer = _CONST_CLS_CONSTRUCTORS[initializer_cls] - return initializer(initializer_cls, elts) - except (KeyError, AttributeError): - node = EmptyNode() - node.object = value - return node - - -# Backward-compatibility aliases - -Backquote = util.proxy_alias('Backquote', Repr) -Discard = util.proxy_alias('Discard', Expr) -AssName = util.proxy_alias('AssName', AssignName) -AssAttr = util.proxy_alias('AssAttr', AssignAttr) -Getattr = util.proxy_alias('Getattr', Attribute) -CallFunc = util.proxy_alias('CallFunc', Call) -From = util.proxy_alias('From', ImportFrom) diff --git a/pymode/libs/logilab b/pymode/libs/logilab new file mode 120000 index 00000000..1100ab45 --- /dev/null +++ b/pymode/libs/logilab @@ -0,0 +1 @@ +logilab-common-1.4.1/logilab \ No newline at end of file diff --git a/pymode/libs/logilab-common-1.4.1/COPYING b/pymode/libs/logilab-common-1.4.1/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/pymode/libs/logilab-common-1.4.1/COPYING.LESSER b/pymode/libs/logilab-common-1.4.1/COPYING.LESSER new file mode 100644 index 00000000..2d2d780e --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/COPYING.LESSER @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library 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. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/pymode/libs/logilab-common-1.4.1/ChangeLog b/pymode/libs/logilab-common-1.4.1/ChangeLog new file mode 100644 index 00000000..95c96f6a --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/ChangeLog @@ -0,0 +1,1613 @@ +ChangeLog for logilab.common +============================ + +2016-10-03 -- 1.3.0 + + * pytest: executable deprecated and renamed as logilab-pytest to prevent + conflict with pytest provided by http://pytest.org/ + +2016-03-15 -- 1.2.0 + + * pytest: TraceController class, pause_tracing and resume_tracing + functions, deprecated from 0.63.1, got removed. The nocoverage + and pause_trace utilities are now available from the testlib + module rather than pytest. + + * date: datetime2ticks uses the milliseconds from the datetime objects + +2015-10-12 -- 1.1.0 + * configuration: have a stable order for sections (#298658) + + * testlib: clean out deprecated TestCase methods (#1716063), move pytest + specifics to pytest.py (#1716053) + + * fix a few python3 bugs in umessage, configuration and optik_ext modules + + * testlib: report failures and skips in generative tests properly + + * optik_ext: return bytes as ints and not floats (#2086835) + +2015-07-08 -- 1.0.2 + * declare setuptools requirement in __pkginfo__/setup.py + + * randomize order of test modules in pytest -t + +2015-07-01 -- 1.0.1 + * restore __pkginfo__.version, which pylint < 1.4.4 uses + +2015-06-30 -- 1.0.0 + * remove unused/deprecated modules: cli, contexts, corbautils, dbf, + pyro_ext, xmlrpcutils. __pkginfo__ is no longer installed. + + * major layout change + + * use setuptools exclusively + + * 'logilab' is now a proper namespace package + + * modutils: basic support for namespace packages + + * registry: ambiguous selects now raise a specific exception + + * testlib: better support for non-pytest launchers + + * testlib: Tags() now work with py3k + +2014-11-30 -- 0.63.2 + * fix 2 minor regressions from 0.63.1 + +2014-11-28 -- 0.63.1 + * fix fallout from py3k conversion + + * pytest: fix TestSuite.run wrapper (#280806) + + * daemon: change umask after creating pid file + +2014-11-05 -- 0.63.0 + * drop compatibility with python <= 2.5 (#264017) + + * fix textutils.py doctests for py3k + + * produce a clearer exception when dot is not installed (#253516) + + * make source python3-compatible (3.3+), without using 2to3. This + introduces a dependency on six (#265740) + + * fix umessage header decoding on python 3.3 and newer (#149345) + + * WARNING: the compat module no longer exports 'callable', 'izip', 'imap', + 'chain', 'sum', 'enumerate', 'frozenset', 'reversed', 'sorted', 'max', + 'relpath', 'InheritableSet', or any subprocess-related names. + +2014-07-30 -- 0.62.1 + * shellutils: restore py 2.5 compat by removing usage of class decorator + + * pytest: drop broken --coverage option + + * testlib: support for skipping whole test class and conditional skip, don't + run setUp for skipped tests + + * configuration: load options in config file order (#185648) + + + +2014-03-07 -- 0.62.0 + * modutils: cleanup_sys_modules returns the list of cleaned modules + + + +2014-02-11 -- 0.61.0 + * pdf_ext: removed, it had no known users (CVE-2014-1838) + + * shellutils: fix tempfile issue in Execute, and deprecate it + (CVE-2014-1839) + + * pytest: use 'env' to run the python interpreter + + * graph: ensure output is ordered on node and graph ids (#202314) + + + +2013-16-12 -- 0.60.1 + * modutils: + + * don't propagate IOError when package's __init__.py file doesn't + exist (#174606) + + * ensure file is closed, may cause pb depending on the interpreter, eg + pypy) (#180876) + + * fix support for `extend_path` based nested namespace packages ; + Report and patch by John Johnson (#177651) + + * fix some cases of failing python3 install on windows platform / cross + compilation (#180836) + + + +2013-07-26 -- 0.60.0 + * configuration: rename option_name method into option_attrname (#140667) + + * deprecation: new DeprecationManager class (closes #108205) + + * modutils: + + - fix typo causing name error in python3 / bad message in python2 + (#136037) + - fix python3.3 crash in file_from_modpath due to implementation + change of imp.find_module wrt builtin modules (#137244) + + * testlib: use assertCountEqual instead of assertSameElements/assertItemsEqual + (deprecated), fixing crash with python 3.3 (#144526) + + * graph: use codecs.open avoid crash when writing utf-8 data under python3 + (#155138) + + + +2013-04-16 -- 0.59.1 + * graph: added pruning of the recursive search tree for detecting cycles in + graphs (closes #2469) + + * testlib: check for generators in with_tempdir (closes #117533) + + * registry: + + - select_or_none should not silent ObjectNotFound exception + (closes #119819) + - remove 2 accidentally introduced tabs breaking python 3 compat + (closes #117580) + + * fix umessages test w/ python 3 and LC_ALL=C (closes #119967, report and + patch by Ian Delaney) + + + +2013-01-21 -- 0.59.0 + * registry: + + - introduce RegistrableObject base class, mandatory to make + classes automatically registrable, and cleanup code + accordingly + - introduce objid and objname methods on Registry instead of + classid function and inlined code plus other refactorings to allow + arbitrary objects to be registered, provided they inherit from new + RegistrableInstance class (closes #98742) + - deprecate usage of leading underscore to skip object registration, using + __abstract__ explicitly is better and notion of registered object 'name' + is now somewhat fuzzy + - use register_all when no registration callback defined (closes #111011) + + * logging_ext: on windows, use colorama to display colored logs, if available (closes #107436) + + * packaging: remove references to ftp at logilab + + * deprecations: really check them + + * packaging: steal spec file from fedora (closes #113099) + + * packaging force python2.6 on rhel5 (closes #113099) + + * packaging Update download and project urls (closes #113099) + + * configuration: enhance merge_options function (closes #113458) + + * decorators: fix @monkeypatch decorator contract for dark corner + cases such as monkeypatching of a callable instance: no more + turned into an unbound method, which was broken in python 3 and + probably not used anywhere (actually closes #104047). + + + +2012-11-14 -- 0.58.3 + * date: fix ustrftime() impl. for python3 (closes #82161, patch by Arfrever + Frehtes Taifersar Arahesis) and encoding detection for python2 (closes + #109740) + + * other python3 code and test fixes (closes #104047) + + * registry: Store.setdefault shouldn't raise RegistryNotFound (closes #111010) + + * table: stop encoding to iso-8859-1, use unicode (closes #105847) + + * setup: properly install additional files during build instead of install (closes #104045) + + + +2012-07-30 -- 0.58.2 + * modutils: fixes (closes #100757 and #100935) + + + +2012-07-17 -- 0.58.1 + * modutils, testlib: be more python implementation independant (closes #99493 and #99627) + + + +2012-04-12 -- 0.58.0 + * new `registry` module containing a backport of CubicWeb selectable objects registry (closes #84654) + + * testlib: DocTestCase fix builtins pollution after doctest execution. + + * shellutil: add argument to ``ProgressBar.update`` to tune cursor progression (closes #88981) + + * deprecated: new DeprecationWrapper class (closes #88942) + + + +2012-03-22 -- 0.57.2 + * texutils: apply_units raise ValueError if string isn'nt valid (closes #88808) + + * daemon: don't call putenv directly + + * pytest: do not enable extra warning other than DeprecationWarning. + + * testlib: DocTestCase fix builtins pollution after doctest execution. + + * testlib: replace sys.exit with raise ImportError (closes: #84159) + + * fix license in README + + * add trove classifiers (tell about python 3 support for pypi) + + + +2011-10-28 -- 0.57.1 + * daemon: change $HOME after dropping privileges (closes #81297) + + * compat: method_type for py3k use instance of the class to have a + real instance method (closes: #79268) + + + +2011-10-12 -- 0.57.0 + * only install unittest2 when python version < 2.7 (closes: #76068) + + * daemon: make pidfile world-readable (closes #75968) + + * daemon: remove unused(?) DaemonMixin class + + * update compat module for callable() and method_type() + + * decorators: fix monkeypatch py3k compat (closes #75290) + + * decorators: provide a @cachedproperty decorator + + + +2011-09-08 -- 0.56.2 + * daemon: call initgroups/setgid before setuid (closes #74173) + + * decorators: @monkeypatch should produce a method object (closes #73920) + + * modutils: allow overriding of _getobj by suppressing mangling + + + +2011-08-05 -- 0.56.1 + * clcommands: #72450 --rc-file option doesn't work + + + +2011-06-09 -- 0.56.0 + * clcommands: make registration possible by class decoration + + * date: new datetime/delta <-> seconds/days conversion function + + * decorators: refactored @cached to allow usages such as + @cached(cacheattr='_cachename') while keeping bw compat + + + +2011-04-01 -- 0.55.2 + * new function for password generation in shellutils + + * pyro_ext: allow to create a server without registering with a pyrons + + + +2011-03-28 -- 0.55.1 + * fix date.ustrftime break if year <= 1900 + + * fix graph.py incorrectly builds command lines using %s to call dot + + * new functions to get UTC datetime / time + + + +2011-02-18 -- 0.55.0 + * new urllib2ext module providing a GSSAPI authentication handler, based on python-kerberos + + * graph: test and fix ordered_nodes() [closes #60288] + + * changelog: refactor ChangeLog class to ease overriding + + * testlib: Fix tag handling for generator. + + + +2011-01-12 -- 0.54.0 + * dropped python 2.3 support + + * daemon: we can now specify umask to daemonize function, and it return + different exit code according to the process + + * pyro_ext: new ns_reregister function to ensure a name is still properly + registered in the pyro name server + + * hg: new incoming/outgoing functions backward compatible with regards to + mercurial version (eg hg 1.6 and earlier) + + * testlib/pytest: more deprecation and removed code. Still on the way to + unittest2 + + + +2010-11-15 -- 0.53.0 + * first python3.x compatible release + + * __init__: tempattr context manager + + * shellutils: progress context manager + + + +2010-10-11 -- 0.52.1 + * configuration: fix pb with option names as unicode string w/ + python 2.5. Makes OptionError available through the module + + * textutils: text_to_dict skip comments (# lines) + + * compat: dropped some 2.2 compat + + * modutils: Consider arch-specific installation for STD_LIB_DIR definition + + + +2010-09-28 -- 0.52.0 + * testlib is now based on unittest2, to prepare its own extinction. + Warning are printed so you can easily migrate step by step. + + * restored python 2.3 compat in some modules, so one get a change to run + pylint at least + + * textutils: use NFKD decomposition in unormalize() + + * logging_ext: don't try to use ansi colorized formatter when not in debug + mode + + + +2010-09-10 -- 0.51.1 + * logging_ext: init_log function splitted into smaller chunk to ease reuse + in other contexts + + * clcommands: enhanced/cleaned api, nicer usage display + + * various pylint detected errors fixed + + + +2010-08-26 -- 0.51.0 + * testlib: don't raise string exception (closes #35331) + + * hg: new module regrouping some mercurial utility functions + + * clcommands: refactored to get more object oriented api. + + * optparser: module is now deprecated, use clcommands instead + + * textutils: new split_url_or_path and text_to_dict functions + + * logging_ext: + - init_log now accept optionaly any arbitrary handler + - threshold default to DEBUG if debug flag is true and no threshold specified + + * date: new ustrftime implementation working around datetime limitaion on dates < 1900 + + + +2010-06-04 -- 0.50.3 + * logging: added new optional kw argument to init_log rotating_parameters + + * date: fix nb_open_days() codomain, positive natural numbers are expected + + * configuration: + - skip option with no type, avoid pb with generated option such as long-help + - handle level on man page generation + + + +2010-05-21 -- 0.50.2 + * fix licensing information: LGPL v2.1 or greater + + * daemon: new daemonize function + + * modutils: fix some false negative of is_standard_module with + 'from module import something" where something isn't a submodule + + * optik_ext: fix help generation for normal optparse using script if + optik_ext has been imported (#24450) + + * textutils support 256 colors when available + + * testlib] add option splitlines to assertTextEquals + + + +2010-04-26 -- 0.50.1 + * implements __repr__ on nullobject + + * configuration: avoid crash by skipping option without 'type' + entry while input a config + + * pyro_ext: raise PyroError instead of exception + + + +2010-04-20 -- 0.50.0 + * graph: + - generate methods now takes an optional mapfile argument to generate + html image maps + - new ordered_nodes function taking a dependency graph dict as arguments + and returning an ordered list of nodes + + * configuration: + - nicer serialization of bytes / time option + - may now contains several option provider with the same name + - consider 'level' in option dict, --help displaying only option with level + 0, and automatically adding --long-help options for higher levels + + * textutils: case insensitive apply_unit + + * sphinx_ext: new module usable as a sphinx pluggin and containing a new + 'autodocstring' directive + + * ureports: output   instead of   for strict xhtml compliance + + * decorators: @cached propery copy inner function docstring + + + +2010-03-16 -- 0.49.0 + * date: new 'totime' function + + * adbh, db, sqlgen modules moved to the new logilab-database package + + * pytest: when -x option is given, stop on the first error even if + there are multiple test directories + + + +2010-02-26 -- 0.48.1 + * adbh: added dbport optional argument to [backup|restore]_commands + + * db: fix date processing for SQLServer 2005 + + * testlib: improve XML assertion by using ElementTree parser and a new 'context' lines argument + + + +2010-02-17 -- 0.48.0 + * date: fixed mx date time compat for date_range (#20651) + + * testlib: generative test should not be interrupted by self.skip() (#20648) + + + +2010-02-10 -- 0.47.0 + * adbh: changed backup / restore api (BREAKS COMPAT): + - backup_command is now backup_commands (eg return a list of commands) + - each command returned in backup_commands/restore_commands may now + be list that may be used as argument to subprocess.call, or a string + which will the requires a subshell + - new sql_rename_col method + + * deprecation: deprecated now takes an optional 'stacklevel' argument, default to 2 + + * date: some functions to ease python's datetime module usage have been backported + from cubicweb + + + +2009-12-23 -- 0.46.0 + * db / adbh: added SQL Server support using Pyodbc + + * db: + - New optional extra_args argument to get_connection. + - Support Windows Auth for SQLServer by giving + extra_args='Trusted_Connection' to the sqlserver2005 driver + + + +2009-11-23 -- 0.45.2 + * configuration: + - proper bytes and time option types support + - make Method usable as 'callback' value + - fix #8849 Using plugins, options and .pylintrc crashes PyLint + + * graph: fix has_path returned value to include the destination node, else we get + an empty list which makes think there is no path (test added) + + + +2009-08-26 -- 0.45.0 + * added function for parsing XML processing instructions + + + +2009-08-07 -- 0.44.0 + * remove code deprecated for a while now + + * shellutils: replace confirm function by RawInput class /ASK singleton + + * deprecation: new deprecated decorator, replacing both obsolete and deprecated_function + + + +2009-07-21 -- 0.43.0 + * dbf: a DBF reader which reads Visual Fox Pro DBF format with Memo field (module from Yusdi Santoso) + + * shellutils: + - #9764 add title to shellutils.ProgressBar + - #9796 new confirm function + + * testlib: + - simplify traceback manipulation (skip first frames corresponding to testlib functions) + - -c now captures DeprecationWarnings + + * sphinxutils: simplified API + + * modutils: new cleanup_sys_modules function that removes modules under a list + of directories from sys.modules + + + +2009-07-17 -- 0.42.0 + * pyro_ext: new module for pyro utilities + + * adbh: fix default set_null_allowed implementation, new case_sensitive + resource descriptor + + + +2009-06-03 -- 0.41.0 + * modutils: new extrapath argument to modpath_from_file (see function's + docstring for explanation) + + * adbh: new alter_column_support flag, sql_set_null_allowed and + sql_change_col_type methods + + + +2009-05-28 -- 0.40.1 + * date: handle both mx.DateTime and datetime representations + + * db: use sqlite native module's Binary, not StringIO + + + +2009-05-14 -- 0.40.0 + * python < 2.3 are now officially unsupported + + * #9162: new module with some sphinx utilities + + * #9166: use a global variable to control mx datetime / py datetime usage + + * db: add time adapter for pysqlite2, fix mysql bool and string handling + + * configuration: don't print default for store_true / store_false option + or option with None as default + + + +2009-04-07 -- 0.39.1 + * fix #6760 umessage.decode_QP() crashes on unknown encoding + + + +2009-03-25 -- 0.39.0 + * fix #7915 (shellutils unusable under windows) + + * testlib: + + * new profile option using cProfile + + * allows to skip a module by raising TestSkipped from module import + + * modutils: locate modules in zip/egg archive + + * db: USE_MX_DATETIME global to control usage of mx.DateTime / py datetime + + + +2009-01-26 -- 0.38.0 + * setuptools / easy_install support! + + * removed some old backward compat code + + * adbh: new intersect_all_support attribute + + * contexts: new pushd context manager + + * shellutils: enhance acquire_lock method w/ race condition + + * configuration: fix case sensitivity pb w/ config file sections + + * pytest: reimplemented colorization + + + +2009-01-08 -- 0.37.2 + * configuration: encoding handling for configuration file generation + + * adbh: fix Datetime type map for mysql + + * logging_ext: drop lldebug level which shouldn't be there + + + +2008-12-11 -- 0.37.1 + * contexts: make the module syntactically correct wrt python2.4 + + + +2008-12-09 -- 0.37.0 + * contexts: new module for context managers, keeping py <2.4 syntax compat + for distribution (only `tempdir` cm for now) + + * tasksqueue: new module containing a class to handle prioritized tasks queue + + * proc: new module for process information / resource control + + * optik_ext: new time/bytes option types, using textutils conversion function + + * logging_ext: new set_log_methods / init_log utility functions + + + +2008-10-30 -- 0.36.0 + * configuration: + - option yn is now behaving like a flag (i.e --ex : if ex.default=True and --ex in sys.args then ex.value=False) + - new attribute hide in option (i.e --ex : if --ex has 'hide':True then the option will not be displayed in man or --help) + + * pytest: + - add colors in display + - new option --restart that skips tests that succeeded on last run + + * cache: new herits from dict class + + * decorators: add @require_version @require_module that skip test if decorators are not satisfied + + + +2008-10-09 -- 0.35.3 + * graph: new has_path method + + + +2008-10-01 -- 0.35.2 + * configuration: + - fix #6011: lgc.configuration ignore customized option values + - fix #3278: man page generation broken + + * dropped context.py module which broke the debian package when + some python <2.5 is installed (#5979) + + + +2008-09-10 -- 0.35.0 + * fix #5945: wrong edge properties in graph.DotBackend + + * testlib: filter tests with tag decorator + + * shellutils: new simple unzip function + + + +2008-08-07 -- 0.34.0 + * changelog: properly adds new line at the end of each entry + + * testlib: add a with_tempdir decorator ensuring all temporary files and dirs are removed + + * graph: improve DotBackend configuration. graphiz rendered can now be selected + and additional graph parameter used + + * db: support of Decimal Type + + + +2008-06-25 -- 0.33.0 + * decorators: new @locked decorator + + * cache: make it thread safe, changed behaviour so that when cache size is 0 + and __delitem__ is called, a KeyError is raised (more consistent) + + * testlib: + - added assertIsNot, assertNone and assertNotNone assertion + - added assertUnorderedIterableEquals + - added assertDirEquals + - various failure output improvement + + * umessage: umessage.date() may return unparsable string as is instead of None + + * compat: adds a max function taking 'key' as keyword argument as in 2.5 + + * configuration: escape rest when printing for default value + + + +2008-06-08 -- 0.32.0 + * textutils: add the apply_unit function + + * testlib: + - added a assertXMLEqualsTuple test assertion + - added a assertIs assertion + + + +2008-05-08 -- 0.31.0 + * improved documentation and error messages + + * testlib: support a msg argument on more assertions, pysqlite2 as default + + * pytest: pytestconf.py for customization + + + +2008-03-26 -- 0.30.0 + * db: remember logged user on the connection + + * clcommands: commands may be hidden (e.g. not displayed in help), generic + ListCommandsCommand useful to build bash completion helpers + + * changelog: module to parse ChangeLog file as this one, backported from + logilab.devtools + + + +2008-03-12 -- 0.29.1 + * date: new nb_open_days function counting worked days between two date + + * adbh: add -p option to mysql commands to ask for password + + + +2008-03-05 -- 0.29.0 + * adbh: mysql doesn't support ILIKE, implement list_indices for mysql + + * db: mysql adapter use mx DateTime when available, fix unicode handling + + + +2008-02-18 -- 0.28.2 + * testlib: restore python2.3 compatibility + + + +2008-02-15 -- 0.28.1 + * testlib: introduce InnerTest class to name generative tests, fix + generative tests description storage + + * pytest: fix -s option + + * modutils: included Stefan Rank's patch to deal with 2.4 relative import + + * configuration: don't give option's keywords not recognized by optparse, + fix merge_options function + + + +2008-02-05 -- 0.28.0 + * date: new `add_days_worked` function + + * shellutils: new `chown` function + + * testlib: new `strict` argument to assertIsInstance + + * __init__: new `attrdict` and `nullobject` classes + + + +2008-01-25 -- 0.27.0 + * deprecation: new class_moved utility function + + * interface: fix subinterface handling + + + +2008-01-10 -- 0.26.1 + * optparser: support --version at main command level + + * testlib: added man page for pytest + + * textutils: fix a bug in normalize{_,_rest_}paragraph which may cause + infinite loop if an indent string containing some spaces is given + + + +2008-01-07 -- 0.26.0 + * db: binarywrap support + + * modutils: new LazyObject class + + + +2007-12-20 -- 0.25.2 + * adbh: new needs_from_clause variable on db helper + + + +2007-12-11 -- 0.25.1 + * pytest: new --profile option, setup module / teardown module hook, + other fixes and enhancements + + * db: mysql support fixes + + * adbh: fix postgres list_indices implementation + + + +2007-11-26 -- 0.25.0 + * adbh: + - list_tables implementation for sqlite + - new list_indices, create_index, drop_index methods + + * restore python < 2.4 compat + + + +2007-10-29 -- 0.24.0 + * decorators: new classproperty decorator + + * adbh: new module containing advanced db helper which were in the "db" + module, with additional registered procedures handling + + + +2007-10-23 -- 0.23.1 + * modutils: fix load_module_from_* (even with use_sys=False, it should + try to get outer packages from sys.modules) + + + +2007-10-17 -- 0.23.0 + * db: + + - mark support_users and support_groups methods as obsolete in + favor of users_support and groups_support attributes + - new ilike_support property on dbms helpers + - extended db helper api + - completed mysql support + + * textutils: new unormalize function to normalize diacritical chars by + their ascii equivalent + + * modutils: new load_module_from_file shortcut function + + * clcommands: pop_args accept None as value for expected_size_after, + meaning remaining args should not be checked + + * interface: new extend function to dynamically add an implemented interface + to a new style class + + + +2007-06-25 -- 0.22.2 + * new 'typechanged' action for configuration.read_old_config + + + +2007-05-14 -- 0.22.1 + * important bug fix in db.py + + * added history in pytest debugger sessions + + * fix pytest coverage bug + + * fix textutils test + + * fix a bug which provoked a crash if devtools was not installed + + + +2007-05-14 -- 0.22.0 + * pytest improvements + + * shellutils: use shutil.move instead of os.rename as default action + of mv + + * db: new `list_users` and `sql_drop_unique_constraint` methods on + advanced helpers + + * deprecation: new `obsolete` decorator + + + +2007-02-12 -- 0.21.3 + * fixed cached decorator to use __dict__ instead of attribute lookup, + avoiding potential bugs with inheritance when using cached class + methods + + + +2007-02-05 -- 0.21.2 + * fix ReST normalization (#3471) + + + +2006-12-19 -- 0.21.1 + * tree: make Node iterable (iter on its children) + + * configuration: fix #3197 (OptionsManagerMixin __init__ isn't passing + correctly its "version" argument) + + * textutils: new 'rest' argument to normalize_text to better deal with + ReST formated text + + * some packaging fixes + + + +2006-11-14 -- 0.21.0 + * db: + + - new optional keepownership argument to backup|restore_database methods + - only register mxDatetime converters on psycopg2 adapter if + mx.DateTime is available + + * moved some stuff which was in common __init__ file into specific + module. At this occasion new "decorators" and "deprecation" modules + has been added + + * deprecated fileutils.[files_by_ext,include_files_by_ext,exclude_files_by_ext] + functions in favor of new function shellutils.find + + * mark the following modules for deprecation, they will be removed in a + near version: + + * astutils: moved to astng + + * bind (never been used) + + * html: deprecated + + * logger/logservice: use logging module + + * monclient/monserver (not used anymore) + + * patricia (never been used) + + * twisted_distutils (not used anymore) + + * removed the following functions/methods which have been deprecated for a + while now: + + * modutils.load_module_from_parts + + * textutils.searchall + + * tree.Node.leafs + + * fileutils.get_by_ext, filetutils.get_mode, fileutils.ensure_mode + + * umessage: more robust charset handling + + + +2006-11-03 -- 0.20.2 + * fileutils: new remove_dead_links function + + * date: add missing strptime import + + + +2006-11-01 -- 0.20.1 + * umessage: + - new message_from_string function + - fixed get_payload encoding bug + + * db: default postgres module is now psycopg2, which has been customized + to return mx.Datetime objects for date/time related types + + + +2006-10-27 -- 0.20.0 + * db: + - fixed date handling + - new methods on advanced helper to generate backup commands + + * configuration: basic deprecated config handling support + + * new implementation of pytest + + * backport a dot backend from yams into a new "graph" module + + + +2006-10-03 -- 0.19.3 + * fixed bug in textutils.normalise_[text|paragraph] with unsplitable + word larger than the maximum line size + + * added pytest.bat for windows installation + + * changed configuration.generate_config to include None values into the + generated file + + + +2006-09-25 -- 0.19.2 + * testlib: + - fixed a bug in find_test making it returns some bad test names + - new assertIsInstance method on TestCase + + * optik_ext: make it works if mx.DateTime is not installed, in which case + the date type option won't be available + + * test fixes + + + +2006-09-22 -- 0.19.1 + * db: + + - fixed bug when querying boolean on sqlite using python's bool type + - fixed time handling and added an adapter for DateTimeDeltaType + - added "drop_on_commit" argument to create_temporary_table on db helper + - added missing implementation of executemany on pysqlite2 wrapper to + support pyargs correctly like execute + + * optik_ext: fixed "named" type option to support csv values and to return + a dictionary + + + +2006-09-05 -- 0.19.0 + * new umessage module which provides a class similar to the standard + email.Message class but returning unicode strings + + * new clcommands module to handle commands based command line tool + (based on the configuration module) + + * new "date" option type in optik_ext + + * new AttrObject in testlib to create objects in test with arbitrary attributes + + * add pytest to run project's tests and get rid of all runtests.py + + * add pytest option to enable design-by-contract using aspects + + * some enhancements to the configuration module + + + +2006-08-09 -- 0.18.0 + * added -c / --capture option to testlib.unittest_main + + * fixed bugs in lgc.configuration + + * optparser: added a OptionParser that extends optparse's with commands + + + +2006-07-13 -- 0.17.0 + * python2.5 compatibility (testlib.py + compat.py) + + * testlib.assertListEquals return all errors at once + + * new "password" option type in optik_ext + + * configuration: refactored to support interactive input of a configuration + + + +2006-06-08 -- 0.16.1 + * testlib: improved test collections + + * compat: added cmp argument to sorted + + + +2006-05-19 -- 0.16.0 + * testlib: + + - added a set of command line options (PYDEBUG is deprecated, + use the -i/--pdb option, and added -x/--exitfirst option) + - added support for generative tests + + * db: + - fix get_connection parameter order and host/port handling + - added .sql_temporary_table method to advanced func helpers + - started a psycopg2 adapter + + * configuration: enhanced to handle default value in help and man pages + generation (require python >= 2.4) + + + +2006-04-25 -- 0.15.1 + * db: add missing port handling to get_connection function and + dbapimodule.connect methods + + * testlib: various fixes and minor improvements + + + +2006-03-28 -- 0.15.0 + * added "cached" decorator and a simple text progression bar into __init__ + + * added a simple text progress bar into __init__ + + * configuration: fixed man page generation when using python 2.4 + + * db: added pysqllite2 support, preconfigured to handle timestamp using + mxDatetime and to correctly handle boolean types + + + +2006-03-06 -- 0.14.1 + * backported file support and add LOG_CRIT to builtin in logservice module + + + +2006-02-28 -- 0.14.0 + * renamed assertXML*Valid to assertXML*WellFormed and deprecated the old name + + * fixed modutils.load_module_from_* + + + +2006-02-03 -- 0.13.1 + * fix some tests, patch contributed by Marien Zwart + + * added ability to log into a file with make_logger() + + + +2006-01-06 -- 0.13.0 + * testlib: ability to skip a test + + * configuration: + + - cleaner configuration file generation + - refactoring so that we can have more control on file + configuration loading using read_config_file and load_config_file + instead of load_file_configuration + + * modutils: fix is_relative to return False when from_file is a file + located somewhere in sys.path + + * ureport: new "escaped" attribute on Text nodes, controling html escaping + + * compat: make set iterable and support more other set operations... + + * removed the astng sub-package, since it's now self-distributed as + logilab-astng + + + +2005-09-06 -- 0.12.0 + * shellutils: bug fix in mv() + + * compat: + - use set when available + - added sorted and reversed + + * table: new methods and some optimizations + + * tree: added some deprecation warnings + + + +2005-07-25 -- 0.11.0 + * db: refactoring, added sqlite support, new helpers to support DBMS + specific features + + + +2005-07-07 -- 0.10.1 + * configuration: added basic man page generation feature + + * ureports: unicode handling, some minor fixes + + * testlib: enhance MockConnection + + * python2.2 related fixes in configuration and astng + + + +2005-05-04 -- 0.10.0 + * astng: improve unit tests coverage + + * astng.astng: fix Function.format_args, new method + Function.default_value, bug fix in Node.resolve + + * astng.builder: handle classmethod and staticmethod as decorator, + handle data descriptors when building from living objects + + * ureports: + - new docbook formatter + - handle ReST like urls in the text writer + - new build_summary utility function + + + +2005-04-14 -- 0.9.3 + * optik_ext: add man page generation based on optik/optparse options + definition + + * modutils: new arguments to get_source_file to handle files without + extensions + + * astng: fix problem with the manager and python 2.2 (optik related) + + + +2005-02-16 -- 0.9.2 + * textutils: + + - added epydoc documentation + - new sep argument to the get_csv function + - fix pb with normalize_* functions on windows platforms + + * fileutils: + + - added epydoc documentation + - fixed bug in get_by_ext (renamed files_by_ext) with the + exclude_dirs argument + + * configuration: + - fixed a bug in configuration file generation on windows platforms + - better test coverage + + * fixed testlib.DocTest which wasn't working anymore with recent + versions of pyunit + + * added "context_file" argument to file_from_modpath to avoid + possible relative import problems + + * astng: use the new context_file argument from Node.resolve() + + + +2005-02-04 -- 0.9.1 + * astng: + + - remove buggy print + - fixed builder to deal with builtin methods + - fixed raw_building.build_function with python 2.4 + + * modutils: code cleanup, some reimplementation based on "imp", + better handling of windows specific extensions, epydoc documentation + + * fileutils: new exclude_dirs argument to the get_by_ext function + + * testlib: main() support -p option to run test in a profiled mode + + * generated documentation for modutils in the doc/ subdirectory + + + +2005-01-20 -- 0.9.0 + * astng: + + - refactoring of some huge methods + - fix interface resolving when __implements__ is defined in a parent + class in another module + - add special code in the builder to fix problem with qt + - new source_line method on Node + - fix sys.path during parsing to avoid some failure when trying + to get imported names by `from module import *`, and use an astng + building instead of exec'ing the statement + - fix possible AttributeError with Function.type + - manager.astng_from_file fallback to astng_from_module if possible + + * textutils: fix bug in normalize_paragraph, unquote handle empty string + correctly + + * modutils: + + - use a cache in has_module to speed up things when heavily used + - fix file_from_modpath to handle pyxml and os.path + + * configuration: fix problem with serialization/deserialization of empty + string + + + +2005-01-04 -- 0.8.0 + * modutils: a lot of fixes/rewrite on various functions to avoid + unnecessary imports, sys.path pollution, and other bugs (notably + making pylint reporting wrong modules name/path) + + * astng: new "inspector" module, initially taken from pyreverse code + (http://www.logilab.org/projects/pyreverse), miscellaneous bug fixes + + * configuration: new 'usage' parameter on the Configuration + initializer + + * logger: unicode support + + * fileutils: get_by_ext also ignore ".svn" directories, not only "CVS" + + + +2004-11-03 -- 0.7.1 + * astng: + + - don't raise a syntax error on files missing a trailing \n. + - fix utils.is_abstract (was causing an unexpected exception if a + string exception was raised). + - fix utils.get_implemented. + - fix file based manager's cache problem. + + * textutils: fixed normalize_text / normalize_paragraph functions + + + +2004-10-11 -- 0.7.0 + * astng: new methods on the manager, returning astng with nodes for + packages (i.e. recursive structure instead of the flat one), with + automatic lazy loading + introduction of a dict like interface to + manipulate those nodes and Module, Class and Function nodes. + + * logservice: module imported from the ginco project + + * configuration: added new classes Configuration and + OptionsManager2Configuration adapter, fix bug in loading options + from file + + * optik_ext/configuration: some new option type "multiple_choice" + + * fileutils: new ensure_mode function + + * compat: support for sum and enumerate + + + +2004-09-23 -- 0.6.0 + * db: added DBAPIAdapter + + * textutils: fix in pretty_match causing malformated messages in pylint + added ansi colorization management + + * modutils: new functions get_module_files, has_module and file_from_modpath + + * astng: some new utility functions taken from pylint, minor changes to the + manager API, Node.resolve doesn't support anymore "living" resolution, + some new methods on astng nodes + + * compat: new module for a transparent compatibility layer between + different python version (actually 2.2 vs 2.3 for now) + + + +2004-07-08 -- 0.5.2 + * astng: fix another bug in klassnode.ancestors() method... + + * db: fix mysql access + + * cli: added a space after the prompt + + + +2004-06-04 -- 0.5.1 + * astng: fix undefined var bug in klassnode.ancestors() method + + * ureports: fix attributes on title layout + + * packaging:fix the setup.py script to allow bdist_winst (well, the + generated installer has not been tested...) with the necessary + logilab/__init__.py file + + + +2004-05-10 -- 0.5.0 + * ureports: new Universal Reports sub-package + + * xmlrpcutils: new xmlrpc utilities module + + * astng: resolve(name) now handle (at least try) builtins + + * astng: fixed Class.as_string (empty parent when no base classes) + + * astng.builder: knows a little about method descriptors, Function with + unknown arguments have argnames==None. + + * fileutils: new is_binary(filename) function + + * textutils: fixed some Windows bug + + * tree: base not doesn't have the "title" attribute anymore + + * testlib: removed the spawn function (who used that ?!), added MockSMTP, + MockConfigParser, MockConnexion and DocTestCase (test class for + modules embedding doctest). All mocks objects are very basic and will be + enhanced as the need comes. + + * testlib: added a TestCase class with some additional methods then + the regular unittest.TestCase class + + * cli: allow specifying a command prefix by a class attributes,more + robust, print available commands on help + + * db: new "binary" function to get the binary wrapper for a given driver, + and new "system_database" function returning the system database name + for different DBMS. + + * configuration: better group control + + + +2004-02-20 -- 0.4.5 + * db: it's now possible to fix the modules search order. By default call + set_isolation_level if psycopg is used + + + +2004-02-17 -- 0.4.4 + * modutils: special case for os.path in get_module_part + + * astng: 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 in Node.resolve + + * fileutils: new get_by_ext function + + + +2004-02-11 -- 0.4.3 + * astng: refactoring of Class.ancestor_for_* methods (now + depends on python 2.2 generators) + + * astng: make it more robust + + * configuration: more explicit exception when a bad option is + provided + + * configuration: define a short version of an option using the "short" + keyword, taking a single letter as value + + * configuration: new method global_set_option on the manager + + * testlib : allow no "suite" nor "Run" function in test modules + + * shellutils: fix bug in *mv* + + + +2003-12-23 -- 0.4.2 + * added Project class and some new methods to the ASTNGManger + + * some new functions in astng.utils + + * fixed bugs in some as_string methods + + * fixed bug in textutils.get_csv + + * fileutils.lines now take a "comments" argument, allowing to ignore + comment lines + + + +2003-11-24 -- 0.4.1 + * added missing as_string methods on astng nodes + + * bug fixes on Node.resolve + + * minor fixes in textutils and fileutils + + * better test coverage (need more !) + + + +2003-11-13 -- 0.4.0 + * new textutils and shellutils modules + + * full astng rewrite, now based on the compiler.ast package from the + standard library + + * added next_sbling and previous_sibling methods to Node + + * fix get_cycles + + + +2003-10-14 -- 0.3.5 + * fixed null size cache bug + + * added 'sort_by_column*' methods for tables + + + +2003-10-08 -- 0.3.4 + * fix bug in asntg, occurring with python2.3 and modules including an + encoding declaration + + * fix bug in astutils.get_rhs_consumed_names, occurring in lists + comprehension + + * remove debug print statement from configuration.py which caused a + generation of incorrect configuration files. + + + +2003-10-01 -- 0.3.3 + * fix bug in modutils.modpath_from_file + + * new module corbautils + + + +2003-09-18 -- 0.3.2 + * fix bug in modutils.load_module_from_parts + + * add missing __future__ imports + + + +2003-09-18 -- 0.3.1 + * change implementation of modutils.load_module_from_name (use find_module + and load_module instead of __import__) + + * more bug fixes in astng + + * new functions in fileutils (lines, export) and __init__ (Execute) + + + +2003-09-12 -- 0.3 + * expect "def suite" or "def Run(runner=None)" on unittest module + + * fixes in modutils + + * major fixes in astng + + * new fileutils and astutils modules + + * enhancement of the configuration module + + * new option type "named" in optik_the ext module + + + +2003-06-18 -- 0.2.2 + * astng bug fixes + + + +2003-06-04 -- 0.2.1 + * bug fixes + + * fix packaging problem + + + +2003-06-02 -- 0.2.0 + * add the interface, modutils, optik_ext and configuration modules + + * add the astng sub-package + + * miscellaneous fixes + + + +2003-04-17 -- 0.1.2 + * add the stringio module + + * minor fixes + + + +2003-02-28 -- 0.1.1 + * fix bug in tree.py + + * new file distutils_twisted + + + +2003-02-17 -- 0.1.0 + * initial revision + + + diff --git a/pymode/libs/logilab-common-1.4.1/MANIFEST.in b/pymode/libs/logilab-common-1.4.1/MANIFEST.in new file mode 100644 index 00000000..faee190f --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/MANIFEST.in @@ -0,0 +1,14 @@ +include ChangeLog +include README* +include COPYING +include COPYING.LESSER +include bin/logilab-pytest +include bin/logilab-pytest.bat +include test/data/ChangeLog +recursive-include test *.py *.txt *.msg *.ini *.zip *.egg +recursive-include test/data/*_dir * +recursive-include test/input *.py +recursive-include doc/html * +include doc/logilab-pytest.1 +include doc/makefile +include __pkginfo__.py diff --git a/pymode/libs/logilab-common-1.4.1/PKG-INFO b/pymode/libs/logilab-common-1.4.1/PKG-INFO new file mode 100644 index 00000000..9dca2cdd --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/PKG-INFO @@ -0,0 +1,164 @@ +Metadata-Version: 1.1 +Name: logilab-common +Version: 1.4.1 +Summary: collection of low-level Python packages and modules used by Logilab projects +Home-page: http://www.logilab.org/project/logilab-common +Author: Logilab +Author-email: contact@logilab.fr +License: LGPL +Description: Logilab's common library + ======================== + + What's this ? + ------------- + + This package contains some modules used by different Logilab projects. + + It is released under the GNU Lesser General Public License. + + There is no documentation available yet but the source code should be clean and + well documented. + + Designed to ease: + + * handling command line options and configuration files + * writing interactive command line tools + * manipulation of files and character strings + * manipulation of common structures such as graph, tree, and pattern such as visitor + * generating text and HTML reports + * more... + + + Installation + ------------ + + Extract the tarball, jump into the created directory and run :: + + python setup.py install + + For installation options, see :: + + python setup.py install --help + + + Provided modules + ---------------- + + Here is a brief description of the available modules. + + Modules providing high-level features + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * `cache`, a cache implementation with a least recently used algorithm. + + * `changelog`, a tiny library to manipulate our simplified ChangeLog file format. + + * `clcommands`, high-level classes to define command line programs handling + different subcommands. It is based on `configuration` to get easy command line + / configuration file handling. + + * `configuration`, some classes to handle unified configuration from both + command line (using optparse) and configuration file (using ConfigParser). + + * `proc`, interface to Linux /proc. + + * `umessage`, unicode email support. + + * `ureports`, micro-reports, a way to create simple reports using python objects + without care of the final formatting. ReST and html formatters are provided. + + + Modules providing low-level functions and structures + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * `compat`, provides a transparent compatibility layer between different python + versions. + + * `date`, a set of date manipulation functions. + + * `daemon`, a daemon function and mix-in class to properly start an Unix daemon + process. + + * `decorators`, function decorators such as cached, timed... + + * `deprecation`, decorator, metaclass & all to mark functions / classes as + deprecated or moved + + * `fileutils`, some file / file path manipulation utilities. + + * `graph`, graph manipulations functions such as cycle detection, bases for dot + file generation. + + * `modutils`, python module manipulation functions. + + * `shellutils`, some powerful shell like functions to replace shell scripts with + python scripts. + + * `tasksqueue`, a prioritized tasks queue implementation. + + * `textutils`, some text manipulation functions (ansi colorization, line wrapping, + rest support...). + + * `tree`, base class to represent tree structure, and some others to make it + works with the visitor implementation (see below). + + * `visitor`, a generic visitor pattern implementation. + + + Modules extending some standard modules + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * `debugger`, `pdb` customization. + + * `logging_ext`, extensions to `logging` module such as a colorized formatter + and an easier initialization function. + + * `optik_ext`, defines some new option types (regexp, csv, color, date, etc.) + for `optik` / `optparse` + + + Modules extending some external modules + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * `sphinx_ext`, Sphinx_ plugin defining a `autodocstring` directive. + + * `vcgutils` , utilities functions to generate file readable with Georg Sander's + vcg tool (Visualization of Compiler Graphs). + + + To be deprecated modules + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Those `logilab.common` modules will much probably be deprecated in future + versions: + + * `testlib`: use `unittest2`_ instead + * `interface`: use `zope.interface`_ if you really want this + * `table`, `xmlutils`: is that used? + * `sphinxutils`: we won't go that way imo (i == syt) + + + Comments, support, bug reports + ------------------------------ + + Project page https://www.logilab.org/project/logilab-common + + Use the python-projects@lists.logilab.org mailing list. + + You can subscribe to this mailing list at + https://lists.logilab.org/mailman/listinfo/python-projects + + Archives are available at + https://lists.logilab.org/pipermail/python-projects/ + + + .. _Sphinx: http://sphinx.pocoo.org/ + .. _`unittest2`: http://pypi.python.org/pypi/unittest2 + .. _`discover`: http://pypi.python.org/pypi/discover + .. _`zope.interface`: http://pypi.python.org/pypi/zope.interface + +Platform: UNKNOWN +Classifier: Topic :: Utilities +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst b/pymode/libs/logilab-common-1.4.1/README similarity index 99% rename from pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst rename to pymode/libs/logilab-common-1.4.1/README index 6b483af3..21cbe78d 100644 --- a/pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst +++ b/pymode/libs/logilab-common-1.4.1/README @@ -125,7 +125,6 @@ Those `logilab.common` modules will much probably be deprecated in future versions: * `testlib`: use `unittest2`_ instead -* `pytest`: use `discover`_ instead * `interface`: use `zope.interface`_ if you really want this * `table`, `xmlutils`: is that used? * `sphinxutils`: we won't go that way imo (i == syt) @@ -149,5 +148,3 @@ https://lists.logilab.org/pipermail/python-projects/ .. _`unittest2`: http://pypi.python.org/pypi/unittest2 .. _`discover`: http://pypi.python.org/pypi/discover .. _`zope.interface`: http://pypi.python.org/pypi/zope.interface - - diff --git a/pymode/libs/logilab-common-1.4.1/__pkginfo__.py b/pymode/libs/logilab-common-1.4.1/__pkginfo__.py new file mode 100644 index 00000000..b9f652fb --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/__pkginfo__.py @@ -0,0 +1,61 @@ +# 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 logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""logilab.common packaging information""" +__docformat__ = "restructuredtext en" +import sys +import os + +distname = 'logilab-common' +modname = 'common' +subpackage_of = 'logilab' +subpackage_master = True + +numversion = (1, 4, 1) +version = '.'.join([str(num) for num in numversion]) + +license = 'LGPL' # 2.1 or later +description = "collection of low-level Python packages and modules used by Logilab projects" +web = "http://www.logilab.org/project/%s" % distname +mailinglist = "mailto://python-projects@lists.logilab.org" +author = "Logilab" +author_email = "contact@logilab.fr" + + +from os.path import join +scripts = [join('bin', 'logilab-pytest')] +include_dirs = [join('test', 'data')] + +install_requires = [ + 'setuptools', + 'six >= 1.4.0', +] +tests_require = [ + 'pytz', + 'egenix-mx-base', +] + +if sys.version_info < (2, 7): + install_requires.append('unittest2 >= 0.5.1') +if os.name == 'nt': + install_requires.append('colorama') + +classifiers = ["Topic :: Utilities", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + ] diff --git a/pymode/libs/logilab-common-1.4.1/bin/logilab-pytest b/pymode/libs/logilab-common-1.4.1/bin/logilab-pytest new file mode 100755 index 00000000..42df3028 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/bin/logilab-pytest @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +import warnings +warnings.simplefilter('default', DeprecationWarning) + +from logilab.common.pytest import run +run() diff --git a/pymode/libs/logilab-common-1.4.1/bin/logilab-pytest.bat b/pymode/libs/logilab-common-1.4.1/bin/logilab-pytest.bat new file mode 100644 index 00000000..c664e882 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/bin/logilab-pytest.bat @@ -0,0 +1,17 @@ +@echo off +rem = """-*-Python-*- script +rem -------------------- DOS section -------------------- +rem You could set PYTHONPATH or TK environment variables here +python -x "%~f0" %* +goto exit + +""" +# -------------------- Python section -------------------- +from logilab.common.pytest import run +run() + +DosExitLabel = """ +:exit +rem """ + + diff --git a/pymode/libs/logilab-common-1.4.1/doc/logilab-pytest.1 b/pymode/libs/logilab-common-1.4.1/doc/logilab-pytest.1 new file mode 100644 index 00000000..51aec2e9 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/doc/logilab-pytest.1 @@ -0,0 +1,54 @@ +.TH logilab-pytest "1" "January 2008" logilab-pytest +.SH NAME +.B logilab-pytest +\- run python unit tests + +.SH SYNOPSIS +usage: logilab-pytest [OPTIONS] [testfile [testpattern]] +.PP +examples: +.PP +logilab-pytest path/to/mytests.py +logilab-pytest path/to/mytests.py TheseTests +logilab-pytest path/to/mytests.py TheseTests.test_thisone +.PP +logilab-pytest one (will run both test_thisone and test_thatone) +logilab-pytest path/to/mytests.py \fB\-s\fR not (will skip test_notthisone) +.PP +logilab-pytest \fB\-\-coverage\fR test_foo.py +.IP +(only if logilab.devtools is available) +.SS "options:" +.TP +\fB\-h\fR, \fB\-\-help\fR +show this help message and exit +.TP +\fB\-t\fR TESTDIR +directory where the tests will be found +.TP +\fB\-d\fR +enable design\-by\-contract +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Verbose output +.TP +\fB\-i\fR, \fB\-\-pdb\fR +Enable test failure inspection (conflicts with +\fB\-\-coverage\fR) +.TP +\fB\-x\fR, \fB\-\-exitfirst\fR +Exit on first failure (only make sense when logilab-pytest run +one test file) +.TP +\fB\-s\fR SKIPPED, \fB\-\-skip\fR=\fISKIPPED\fR +test names matching this name will be skipped to skip +several patterns, use commas +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Minimal output +.TP +\fB\-P\fR PROFILE, \fB\-\-profile\fR=\fIPROFILE\fR +Profile execution and store data in the given file +.TP +\fB\-\-coverage\fR +run tests with pycoverage (conflicts with \fB\-\-pdb\fR) diff --git a/pymode/libs/logilab-common-1.4.1/doc/makefile b/pymode/libs/logilab-common-1.4.1/doc/makefile new file mode 100644 index 00000000..02f5d544 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/doc/makefile @@ -0,0 +1,8 @@ +all: epydoc + +epydoc: + mkdir -p apidoc + -epydoc --parse-only -o apidoc --html -v --no-private --exclude='test' --exclude="__pkginfo__" --exclude="setup" -n "Logilab's common library" $(shell dirname $(CURDIR))/build/lib/logilab/common >/dev/null + +clean: + rm -rf apidoc diff --git a/pymode/libs/logilab-common-1.4.1/logilab/__init__.py b/pymode/libs/logilab-common-1.4.1/logilab/__init__.py new file mode 100644 index 00000000..de40ea7c --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/logilab/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/pymode/libs/logilab/common/__init__.py b/pymode/libs/logilab-common-1.4.1/logilab/common/__init__.py similarity index 98% rename from pymode/libs/logilab/common/__init__.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/__init__.py index fc01e4df..796831a7 100644 --- a/pymode/libs/logilab/common/__init__.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/__init__.py @@ -38,7 +38,7 @@ __pkginfo__.version = __version__ sys.modules['logilab.common.__pkginfo__'] = __pkginfo__ -STD_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build') +STD_BLACKLIST = ('CVS', '.svn', '.hg', '.git', '.tox', 'debian', 'dist', 'build') IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc', '~', '.swp', '.orig') diff --git a/pymode/libs/logilab/common/cache.py b/pymode/libs/logilab-common-1.4.1/logilab/common/cache.py similarity index 100% rename from pymode/libs/logilab/common/cache.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/cache.py diff --git a/pymode/libs/logilab/common/changelog.py b/pymode/libs/logilab-common-1.4.1/logilab/common/changelog.py similarity index 82% rename from pymode/libs/logilab/common/changelog.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/changelog.py index 2fff2ed6..3f62bd4c 100644 --- a/pymode/libs/logilab/common/changelog.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/changelog.py @@ -3,18 +3,18 @@ # # This file is part of logilab-common. # -# logilab-common is free software: you can redistribute it and/or modify it under +# logilab-common is free software: you can redistribute it 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. +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. # # logilab-common 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-common. If not, see . +# You should have received a copy of the GNU Lesser General Public License +# along with logilab-common. If not, see . """Manipulation of upstream change log files. The upstream change log files format handled is simpler than the one @@ -48,6 +48,7 @@ import sys from stat import S_IWRITE +import codecs from six import string_types @@ -55,19 +56,22 @@ SUBBULLET = '-' INDENT = ' ' * 4 + class NoEntry(Exception): """raised when we are unable to find an entry""" + class EntryNotFound(Exception): """raised when we are unable to find a given entry""" + class Version(tuple): """simple class to handle soft version number has a tuple while correctly printing it as X.Y.Z """ def __new__(cls, versionstr): if isinstance(versionstr, string_types): - versionstr = versionstr.strip(' :') # XXX (syt) duh? + versionstr = versionstr.strip(' :') # XXX (syt) duh? parsed = cls.parse(versionstr) else: parsed = versionstr @@ -79,11 +83,13 @@ def parse(cls, versionstr): try: return [int(i) for i in versionstr.split('.')] except ValueError as ex: - raise ValueError("invalid literal for version '%s' (%s)"%(versionstr, ex)) + raise ValueError("invalid literal for version '%s' (%s)" % + (versionstr, ex)) def __str__(self): return '.'.join([str(i) for i in self]) + # upstream change log ######################################################### class ChangeLogEntry(object): @@ -109,44 +115,50 @@ def complete_latest_message(self, msg_suite): """complete the latest added message """ if not self.messages: - raise ValueError('unable to complete last message as there is no previous message)') - if self.messages[-1][1]: # sub messages + raise ValueError('unable to complete last message as ' + 'there is no previous message)') + if self.messages[-1][1]: # sub messages self.messages[-1][1][-1].append(msg_suite) - else: # message + else: # message self.messages[-1][0].append(msg_suite) def add_sub_message(self, sub_msg, key=None): if not self.messages: - raise ValueError('unable to complete last message as there is no previous message)') + raise ValueError('unable to complete last message as ' + 'there is no previous message)') if key is None: self.messages[-1][1].append([sub_msg]) else: - raise NotImplementedError("sub message to specific key are not implemented yet") + raise NotImplementedError('sub message to specific key ' + 'are not implemented yet') def write(self, stream=sys.stdout): """write the entry to file """ - stream.write('%s -- %s\n' % (self.date or '', self.version or '')) + stream.write(u'%s -- %s\n' % (self.date or '', self.version or '')) for msg, sub_msgs in self.messages: - stream.write('%s%s %s\n' % (INDENT, BULLET, msg[0])) - stream.write(''.join(msg[1:])) + stream.write(u'%s%s %s\n' % (INDENT, BULLET, msg[0])) + stream.write(u''.join(msg[1:])) if sub_msgs: - stream.write('\n') + stream.write(u'\n') for sub_msg in sub_msgs: - stream.write('%s%s %s\n' % (INDENT * 2, SUBBULLET, sub_msg[0])) - stream.write(''.join(sub_msg[1:])) - stream.write('\n') + stream.write(u'%s%s %s\n' % + (INDENT * 2, SUBBULLET, sub_msg[0])) + stream.write(u''.join(sub_msg[1:])) + stream.write(u'\n') + + stream.write(u'\n\n') - stream.write('\n\n') class ChangeLog(object): """object representation of a whole ChangeLog file""" entry_class = ChangeLogEntry - def __init__(self, changelog_file, title=''): + def __init__(self, changelog_file, title=u''): self.file = changelog_file + assert isinstance(title, type(u'')), 'title must be a unicode object' self.title = title - self.additional_content = '' + self.additional_content = u'' self.entries = [] self.load() @@ -184,12 +196,12 @@ def add(self, msg, create=None): def load(self): """ read a logilab's ChangeLog from file """ try: - stream = open(self.file) + stream = codecs.open(self.file, encoding='utf-8') except IOError: return last = None expect_sub = False - for line in stream.readlines(): + for line in stream: sline = line.strip() words = sline.split() # if new entry @@ -221,18 +233,17 @@ def load(self): stream.close() def format_title(self): - return '%s\n\n' % self.title.strip() + return u'%s\n\n' % self.title.strip() def save(self): """write back change log""" # filetutils isn't importable in appengine, so import locally from logilab.common.fileutils import ensure_fs_mode ensure_fs_mode(self.file, S_IWRITE) - self.write(open(self.file, 'w')) + self.write(codecs.open(self.file, 'w', encoding='utf-8')) def write(self, stream=sys.stdout): """write changelog to stream""" stream.write(self.format_title()) for entry in self.entries: entry.write(stream) - diff --git a/pymode/libs/logilab/common/clcommands.py b/pymode/libs/logilab-common-1.4.1/logilab/common/clcommands.py similarity index 100% rename from pymode/libs/logilab/common/clcommands.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/clcommands.py diff --git a/pymode/libs/logilab/common/compat.py b/pymode/libs/logilab-common-1.4.1/logilab/common/compat.py similarity index 100% rename from pymode/libs/logilab/common/compat.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/compat.py diff --git a/pymode/libs/logilab/common/configuration.py b/pymode/libs/logilab-common-1.4.1/logilab/common/configuration.py similarity index 98% rename from pymode/libs/logilab/common/configuration.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/configuration.py index b2924277..7a54f1af 100644 --- a/pymode/libs/logilab/common/configuration.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/configuration.py @@ -122,7 +122,7 @@ from copy import copy from warnings import warn -from six import string_types +from six import integer_types, string_types from six.moves import range, configparser as cp, input from logilab.common.compat import str_encode as _encode @@ -372,7 +372,7 @@ def format_option_value(optdict, value): value = value and 'yes' or 'no' elif isinstance(value, string_types) and value.isspace(): value = "'%s'" % value - elif optdict.get('type') == 'time' and isinstance(value, (float, int, long)): + elif optdict.get('type') == 'time' and isinstance(value, (float, ) + integer_types): value = format_time(value) elif optdict.get('type') == 'bytes' and hasattr(value, '__int__'): value = format_bytes(value) @@ -401,6 +401,9 @@ def ini_format(stream, options, encoding): print('#%s=' % optname, file=stream) else: value = _encode(value, encoding).strip() + if optdict.get('type') == 'string' and '\n' in value: + prefix = '\n ' + value = prefix + prefix.join(value.split('\n')) print('%s=%s' % (optname, value), file=stream) format_section = ini_format_section @@ -635,7 +638,7 @@ def helpfunc(option, opt, val, p, level=helplevel): parser = self.cfgfile_parser parser.read([config_file]) # normalize sections'title - for sect, values in parser._sections.items(): + for sect, values in list(parser._sections.items()): if not sect.isupper() and values: parser._sections[sect.upper()] = values elif not self.quiet: @@ -909,7 +912,7 @@ def options_by_section(self): (optname, optdict, self.option_value(optname))) if None in sections: yield None, sections.pop(None) - for section, options in sections.items(): + for section, options in sorted(sections.items()): yield section.upper(), options def options_and_values(self, options=None): @@ -946,15 +949,15 @@ def register_options(self, options): options_by_group = {} for optname, optdict in options: options_by_group.setdefault(optdict.get('group', self.name.upper()), []).append((optname, optdict)) - for group, options in options_by_group.items(): - self.add_option_group(group, None, options, self) + for group, group_options in options_by_group.items(): + self.add_option_group(group, None, group_options, self) self.options += tuple(options) def load_defaults(self): OptionsProviderMixIn.load_defaults(self) def __iter__(self): - return iter(self.config.__dict__.iteritems()) + return iter(self.config.__dict__.items()) def __getitem__(self, key): try: @@ -1042,7 +1045,7 @@ def read_old_config(newconfig, changes, configfile): option, oldtype, newvalue = action[1:] changesindex.setdefault(option, []).append((action[0], oldtype, newvalue)) continue - if action[1] in ('added', 'removed'): + if action[0] in ('added', 'removed'): continue # nothing to do here raise Exception('unknown change %s' % action[0]) # build a config object able to read the old config diff --git a/pymode/libs/logilab/common/daemon.py b/pymode/libs/logilab-common-1.4.1/logilab/common/daemon.py similarity index 100% rename from pymode/libs/logilab/common/daemon.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/daemon.py diff --git a/pymode/libs/logilab/common/date.py b/pymode/libs/logilab-common-1.4.1/logilab/common/date.py similarity index 99% rename from pymode/libs/logilab/common/date.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/date.py index a093a8a9..1d13a770 100644 --- a/pymode/libs/logilab/common/date.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/date.py @@ -237,7 +237,7 @@ def todatetime(somedate): return datetime(somedate.year, somedate.month, somedate.day) def datetime2ticks(somedate): - return timegm(somedate.timetuple()) * 1000 + return timegm(somedate.timetuple()) * 1000 + int(getattr(somedate, 'microsecond', 0) / 1000) def ticks2datetime(ticks): miliseconds, microseconds = divmod(ticks, 1000) diff --git a/pymode/libs/logilab/common/debugger.py b/pymode/libs/logilab-common-1.4.1/logilab/common/debugger.py similarity index 100% rename from pymode/libs/logilab/common/debugger.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/debugger.py diff --git a/pymode/libs/logilab/common/decorators.py b/pymode/libs/logilab-common-1.4.1/logilab/common/decorators.py similarity index 100% rename from pymode/libs/logilab/common/decorators.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/decorators.py diff --git a/pymode/libs/logilab/common/deprecation.py b/pymode/libs/logilab-common-1.4.1/logilab/common/deprecation.py similarity index 100% rename from pymode/libs/logilab/common/deprecation.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/deprecation.py diff --git a/pymode/libs/logilab/common/fileutils.py b/pymode/libs/logilab-common-1.4.1/logilab/common/fileutils.py similarity index 97% rename from pymode/libs/logilab/common/fileutils.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/fileutils.py index b30cf5f8..93439d3b 100644 --- a/pymode/libs/logilab/common/fileutils.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/fileutils.py @@ -28,6 +28,7 @@ __docformat__ = "restructuredtext en" +import io import sys import shutil import mimetypes @@ -67,13 +68,7 @@ def first_level_directory(path): return head def abspath_listdir(path): - """Lists path's content using absolute paths. - - >>> os.listdir('/home') - ['adim', 'alf', 'arthur', 'auc'] - >>> abspath_listdir('/home') - ['/home/adim', '/home/alf', '/home/arthur', '/home/auc'] - """ + """Lists path's content using absolute paths.""" path = abspath(path) return [join(path, filename) for filename in listdir(path)] @@ -288,10 +283,8 @@ def lines(path, comments=None): :warning: at some point this function will probably return an iterator """ - stream = open(path, 'U') - result = stream_lines(stream, comments) - stream.close() - return result + with io.open(path) as stream: + return stream_lines(stream, comments) def stream_lines(stream, comments=None): diff --git a/pymode/libs/logilab/common/graph.py b/pymode/libs/logilab-common-1.4.1/logilab/common/graph.py similarity index 100% rename from pymode/libs/logilab/common/graph.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/graph.py diff --git a/pymode/libs/logilab/common/interface.py b/pymode/libs/logilab-common-1.4.1/logilab/common/interface.py similarity index 100% rename from pymode/libs/logilab/common/interface.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/interface.py diff --git a/pymode/libs/logilab/common/logging_ext.py b/pymode/libs/logilab-common-1.4.1/logilab/common/logging_ext.py similarity index 100% rename from pymode/libs/logilab/common/logging_ext.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/logging_ext.py diff --git a/pymode/libs/logilab/common/modutils.py b/pymode/libs/logilab-common-1.4.1/logilab/common/modutils.py similarity index 93% rename from pymode/libs/logilab/common/modutils.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/modutils.py index dd725d24..030cfa3b 100644 --- a/pymode/libs/logilab/common/modutils.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/modutils.py @@ -32,12 +32,14 @@ import sys import os -from os.path import splitext, join, abspath, isdir, dirname, exists, basename +from os.path import (splitext, join, abspath, isdir, dirname, exists, + basename, expanduser, normcase, realpath) from imp import find_module, load_module, C_BUILTIN, PY_COMPILED, PKG_DIRECTORY from distutils.sysconfig import get_config_var, get_python_lib, get_python_version from distutils.errors import DistutilsPlatformError -from six.moves import range +from six import PY3 +from six.moves import map, range try: import zipimport @@ -47,6 +49,7 @@ ZIPFILE = object() from logilab.common import STD_BLACKLIST, _handle_blacklist +from logilab.common.deprecation import deprecated # Notes about STD_LIB_DIR # Consider arch-specific installation for STD_LIB_DIR definition @@ -165,7 +168,11 @@ def load_module_from_modpath(parts, path=None, use_sys=True): 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) + try: + module = load_module(curname, mp_file, mp_filename, mp_desc) + finally: + if mp_file is not None: + mp_file.close() if prevmodule: setattr(prevmodule, part, module) _file = getattr(module, '__file__', '') @@ -215,8 +222,24 @@ def _check_init(path, mod_path): return True +def _canonicalize_path(path): + return realpath(expanduser(path)) + + +def _path_from_filename(filename): + if PY3: + return filename + else: + if filename.endswith(".pyc"): + return filename[:-1] + return filename + + +@deprecated('you should avoid using modpath_from_file()') def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding splitted module's name + """DEPRECATED: doens't play well with symlinks and sys.meta_path + + Given a file path return the corresponding splitted module's name (i.e name of a module or package splitted on '.') :type filename: str @@ -235,26 +258,29 @@ def modpath_from_file(filename, extrapath=None): :rtype: list(str) :return: the corresponding splitted module's name """ - base = splitext(abspath(filename))[0] + filename = _path_from_filename(filename) + filename = _canonicalize_path(filename) + base = os.path.splitext(filename)[0] + if extrapath is not None: - for path_ in extrapath: + for path_ in map(_canonicalize_path, extrapath): path = abspath(path_) - if path and base[:len(path)] == path: + if path and normcase(base[:len(path)]) == normcase(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): + + for path in map(_canonicalize_path, sys.path): + if path and normcase(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 @@ -471,6 +497,18 @@ def cleanup_sys_modules(directories): return cleaned +def clean_sys_modules(names): + """remove submodules starting with name from `names` from `sys.modules`""" + cleaned = set() + for modname in list(sys.modules): + for name in names: + if modname.startswith(name): + del sys.modules[modname] + cleaned.add(modname) + break + return cleaned + + def is_python_source(filename): """ rtype: bool @@ -632,7 +670,9 @@ def _module_file(modpath, path=None): # 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__ + # use list() to protect against _NamespacePath instance we get with python 3, which + # find_module later doesn't like + path = list(module.__path__) if not modpath: return C_BUILTIN, None imported = [] diff --git a/pymode/libs/logilab/common/optik_ext.py b/pymode/libs/logilab-common-1.4.1/logilab/common/optik_ext.py similarity index 98% rename from pymode/libs/logilab/common/optik_ext.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/optik_ext.py index 1fd2a7f8..95489c28 100644 --- a/pymode/libs/logilab/common/optik_ext.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/optik_ext.py @@ -56,6 +56,8 @@ from copy import copy from os.path import exists +from six import integer_types + # python >= 2.3 from optparse import OptionParser as BaseParser, Option as BaseOption, \ OptionGroup, OptionContainer, OptionValueError, OptionError, \ @@ -169,14 +171,14 @@ def check_color(option, opt, value): raise OptionValueError(msg % (opt, value)) def check_time(option, opt, value): - if isinstance(value, (int, long, float)): + if isinstance(value, integer_types + (float,)): return value return apply_units(value, TIME_UNITS) def check_bytes(option, opt, value): if hasattr(value, '__int__'): return value - return apply_units(value, BYTE_UNITS) + return apply_units(value, BYTE_UNITS, final=int) class Option(BaseOption): diff --git a/pymode/libs/logilab/common/optparser.py b/pymode/libs/logilab-common-1.4.1/logilab/common/optparser.py similarity index 100% rename from pymode/libs/logilab/common/optparser.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/optparser.py diff --git a/pymode/libs/logilab/common/proc.py b/pymode/libs/logilab-common-1.4.1/logilab/common/proc.py similarity index 100% rename from pymode/libs/logilab/common/proc.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/proc.py diff --git a/pymode/libs/logilab/common/pytest.py b/pymode/libs/logilab-common-1.4.1/logilab/common/pytest.py similarity index 85% rename from pymode/libs/logilab/common/pytest.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/pytest.py index 3d8aca34..c644a61f 100644 --- a/pymode/libs/logilab/common/pytest.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/pytest.py @@ -15,14 +15,14 @@ # # You should have received a copy of the GNU Lesser General Public License along # with logilab-common. If not, see . -"""pytest is a tool that eases test running and debugging. +"""logilab-pytest is a tool that eases test running and debugging. -To be able to use pytest, you should either write tests using +To be able to use logilab-pytest, you should either write tests using the logilab.common.testlib's framework or the unittest module of the Python's standard library. -You can customize pytest's behaviour by defining a ``pytestconf.py`` file -somewhere in your test directory. In this file, you can add options or +You can customize logilab-pytest's behaviour by defining a ``pytestconf.py`` +file somewhere in your test directory. In this file, you can add options or change the way tests are run. To add command line options, you must define a ``update_parser`` function in @@ -31,8 +31,8 @@ If you wish to customize the tester, you'll have to define a class named ``CustomPyTester``. This class should extend the default `PyTester` class -defined in the pytest module. Take a look at the `PyTester` and `DjangoTester` -classes for more information about what can be done. +defined in the logilab.common.pytest module. Take a look at the `PyTester` and +`DjangoTester` classes for more information about what can be done. For instance, if you wish to add a custom -l option to specify a loglevel, you could define the following ``pytestconf.py`` file :: @@ -101,13 +101,13 @@ def titi(test): examples: -pytest path/to/mytests.py -pytest path/to/mytests.py TheseTests -pytest path/to/mytests.py TheseTests.test_thisone -pytest path/to/mytests.py -m '(not long and database) or regr' +logilab-pytest path/to/mytests.py +logilab-pytest path/to/mytests.py TheseTests +logilab-pytest path/to/mytests.py TheseTests.test_thisone +logilab-pytest path/to/mytests.py -m '(not long and database) or regr' -pytest one (will run both test_thisone and test_thatone) -pytest path/to/mytests.py -s not (will skip test_notthisone) +logilab-pytest one (will run both test_thisone and test_thatone) +logilab-pytest path/to/mytests.py -s not (will skip test_notthisone) """ ENABLE_DBC = False @@ -118,16 +118,20 @@ def titi(test): from time import time, clock import warnings import types +import inspect +import traceback from inspect import isgeneratorfunction, isclass -from contextlib import contextmanager from random import shuffle +from itertools import dropwhile +from logilab.common.deprecation import deprecated from logilab.common.fileutils import abspath_listdir from logilab.common import textutils from logilab.common import testlib, STD_BLACKLIST # use the same unittest module as testlib from logilab.common.testlib import unittest, start_interactive_mode -from logilab.common.deprecation import deprecated +from logilab.common.testlib import nocoverage, pause_trace, replace_trace # bwcompat +from logilab.common.debugger import Debugger, colorize_source import doctest import unittest as unittest_legacy @@ -148,62 +152,6 @@ def titi(test): CONF_FILE = 'pytestconf.py' -## coverage pausing tools - -@contextmanager -def replace_trace(trace=None): - """A context manager that temporary replaces the trace function""" - oldtrace = sys.gettrace() - sys.settrace(trace) - try: - yield - finally: - # specific hack to work around a bug in pycoverage, see - # https://bitbucket.org/ned/coveragepy/issue/123 - if (oldtrace is not None and not callable(oldtrace) and - hasattr(oldtrace, 'pytrace')): - oldtrace = oldtrace.pytrace - sys.settrace(oldtrace) - - -def pause_trace(): - """A context manager that temporary pauses any tracing""" - return replace_trace() - -class TraceController(object): - ctx_stack = [] - - @classmethod - @deprecated('[lgc 0.63.1] Use the pause_trace() context manager') - def pause_tracing(cls): - cls.ctx_stack.append(pause_trace()) - cls.ctx_stack[-1].__enter__() - - @classmethod - @deprecated('[lgc 0.63.1] Use the pause_trace() context manager') - def resume_tracing(cls): - cls.ctx_stack.pop().__exit__(None, None, None) - - -pause_tracing = TraceController.pause_tracing -resume_tracing = TraceController.resume_tracing - - -def nocoverage(func): - """Function decorator that pauses tracing functions""" - if hasattr(func, 'uncovered'): - return func - func.uncovered = True - - def not_covered(*args, **kwargs): - with pause_trace(): - return func(*args, **kwargs) - not_covered.uncovered = True - return not_covered - -## end of coverage pausing tools - - TESTFILE_RE = re.compile("^((unit)?test.*|smoketest)\.py$") def this_is_a_testfile(filename): """returns True if `filename` seems to be a test file""" @@ -611,12 +559,12 @@ def capture_and_rebuild(option, opt, value, parser): warnings.simplefilter('ignore', DeprecationWarning) rebuild_cmdline(option, opt, value, parser) - # pytest options + # logilab-pytest options parser.add_option('-t', dest='testdir', default=None, help="directory where the tests will be found") parser.add_option('-d', dest='dbc', default=False, action="store_true", help="enable design-by-contract") - # unittest_main options provided and passed through pytest + # unittest_main options provided and passed through logilab-pytest parser.add_option('-v', '--verbose', callback=rebuild_cmdline, action="callback", help="Verbose output") parser.add_option('-i', '--pdb', callback=rebuild_and_store, @@ -625,7 +573,7 @@ def capture_and_rebuild(option, opt, value, parser): parser.add_option('-x', '--exitfirst', callback=rebuild_and_store, dest="exitfirst", default=False, action="callback", help="Exit on first failure " - "(only make sense when pytest run one test file)") + "(only make sense when logilab-pytest run one test file)") parser.add_option('-R', '--restart', callback=rebuild_and_store, dest="restart", default=False, action="callback", @@ -651,7 +599,7 @@ def capture_and_rebuild(option, opt, value, parser): if DJANGO_FOUND: parser.add_option('-J', '--django', dest='django', default=False, action="store_true", - help='use pytest for django test cases') + help='use logilab-pytest for django test cases') return parser @@ -684,6 +632,7 @@ def parseargs(parser): +@deprecated('[logilab-common 1.3] logilab-pytest is deprecated, use another test runner') def run(): parser = make_parser() rootdir, testercls = project_root(parser) @@ -938,7 +887,7 @@ def does_match_tags(self, test): return True # no pattern def _makeResult(self): - return testlib.SkipAwareTestResult(self.stream, self.descriptions, + return SkipAwareTestResult(self.stream, self.descriptions, self.verbosity, self.exitfirst, self.pdbmode, self.cvg, self.colorize) @@ -983,6 +932,155 @@ def run(self, test): self.stream.writeln("") return result + +class SkipAwareTestResult(unittest._TextTestResult): + + def __init__(self, stream, descriptions, verbosity, + exitfirst=False, pdbmode=False, cvg=None, colorize=False): + super(SkipAwareTestResult, self).__init__(stream, + descriptions, verbosity) + self.skipped = [] + self.debuggers = [] + self.fail_descrs = [] + self.error_descrs = [] + self.exitfirst = exitfirst + self.pdbmode = pdbmode + self.cvg = cvg + self.colorize = colorize + self.pdbclass = Debugger + self.verbose = verbosity > 1 + + def descrs_for(self, flavour): + return getattr(self, '%s_descrs' % flavour.lower()) + + def _create_pdb(self, test_descr, flavour): + self.descrs_for(flavour).append( (len(self.debuggers), test_descr) ) + if self.pdbmode: + self.debuggers.append(self.pdbclass(sys.exc_info()[2])) + + def _iter_valid_frames(self, frames): + """only consider non-testlib frames when formatting traceback""" + lgc_testlib = osp.abspath(__file__) + std_testlib = osp.abspath(unittest.__file__) + invalid = lambda fi: osp.abspath(fi[1]) in (lgc_testlib, std_testlib) + for frameinfo in dropwhile(invalid, frames): + yield frameinfo + + def _exc_info_to_string(self, err, test): + """Converts a sys.exc_info()-style tuple of values into a string. + + This method is overridden here because we want to colorize + lines if --color is passed, and display local variables if + --verbose is passed + """ + exctype, exc, tb = err + output = ['Traceback (most recent call last)'] + frames = inspect.getinnerframes(tb) + colorize = self.colorize + frames = enumerate(self._iter_valid_frames(frames)) + for index, (frame, filename, lineno, funcname, ctx, ctxindex) in frames: + filename = osp.abspath(filename) + if ctx is None: # pyc files or C extensions for instance + source = '' + else: + source = ''.join(ctx) + if colorize: + filename = textutils.colorize_ansi(filename, 'magenta') + source = colorize_source(source) + output.append(' File "%s", line %s, in %s' % (filename, lineno, funcname)) + output.append(' %s' % source.strip()) + if self.verbose: + output.append('%r == %r' % (dir(frame), test.__module__)) + output.append('') + output.append(' ' + ' local variables '.center(66, '-')) + for varname, value in sorted(frame.f_locals.items()): + output.append(' %s: %r' % (varname, value)) + if varname == 'self': # special handy processing for self + for varname, value in sorted(vars(value).items()): + output.append(' self.%s: %r' % (varname, value)) + output.append(' ' + '-' * 66) + output.append('') + output.append(''.join(traceback.format_exception_only(exctype, exc))) + return '\n'.join(output) + + def addError(self, test, err): + """err -> (exc_type, exc, tcbk)""" + exc_type, exc, _ = err + if isinstance(exc, testlib.SkipTest): + assert exc_type == SkipTest + self.addSkip(test, exc) + else: + if self.exitfirst: + self.shouldStop = True + descr = self.getDescription(test) + super(SkipAwareTestResult, self).addError(test, err) + self._create_pdb(descr, 'error') + + def addFailure(self, test, err): + if self.exitfirst: + self.shouldStop = True + descr = self.getDescription(test) + super(SkipAwareTestResult, self).addFailure(test, err) + self._create_pdb(descr, 'fail') + + def addSkip(self, test, reason): + self.skipped.append((test, reason)) + if self.showAll: + self.stream.writeln("SKIPPED") + elif self.dots: + self.stream.write('S') + + def printErrors(self): + super(SkipAwareTestResult, self).printErrors() + self.printSkippedList() + + def printSkippedList(self): + # format (test, err) compatible with unittest2 + for test, err in self.skipped: + descr = self.getDescription(test) + self.stream.writeln(self.separator1) + self.stream.writeln("%s: %s" % ('SKIPPED', descr)) + self.stream.writeln("\t%s" % err) + + def printErrorList(self, flavour, errors): + for (_, descr), (test, err) in zip(self.descrs_for(flavour), errors): + self.stream.writeln(self.separator1) + self.stream.writeln("%s: %s" % (flavour, descr)) + self.stream.writeln(self.separator2) + self.stream.writeln(err) + self.stream.writeln('no stdout'.center(len(self.separator2))) + self.stream.writeln('no stderr'.center(len(self.separator2))) + + +from .decorators import monkeypatch +orig_call = testlib.TestCase.__call__ +@monkeypatch(testlib.TestCase, '__call__') +def call(self, result=None, runcondition=None, options=None): + orig_call(self, result=result, runcondition=runcondition, options=options) + if hasattr(options, "exitfirst") and options.exitfirst: + # add this test to restart file + try: + restartfile = open(FILE_RESTART, 'a') + try: + descr = '.'.join((self.__class__.__module__, + self.__class__.__name__, + self._testMethodName)) + restartfile.write(descr+os.linesep) + finally: + restartfile.close() + except Exception: + print("Error while saving succeeded test into", + osp.join(os.getcwd(), FILE_RESTART), + file=sys.__stderr__) + raise + + +@monkeypatch(testlib.TestCase) +def defaultTestResult(self): + """return a new instance of the defaultTestResult""" + return SkipAwareTestResult() + + class NonStrictTestLoader(unittest.TestLoader): """ Overrides default testloader to be able to omit classname when @@ -1186,7 +1284,7 @@ def enable_dbc(*args): # monkeypatch unittest and doctest (ouch !) -unittest._TextTestResult = testlib.SkipAwareTestResult +unittest._TextTestResult = SkipAwareTestResult unittest.TextTestRunner = SkipAwareTextTestRunner unittest.TestLoader = NonStrictTestLoader unittest.TestProgram = SkipAwareTestProgram @@ -1200,3 +1298,7 @@ def enable_dbc(*args): unittest.FunctionTestCase.__bases__ = (testlib.TestCase,) unittest.TestSuite.run = _ts_run unittest.TestSuite._wrapped_run = _ts_wrapped_run + +if __name__ == '__main__': + run() + diff --git a/pymode/libs/logilab/common/registry.py b/pymode/libs/logilab-common-1.4.1/logilab/common/registry.py similarity index 95% rename from pymode/libs/logilab/common/registry.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/registry.py index 86a85f94..07d43532 100644 --- a/pymode/libs/logilab/common/registry.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/registry.py @@ -58,8 +58,11 @@ Predicates ---------- .. autoclass:: Predicate -.. autofunc:: objectify_predicate +.. autofunction:: objectify_predicate .. autoclass:: yes +.. autoclass:: AndPredicate +.. autoclass:: OrPredicate +.. autoclass:: NotPredicate Debugging --------- @@ -78,6 +81,7 @@ __docformat__ = "restructuredtext en" import sys +import pkgutil import types import weakref import traceback as tb @@ -91,6 +95,7 @@ from logilab.common.modutils import modpath_from_file from logilab.common.logging_ext import set_log_methods from logilab.common.decorators import classproperty +from logilab.common.deprecation import deprecated class RegistryException(Exception): @@ -207,12 +212,22 @@ def __new__(cls, *args, **kwargs): """Add a __module__ attribute telling the module where the instance was created, for automatic registration. """ + module = kwargs.pop('__module__', None) obj = super(RegistrableInstance, cls).__new__(cls) - # XXX subclass must no override __new__ - filepath = tb.extract_stack(limit=2)[0][0] - obj.__module__ = _modname_from_path(filepath) + if module is None: + warn('instantiate {0} with ' + '__module__=__name__'.format(cls.__name__), + DeprecationWarning) + # XXX subclass must no override __new__ + filepath = tb.extract_stack(limit=2)[0][0] + obj.__module__ = _modname_from_path(filepath) + else: + obj.__module__ = module return obj + def __init__(self, __module__=None): + super(RegistrableInstance, self).__init__() + class Registry(dict): """The registry store a set of implementations associated to identifier: @@ -237,15 +252,15 @@ class Registry(dict): Registration methods: - .. automethod: register - .. automethod: unregister + .. automethod:: register + .. automethod:: unregister Selection methods: - .. automethod: select - .. automethod: select_or_none - .. automethod: possible_objects - .. automethod: object_by_id + .. automethod:: select + .. automethod:: select_or_none + .. automethod:: possible_objects + .. automethod:: object_by_id """ def __init__(self, debugmode): super(Registry, self).__init__() @@ -483,11 +498,10 @@ def f(self, arg1): Controlling object registration ------------------------------- - Dynamic loading is triggered by calling the - :meth:`register_objects` method, given a list of directories to - inspect for python modules. + Dynamic loading is triggered by calling the :meth:`register_modnames` + method, given a list of modules names to inspect. - .. automethod: register_objects + .. automethod:: register_modnames For each module, by default, all compatible objects are registered automatically. However if some objects come as replacement of @@ -672,6 +686,7 @@ def init_registration(self, path, extrapath=None): self._loadedmods = {} return filemods + @deprecated('use register_modnames() instead') def register_objects(self, path, extrapath=None): """register all objects found walking down """ # load views from each directory in the instance's path @@ -681,6 +696,23 @@ def register_objects(self, path, extrapath=None): self.load_file(filepath, modname) self.initialization_completed() + def register_modnames(self, modnames): + """register all objects found in """ + self.reset() + self._loadedmods = {} + self._toloadmods = {} + toload = [] + for modname in modnames: + filepath = pkgutil.find_loader(modname).get_filename() + if filepath[-4:] in ('.pyc', '.pyo'): + # The source file *must* exists + filepath = filepath[:-1] + self._toloadmods[modname] = filepath + toload.append((filepath, modname)) + for filepath, modname in toload: + self.load_file(filepath, modname) + self.initialization_completed() + def initialization_completed(self): """call initialization_completed() on all known registries""" for reg in self.values(): @@ -720,7 +752,6 @@ def is_reload_needed(self, path): def load_file(self, filepath, modname): """ load registrable objects (if any) from a python file """ - from logilab.common.modutils import load_module_from_name if modname in self._loadedmods: return self._loadedmods[modname] = {} @@ -735,7 +766,9 @@ def load_file(self, filepath, modname): # module self._lastmodifs[filepath] = mdate # load the module - module = load_module_from_name(modname) + if sys.version_info < (3,) and not isinstance(modname, str): + modname = str(modname) + module = __import__(modname, fromlist=modname.split('.')[:-1]) self.load_module(module) def load_module(self, module): @@ -1113,8 +1146,6 @@ def __call__(self, *args, **kwargs): # deprecated stuff ############################################################# -from logilab.common.deprecation import deprecated - @deprecated('[lgc 0.59] use Registry.objid class method instead') def classid(cls): return '%s.%s' % (cls.__module__, cls.__name__) diff --git a/pymode/libs/logilab/common/shellutils.py b/pymode/libs/logilab-common-1.4.1/logilab/common/shellutils.py similarity index 85% rename from pymode/libs/logilab/common/shellutils.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/shellutils.py index 4e689560..b9d5fa6d 100644 --- a/pymode/libs/logilab/common/shellutils.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/shellutils.py @@ -44,15 +44,6 @@ from logilab.common.compat import str_to_bytes from logilab.common.deprecation import deprecated -try: - from logilab.common.proc import ProcInfo, NoSuchProcess -except ImportError: - # windows platform - class NoSuchProcess(Exception): pass - - def ProcInfo(pid): - raise NoSuchProcess() - class tempdir(object): @@ -245,53 +236,6 @@ def __init__(self, command): Execute = deprecated('Use subprocess.Popen instead')(Execute) -def acquire_lock(lock_file, max_try=10, delay=10, max_delay=3600): - """Acquire a lock represented by a file on the file system - - If the process written in lock file doesn't exist anymore, we remove the - lock file immediately - If age of the lock_file is greater than max_delay, then we raise a UserWarning - """ - count = abs(max_try) - while count: - try: - fd = os.open(lock_file, os.O_EXCL | os.O_RDWR | os.O_CREAT) - os.write(fd, str_to_bytes(str(os.getpid())) ) - os.close(fd) - return True - except OSError as e: - if e.errno == errno.EEXIST: - try: - fd = open(lock_file, "r") - pid = int(fd.readline()) - pi = ProcInfo(pid) - age = (time.time() - os.stat(lock_file)[stat.ST_MTIME]) - if age / max_delay > 1 : - raise UserWarning("Command '%s' (pid %s) has locked the " - "file '%s' for %s minutes" - % (pi.name(), pid, lock_file, age/60)) - except UserWarning: - raise - except NoSuchProcess: - os.remove(lock_file) - except Exception: - # The try block is not essential. can be skipped. - # Note: ProcInfo object is only available for linux - # process information are not accessible... - # or lock_file is no more present... - pass - else: - raise - count -= 1 - time.sleep(delay) - else: - raise Exception('Unable to acquire %s' % lock_file) - -def release_lock(lock_file): - """Release a lock represented by a file on the file system.""" - os.remove(lock_file) - - class ProgressBar(object): """A simple text progression bar.""" @@ -360,7 +304,7 @@ def finish(self): class DummyProgressBar(object): - __slot__ = ('text',) + __slots__ = ('text',) def refresh(self): pass diff --git a/pymode/libs/logilab/common/sphinx_ext.py b/pymode/libs/logilab-common-1.4.1/logilab/common/sphinx_ext.py similarity index 100% rename from pymode/libs/logilab/common/sphinx_ext.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/sphinx_ext.py diff --git a/pymode/libs/logilab/common/sphinxutils.py b/pymode/libs/logilab-common-1.4.1/logilab/common/sphinxutils.py similarity index 100% rename from pymode/libs/logilab/common/sphinxutils.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/sphinxutils.py diff --git a/pymode/libs/logilab/common/table.py b/pymode/libs/logilab-common-1.4.1/logilab/common/table.py similarity index 100% rename from pymode/libs/logilab/common/table.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/table.py diff --git a/pymode/libs/logilab/common/tasksqueue.py b/pymode/libs/logilab-common-1.4.1/logilab/common/tasksqueue.py similarity index 100% rename from pymode/libs/logilab/common/tasksqueue.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/tasksqueue.py diff --git a/pymode/libs/logilab-common-1.4.1/logilab/common/testlib.py b/pymode/libs/logilab-common-1.4.1/logilab/common/testlib.py new file mode 100644 index 00000000..fa3e36ee --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/testlib.py @@ -0,0 +1,708 @@ +# -*- coding: utf-8 -*- +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""Run tests. + +This will find all modules whose name match a given prefix in the test +directory, and run them. Various command line options provide +additional facilities. + +Command line options: + + -v verbose -- run tests in verbose mode with output to stdout + -q quiet -- don't print anything except if a test fails + -t testdir -- directory where the tests will be found + -x exclude -- add a test to exclude + -p profile -- profiled execution + -d dbc -- enable design-by-contract + -m match -- only run test matching the tag pattern which follow + +If no non-option arguments are present, prefixes used are 'test', +'regrtest', 'smoketest' and 'unittest'. + +""" + +from __future__ import print_function + +__docformat__ = "restructuredtext en" +# modified copy of some functions from test/regrtest.py from PyXml +# disable camel case warning +# pylint: disable=C0103 + +from contextlib import contextmanager +import sys +import os, os.path as osp +import re +import difflib +import tempfile +import math +import warnings +from shutil import rmtree +from operator import itemgetter +from inspect import isgeneratorfunction + +from six import PY2, add_metaclass, string_types +from six.moves import builtins, range, configparser, input + +from logilab.common.deprecation import class_deprecated, deprecated + +import unittest as unittest_legacy +if not getattr(unittest_legacy, "__package__", None): + try: + import unittest2 as unittest + from unittest2 import SkipTest + except ImportError: + raise ImportError("You have to install python-unittest2 to use %s" % __name__) +else: + import unittest as unittest + from unittest import SkipTest + +from functools import wraps + +from logilab.common.debugger import Debugger +from logilab.common.decorators import cached, classproperty +from logilab.common import textutils + + +__all__ = ['unittest_main', 'find_tests', 'nocoverage', 'pause_trace'] + +DEFAULT_PREFIXES = ('test', 'regrtest', 'smoketest', 'unittest', + 'func', 'validation') + +is_generator = deprecated('[lgc 0.63] use inspect.isgeneratorfunction')(isgeneratorfunction) + +# used by unittest to count the number of relevant levels in the traceback +__unittest = 1 + + +@deprecated('with_tempdir is deprecated, use {0}.TemporaryDirectory.'.format( + 'tempfile' if not PY2 else 'backports.tempfile')) +def with_tempdir(callable): + """A decorator ensuring no temporary file left when the function return + Work only for temporary file created with the tempfile module""" + if isgeneratorfunction(callable): + def proxy(*args, **kwargs): + old_tmpdir = tempfile.gettempdir() + new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-") + tempfile.tempdir = new_tmpdir + try: + for x in callable(*args, **kwargs): + yield x + finally: + try: + rmtree(new_tmpdir, ignore_errors=True) + finally: + tempfile.tempdir = old_tmpdir + return proxy + + @wraps(callable) + def proxy(*args, **kargs): + + old_tmpdir = tempfile.gettempdir() + new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-") + tempfile.tempdir = new_tmpdir + try: + return callable(*args, **kargs) + finally: + try: + rmtree(new_tmpdir, ignore_errors=True) + finally: + tempfile.tempdir = old_tmpdir + return proxy + +def in_tempdir(callable): + """A decorator moving the enclosed function inside the tempfile.tempfdir + """ + @wraps(callable) + def proxy(*args, **kargs): + + old_cwd = os.getcwd() + os.chdir(tempfile.tempdir) + try: + return callable(*args, **kargs) + finally: + os.chdir(old_cwd) + return proxy + +def within_tempdir(callable): + """A decorator run the enclosed function inside a tmpdir removed after execution + """ + proxy = with_tempdir(in_tempdir(callable)) + proxy.__name__ = callable.__name__ + return proxy + +def find_tests(testdir, + prefixes=DEFAULT_PREFIXES, suffix=".py", + excludes=(), + remove_suffix=True): + """ + Return a list of all applicable test modules. + """ + tests = [] + for name in os.listdir(testdir): + if not suffix or name.endswith(suffix): + for prefix in prefixes: + if name.startswith(prefix): + if remove_suffix and name.endswith(suffix): + name = name[:-len(suffix)] + if name not in excludes: + tests.append(name) + tests.sort() + return tests + + +## PostMortem Debug facilities ##### +def start_interactive_mode(result): + """starts an interactive shell so that the user can inspect errors + """ + debuggers = result.debuggers + descrs = result.error_descrs + result.fail_descrs + if len(debuggers) == 1: + # don't ask for test name if there's only one failure + debuggers[0].start() + else: + while True: + testindex = 0 + print("Choose a test to debug:") + # order debuggers in the same way than errors were printed + print("\n".join(['\t%s : %s' % (i, descr) for i, (_, descr) + in enumerate(descrs)])) + print("Type 'exit' (or ^D) to quit") + print() + try: + todebug = input('Enter a test name: ') + if todebug.strip().lower() == 'exit': + print() + break + else: + try: + testindex = int(todebug) + debugger = debuggers[descrs[testindex][0]] + except (ValueError, IndexError): + print("ERROR: invalid test number %r" % (todebug, )) + else: + debugger.start() + except (EOFError, KeyboardInterrupt): + print() + break + + +# coverage pausing tools ##################################################### + +@contextmanager +def replace_trace(trace=None): + """A context manager that temporary replaces the trace function""" + oldtrace = sys.gettrace() + sys.settrace(trace) + try: + yield + finally: + # specific hack to work around a bug in pycoverage, see + # https://bitbucket.org/ned/coveragepy/issue/123 + if (oldtrace is not None and not callable(oldtrace) and + hasattr(oldtrace, 'pytrace')): + oldtrace = oldtrace.pytrace + sys.settrace(oldtrace) + + +pause_trace = replace_trace + + +def nocoverage(func): + """Function decorator that pauses tracing functions""" + if hasattr(func, 'uncovered'): + return func + func.uncovered = True + + def not_covered(*args, **kwargs): + with pause_trace(): + return func(*args, **kwargs) + not_covered.uncovered = True + return not_covered + + +# test utils ################################################################## + + +# Add deprecation warnings about new api used by module level fixtures in unittest2 +# http://www.voidspace.org.uk/python/articles/unittest2.shtml#setupmodule-and-teardownmodule +class _DebugResult(object): # simplify import statement among unittest flavors.. + "Used by the TestSuite to hold previous class when running in debug." + _previousTestClass = None + _moduleSetUpFailed = False + shouldStop = False + +# backward compatibility: TestSuite might be imported from lgc.testlib +TestSuite = unittest.TestSuite + +class keywords(dict): + """Keyword args (**kwargs) support for generative tests.""" + +class starargs(tuple): + """Variable arguments (*args) for generative tests.""" + def __new__(cls, *args): + return tuple.__new__(cls, args) + +unittest_main = unittest.main + + +class InnerTestSkipped(SkipTest): + """raised when a test is skipped""" + pass + +def parse_generative_args(params): + args = [] + varargs = () + kwargs = {} + flags = 0 # 2 <=> starargs, 4 <=> kwargs + for param in params: + if isinstance(param, starargs): + varargs = param + if flags: + raise TypeError('found starargs after keywords !') + flags |= 2 + args += list(varargs) + elif isinstance(param, keywords): + kwargs = param + if flags & 4: + raise TypeError('got multiple keywords parameters') + flags |= 4 + elif flags & 2 or flags & 4: + raise TypeError('found parameters after kwargs or args') + else: + args.append(param) + + return args, kwargs + + +class InnerTest(tuple): + def __new__(cls, name, *data): + instance = tuple.__new__(cls, data) + instance.name = name + return instance + +class Tags(set): + """A set of tag able validate an expression""" + + def __init__(self, *tags, **kwargs): + self.inherit = kwargs.pop('inherit', True) + if kwargs: + raise TypeError("%s are an invalid keyword argument for this function" % kwargs.keys()) + + if len(tags) == 1 and not isinstance(tags[0], string_types): + tags = tags[0] + super(Tags, self).__init__(tags, **kwargs) + + def __getitem__(self, key): + return key in self + + def match(self, exp): + return eval(exp, {}, self) + + def __or__(self, other): + return Tags(*super(Tags, self).__or__(other)) + + +# duplicate definition from unittest2 of the _deprecate decorator +def _deprecate(original_func): + def deprecated_func(*args, **kwargs): + warnings.warn( + ('Please use %s instead.' % original_func.__name__), + DeprecationWarning, 2) + return original_func(*args, **kwargs) + return deprecated_func + +class TestCase(unittest.TestCase): + """A unittest.TestCase extension with some additional methods.""" + maxDiff = None + tags = Tags() + + def __init__(self, methodName='runTest'): + super(TestCase, self).__init__(methodName) + self.__exc_info = sys.exc_info + self.__testMethodName = self._testMethodName + self._current_test_descr = None + self._options_ = None + + @classproperty + @cached + def datadir(cls): # pylint: disable=E0213 + """helper attribute holding the standard test's data directory + + NOTE: this is a logilab's standard + """ + mod = sys.modules[cls.__module__] + return osp.join(osp.dirname(osp.abspath(mod.__file__)), 'data') + # cache it (use a class method to cache on class since TestCase is + # instantiated for each test run) + + @classmethod + def datapath(cls, *fname): + """joins the object's datadir and `fname`""" + return osp.join(cls.datadir, *fname) + + def set_description(self, descr): + """sets the current test's description. + This can be useful for generative tests because it allows to specify + a description per yield + """ + self._current_test_descr = descr + + # override default's unittest.py feature + def shortDescription(self): + """override default unittest shortDescription to handle correctly + generative tests + """ + if self._current_test_descr is not None: + return self._current_test_descr + return super(TestCase, self).shortDescription() + + def quiet_run(self, result, func, *args, **kwargs): + try: + func(*args, **kwargs) + except (KeyboardInterrupt, SystemExit): + raise + except unittest.SkipTest as e: + if hasattr(result, 'addSkip'): + result.addSkip(self, str(e)) + else: + warnings.warn("TestResult has no addSkip method, skips not reported", + RuntimeWarning, 2) + result.addSuccess(self) + return False + except: + result.addError(self, self.__exc_info()) + return False + return True + + def _get_test_method(self): + """return the test method""" + return getattr(self, self._testMethodName) + + def optval(self, option, default=None): + """return the option value or default if the option is not define""" + return getattr(self._options_, option, default) + + def __call__(self, result=None, runcondition=None, options=None): + """rewrite TestCase.__call__ to support generative tests + This is mostly a copy/paste from unittest.py (i.e same + variable names, same logic, except for the generative tests part) + """ + if result is None: + result = self.defaultTestResult() + self._options_ = options + # if result.cvg: + # result.cvg.start() + testMethod = self._get_test_method() + if (getattr(self.__class__, "__unittest_skip__", False) or + getattr(testMethod, "__unittest_skip__", False)): + # If the class or method was skipped. + try: + skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') + or getattr(testMethod, '__unittest_skip_why__', '')) + if hasattr(result, 'addSkip'): + result.addSkip(self, skip_why) + else: + warnings.warn("TestResult has no addSkip method, skips not reported", + RuntimeWarning, 2) + result.addSuccess(self) + finally: + result.stopTest(self) + return + if runcondition and not runcondition(testMethod): + return # test is skipped + result.startTest(self) + try: + if not self.quiet_run(result, self.setUp): + return + generative = isgeneratorfunction(testMethod) + # generative tests + if generative: + self._proceed_generative(result, testMethod, + runcondition) + else: + status = self._proceed(result, testMethod) + success = (status == 0) + if not self.quiet_run(result, self.tearDown): + return + if not generative and success: + result.addSuccess(self) + finally: + # if result.cvg: + # result.cvg.stop() + result.stopTest(self) + + def _proceed_generative(self, result, testfunc, runcondition=None): + # cancel startTest()'s increment + result.testsRun -= 1 + success = True + try: + for params in testfunc(): + if runcondition and not runcondition(testfunc, + skipgenerator=False): + if not (isinstance(params, InnerTest) + and runcondition(params)): + continue + if not isinstance(params, (tuple, list)): + params = (params, ) + func = params[0] + args, kwargs = parse_generative_args(params[1:]) + # increment test counter manually + result.testsRun += 1 + status = self._proceed(result, func, args, kwargs) + if status == 0: + result.addSuccess(self) + success = True + else: + success = False + # XXX Don't stop anymore if an error occured + #if status == 2: + # result.shouldStop = True + if result.shouldStop: # either on error or on exitfirst + error + break + except self.failureException: + result.addFailure(self, self.__exc_info()) + success = False + except SkipTest as e: + result.addSkip(self, e) + except: + # if an error occurs between two yield + result.addError(self, self.__exc_info()) + success = False + return success + + def _proceed(self, result, testfunc, args=(), kwargs=None): + """proceed the actual test + returns 0 on success, 1 on failure, 2 on error + + Note: addSuccess can't be called here because we have to wait + for tearDown to be successfully executed to declare the test as + successful + """ + kwargs = kwargs or {} + try: + testfunc(*args, **kwargs) + except self.failureException: + result.addFailure(self, self.__exc_info()) + return 1 + except KeyboardInterrupt: + raise + except InnerTestSkipped as e: + result.addSkip(self, e) + return 1 + except SkipTest as e: + result.addSkip(self, e) + return 0 + except: + result.addError(self, self.__exc_info()) + return 2 + return 0 + + def innerSkip(self, msg=None): + """mark a generative test as skipped for the reason""" + msg = msg or 'test was skipped' + raise InnerTestSkipped(msg) + + if sys.version_info >= (3,2): + assertItemsEqual = unittest.TestCase.assertCountEqual + else: + assertCountEqual = unittest.TestCase.assertItemsEqual + +TestCase.assertItemsEqual = deprecated('assertItemsEqual is deprecated, use assertCountEqual')( + TestCase.assertItemsEqual) + +import doctest + +class SkippedSuite(unittest.TestSuite): + def test(self): + """just there to trigger test execution""" + self.skipped_test('doctest module has no DocTestSuite class') + + +class DocTestFinder(doctest.DocTestFinder): + + def __init__(self, *args, **kwargs): + self.skipped = kwargs.pop('skipped', ()) + doctest.DocTestFinder.__init__(self, *args, **kwargs) + + def _get_test(self, obj, name, module, globs, source_lines): + """override default _get_test method to be able to skip tests + according to skipped attribute's value + """ + if getattr(obj, '__name__', '') in self.skipped: + return None + return doctest.DocTestFinder._get_test(self, obj, name, module, + globs, source_lines) + + +@add_metaclass(class_deprecated) +class DocTest(TestCase): + """trigger module doctest + I don't know how to make unittest.main consider the DocTestSuite instance + without this hack + """ + __deprecation_warning__ = 'use stdlib doctest module with unittest API directly' + skipped = () + def __call__(self, result=None, runcondition=None, options=None):\ + # pylint: disable=W0613 + try: + finder = DocTestFinder(skipped=self.skipped) + suite = doctest.DocTestSuite(self.module, test_finder=finder) + # XXX iirk + doctest.DocTestCase._TestCase__exc_info = sys.exc_info + except AttributeError: + suite = SkippedSuite() + # doctest may gork the builtins dictionnary + # This happen to the "_" entry used by gettext + old_builtins = builtins.__dict__.copy() + try: + return suite.run(result) + finally: + builtins.__dict__.clear() + builtins.__dict__.update(old_builtins) + run = __call__ + + def test(self): + """just there to trigger test execution""" + + +class MockConnection: + """fake DB-API 2.0 connexion AND cursor (i.e. cursor() return self)""" + + def __init__(self, results): + self.received = [] + self.states = [] + self.results = results + + def cursor(self): + """Mock cursor method""" + return self + def execute(self, query, args=None): + """Mock execute method""" + self.received.append( (query, args) ) + def fetchone(self): + """Mock fetchone method""" + return self.results[0] + def fetchall(self): + """Mock fetchall method""" + return self.results + def commit(self): + """Mock commiy method""" + self.states.append( ('commit', len(self.received)) ) + def rollback(self): + """Mock rollback method""" + self.states.append( ('rollback', len(self.received)) ) + def close(self): + """Mock close method""" + pass + + +def mock_object(**params): + """creates an object using params to set attributes + >>> option = mock_object(verbose=False, index=range(5)) + >>> option.verbose + False + >>> option.index + [0, 1, 2, 3, 4] + """ + return type('Mock', (), params)() + + +def create_files(paths, chroot): + """Creates directories and files found in . + + :param paths: list of relative paths to files or directories + :param chroot: the root directory in which paths will be created + + >>> from os.path import isdir, isfile + >>> isdir('/tmp/a') + False + >>> create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], '/tmp') + >>> isdir('/tmp/a') + True + >>> isdir('/tmp/a/b/c') + True + >>> isfile('/tmp/a/b/c/d/e.py') + True + >>> isfile('/tmp/a/b/foo.py') + True + """ + dirs, files = set(), set() + for path in paths: + path = osp.join(chroot, path) + filename = osp.basename(path) + # path is a directory path + if filename == '': + dirs.add(path) + # path is a filename path + else: + dirs.add(osp.dirname(path)) + files.add(path) + for dirpath in dirs: + if not osp.isdir(dirpath): + os.makedirs(dirpath) + for filepath in files: + open(filepath, 'w').close() + + +class AttrObject: # XXX cf mock_object + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + +def tag(*args, **kwargs): + """descriptor adding tag to a function""" + def desc(func): + assert not hasattr(func, 'tags') + func.tags = Tags(*args, **kwargs) + return func + return desc + +def require_version(version): + """ Compare version of python interpreter to the given one. Skip the test + if older. + """ + def check_require_version(f): + version_elements = version.split('.') + try: + compare = tuple([int(v) for v in version_elements]) + except ValueError: + raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version) + current = sys.version_info[:3] + if current < compare: + def new_f(self, *args, **kwargs): + self.skipTest('Need at least %s version of python. Current version is %s.' % (version, '.'.join([str(element) for element in current]))) + new_f.__name__ = f.__name__ + return new_f + else: + return f + return check_require_version + +def require_module(module): + """ Check if the given module is loaded. Skip the test if not. + """ + def check_require_module(f): + try: + __import__(module) + return f + except ImportError: + def new_f(self, *args, **kwargs): + self.skipTest('%s can not be imported.' % module) + new_f.__name__ = f.__name__ + return new_f + return check_require_module + diff --git a/pymode/libs/logilab/common/textutils.py b/pymode/libs/logilab-common-1.4.1/logilab/common/textutils.py similarity index 99% rename from pymode/libs/logilab/common/textutils.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/textutils.py index 9046f975..356b1a89 100644 --- a/pymode/libs/logilab/common/textutils.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/textutils.py @@ -70,6 +70,8 @@ u'\xf8': u'o', # LATIN SMALL LETTER O WITH STROKE u'\xbb': u'"', # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK u'\xdf': u'ss', # LATIN SMALL LETTER SHARP S + u'\u2013': u'-', # HYPHEN + u'\u2019': u"'", # SIMPLE QUOTE } def unormalize(ustring, ignorenonascii=None, substitute=None): diff --git a/pymode/libs/logilab/common/tree.py b/pymode/libs/logilab-common-1.4.1/logilab/common/tree.py similarity index 100% rename from pymode/libs/logilab/common/tree.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/tree.py diff --git a/pymode/libs/logilab/common/umessage.py b/pymode/libs/logilab-common-1.4.1/logilab/common/umessage.py similarity index 64% rename from pymode/libs/logilab/common/umessage.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/umessage.py index a5e47995..a0394bc6 100644 --- a/pymode/libs/logilab/common/umessage.py +++ b/pymode/libs/logilab-common-1.4.1/logilab/common/umessage.py @@ -22,15 +22,13 @@ import email from encodings import search_function import sys -if sys.version_info >= (2, 5): - from email.utils import parseaddr, parsedate - from email.header import decode_header -else: - from email.Utils import parseaddr, parsedate - from email.Header import decode_header +from email.utils import parseaddr, parsedate +from email.header import decode_header from datetime import datetime +from six import text_type, binary_type + try: from mx.DateTime import DateTime except ImportError: @@ -44,7 +42,14 @@ def decode_QP(string): for decoded, charset in decode_header(string): if not charset : charset = 'iso-8859-15' - parts.append(decoded.decode(charset, 'replace')) + # python 3 sometimes returns str and sometimes bytes. + # the 'official' fix is to use the new 'policy' APIs + # https://bugs.python.org/issue24797 + # let's just handle this bug ourselves for now + if isinstance(decoded, binary_type): + decoded = decoded.decode(charset, 'replace') + assert isinstance(decoded, text_type) + parts.append(decoded) if sys.version_info < (3, 3): # decoding was non-RFC compliant wrt to whitespace handling @@ -55,13 +60,13 @@ def decode_QP(string): def message_from_file(fd): try: return UMessage(email.message_from_file(fd)) - except email.Errors.MessageParseError: + except email.errors.MessageParseError: return '' def message_from_string(string): try: return UMessage(email.message_from_string(string)) - except email.Errors.MessageParseError: + except email.errors.MessageParseError: return '' class UMessage: @@ -96,61 +101,39 @@ def walk(self): for part in self.message.walk(): yield UMessage(part) - if sys.version_info < (3, 0): - - def get_payload(self, index=None, decode=False): - message = self.message - if index is None: - payload = message.get_payload(index, decode) - if isinstance(payload, list): - return [UMessage(msg) for msg in payload] - if message.get_content_maintype() != 'text': - return payload - - charset = message.get_content_charset() or 'iso-8859-1' - if search_function(charset) is None: - charset = 'iso-8859-1' - return unicode(payload or '', charset, "replace") - else: - payload = UMessage(message.get_payload(index, decode)) - return payload - - def get_content_maintype(self): - return unicode(self.message.get_content_maintype()) - - def get_content_type(self): - return unicode(self.message.get_content_type()) - - def get_filename(self, failobj=None): - value = self.message.get_filename(failobj) - if value is failobj: - return value - try: - return unicode(value) - except UnicodeDecodeError: - return u'error decoding filename' - - else: - - def get_payload(self, index=None, decode=False): - message = self.message - if index is None: - payload = message.get_payload(index, decode) - if isinstance(payload, list): - return [UMessage(msg) for msg in payload] + def get_payload(self, index=None, decode=False): + message = self.message + if index is None: + payload = message.get_payload(index, decode) + if isinstance(payload, list): + return [UMessage(msg) for msg in payload] + if message.get_content_maintype() != 'text': + return payload + if isinstance(payload, text_type): return payload - else: - payload = UMessage(message.get_payload(index, decode)) - return payload - - def get_content_maintype(self): - return self.message.get_content_maintype() - - def get_content_type(self): - return self.message.get_content_type() - def get_filename(self, failobj=None): - return self.message.get_filename(failobj) + charset = message.get_content_charset() or 'iso-8859-1' + if search_function(charset) is None: + charset = 'iso-8859-1' + return text_type(payload or b'', charset, "replace") + else: + payload = UMessage(message.get_payload(index, decode)) + return payload + + def get_content_maintype(self): + return text_type(self.message.get_content_maintype()) + + def get_content_type(self): + return text_type(self.message.get_content_type()) + + def get_filename(self, failobj=None): + value = self.message.get_filename(failobj) + if value is failobj: + return value + try: + return text_type(value) + except UnicodeDecodeError: + return u'error decoding filename' # other convenience methods ############################################### diff --git a/pymode/libs/logilab/common/ureports/__init__.py b/pymode/libs/logilab-common-1.4.1/logilab/common/ureports/__init__.py similarity index 100% rename from pymode/libs/logilab/common/ureports/__init__.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/ureports/__init__.py diff --git a/pymode/libs/logilab/common/ureports/docbook_writer.py b/pymode/libs/logilab-common-1.4.1/logilab/common/ureports/docbook_writer.py similarity index 100% rename from pymode/libs/logilab/common/ureports/docbook_writer.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/ureports/docbook_writer.py diff --git a/pymode/libs/logilab/common/ureports/html_writer.py b/pymode/libs/logilab-common-1.4.1/logilab/common/ureports/html_writer.py similarity index 100% rename from pymode/libs/logilab/common/ureports/html_writer.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/ureports/html_writer.py diff --git a/pymode/libs/logilab/common/ureports/nodes.py b/pymode/libs/logilab-common-1.4.1/logilab/common/ureports/nodes.py similarity index 100% rename from pymode/libs/logilab/common/ureports/nodes.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/ureports/nodes.py diff --git a/pymode/libs/logilab/common/ureports/text_writer.py b/pymode/libs/logilab-common-1.4.1/logilab/common/ureports/text_writer.py similarity index 100% rename from pymode/libs/logilab/common/ureports/text_writer.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/ureports/text_writer.py diff --git a/pymode/libs/logilab/common/urllib2ext.py b/pymode/libs/logilab-common-1.4.1/logilab/common/urllib2ext.py similarity index 100% rename from pymode/libs/logilab/common/urllib2ext.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/urllib2ext.py diff --git a/pymode/libs/logilab/common/vcgutils.py b/pymode/libs/logilab-common-1.4.1/logilab/common/vcgutils.py similarity index 100% rename from pymode/libs/logilab/common/vcgutils.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/vcgutils.py diff --git a/pymode/libs/logilab/common/visitor.py b/pymode/libs/logilab-common-1.4.1/logilab/common/visitor.py similarity index 100% rename from pymode/libs/logilab/common/visitor.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/visitor.py diff --git a/pymode/libs/logilab/common/xmlutils.py b/pymode/libs/logilab-common-1.4.1/logilab/common/xmlutils.py similarity index 100% rename from pymode/libs/logilab/common/xmlutils.py rename to pymode/libs/logilab-common-1.4.1/logilab/common/xmlutils.py diff --git a/pymode/libs/logilab-common-1.4.1/setup.cfg b/pymode/libs/logilab-common-1.4.1/setup.cfg new file mode 100644 index 00000000..8b48b197 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/setup.cfg @@ -0,0 +1,9 @@ +[bdist_rpm] +packager = Sylvain Thenault +provides = logilab.common + +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/pymode/libs/logilab-common-1.4.1/setup.py b/pymode/libs/logilab-common-1.4.1/setup.py new file mode 100644 index 00000000..c565ee15 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/setup.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# pylint: disable=W0404,W0622,W0704,W0613,W0152 +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""Generic Setup script, takes package info from __pkginfo__.py file. +""" +__docformat__ = "restructuredtext en" + +from setuptools import setup, find_packages +from io import open +from os import path + +here = path.abspath(path.dirname(__file__)) + +pkginfo = {} +with open(path.join(here, '__pkginfo__.py')) as f: + exec(f.read(), pkginfo) + +# Get the long description from the relevant file +with open(path.join(here, 'README'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name=pkginfo['distname'], + version=pkginfo['version'], + description=pkginfo['description'], + long_description=long_description, + url=pkginfo['web'], + author=pkginfo['author'], + author_email=pkginfo['author_email'], + license=pkginfo['license'], + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=pkginfo['classifiers'], + packages=find_packages(exclude=['contrib', 'docs', 'test*']), + namespace_packages=[pkginfo['subpackage_of']], + install_requires=pkginfo['install_requires'], + tests_require=pkginfo['tests_require'], + scripts=pkginfo['scripts'], +) diff --git a/pymode/libs/logilab-common-1.4.1/test/data/ChangeLog b/pymode/libs/logilab-common-1.4.1/test/data/ChangeLog new file mode 100644 index 00000000..22a45529 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/ChangeLog @@ -0,0 +1,184 @@ +ChangeLog for logilab.devtools +============================== + + -- + * added the missing dos2unix script to the distribution + + * major debianize refactoring using class / inheritance instead of + functions composition + + * import the version control library from oobrother extended with code + from devtools / apycot + + * Singing in the rain: + + - I'm + - singing in the rain + + * Big change multiline + tata titi toto + + - small change + - other change + - multiline change + really ? + - Eat your vegetable and brush after every meals + + + +2004-02-13 -- 0.4.5 + * fix debianize to handle dependencies to python standalone package + (ie no "python" prefix in the default package) + + * fixed cvslog in rlog mode + + + +2004-02-11 -- 0.4.4 + * check web and ftp variables from __pkginfo__ + + * check for long and short descriptions in __pkginfo__ + + * outdated copyright is now a warning + + * consider distuils automaticaly install .c files + + * fix check_package exit status + + * merged sgml, elisp and data packages in generated debian files + + + +2003-12-05 -- 0.4.3 + * fix bug in buildeb making it usable from buildpackage... + + + +2003-11-24 -- 0.4.2 + * fixed pb with check_info_module and catalog, when not launched from the + package directory + + * ignore build directory in check_manifest + + * fix to avoid pb with "non executed" docstring in pycoverage + + * add support for --help and fix exit status to pycoverage + + + +2003-11-20 -- 0.4.1 + * added code coverage tool, starting from + http://www.garethrees.org/2001/12/04/python-coverage/ + + * added --help option to buildeb + + + +2003-11-14 -- 0.4.0 + * added a python script buildeb to build debian package (buildpackage call + this script now) + + * debianize now puts tests in a separated package (-test) and generate + package for zope >= 2.6.2 (i.e. python 2.2) + + * fix detection of examples directory in pkginfo + + * fix debhelper dependency in build-depends + + * remove minor bug in buildpackage (try to move archive.gz instead of + archive.tar.gz + + * bug fix in debianize zope handler + + + +2003-10-06 -- 0.3.4 + * remove important bug in buildpackage (rm sourcetree when building a + source distrib) + + * add version to dependency between main packages and sub-packages (-data, + -elisp and -sgml) + + * change way of creating the .orig.tar.gz + + * create source distribution when building debian package + + * fix path in log message for MANIFEST.in, __pkginfo__ and bin directory + + * make changelog more robust + + * debianize bug fixes + + + +2003-09-22 -- 0.3.3 + * fix python.postinst script to avoid compiling of others packages :) + + + +2003-09-19 -- 0.3.2 + * add basic support for XSLT distribution + + * fix DTD and catalog handling in debianize + + * fix bug in check_pkginfo + + * updated documentation + + + +2003-09-18 -- 0.3.1 + * add support for data files in debianize + + * test python version in debianize + + * minor fixes + + * updated setup.py template + + + +2003-09-18 -- 0.3.0 + * updates for a new packaging standard + + * removed jabbercli, cvs_filecheck + + * added preparedistrib, tagpackage, pkginfo + + * simpler debianize relying on a generic setup.py + + * fix some debian templates + + * checkpackage rewrite + + * provides checkers for the tester package + + + +2003-08-29 -- 0.2.4 + * added cvs_filecheck + + + +2003-06-20 -- 0.2.2 + * buildpackages fixes + + + +2003-06-17 -- 0.2.1 + * fix setup.py + + * make pkghandlers.export working with python <= 2.1 + + * add the mailinglist variable in __pkginfo__, used for announce + generation in makedistrib + + + +2003-06-16 -- 0.2.0 + * minor enhancements + + * get package information for __pkginfo__.py + + + diff --git a/pymode/libs/logilab-common-1.4.1/test/data/MyPyPa-0.1.0.zip b/pymode/libs/logilab-common-1.4.1/test/data/MyPyPa-0.1.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..a7b3125f999ac1f8e8bd5b5260d3de1eeb840fec GIT binary patch literal 206 zcmWIWW@h1H00CCVN~>2f#aSXiHV6wb$S~wq7E~4_>c_`t=4F<|$LkeThK6u5Fh|`u z=?TK672FJrEZ>Ko&DF0`ZbY d5DS}qtPuOq>. +"""logilab.common packaging information""" +__docformat__ = "restructuredtext en" +import sys +import os + +distname = 'logilab-common' +modname = 'common' +subpackage_of = 'logilab' +subpackage_master = True + +numversion = (0, 63, 2) +version = '.'.join([str(num) for num in numversion]) + +license = 'LGPL' # 2.1 or later +description = "collection of low-level Python packages and modules used by Logilab projects" +web = "http://www.logilab.org/project/%s" % distname +mailinglist = "mailto://python-projects@lists.logilab.org" +author = "Logilab" +author_email = "contact@logilab.fr" + + +from os.path import join +scripts = [join('bin', 'logilab-pytest')] +include_dirs = [join('test', 'data')] + +install_requires = [ + 'six >= 1.4.0', + ] +tests_require = ['pytz'] + +if sys.version_info < (2, 7): + install_requires.append('unittest2 >= 0.5.1') +if os.name == 'nt': + install_requires.append('colorama') + +classifiers = ["Topic :: Utilities", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + ] diff --git a/pymode/libs/astroid/interpreter/_import/__init__.py b/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/NOTHING similarity index 100% rename from pymode/libs/astroid/interpreter/_import/__init__.py rename to pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/NOTHING diff --git a/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/README b/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/README new file mode 100644 index 00000000..27ab0b99 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/README @@ -0,0 +1 @@ +thank you diff --git a/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/coin b/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/coin new file mode 100644 index 00000000..0e46b314 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/coin @@ -0,0 +1 @@ +baba diff --git a/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/toto.txt b/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/toto.txt new file mode 100644 index 00000000..785a58b9 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/content_differ_dir/subdir/toto.txt @@ -0,0 +1,53 @@ +Lorem ipsum dolor sit amet, consectetuer adipisci elit. Necesse qui +quidem constituam tantis, et possunt placeat ipsum ex aut iucunde aut +facta, aut impediente autem totum unum directam eius tum voluptate +sensuum reperiuntur ad ab, quae ac.. Sed eius enim a, tranquillat ob +vexetur permagna potius voluptate eo aliae, vivamus esse solis ut non, +atomis videatur in ut, mihi litteris si ante vivere, deinde +emancipaverat appetendum sine erant ex metu philosophiae fatemur, et +magis non corpora ne, maluisti ita locupletiorem medicorum.. Tradere +imperitos exiguam in sint saluti temeritate hoc, nullam nec quaerat, +eademque vivendum, contra similique. + +Molestiae qui, tam sic ea honesto, graeca consecutionem voluptate +inertissimae sunt, corpora denique fabulis dicere ab et quae ad +politus tum in nostris.. Plane pueriliter, hoc affectus quid iis plus +videtur dolorem vivere ad esse asperiores.. Quorum si nihilo eram +conflixisse nec inpotenti, et bonum ad nostris servare omni, saepe +multis, consequantur id, in fructuosam multi quod, voluptatem abducat +a tantum sit error ipso si respirare corrupte referuntur, maiorem.. +Voluptatem a etiam perspici gravissimas, cuius.. Unum morbis ne esse +conscientia tamen conclusionemque notionem, amentur quam, praeclarorum +eum consulatu iis invitat solum porro, quidem ad patria, fore res +athenis sempiternum alii venire, est mei nam improbis dolorem, +permulta timidiores. + +Et inquam sic familias, sequatur animis quae et quae ea esse, autem +impediri quaeque modo inciderint consecutionem expectata, sed severa +etiamsi, in egregios temporibus infinito ad artibus, voluptatem +aristotele, tandem aliquo industriae collegi timiditatem sibi igitur +aut, se cum tranquillitate loquuntur quod nullo, quam suum illustribus +fugiendam illis tam consequatur.. Quas maximisque impendere ipsum se +petat altera enim ocurreret sibi maxime, possit ea aegritudo aut ulla, +et quod sed. + +Verissimum confirmat accurate totam iisque sequitur aut probabo et et +adhibenda, mihi sed ad et quod erga minima rerum eius quod, tale et +libidinosarum liber, omnis quae et nunc sicine, nec at aut omnem, +sententiae a, repudiandae.. Vero esse crudelis amentur ut, atque +facilius vita invitat, delectus excepturi ex libidinum non qua +consequi beate quae ratio.. Illa poetis videor requirere, quippiam et +autem ut et esset voluptate neque consilia sed voluptatibus est +virtutum minima et, interesse exquirere et peccandi quae carere se, +angere.. Firme nomine oratio perferendis si voluptates cogitavisse, +feci maledici ea vis et, nam quae legantur animum animis temeritate, +amicitiam desideraturam tollatur nisi de voluptatem. + +Ii videri accedit de.. Graeci tum factis ea ea itaque sunt latinis +detractis reprehensiones nostrum sola non tantopere perfruique quoque +fruenda aptissimum nostrum, pueros graeca qui eruditionem est quae, +labore.. Omnia si quaerimus, si praetermissum vero deserunt quia +democriti retinere ignoratione, iam de gerendarum vel a maxime +provident, in eadem si praeterierunt, certa cibo ut utilitatibus nullo +quod voluptatis iis eamque omnia, stare aut, quamquam et, ut illa +susceperant legant consiliisque, est sed quantum igitur. diff --git a/pymode/libs/logilab-common-1.4.1/test/data/deprecation.py b/pymode/libs/logilab-common-1.4.1/test/data/deprecation.py new file mode 100644 index 00000000..be3b1031 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/deprecation.py @@ -0,0 +1,4 @@ +# placeholder used by unittest_deprecation + +def moving_target(): + pass diff --git a/pymode/libs/pylint/extensions/__init__.py b/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/NOTHING similarity index 100% rename from pymode/libs/pylint/extensions/__init__.py rename to pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/NOTHING diff --git a/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/README b/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/README new file mode 100644 index 00000000..27ab0b99 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/README @@ -0,0 +1 @@ +thank you diff --git a/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/subdir/toto.txt b/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/subdir/toto.txt new file mode 100644 index 00000000..4bf7233a --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/subdir/toto.txt @@ -0,0 +1,53 @@ +Lorem ipsum dolor sit amet, consectetuer adipisci elit. Necesse qui +quidem constituam tantis, et possunt placeat ipsum ex aut iucunde aut +facta, aut impediente autem totum unum directam eius tum voluptate +sensuum reperiuntur ad ab, quae ac.. Sed eius enim a, tranquillat ob +vexetur permagna potius voluptate eo aliae, vivamus esse solis ut non, +atomis videatur in ut, mihi litteris si ante vivere, deinde +emancipaverat appetendum sine erant ex metu philosophiae fatemur, et +magis non corpora ne, maluisti ita locupletiorem medicorum.. Tradere +imperitos exiguam in sint saluti temeritate hoc, nullam nec quaerat, +eademque vivendum, contra similique. + +Molestiae qui, tam sic ea honesto, graeca consecutionem voluptate +inertissimae sunt, corpora denique fabulis dicere ab et quae ad +politus tum in nostris.. Plane pueriliter, hoc affectus quid iis plus +videtur dolorem vivere ad esse asperiores.. Quorum si nihilo eram +pedalis pertinax ii minus, referta mediocrem iustitiam acutum quo +rerum constringendos ex pondere lucilius essent neglexerit insequitur +a tantum sit error ipso si respirare corrupte referuntur, maiorem.. +Voluptatem a etiam perspici gravissimas, cuius.. Unum morbis ne esse +conscientia tamen conclusionemque notionem, amentur quam, praeclarorum +eum consulatu iis invitat solum porro, quidem ad patria, fore res +athenis sempiternum alii venire, est mei nam improbis dolorem, +permulta timidiores. + +Et inquam sic familias, sequatur animis quae et quae ea esse, autem +impediri quaeque modo inciderint consecutionem expectata, sed severa +etiamsi, in egregios temporibus infinito ad artibus, voluptatem +aristotele, tandem aliquo industriae collegi timiditatem sibi igitur +aut, se cum tranquillitate loquuntur quod nullo, quam suum illustribus +fugiendam illis tam consequatur.. Quas maximisque impendere ipsum se +petat altera enim ocurreret sibi maxime, possit ea aegritudo aut ulla, +et quod sed. + +Verissimum confirmat accurate totam iisque sequitur aut probabo et et +adhibenda, mihi sed ad et quod erga minima rerum eius quod, tale et +libidinosarum liber, omnis quae et nunc sicine, nec at aut omnem, +sententiae a, repudiandae.. Vero esse crudelis amentur ut, atque +facilius vita invitat, delectus excepturi ex libidinum non qua +consequi beate quae ratio.. Illa poetis videor requirere, quippiam et +autem ut et esset voluptate neque consilia sed voluptatibus est +virtutum minima et, interesse exquirere et peccandi quae carere se, +angere.. Firme nomine oratio perferendis si voluptates cogitavisse, +feci maledici ea vis et, nam quae legantur animum animis temeritate, +amicitiam desideraturam tollatur nisi de voluptatem. + +Ii videri accedit de.. Graeci tum factis ea ea itaque sunt latinis +detractis reprehensiones nostrum sola non tantopere perfruique quoque +fruenda aptissimum nostrum, pueros graeca qui eruditionem est quae, +labore.. Omnia si quaerimus, si praetermissum vero deserunt quia +democriti retinere ignoratione, iam de gerendarum vel a maxime +provident, in eadem si praeterierunt, certa cibo ut utilitatibus nullo +quod voluptatis iis eamque omnia, stare aut, quamquam et, ut illa +susceperant legant consiliisque, est sed quantum igitur. diff --git a/pymode/libs/rope/base/oi/type_hinting/__init__.py b/pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/subdirtwo/Hello similarity index 100% rename from pymode/libs/rope/base/oi/type_hinting/__init__.py rename to pymode/libs/logilab-common-1.4.1/test/data/file_differ_dir/subdirtwo/Hello diff --git a/pymode/libs/rope/base/oi/type_hinting/providers/__init__.py b/pymode/libs/logilab-common-1.4.1/test/data/find_test/__init__.py similarity index 100% rename from pymode/libs/rope/base/oi/type_hinting/providers/__init__.py rename to pymode/libs/logilab-common-1.4.1/test/data/find_test/__init__.py diff --git a/pymode/libs/rope/base/oi/type_hinting/resolvers/__init__.py b/pymode/libs/logilab-common-1.4.1/test/data/find_test/foo.txt similarity index 100% rename from pymode/libs/rope/base/oi/type_hinting/resolvers/__init__.py rename to pymode/libs/logilab-common-1.4.1/test/data/find_test/foo.txt diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/module.py b/pymode/libs/logilab-common-1.4.1/test/data/find_test/module.py new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/module2.py b/pymode/libs/logilab-common-1.4.1/test/data/find_test/module2.py new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/newlines.txt b/pymode/libs/logilab-common-1.4.1/test/data/find_test/newlines.txt new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/noendingnewline.py b/pymode/libs/logilab-common-1.4.1/test/data/find_test/noendingnewline.py new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/nonregr.py b/pymode/libs/logilab-common-1.4.1/test/data/find_test/nonregr.py new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/normal_file.txt b/pymode/libs/logilab-common-1.4.1/test/data/find_test/normal_file.txt new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/spam.txt b/pymode/libs/logilab-common-1.4.1/test/data/find_test/spam.txt new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/sub/doc.txt b/pymode/libs/logilab-common-1.4.1/test/data/find_test/sub/doc.txt new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/sub/momo.py b/pymode/libs/logilab-common-1.4.1/test/data/find_test/sub/momo.py new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/test.ini b/pymode/libs/logilab-common-1.4.1/test/data/find_test/test.ini new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/test1.msg b/pymode/libs/logilab-common-1.4.1/test/data/find_test/test1.msg new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/test2.msg b/pymode/libs/logilab-common-1.4.1/test/data/find_test/test2.msg new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/find_test/write_protected_file.txt b/pymode/libs/logilab-common-1.4.1/test/data/find_test/write_protected_file.txt new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/foo.txt b/pymode/libs/logilab-common-1.4.1/test/data/foo.txt new file mode 100644 index 00000000..a08c29e4 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/foo.txt @@ -0,0 +1,9 @@ +a +b +c +d +e +f +g +h + diff --git a/pymode/libs/logilab-common-1.4.1/test/data/lmfp/__init__.py b/pymode/libs/logilab-common-1.4.1/test/data/lmfp/__init__.py new file mode 100644 index 00000000..74b26b82 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/lmfp/__init__.py @@ -0,0 +1,2 @@ +# force a "direct" python import +from . import foo diff --git a/pymode/libs/logilab-common-1.4.1/test/data/lmfp/foo.py b/pymode/libs/logilab-common-1.4.1/test/data/lmfp/foo.py new file mode 100644 index 00000000..8f7de1e8 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/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/pymode/libs/logilab-common-1.4.1/test/data/module.py b/pymode/libs/logilab-common-1.4.1/test/data/module.py new file mode 100644 index 00000000..493e6762 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/module.py @@ -0,0 +1,69 @@ +# -*- coding: Latin-1 -*- +"""test module for astng +""" +from __future__ import print_function + +from logilab.common import modutils, Execute as spawn +from logilab.common.astutils 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 + +#print('*****>',YO.__dict__) +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=' ') + elif 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) diff --git a/pymode/libs/logilab-common-1.4.1/test/data/module2.py b/pymode/libs/logilab-common-1.4.1/test/data/module2.py new file mode 100644 index 00000000..51509f3b --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/module2.py @@ -0,0 +1,77 @@ +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 +exec('c = 3') +exec('c = 3', {}, {}) + +def raise_string(a=2, *args, **kwargs): + raise 'pas glop' + raise Exception('yo') + yield 'coucou' + +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 diff --git a/pymode/libs/logilab-common-1.4.1/test/data/newlines.txt b/pymode/libs/logilab-common-1.4.1/test/data/newlines.txt new file mode 100644 index 00000000..e1f25c09 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/newlines.txt @@ -0,0 +1,3 @@ +# mixed new lines +1 +2 3 diff --git a/pymode/libs/logilab-common-1.4.1/test/data/noendingnewline.py b/pymode/libs/logilab-common-1.4.1/test/data/noendingnewline.py new file mode 100644 index 00000000..110f902d --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/noendingnewline.py @@ -0,0 +1,36 @@ +from __future__ import print_function + +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/pymode/libs/logilab-common-1.4.1/test/data/nonregr.py b/pymode/libs/logilab-common-1.4.1/test/data/nonregr.py new file mode 100644 index 00000000..a4b5ef7d --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/nonregr.py @@ -0,0 +1,16 @@ +from __future__ import print_function + +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')) diff --git a/pymode/libs/logilab-common-1.4.1/test/data/normal_file.txt b/pymode/libs/logilab-common-1.4.1/test/data/normal_file.txt new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/NOTHING b/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/NOTHING new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/README b/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/README new file mode 100644 index 00000000..27ab0b99 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/README @@ -0,0 +1 @@ +thank you diff --git a/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/coin b/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/coin new file mode 100644 index 00000000..0e46b314 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/coin @@ -0,0 +1 @@ +baba diff --git a/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/toto.txt b/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/toto.txt new file mode 100644 index 00000000..4bf7233a --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/reference_dir/subdir/toto.txt @@ -0,0 +1,53 @@ +Lorem ipsum dolor sit amet, consectetuer adipisci elit. Necesse qui +quidem constituam tantis, et possunt placeat ipsum ex aut iucunde aut +facta, aut impediente autem totum unum directam eius tum voluptate +sensuum reperiuntur ad ab, quae ac.. Sed eius enim a, tranquillat ob +vexetur permagna potius voluptate eo aliae, vivamus esse solis ut non, +atomis videatur in ut, mihi litteris si ante vivere, deinde +emancipaverat appetendum sine erant ex metu philosophiae fatemur, et +magis non corpora ne, maluisti ita locupletiorem medicorum.. Tradere +imperitos exiguam in sint saluti temeritate hoc, nullam nec quaerat, +eademque vivendum, contra similique. + +Molestiae qui, tam sic ea honesto, graeca consecutionem voluptate +inertissimae sunt, corpora denique fabulis dicere ab et quae ad +politus tum in nostris.. Plane pueriliter, hoc affectus quid iis plus +videtur dolorem vivere ad esse asperiores.. Quorum si nihilo eram +pedalis pertinax ii minus, referta mediocrem iustitiam acutum quo +rerum constringendos ex pondere lucilius essent neglexerit insequitur +a tantum sit error ipso si respirare corrupte referuntur, maiorem.. +Voluptatem a etiam perspici gravissimas, cuius.. Unum morbis ne esse +conscientia tamen conclusionemque notionem, amentur quam, praeclarorum +eum consulatu iis invitat solum porro, quidem ad patria, fore res +athenis sempiternum alii venire, est mei nam improbis dolorem, +permulta timidiores. + +Et inquam sic familias, sequatur animis quae et quae ea esse, autem +impediri quaeque modo inciderint consecutionem expectata, sed severa +etiamsi, in egregios temporibus infinito ad artibus, voluptatem +aristotele, tandem aliquo industriae collegi timiditatem sibi igitur +aut, se cum tranquillitate loquuntur quod nullo, quam suum illustribus +fugiendam illis tam consequatur.. Quas maximisque impendere ipsum se +petat altera enim ocurreret sibi maxime, possit ea aegritudo aut ulla, +et quod sed. + +Verissimum confirmat accurate totam iisque sequitur aut probabo et et +adhibenda, mihi sed ad et quod erga minima rerum eius quod, tale et +libidinosarum liber, omnis quae et nunc sicine, nec at aut omnem, +sententiae a, repudiandae.. Vero esse crudelis amentur ut, atque +facilius vita invitat, delectus excepturi ex libidinum non qua +consequi beate quae ratio.. Illa poetis videor requirere, quippiam et +autem ut et esset voluptate neque consilia sed voluptatibus est +virtutum minima et, interesse exquirere et peccandi quae carere se, +angere.. Firme nomine oratio perferendis si voluptates cogitavisse, +feci maledici ea vis et, nam quae legantur animum animis temeritate, +amicitiam desideraturam tollatur nisi de voluptatem. + +Ii videri accedit de.. Graeci tum factis ea ea itaque sunt latinis +detractis reprehensiones nostrum sola non tantopere perfruique quoque +fruenda aptissimum nostrum, pueros graeca qui eruditionem est quae, +labore.. Omnia si quaerimus, si praetermissum vero deserunt quia +democriti retinere ignoratione, iam de gerendarum vel a maxime +provident, in eadem si praeterierunt, certa cibo ut utilitatibus nullo +quod voluptatis iis eamque omnia, stare aut, quamquam et, ut illa +susceperant legant consiliisque, est sed quantum igitur. diff --git a/pymode/libs/logilab-common-1.4.1/test/data/regobjects.py b/pymode/libs/logilab-common-1.4.1/test/data/regobjects.py new file mode 100644 index 00000000..6cea558b --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/regobjects.py @@ -0,0 +1,22 @@ +"""unittest_registry data file""" +from logilab.common.registry import yes, RegistrableObject, RegistrableInstance + +class Proxy(object): + """annoying object should that not be registered, nor cause error""" + def __getattr__(self, attr): + return 1 + +trap = Proxy() + +class AppObjectClass(RegistrableObject): + __registry__ = 'zereg' + __regid__ = 'appobject1' + __select__ = yes() + +class AppObjectInstance(RegistrableInstance): + __registry__ = 'zereg' + __select__ = yes() + def __init__(self, regid): + self.__regid__ = regid + +appobject2 = AppObjectInstance('appobject2') diff --git a/pymode/libs/logilab-common-1.4.1/test/data/regobjects2.py b/pymode/libs/logilab-common-1.4.1/test/data/regobjects2.py new file mode 100644 index 00000000..091b9f7d --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/regobjects2.py @@ -0,0 +1,8 @@ +from logilab.common.registry import RegistrableObject, RegistrableInstance, yes + +class MyRegistrableInstance(RegistrableInstance): + __regid__ = 'appobject3' + __select__ = yes() + __registry__ = 'zereg' + +instance = MyRegistrableInstance(__module__=__name__) diff --git a/pymode/libs/logilab-common-1.4.1/test/data/same_dir/NOTHING b/pymode/libs/logilab-common-1.4.1/test/data/same_dir/NOTHING new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/same_dir/README b/pymode/libs/logilab-common-1.4.1/test/data/same_dir/README new file mode 100644 index 00000000..27ab0b99 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/same_dir/README @@ -0,0 +1 @@ +thank you diff --git a/pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/coin b/pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/coin new file mode 100644 index 00000000..0e46b314 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/coin @@ -0,0 +1 @@ +baba diff --git a/pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/toto.txt b/pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/toto.txt new file mode 100644 index 00000000..4bf7233a --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/same_dir/subdir/toto.txt @@ -0,0 +1,53 @@ +Lorem ipsum dolor sit amet, consectetuer adipisci elit. Necesse qui +quidem constituam tantis, et possunt placeat ipsum ex aut iucunde aut +facta, aut impediente autem totum unum directam eius tum voluptate +sensuum reperiuntur ad ab, quae ac.. Sed eius enim a, tranquillat ob +vexetur permagna potius voluptate eo aliae, vivamus esse solis ut non, +atomis videatur in ut, mihi litteris si ante vivere, deinde +emancipaverat appetendum sine erant ex metu philosophiae fatemur, et +magis non corpora ne, maluisti ita locupletiorem medicorum.. Tradere +imperitos exiguam in sint saluti temeritate hoc, nullam nec quaerat, +eademque vivendum, contra similique. + +Molestiae qui, tam sic ea honesto, graeca consecutionem voluptate +inertissimae sunt, corpora denique fabulis dicere ab et quae ad +politus tum in nostris.. Plane pueriliter, hoc affectus quid iis plus +videtur dolorem vivere ad esse asperiores.. Quorum si nihilo eram +pedalis pertinax ii minus, referta mediocrem iustitiam acutum quo +rerum constringendos ex pondere lucilius essent neglexerit insequitur +a tantum sit error ipso si respirare corrupte referuntur, maiorem.. +Voluptatem a etiam perspici gravissimas, cuius.. Unum morbis ne esse +conscientia tamen conclusionemque notionem, amentur quam, praeclarorum +eum consulatu iis invitat solum porro, quidem ad patria, fore res +athenis sempiternum alii venire, est mei nam improbis dolorem, +permulta timidiores. + +Et inquam sic familias, sequatur animis quae et quae ea esse, autem +impediri quaeque modo inciderint consecutionem expectata, sed severa +etiamsi, in egregios temporibus infinito ad artibus, voluptatem +aristotele, tandem aliquo industriae collegi timiditatem sibi igitur +aut, se cum tranquillitate loquuntur quod nullo, quam suum illustribus +fugiendam illis tam consequatur.. Quas maximisque impendere ipsum se +petat altera enim ocurreret sibi maxime, possit ea aegritudo aut ulla, +et quod sed. + +Verissimum confirmat accurate totam iisque sequitur aut probabo et et +adhibenda, mihi sed ad et quod erga minima rerum eius quod, tale et +libidinosarum liber, omnis quae et nunc sicine, nec at aut omnem, +sententiae a, repudiandae.. Vero esse crudelis amentur ut, atque +facilius vita invitat, delectus excepturi ex libidinum non qua +consequi beate quae ratio.. Illa poetis videor requirere, quippiam et +autem ut et esset voluptate neque consilia sed voluptatibus est +virtutum minima et, interesse exquirere et peccandi quae carere se, +angere.. Firme nomine oratio perferendis si voluptates cogitavisse, +feci maledici ea vis et, nam quae legantur animum animis temeritate, +amicitiam desideraturam tollatur nisi de voluptatem. + +Ii videri accedit de.. Graeci tum factis ea ea itaque sunt latinis +detractis reprehensiones nostrum sola non tantopere perfruique quoque +fruenda aptissimum nostrum, pueros graeca qui eruditionem est quae, +labore.. Omnia si quaerimus, si praetermissum vero deserunt quia +democriti retinere ignoratione, iam de gerendarum vel a maxime +provident, in eadem si praeterierunt, certa cibo ut utilitatibus nullo +quod voluptatis iis eamque omnia, stare aut, quamquam et, ut illa +susceperant legant consiliisque, est sed quantum igitur. diff --git a/pymode/libs/logilab-common-1.4.1/test/data/spam.txt b/pymode/libs/logilab-common-1.4.1/test/data/spam.txt new file mode 100644 index 00000000..068911b1 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/spam.txt @@ -0,0 +1,9 @@ +a +b +c +h +e +f +g +h + diff --git a/pymode/libs/logilab-common-1.4.1/test/data/sub/doc.txt b/pymode/libs/logilab-common-1.4.1/test/data/sub/doc.txt new file mode 100644 index 00000000..c60eb160 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/sub/doc.txt @@ -0,0 +1 @@ +hhh diff --git a/pymode/libs/logilab-common-1.4.1/test/data/sub/momo.py b/pymode/libs/logilab-common-1.4.1/test/data/sub/momo.py new file mode 100644 index 00000000..746b5d04 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/sub/momo.py @@ -0,0 +1,3 @@ +from __future__ import print_function + +print('yo') diff --git a/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/NOTHING b/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/NOTHING new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/README b/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/README new file mode 100644 index 00000000..27ab0b99 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/README @@ -0,0 +1 @@ +thank you diff --git a/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/coin b/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/coin new file mode 100644 index 00000000..0e46b314 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/coin @@ -0,0 +1 @@ +baba diff --git a/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/toto.txt b/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/toto.txt new file mode 100644 index 00000000..4bf7233a --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/subdir_differ_dir/subdir/toto.txt @@ -0,0 +1,53 @@ +Lorem ipsum dolor sit amet, consectetuer adipisci elit. Necesse qui +quidem constituam tantis, et possunt placeat ipsum ex aut iucunde aut +facta, aut impediente autem totum unum directam eius tum voluptate +sensuum reperiuntur ad ab, quae ac.. Sed eius enim a, tranquillat ob +vexetur permagna potius voluptate eo aliae, vivamus esse solis ut non, +atomis videatur in ut, mihi litteris si ante vivere, deinde +emancipaverat appetendum sine erant ex metu philosophiae fatemur, et +magis non corpora ne, maluisti ita locupletiorem medicorum.. Tradere +imperitos exiguam in sint saluti temeritate hoc, nullam nec quaerat, +eademque vivendum, contra similique. + +Molestiae qui, tam sic ea honesto, graeca consecutionem voluptate +inertissimae sunt, corpora denique fabulis dicere ab et quae ad +politus tum in nostris.. Plane pueriliter, hoc affectus quid iis plus +videtur dolorem vivere ad esse asperiores.. Quorum si nihilo eram +pedalis pertinax ii minus, referta mediocrem iustitiam acutum quo +rerum constringendos ex pondere lucilius essent neglexerit insequitur +a tantum sit error ipso si respirare corrupte referuntur, maiorem.. +Voluptatem a etiam perspici gravissimas, cuius.. Unum morbis ne esse +conscientia tamen conclusionemque notionem, amentur quam, praeclarorum +eum consulatu iis invitat solum porro, quidem ad patria, fore res +athenis sempiternum alii venire, est mei nam improbis dolorem, +permulta timidiores. + +Et inquam sic familias, sequatur animis quae et quae ea esse, autem +impediri quaeque modo inciderint consecutionem expectata, sed severa +etiamsi, in egregios temporibus infinito ad artibus, voluptatem +aristotele, tandem aliquo industriae collegi timiditatem sibi igitur +aut, se cum tranquillitate loquuntur quod nullo, quam suum illustribus +fugiendam illis tam consequatur.. Quas maximisque impendere ipsum se +petat altera enim ocurreret sibi maxime, possit ea aegritudo aut ulla, +et quod sed. + +Verissimum confirmat accurate totam iisque sequitur aut probabo et et +adhibenda, mihi sed ad et quod erga minima rerum eius quod, tale et +libidinosarum liber, omnis quae et nunc sicine, nec at aut omnem, +sententiae a, repudiandae.. Vero esse crudelis amentur ut, atque +facilius vita invitat, delectus excepturi ex libidinum non qua +consequi beate quae ratio.. Illa poetis videor requirere, quippiam et +autem ut et esset voluptate neque consilia sed voluptatibus est +virtutum minima et, interesse exquirere et peccandi quae carere se, +angere.. Firme nomine oratio perferendis si voluptates cogitavisse, +feci maledici ea vis et, nam quae legantur animum animis temeritate, +amicitiam desideraturam tollatur nisi de voluptatem. + +Ii videri accedit de.. Graeci tum factis ea ea itaque sunt latinis +detractis reprehensiones nostrum sola non tantopere perfruique quoque +fruenda aptissimum nostrum, pueros graeca qui eruditionem est quae, +labore.. Omnia si quaerimus, si praetermissum vero deserunt quia +democriti retinere ignoratione, iam de gerendarum vel a maxime +provident, in eadem si praeterierunt, certa cibo ut utilitatibus nullo +quod voluptatis iis eamque omnia, stare aut, quamquam et, ut illa +susceperant legant consiliisque, est sed quantum igitur. diff --git a/pymode/libs/logilab-common-1.4.1/test/data/test.ini b/pymode/libs/logilab-common-1.4.1/test/data/test.ini new file mode 100644 index 00000000..3785702c --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/test.ini @@ -0,0 +1,20 @@ +# test configuration +[TEST] + +dothis=yes + +value=' ' + +# you can also document the option +multiple=yop + +number=2 + +#choice +renamed=yo + +multiple-choice=yo,ye + + +[OLD] +named=key:val diff --git a/pymode/libs/logilab-common-1.4.1/test/data/test1.msg b/pymode/libs/logilab-common-1.4.1/test/data/test1.msg new file mode 100644 index 00000000..33b75c83 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/test1.msg @@ -0,0 +1,30 @@ +From Nicolas.Chauvat@logilab.fr Wed Jul 20 12:03:06 2005 +Return-Path: +X-Original-To: nico@logilab.fr +Delivered-To: nico@logilab.fr +Received: from logilab.fr (crater.logilab.fr [172.17.1.4]) + by orion.logilab.fr (Postfix) with SMTP id 7D3412BDA6 + for ; Wed, 20 Jul 2005 12:03:06 +0200 (CEST) +Received: (nullmailer pid 8382 invoked by uid 1000); + Wed, 20 Jul 2005 10:03:20 -0000 +Date: Wed, 20 Jul 2005 12:03:20 +0200 +From: Nicolas Chauvat +To: Nicolas Chauvat +Subject: autre message +Message-ID: <20050720100320.GA8371@logilab.fr> +Mime-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Disposition: inline +Content-Transfer-Encoding: 8bit +User-Agent: Mutt/1.5.9i +X-Spambayes-Classification: ham; 0.01 +Content-Length: 106 +Lines: 6 + +bonjour + +-- +Nicolas Chauvat + +logilab.fr - services en informatique avancée et gestion de connaissances + diff --git a/pymode/libs/logilab-common-1.4.1/test/data/test2.msg b/pymode/libs/logilab-common-1.4.1/test/data/test2.msg new file mode 100644 index 00000000..3a5ca812 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/data/test2.msg @@ -0,0 +1,42 @@ +From alexandre.fayolle@logilab.fr Wed Jul 27 11:21:57 2005 +Date: Wed, 27 Jul 2005 11:21:57 +0200 +From: Alexandre =?iso-8859-1?Q?'d=E9couvreur?= de bugs' Fayolle +To: =?iso-8859-1?B?6WzpbWVudCDg?= accents +Subject: =?iso-8859-1?Q?=C0?= LA MER +Message-ID: <20050727092157.GB3923@logilab.fr> +Mime-Version: 1.0 +Content-Type: multipart/signed; micalg=pgp-sha1; + protocol="application/pgp-signature"; boundary="wULyF7TL5taEdwHz" +Content-Disposition: inline +User-Agent: Mutt/1.5.9i +Status: RO +Content-Length: 692 +Lines: 26 + + +--wULyF7TL5taEdwHz +Content-Type: text/plain; charset=iso-8859-1 +Content-Disposition: inline +Content-Transfer-Encoding: quoted-printable + +il s'est pass=E9 de dr=F4les de choses.=20 + +--=20 +Alexandre Fayolle LOGILAB, Paris (France). +http://www.logilab.com http://www.logilab.fr http://www.logilab.org + +--wULyF7TL5taEdwHz +Content-Type: application/pgp-signature; name="signature.asc" +Content-Description: Digital signature +Content-Disposition: inline + +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +iD8DBQFC51I1Ll/b4N9npV4RAsaLAJ4k9C8Hnrjg+Q3ocrUYnYppTVcgyQCeO8yT +B7AM5XzlRD1lYqlxq+h80K8= +=zfVV +-----END PGP SIGNATURE----- + +--wULyF7TL5taEdwHz-- + diff --git a/pymode/libs/logilab-common-1.4.1/test/data/write_protected_file.txt b/pymode/libs/logilab-common-1.4.1/test/data/write_protected_file.txt new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_cache.py b/pymode/libs/logilab-common-1.4.1/test/unittest_cache.py new file mode 100644 index 00000000..459f1720 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_cache.py @@ -0,0 +1,129 @@ +# unit tests for the cache module +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . + +from logilab.common.testlib import TestCase, unittest_main, TestSuite +from logilab.common.cache import Cache + +class CacheTestCase(TestCase): + + def setUp(self): + self.cache = Cache(5) + self.testdict = {} + + def test_setitem1(self): + """Checks that the setitem method works""" + self.cache[1] = 'foo' + self.assertEqual(self.cache[1], 'foo', "1:foo is not in cache") + self.assertEqual(len(self.cache._usage), 1) + self.assertEqual(self.cache._usage[-1], 1, + '1 is not the most recently used key') + self.assertCountEqual(self.cache._usage, + self.cache.keys(), + "usage list and data keys are different") + + def test_setitem2(self): + """Checks that the setitem method works for multiple items""" + self.cache[1] = 'foo' + self.cache[2] = 'bar' + self.assertEqual(self.cache[2], 'bar', + "2 : 'bar' is not in cache.data") + self.assertEqual(len(self.cache._usage), 2, + "lenght of usage list is not 2") + self.assertEqual(self.cache._usage[-1], 2, + '1 is not the most recently used key') + self.assertCountEqual(self.cache._usage, + self.cache.keys())# usage list and data keys are different + + def test_setitem3(self): + """Checks that the setitem method works when replacing an element in the cache""" + self.cache[1] = 'foo' + self.cache[1] = 'bar' + self.assertEqual(self.cache[1], 'bar', "1 : 'bar' is not in cache.data") + self.assertEqual(len(self.cache._usage), 1, "lenght of usage list is not 1") + self.assertEqual(self.cache._usage[-1], 1, '1 is not the most recently used key') + self.assertCountEqual(self.cache._usage, + self.cache.keys())# usage list and data keys are different + + def test_recycling1(self): + """Checks the removal of old elements""" + self.cache[1] = 'foo' + self.cache[2] = 'bar' + self.cache[3] = 'baz' + self.cache[4] = 'foz' + self.cache[5] = 'fuz' + self.cache[6] = 'spam' + self.assertTrue(1 not in self.cache, + 'key 1 has not been suppressed from the cache dictionnary') + self.assertTrue(1 not in self.cache._usage, + 'key 1 has not been suppressed from the cache LRU list') + self.assertEqual(len(self.cache._usage), 5, "lenght of usage list is not 5") + self.assertEqual(self.cache._usage[-1], 6, '6 is not the most recently used key') + self.assertCountEqual(self.cache._usage, + self.cache.keys())# usage list and data keys are different + + def test_recycling2(self): + """Checks that accessed elements get in the front of the list""" + self.cache[1] = 'foo' + self.cache[2] = 'bar' + self.cache[3] = 'baz' + self.cache[4] = 'foz' + a = self.cache[1] + self.assertEqual(a, 'foo') + self.assertEqual(self.cache._usage[-1], 1, '1 is not the most recently used key') + self.assertCountEqual(self.cache._usage, + self.cache.keys())# usage list and data keys are different + + def test_delitem(self): + """Checks that elements are removed from both element dict and element + list. + """ + self.cache['foo'] = 'bar' + del self.cache['foo'] + self.assertTrue('foo' not in self.cache.keys(), "Element 'foo' was not removed cache dictionnary") + self.assertTrue('foo' not in self.cache._usage, "Element 'foo' was not removed usage list") + self.assertCountEqual(self.cache._usage, + self.cache.keys())# usage list and data keys are different + + + def test_nullsize(self): + """Checks that a 'NULL' size cache doesn't store anything + """ + null_cache = Cache(0) + null_cache['foo'] = 'bar' + self.assertEqual(null_cache.size, 0, 'Cache size should be O, not %d' % \ + null_cache.size) + self.assertEqual(len(null_cache), 0, 'Cache should be empty !') + # Assert null_cache['foo'] raises a KeyError + self.assertRaises(KeyError, null_cache.__getitem__, 'foo') + # Deleting element raises a KeyError + self.assertRaises(KeyError, null_cache.__delitem__, 'foo') + + def test_getitem(self): + """ Checks that getitem doest not modify the _usage attribute + """ + try: + self.cache['toto'] + except KeyError: + self.assertTrue('toto' not in self.cache._usage) + else: + self.fail('excepted KeyError') + + +if __name__ == "__main__": + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_changelog.py b/pymode/libs/logilab-common-1.4.1/test/unittest_changelog.py new file mode 100644 index 00000000..c2572d70 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_changelog.py @@ -0,0 +1,40 @@ +# copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common is free software: you can redistribute it 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-common 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-common. If not, see . + +from os.path import join, dirname + +from io import StringIO +from logilab.common.testlib import TestCase, unittest_main + +from logilab.common.changelog import ChangeLog + + +class ChangeLogTC(TestCase): + cl_class = ChangeLog + cl_file = join(dirname(__file__), 'data', 'ChangeLog') + + def test_round_trip(self): + cl = self.cl_class(self.cl_file) + out = StringIO() + cl.write(out) + with open(self.cl_file) as stream: + self.assertMultiLineEqual(stream.read(), out.getvalue()) + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_configuration.py b/pymode/libs/logilab-common-1.4.1/test/unittest_configuration.py new file mode 100644 index 00000000..ea7cdca6 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_configuration.py @@ -0,0 +1,509 @@ +# 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 logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +import tempfile +import os +from os.path import join, dirname, abspath +import re + +from sys import version_info + +from six import integer_types + +from logilab.common import attrdict +from logilab.common.compat import StringIO +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.optik_ext import OptionValueError +from logilab.common.configuration import Configuration, OptionError, \ + OptionsManagerMixIn, OptionsProviderMixIn, Method, read_old_config, \ + merge_options + +DATA = join(dirname(abspath(__file__)), 'data') + +OPTIONS = [('dothis', {'type':'yn', 'action': 'store', 'default': True, 'metavar': ''}), + ('value', {'type': 'string', 'metavar': '', 'short': 'v'}), + ('multiple', {'type': 'csv', 'default': ['yop', 'yep'], + 'metavar': '', + 'help': 'you can also document the option'}), + ('number', {'type': 'int', 'default':2, 'metavar':'', 'help': 'boom'}), + ('bytes', {'type': 'bytes', 'default':'1KB', 'metavar':''}), + ('choice', {'type': 'choice', 'default':'yo', 'choices': ('yo', 'ye'), + 'metavar':''}), + ('multiple-choice', {'type': 'multiple_choice', 'default':['yo', 'ye'], + 'choices': ('yo', 'ye', 'yu', 'yi', 'ya'), + 'metavar':''}), + ('named', {'type':'named', 'default':Method('get_named'), + 'metavar': ''}), + + ('diffgroup', {'type':'string', 'default':'pouet', 'metavar': '', + 'group': 'agroup'}), + ('reset-value', {'type': 'string', 'metavar': '', 'short': 'r', + 'dest':'value'}), + + ('opt-b-1', {'type': 'string', 'metavar': '', 'group': 'bgroup'}), + ('opt-b-2', {'type': 'string', 'metavar': '', 'group': 'bgroup'}), + ] + +class MyConfiguration(Configuration): + """test configuration""" + def get_named(self): + return {'key': 'val'} + +class ConfigurationTC(TestCase): + + def setUp(self): + self.cfg = MyConfiguration(name='test', options=OPTIONS, usage='Just do it ! (tm)') + + def test_default(self): + cfg = self.cfg + self.assertEqual(cfg['dothis'], True) + self.assertEqual(cfg['value'], None) + self.assertEqual(cfg['multiple'], ['yop', 'yep']) + self.assertEqual(cfg['number'], 2) + self.assertEqual(cfg['bytes'], 1024) + self.assertIsInstance(cfg['bytes'], integer_types) + self.assertEqual(cfg['choice'], 'yo') + self.assertEqual(cfg['multiple-choice'], ['yo', 'ye']) + self.assertEqual(cfg['named'], {'key': 'val'}) + + def test_base(self): + cfg = self.cfg + cfg.set_option('number', '0') + self.assertEqual(cfg['number'], 0) + self.assertRaises(OptionValueError, cfg.set_option, 'number', 'youpi') + self.assertRaises(OptionValueError, cfg.set_option, 'choice', 'youpi') + self.assertRaises(OptionValueError, cfg.set_option, 'multiple-choice', ('yo', 'y', 'ya')) + cfg.set_option('multiple-choice', 'yo, ya') + self.assertEqual(cfg['multiple-choice'], ['yo', 'ya']) + self.assertEqual(cfg.get('multiple-choice'), ['yo', 'ya']) + self.assertEqual(cfg.get('whatever'), None) + + def test_load_command_line_configuration(self): + cfg = self.cfg + args = cfg.load_command_line_configuration(['--choice', 'ye', '--number', '4', + '--multiple=1,2,3', '--dothis=n', + '--bytes=10KB', + 'other', 'arguments']) + self.assertEqual(args, ['other', 'arguments']) + self.assertEqual(cfg['dothis'], False) + self.assertEqual(cfg['multiple'], ['1', '2', '3']) + self.assertEqual(cfg['number'], 4) + self.assertEqual(cfg['bytes'], 10240) + self.assertEqual(cfg['choice'], 'ye') + self.assertEqual(cfg['value'], None) + args = cfg.load_command_line_configuration(['-v', 'duh']) + self.assertEqual(args, []) + self.assertEqual(cfg['value'], 'duh') + self.assertEqual(cfg['dothis'], False) + self.assertEqual(cfg['multiple'], ['1', '2', '3']) + self.assertEqual(cfg['number'], 4) + self.assertEqual(cfg['bytes'], 10240) + self.assertEqual(cfg['choice'], 'ye') + + def test_load_configuration(self): + cfg = self.cfg + args = cfg.load_configuration(choice='ye', number='4', + multiple='1,2,3', dothis='n', + multiple_choice=('yo', 'ya')) + self.assertEqual(cfg['dothis'], False) + self.assertEqual(cfg['multiple'], ['1', '2', '3']) + self.assertEqual(cfg['number'], 4) + self.assertEqual(cfg['choice'], 'ye') + self.assertEqual(cfg['value'], None) + self.assertEqual(cfg['multiple-choice'], ('yo', 'ya')) + + def test_load_configuration_file_case_insensitive(self): + file = tempfile.mktemp() + stream = open(file, 'w') + try: + stream.write("""[Test] + +dothis=no + +#value= + +# you can also document the option +multiple=yop,yepii + +# boom +number=3 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + + +[agroup] + +diffgroup=zou +""") + stream.close() + self.cfg.load_file_configuration(file) + self.assertEqual(self.cfg['dothis'], False) + self.assertEqual(self.cfg['value'], None) + self.assertEqual(self.cfg['multiple'], ['yop', 'yepii']) + self.assertEqual(self.cfg['diffgroup'], 'zou') + finally: + os.remove(file) + + def test_option_order(self): + """ Check that options are taken into account in the command line order + and not in the order they are defined in the Configuration object. + """ + file = tempfile.mktemp() + stream = open(file, 'w') + try: + stream.write("""[Test] +reset-value=toto +value=tata +""") + stream.close() + self.cfg.load_file_configuration(file) + finally: + os.remove(file) + self.assertEqual(self.cfg['value'], 'tata') + + def test_unsupported_options(self): + file = tempfile.mktemp() + stream = open(file, 'w') + try: + stream.write("""[Test] +whatever=toto +value=tata +""") + stream.close() + self.cfg.load_file_configuration(file) + finally: + os.remove(file) + self.assertEqual(self.cfg['value'], 'tata') + self.assertRaises(OptionError, self.cfg.__getitem__, 'whatever') + + def test_generate_config(self): + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +#value= + +# you can also document the option +multiple=yop,yep + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +#reset-value= + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + + def test_generate_config_with_space_string(self): + self.cfg['value'] = ' ' + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +value=' ' + +# you can also document the option +multiple=yop,yep + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +reset-value=' ' + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + + def test_generate_config_with_multiline_string(self): + self.cfg['value'] = 'line1\nline2\nline3' + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +value= + line1 + line2 + line3 + +# you can also document the option +multiple=yop,yep + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +reset-value= + line1 + line2 + line3 + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + + + def test_roundtrip(self): + cfg = self.cfg + f = tempfile.mktemp() + stream = open(f, 'w') + try: + self.cfg['dothis'] = False + self.cfg['multiple'] = ["toto", "tata"] + self.cfg['number'] = 3 + self.cfg['bytes'] = 2048 + cfg.generate_config(stream) + stream.close() + new_cfg = MyConfiguration(name='test', options=OPTIONS) + new_cfg.load_file_configuration(f) + self.assertEqual(cfg['dothis'], new_cfg['dothis']) + self.assertEqual(cfg['multiple'], new_cfg['multiple']) + self.assertEqual(cfg['number'], new_cfg['number']) + self.assertEqual(cfg['bytes'], new_cfg['bytes']) + self.assertEqual(cfg['choice'], new_cfg['choice']) + self.assertEqual(cfg['value'], new_cfg['value']) + self.assertEqual(cfg['multiple-choice'], new_cfg['multiple-choice']) + finally: + os.remove(f) + + def test_setitem(self): + self.assertRaises(OptionValueError, + self.cfg.__setitem__, 'multiple-choice', ('a', 'b')) + self.cfg['multiple-choice'] = ('yi', 'ya') + self.assertEqual(self.cfg['multiple-choice'], ('yi', 'ya')) + + def test_help(self): + self.cfg.add_help_section('bonus', 'a nice additional help') + help = self.cfg.help().strip() + # at least in python 2.4.2 the output is: + # ' -v , --value=' + # it is not unlikely some optik/optparse versions do print -v + # so accept both + help = help.replace(' -v , ', ' -v, ') + help = re.sub('[ ]*(\r?\n)', '\\1', help) + USAGE = """Usage: Just do it ! (tm) + +Options: + -h, --help show this help message and exit + --dothis= + -v, --value= + --multiple= + you can also document the option [current: yop,yep] + --number= boom [current: 2] + --bytes= + --choice= + --multiple-choice= + --named= + -r , --reset-value= + + Agroup: + --diffgroup= + + Bgroup: + --opt-b-1= + --opt-b-2= + + Bonus: + a nice additional help""" + if version_info < (2, 5): + # 'usage' header is not capitalized in this version + USAGE = USAGE.replace('Usage: ', 'usage: ') + elif version_info < (2, 4): + USAGE = """usage: Just do it ! (tm) + +options: + -h, --help show this help message and exit + --dothis= + -v, --value= + --multiple= + you can also document the option + --number= + --choice= + --multiple-choice= + --named= + + Bonus: + a nice additional help +""" + self.assertMultiLineEqual(help, USAGE) + + + def test_manpage(self): + pkginfo = {} + with open(join(DATA, '__pkginfo__.py')) as fobj: + exec(fobj.read(), pkginfo) + self.cfg.generate_manpage(attrdict(pkginfo), stream=StringIO()) + + def test_rewrite_config(self): + changes = [('renamed', 'renamed', 'choice'), + ('moved', 'named', 'old', 'test'), + ] + read_old_config(self.cfg, changes, join(DATA, 'test.ini')) + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +value=' ' + +# you can also document the option +multiple=yop + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +reset-value=' ' + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + +class Linter(OptionsManagerMixIn, OptionsProviderMixIn): + options = ( + ('profile', {'type' : 'yn', 'metavar' : '', + 'default': False, + 'help' : 'Profiled execution.'}), + ) + def __init__(self): + OptionsManagerMixIn.__init__(self, usage="") + OptionsProviderMixIn.__init__(self) + self.register_options_provider(self) + self.load_provider_defaults() + +class RegrTC(TestCase): + + def setUp(self): + self.linter = Linter() + + def test_load_defaults(self): + self.linter.load_command_line_configuration([]) + self.assertEqual(self.linter.config.profile, False) + + def test_register_options_multiple_groups(self): + """ensure multiple option groups can be registered at once""" + config = Configuration() + self.assertEqual(config.options, ()) + new_options = ( + ('option1', {'type': 'string', 'help': '', + 'group': 'g1', 'level': 2}), + ('option2', {'type': 'string', 'help': '', + 'group': 'g1', 'level': 2}), + ('option3', {'type': 'string', 'help': '', + 'group': 'g2', 'level': 2}), + ) + config.register_options(new_options) + self.assertEqual(config.options, new_options) + + +class MergeTC(TestCase): + + def test_merge1(self): + merged = merge_options([('dothis', {'type':'yn', 'action': 'store', 'default': True, 'metavar': ''}), + ('dothis', {'type':'yn', 'action': 'store', 'default': False, 'metavar': ''}), + ]) + self.assertEqual(len(merged), 1) + self.assertEqual(merged[0][0], 'dothis') + self.assertEqual(merged[0][1]['default'], True) + + def test_merge2(self): + merged = merge_options([('dothis', {'type':'yn', 'action': 'store', 'default': True, 'metavar': ''}), + ('value', {'type': 'string', 'metavar': '', 'short': 'v'}), + ('dothis', {'type':'yn', 'action': 'store', 'default': False, 'metavar': ''}), + ]) + self.assertEqual(len(merged), 2) + self.assertEqual(merged[0][0], 'value') + self.assertEqual(merged[1][0], 'dothis') + self.assertEqual(merged[1][1]['default'], True) + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_date.py b/pymode/libs/logilab-common-1.4.1/test/unittest_date.py new file mode 100644 index 00000000..9ae444bb --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_date.py @@ -0,0 +1,206 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +""" +Unittests for date helpers +""" +from logilab.common.testlib import TestCase, unittest_main, tag + +from logilab.common.date import (date_range, endOfMonth, add_days_worked, + nb_open_days, get_national_holidays, ustrftime, ticks2datetime, + utcdatetime, datetime2ticks) + +from datetime import date, datetime, timedelta +from calendar import timegm +import pytz + +try: + from mx.DateTime import Date as mxDate, DateTime as mxDateTime, \ + now as mxNow, RelativeDateTime, RelativeDate +except ImportError: + mxDate = mxDateTime = RelativeDateTime = mxNow = None + +class DateTC(TestCase): + datecls = date + datetimecls = datetime + timedeltacls = timedelta + now = datetime.now + + def test_day(self): + """enumerate days""" + r = list(date_range(self.datecls(2000, 1, 1), self.datecls(2000, 1, 4))) + expected = [self.datecls(2000, 1, 1), self.datecls(2000, 1, 2), self.datecls(2000, 1, 3)] + self.assertListEqual(r, expected) + r = list(date_range(self.datecls(2000, 1, 31), self.datecls(2000, 2, 3))) + expected = [self.datecls(2000, 1, 31), self.datecls(2000, 2, 1), self.datecls(2000, 2, 2)] + self.assertListEqual(r, expected) + r = list(date_range(self.datecls(2000, 1, 1), self.datecls(2000, 1, 6), 2)) + expected = [self.datecls(2000, 1, 1), self.datecls(2000, 1, 3), self.datecls(2000, 1, 5)] + self.assertListEqual(r, expected) + + def test_add_days_worked(self): + add = add_days_worked + # normal + self.assertEqual(add(self.datecls(2008, 1, 3), 1), self.datecls(2008, 1, 4)) + # skip week-end + self.assertEqual(add(self.datecls(2008, 1, 3), 2), self.datecls(2008, 1, 7)) + # skip 2 week-ends + self.assertEqual(add(self.datecls(2008, 1, 3), 8), self.datecls(2008, 1, 15)) + # skip holiday + week-end + self.assertEqual(add(self.datecls(2008, 4, 30), 2), self.datecls(2008, 5, 5)) + + def test_get_national_holidays(self): + holidays = get_national_holidays + yield self.assertEqual, holidays(self.datecls(2008, 4, 29), self.datecls(2008, 5, 2)), \ + [self.datecls(2008, 5, 1)] + yield self.assertEqual, holidays(self.datecls(2008, 5, 7), self.datecls(2008, 5, 8)), [] + x = self.datetimecls(2008, 5, 7, 12, 12, 12) + yield self.assertEqual, holidays(x, x + self.timedeltacls(days=1)), [] + + def test_open_days_now_and_before(self): + nb = nb_open_days + x = self.now() + y = x - self.timedeltacls(seconds=1) + self.assertRaises(AssertionError, nb, x, y) + + def assertOpenDays(self, start, stop, expected): + got = nb_open_days(start, stop) + self.assertEqual(got, expected) + + def test_open_days_tuesday_friday(self): + self.assertOpenDays(self.datecls(2008, 3, 4), self.datecls(2008, 3, 7), 3) + + def test_open_days_day_nextday(self): + self.assertOpenDays(self.datecls(2008, 3, 4), self.datecls(2008, 3, 5), 1) + + def test_open_days_friday_monday(self): + self.assertOpenDays(self.datecls(2008, 3, 7), self.datecls(2008, 3, 10), 1) + + def test_open_days_friday_monday_with_two_weekends(self): + self.assertOpenDays(self.datecls(2008, 3, 7), self.datecls(2008, 3, 17), 6) + + def test_open_days_tuesday_wednesday(self): + """week-end + easter monday""" + self.assertOpenDays(self.datecls(2008, 3, 18), self.datecls(2008, 3, 26), 5) + + def test_open_days_friday_saturday(self): + self.assertOpenDays(self.datecls(2008, 3, 7), self.datecls(2008, 3, 8), 1) + + def test_open_days_friday_sunday(self): + self.assertOpenDays(self.datecls(2008, 3, 7), self.datecls(2008, 3, 9), 1) + + def test_open_days_saturday_sunday(self): + self.assertOpenDays(self.datecls(2008, 3, 8), self.datecls(2008, 3, 9), 0) + + def test_open_days_saturday_monday(self): + self.assertOpenDays(self.datecls(2008, 3, 8), self.datecls(2008, 3, 10), 0) + + def test_open_days_saturday_tuesday(self): + self.assertOpenDays(self.datecls(2008, 3, 8), self.datecls(2008, 3, 11), 1) + + def test_open_days_now_now(self): + x = self.now() + self.assertOpenDays(x, x, 0) + + def test_open_days_now_now2(self): + x = self.datetimecls(2010, 5, 24) + self.assertOpenDays(x, x, 0) + + def test_open_days_afternoon_before_holiday(self): + self.assertOpenDays(self.datetimecls(2008, 5, 7, 14), self.datetimecls(2008, 5, 8, 0), 1) + + def test_open_days_afternoon_before_saturday(self): + self.assertOpenDays(self.datetimecls(2008, 5, 9, 14), self.datetimecls(2008, 5, 10, 14), 1) + + def test_open_days_afternoon(self): + self.assertOpenDays(self.datetimecls(2008, 5, 6, 14), self.datetimecls(2008, 5, 7, 14), 1) + + @tag('posix', '1900') + def test_ustrftime_before_1900(self): + date = self.datetimecls(1328, 3, 12, 6, 30) + self.assertEqual(ustrftime(date, '%Y-%m-%d %H:%M:%S'), u'1328-03-12 06:30:00') + + @tag('posix', '1900') + def test_ticks2datetime_before_1900(self): + ticks = -2209075200000 + date = ticks2datetime(ticks) + self.assertEqual(ustrftime(date, '%Y-%m-%d'), u'1899-12-31') + + def test_month(self): + """enumerate months""" + r = list(date_range(self.datecls(2006, 5, 6), self.datecls(2006, 8, 27), + incmonth=True)) + expected = [self.datecls(2006, 5, 6), self.datecls(2006, 6, 1), self.datecls(2006, 7, 1), self.datecls(2006, 8, 1)] + self.assertListEqual(expected, r) + + def test_utcdatetime(self): + if self.datetimecls is mxDateTime: + return + d = self.datetimecls(2014, 11, 26, 12, 0, 0, 57, tzinfo=pytz.utc) + d = utcdatetime(d) + self.assertEqual(d, self.datetimecls(2014, 11, 26, 12, 0, 0, 57)) + self.assertIsNone(d.tzinfo) + + d = pytz.timezone('Europe/Paris').localize( + self.datetimecls(2014, 11, 26, 12, 0, 0, 57)) + d = utcdatetime(d) + self.assertEqual(d, self.datetimecls(2014, 11, 26, 11, 0, 0, 57)) + self.assertIsNone(d.tzinfo) + + d = pytz.timezone('Europe/Paris').localize( + self.datetimecls(2014, 7, 26, 12, 0, 0, 57)) + d = utcdatetime(d) + self.assertEqual(d, self.datetimecls(2014, 7, 26, 10, 0, 0, 57)) + self.assertIsNone(d.tzinfo) + + def test_datetime2ticks(self): + d = datetime(2014, 11, 26, 12, 0, 0, 57, tzinfo=pytz.utc) + timestamp = timegm(d.timetuple()) + self.assertEqual(datetime2ticks(d), timestamp * 1000) + d = d.replace(microsecond=123456) + self.assertEqual(datetime2ticks(d), timestamp * 1000 + 123) + + def test_datetime2ticks_date_argument(self): + d = date(2014, 11, 26) + timestamp = timegm(d.timetuple()) + self.assertEqual(datetime2ticks(d), timestamp * 1000) + + +class MxDateTC(DateTC): + datecls = mxDate + datetimecls = mxDateTime + timedeltacls = RelativeDateTime + now = mxNow + + def check_mx(self): + if mxDate is None: + self.skipTest('mx.DateTime is not installed') + + def setUp(self): + self.check_mx() + + def test_month(self): + """enumerate months""" + r = list(date_range(self.datecls(2000, 1, 2), self.datecls(2000, 4, 4), endOfMonth)) + expected = [self.datecls(2000, 1, 2), self.datecls(2000, 2, 29), self.datecls(2000, 3, 31)] + self.assertListEqual(r, expected) + r = list(date_range(self.datecls(2000, 11, 30), self.datecls(2001, 2, 3), endOfMonth)) + expected = [self.datecls(2000, 11, 30), self.datecls(2000, 12, 31), self.datecls(2001, 1, 31)] + self.assertListEqual(r, expected) + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_decorators.py b/pymode/libs/logilab-common-1.4.1/test/unittest_decorators.py new file mode 100644 index 00000000..e97a56f2 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_decorators.py @@ -0,0 +1,208 @@ +# 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 logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""unit tests for the decorators module +""" +import sys +import types + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.decorators import (monkeypatch, cached, clear_cache, + copy_cache, cachedproperty) + +class DecoratorsTC(TestCase): + + def test_monkeypatch_instance_method(self): + class MyClass: pass + @monkeypatch(MyClass) + def meth1(self): + return 12 + class XXX(object): + @monkeypatch(MyClass) + def meth2(self): + return 12 + if sys.version_info < (3, 0): + self.assertIsInstance(MyClass.meth1, types.MethodType) + self.assertIsInstance(MyClass.meth2, types.MethodType) + else: + # with python3, unbound method are functions + self.assertIsInstance(MyClass.meth1, types.FunctionType) + self.assertIsInstance(MyClass.meth2, types.FunctionType) + self.assertEqual(MyClass().meth1(), 12) + self.assertEqual(MyClass().meth2(), 12) + + def test_monkeypatch_property(self): + class MyClass: pass + @monkeypatch(MyClass, methodname='prop1') + @property + def meth1(self): + return 12 + self.assertIsInstance(MyClass.prop1, property) + self.assertEqual(MyClass().prop1, 12) + + def test_monkeypatch_arbitrary_callable(self): + class MyClass: pass + class ArbitraryCallable(object): + def __call__(self): + return 12 + # ensure it complains about missing __name__ + with self.assertRaises(AttributeError) as cm: + monkeypatch(MyClass)(ArbitraryCallable()) + self.assertTrue(str(cm.exception).endswith('has no __name__ attribute: you should provide an explicit `methodname`')) + # ensure no black magic under the hood + monkeypatch(MyClass, 'foo')(ArbitraryCallable()) + self.assertTrue(callable(MyClass.foo)) + self.assertEqual(MyClass().foo(), 12) + + def test_monkeypatch_with_same_name(self): + class MyClass: pass + @monkeypatch(MyClass) + def meth1(self): + return 12 + self.assertEqual([attr for attr in dir(MyClass) if attr[:2] != '__'], + ['meth1']) + inst = MyClass() + self.assertEqual(inst.meth1(), 12) + + def test_monkeypatch_with_custom_name(self): + class MyClass: pass + @monkeypatch(MyClass, 'foo') + def meth2(self, param): + return param + 12 + self.assertEqual([attr for attr in dir(MyClass) if attr[:2] != '__'], + ['foo']) + inst = MyClass() + self.assertEqual(inst.foo(4), 16) + + def test_cannot_cache_generator(self): + def foo(): + yield 42 + self.assertRaises(AssertionError, cached, foo) + + def test_cached_preserves_docstrings_and_name(self): + class Foo(object): + @cached + def foo(self): + """ what's up doc ? """ + def bar(self, zogzog): + """ what's up doc ? """ + bar = cached(bar, 1) + @cached + def quux(self, zogzog): + """ what's up doc ? """ + self.assertEqual(Foo.foo.__doc__, """ what's up doc ? """) + self.assertEqual(Foo.foo.__name__, 'foo') + self.assertEqual(Foo.bar.__doc__, """ what's up doc ? """) + self.assertEqual(Foo.bar.__name__, 'bar') + self.assertEqual(Foo.quux.__doc__, """ what's up doc ? """) + self.assertEqual(Foo.quux.__name__, 'quux') + + def test_cached_single_cache(self): + class Foo(object): + @cached(cacheattr=u'_foo') + def foo(self): + """ what's up doc ? """ + foo = Foo() + foo.foo() + self.assertTrue(hasattr(foo, '_foo')) + clear_cache(foo, 'foo') + self.assertFalse(hasattr(foo, '_foo')) + + def test_cached_multi_cache(self): + class Foo(object): + @cached(cacheattr=u'_foo') + def foo(self, args): + """ what's up doc ? """ + foo = Foo() + foo.foo(1) + self.assertEqual(foo._foo, {(1,): None}) + clear_cache(foo, 'foo') + self.assertFalse(hasattr(foo, '_foo')) + + def test_cached_keyarg_cache(self): + class Foo(object): + @cached(cacheattr=u'_foo', keyarg=1) + def foo(self, other, args): + """ what's up doc ? """ + foo = Foo() + foo.foo(2, 1) + self.assertEqual(foo._foo, {2: None}) + clear_cache(foo, 'foo') + self.assertFalse(hasattr(foo, '_foo')) + + def test_cached_property(self): + class Foo(object): + @property + @cached(cacheattr=u'_foo') + def foo(self): + """ what's up doc ? """ + foo = Foo() + foo.foo + self.assertEqual(foo._foo, None) + clear_cache(foo, 'foo') + self.assertFalse(hasattr(foo, '_foo')) + + def test_copy_cache(self): + class Foo(object): + @cached(cacheattr=u'_foo') + def foo(self, args): + """ what's up doc ? """ + foo = Foo() + foo.foo(1) + self.assertEqual(foo._foo, {(1,): None}) + foo2 = Foo() + self.assertFalse(hasattr(foo2, '_foo')) + copy_cache(foo2, 'foo', foo) + self.assertEqual(foo2._foo, {(1,): None}) + + + def test_cachedproperty(self): + class Foo(object): + x = 0 + @cachedproperty + def bar(self): + self.__class__.x += 1 + return self.__class__.x + @cachedproperty + def quux(self): + """ some prop """ + return 42 + + foo = Foo() + self.assertEqual(Foo.x, 0) + self.assertFalse('bar' in foo.__dict__) + self.assertEqual(foo.bar, 1) + self.assertTrue('bar' in foo.__dict__) + self.assertEqual(foo.bar, 1) + self.assertEqual(foo.quux, 42) + self.assertEqual(Foo.bar.__doc__, + '') + self.assertEqual(Foo.quux.__doc__, + '\n some prop ') + + foo2 = Foo() + self.assertEqual(foo2.bar, 2) + # make sure foo.foo is cached + self.assertEqual(foo.bar, 1) + + class Kallable(object): + def __call__(self): + return 42 + self.assertRaises(TypeError, cachedproperty, Kallable()) + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_deprecation.py b/pymode/libs/logilab-common-1.4.1/test/unittest_deprecation.py new file mode 100644 index 00000000..b0f8a1aa --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_deprecation.py @@ -0,0 +1,147 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""unit tests for logilab.common.deprecation""" + +import warnings + +from six import add_metaclass + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common import deprecation + + +class RawInputTC(TestCase): + + # XXX with 2.6 we could test warnings + # http://docs.python.org/library/warnings.html#testing-warnings + # instead we just make sure it does not crash + + def mock_warn(self, *args, **kwargs): + self.messages.append(args[0]) + + def setUp(self): + self.messages = [] + deprecation.warn = self.mock_warn + + def tearDown(self): + deprecation.warn = warnings.warn + + def mk_func(self): + def any_func(): + pass + return any_func + + def test_class_deprecated(self): + @add_metaclass(deprecation.class_deprecated) + class AnyClass(object): + pass + AnyClass() + self.assertEqual(self.messages, + ['AnyClass is deprecated']) + + def test_deprecated_func(self): + any_func = deprecation.deprecated()(self.mk_func()) + any_func() + any_func = deprecation.deprecated('message')(self.mk_func()) + any_func() + self.assertEqual(self.messages, + ['The function "any_func" is deprecated', 'message']) + + def test_deprecated_decorator(self): + @deprecation.deprecated() + def any_func(): + pass + any_func() + @deprecation.deprecated('message') + def any_func(): + pass + any_func() + self.assertEqual(self.messages, + ['The function "any_func" is deprecated', 'message']) + + def test_moved(self): + module = 'data.deprecation' + any_func = deprecation.moved(module, 'moving_target') + any_func() + self.assertEqual(self.messages, + ['object moving_target has been moved to module data.deprecation']) + + def test_deprecated_manager(self): + deprecator = deprecation.DeprecationManager("module_name") + deprecator.compatibility('1.3') + # This warn should be printed. + deprecator.warn('1.1', "Major deprecation message.", 1) + deprecator.warn('1.1') + + @deprecator.deprecated('1.2', 'Major deprecation message.') + def any_func(): + pass + any_func() + + @deprecator.deprecated('1.2') + def other_func(): + pass + other_func() + + self.assertListEqual(self.messages, + ['[module_name 1.1] Major deprecation message.', + '[module_name 1.1] ', + '[module_name 1.2] Major deprecation message.', + '[module_name 1.2] The function "other_func" is deprecated']) + + def test_class_deprecated_manager(self): + deprecator = deprecation.DeprecationManager("module_name") + deprecator.compatibility('1.3') + @add_metaclass(deprecator.class_deprecated('1.2')) + class AnyClass(object): + pass + AnyClass() + self.assertEqual(self.messages, + ['[module_name 1.2] AnyClass is deprecated']) + + + def test_deprecated_manager_noprint(self): + deprecator = deprecation.DeprecationManager("module_name") + deprecator.compatibility('1.3') + # This warn should not be printed. + deprecator.warn('1.3', "Minor deprecation message.", 1) + + @deprecator.deprecated('1.3', 'Minor deprecation message.') + def any_func(): + pass + any_func() + + @deprecator.deprecated('1.20') + def other_func(): + pass + other_func() + + @deprecator.deprecated('1.4') + def other_func(): + pass + other_func() + + class AnyClass(object): + __metaclass__ = deprecator.class_deprecated((1,5)) + AnyClass() + + self.assertFalse(self.messages) + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_fileutils.py b/pymode/libs/logilab-common-1.4.1/test/unittest_fileutils.py new file mode 100644 index 00000000..555e73f4 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_fileutils.py @@ -0,0 +1,146 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""unit tests for logilab.common.fileutils""" + +import doctest +import io +import sys, os, tempfile, shutil +from stat import S_IWRITE +from os.path import join + +from logilab.common.testlib import TestCase, unittest_main, unittest + +from logilab.common.fileutils import * + +DATA_DIR = join(os.path.abspath(os.path.dirname(__file__)), 'data') +NEWLINES_TXT = join(DATA_DIR, 'newlines.txt') + + +class FirstleveldirectoryTC(TestCase): + + def test_known_values_first_level_directory(self): + """return the first level directory of a path""" + self.assertEqual(first_level_directory('truc/bidule/chouette'), 'truc', None) + self.assertEqual(first_level_directory('/truc/bidule/chouette'), '/', None) + +class IsBinaryTC(TestCase): + def test(self): + self.assertEqual(is_binary('toto.txt'), 0) + #self.assertEqual(is_binary('toto.xml'), 0) + self.assertEqual(is_binary('toto.bin'), 1) + self.assertEqual(is_binary('toto.sxi'), 1) + self.assertEqual(is_binary('toto.whatever'), 1) + +class GetModeTC(TestCase): + def test(self): + self.assertEqual(write_open_mode('toto.txt'), 'w') + #self.assertEqual(write_open_mode('toto.xml'), 'w') + self.assertEqual(write_open_mode('toto.bin'), 'wb') + self.assertEqual(write_open_mode('toto.sxi'), 'wb') + +class NormReadTC(TestCase): + def test_known_values_norm_read(self): + with io.open(NEWLINES_TXT) as f: + data = f.read() + self.assertEqual(data.strip(), '\n'.join(['# mixed new lines', '1', '2', '3'])) + + +class LinesTC(TestCase): + def test_known_values_lines(self): + self.assertEqual(lines(NEWLINES_TXT), + ['# mixed new lines', '1', '2', '3']) + + def test_known_values_lines_comment(self): + self.assertEqual(lines(NEWLINES_TXT, comments='#'), + ['1', '2', '3']) + +class ExportTC(TestCase): + def setUp(self): + self.tempdir = tempfile.mktemp() + os.mkdir(self.tempdir) + + def test(self): + export(DATA_DIR, self.tempdir, verbose=0) + self.assertTrue(exists(join(self.tempdir, '__init__.py'))) + self.assertTrue(exists(join(self.tempdir, 'sub'))) + self.assertTrue(not exists(join(self.tempdir, '__init__.pyc'))) + self.assertTrue(not exists(join(self.tempdir, 'CVS'))) + + def tearDown(self): + shutil.rmtree(self.tempdir) + +class ProtectedFileTC(TestCase): + def setUp(self): + self.rpath = join(DATA_DIR, 'write_protected_file.txt') + self.rwpath = join(DATA_DIR, 'normal_file.txt') + # Make sure rpath is not writable ! + os.chmod(self.rpath, 33060) + # Make sure rwpath is writable ! + os.chmod(self.rwpath, 33188) + + def test_mode_change(self): + """tests that mode is changed when needed""" + # test on non-writable file + #self.assertTrue(not os.access(self.rpath, os.W_OK)) + self.assertTrue(not os.stat(self.rpath).st_mode & S_IWRITE) + wp_file = ProtectedFile(self.rpath, 'w') + self.assertTrue(os.stat(self.rpath).st_mode & S_IWRITE) + self.assertTrue(os.access(self.rpath, os.W_OK)) + # test on writable-file + self.assertTrue(os.stat(self.rwpath).st_mode & S_IWRITE) + self.assertTrue(os.access(self.rwpath, os.W_OK)) + wp_file = ProtectedFile(self.rwpath, 'w') + self.assertTrue(os.stat(self.rwpath).st_mode & S_IWRITE) + self.assertTrue(os.access(self.rwpath, os.W_OK)) + + def test_restore_on_close(self): + """tests original mode is restored on close""" + # test on non-writable file + #self.assertTrue(not os.access(self.rpath, os.W_OK)) + self.assertTrue(not os.stat(self.rpath).st_mode & S_IWRITE) + ProtectedFile(self.rpath, 'w').close() + #self.assertTrue(not os.access(self.rpath, os.W_OK)) + self.assertTrue(not os.stat(self.rpath).st_mode & S_IWRITE) + # test on writable-file + self.assertTrue(os.access(self.rwpath, os.W_OK)) + self.assertTrue(os.stat(self.rwpath).st_mode & S_IWRITE) + ProtectedFile(self.rwpath, 'w').close() + self.assertTrue(os.access(self.rwpath, os.W_OK)) + self.assertTrue(os.stat(self.rwpath).st_mode & S_IWRITE) + + def test_mode_change_on_append(self): + """tests that mode is changed when file is opened in 'a' mode""" + #self.assertTrue(not os.access(self.rpath, os.W_OK)) + self.assertTrue(not os.stat(self.rpath).st_mode & S_IWRITE) + wp_file = ProtectedFile(self.rpath, 'a') + self.assertTrue(os.access(self.rpath, os.W_OK)) + self.assertTrue(os.stat(self.rpath).st_mode & S_IWRITE) + wp_file.close() + #self.assertTrue(not os.access(self.rpath, os.W_OK)) + self.assertTrue(not os.stat(self.rpath).st_mode & S_IWRITE) + + +if sys.version_info < (3, 0): + def load_tests(loader, tests, ignore): + from logilab.common import fileutils + tests.addTests(doctest.DocTestSuite(fileutils)) + return tests + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_graph.py b/pymode/libs/logilab-common-1.4.1/test/unittest_graph.py new file mode 100644 index 00000000..9a2e8bc9 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_graph.py @@ -0,0 +1,89 @@ +# unit tests for the cache module +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.graph import get_cycles, has_path, ordered_nodes, UnorderableGraph + +class getCyclesTC(TestCase): + + def test_known0(self): + self.assertEqual(get_cycles({1:[2], 2:[3], 3:[1]}), [[1, 2, 3]]) + + def test_known1(self): + self.assertEqual(get_cycles({1:[2], 2:[3], 3:[1, 4], 4:[3]}), [[1, 2, 3], [3, 4]]) + + def test_known2(self): + self.assertEqual(get_cycles({1:[2], 2:[3], 3:[0], 0:[]}), []) + + +class hasPathTC(TestCase): + + def test_direct_connection(self): + self.assertEqual(has_path({'A': ['B'], 'B': ['A']}, 'A', 'B'), ['B']) + + def test_indirect_connection(self): + self.assertEqual(has_path({'A': ['B'], 'B': ['A', 'C'], 'C': ['B']}, 'A', 'C'), ['B', 'C']) + + def test_no_connection(self): + self.assertEqual(has_path({'A': ['B'], 'B': ['A']}, 'A', 'C'), None) + + def test_cycle(self): + self.assertEqual(has_path({'A': ['A']}, 'A', 'B'), None) + +class ordered_nodesTC(TestCase): + + def test_one_item(self): + graph = {'a':[]} + ordered = ordered_nodes(graph) + self.assertEqual(ordered, ('a',)) + + def test_single_dependency(self): + graph = {'a':['b'], 'b':[]} + ordered = ordered_nodes(graph) + self.assertEqual(ordered, ('a','b')) + graph = {'a':[], 'b':['a']} + ordered = ordered_nodes(graph) + self.assertEqual(ordered, ('b','a')) + + def test_two_items_no_dependency(self): + graph = {'a':[], 'b':[]} + ordered = ordered_nodes(graph) + self.assertEqual(ordered, ('a','b')) + + def test_three_items_no_dependency(self): + graph = {'a':[], 'b':[], 'c':[]} + ordered = ordered_nodes(graph) + self.assertEqual(ordered, ('a', 'b', 'c')) + + def test_three_items_one_dependency(self): + graph = {'a': ['c'], 'b': [], 'c':[]} + ordered = ordered_nodes(graph) + self.assertEqual(ordered, ('a', 'b', 'c')) + + def test_three_items_two_dependencies(self): + graph = {'a': ['b'], 'b': ['c'], 'c':[]} + ordered = ordered_nodes(graph) + self.assertEqual(ordered, ('a', 'b', 'c')) + + def test_bad_graph(self): + graph = {'a':['b']} + self.assertRaises(UnorderableGraph, ordered_nodes, graph) + +if __name__ == "__main__": + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_interface.py b/pymode/libs/logilab-common-1.4.1/test/unittest_interface.py new file mode 100644 index 00000000..1dbed7a1 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_interface.py @@ -0,0 +1,87 @@ +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.interface import * + +class IFace1(Interface): pass +class IFace2(Interface): pass +class IFace3(Interface): pass + + +class A(object): + __implements__ = (IFace1,) + + +class B(A): pass + + +class C1(B): + __implements__ = list(B.__implements__) + [IFace3] + +class C2(B): + __implements__ = B.__implements__ + (IFace2,) + +class D(C1): + __implements__ = () + +class Z(object): pass + +class ExtendTC(TestCase): + + def setUp(self): + global aimpl, c1impl, c2impl, dimpl + aimpl = A.__implements__ + c1impl = C1.__implements__ + c2impl = C2.__implements__ + dimpl = D.__implements__ + + def test_base(self): + extend(A, IFace2) + self.assertEqual(A.__implements__, (IFace1, IFace2)) + self.assertEqual(B.__implements__, (IFace1, IFace2)) + self.assertTrue(B.__implements__ is A.__implements__) + self.assertEqual(C1.__implements__, [IFace1, IFace3, IFace2]) + self.assertEqual(C2.__implements__, (IFace1, IFace2)) + self.assertTrue(C2.__implements__ is c2impl) + self.assertEqual(D.__implements__, (IFace2,)) + + def test_already_impl(self): + extend(A, IFace1) + self.assertTrue(A.__implements__ is aimpl) + + def test_no_impl(self): + extend(Z, IFace1) + self.assertEqual(Z.__implements__, (IFace1,)) + + def test_notimpl_explicit(self): + extend(C1, IFace3) + self.assertTrue(C1.__implements__ is c1impl) + self.assertTrue(D.__implements__ is dimpl) + + + def test_nonregr_implements_baseinterface(self): + class SubIFace(IFace1): pass + class X(object): + __implements__ = (SubIFace,) + + self.assertTrue(SubIFace.is_implemented_by(X)) + self.assertTrue(IFace1.is_implemented_by(X)) + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_modutils.py b/pymode/libs/logilab-common-1.4.1/test/unittest_modutils.py new file mode 100644 index 00000000..ec2a5c82 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_modutils.py @@ -0,0 +1,296 @@ +# 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 logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +""" +unit tests for module modutils (module manipulation utilities) +""" + +import doctest +import sys +import warnings +try: + __file__ +except NameError: + __file__ = sys.argv[0] + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common import modutils + +from os import path, getcwd, sep +from logilab import common +from logilab.common import tree + +sys.path.insert(0, path.dirname(__file__)) +DATADIR = path.join(path.dirname(__file__), 'data') + + +class ModutilsTestCase(TestCase): + def setUp(self): + super(ModutilsTestCase, self).setUp() + self.__common_in_path = common.__path__[0] in sys.path + if self.__common_in_path: + sys.path.remove(common.__path__[0]) + + def tearDown(self): + if self.__common_in_path: + sys.path.insert(0, common.__path__[0]) + super(ModutilsTestCase, self).tearDown() + + +class ModuleFileTC(ModutilsTestCase): + package = "mypypa" + + def setUp(self): + super(ModuleFileTC, self).setUp() + 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.zip')]) + self.assertEqual(mtype, modutils.ZIPFILE) + self.assertEqual(mfile.split(sep)[-4:], ["test", "data", "MyPyPa-0.1.0.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(ModutilsTestCase): + """ 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(ModutilsTestCase): + """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('logilab.common.modutils'), + 'logilab.common.modutils') + + def test_knownValues_get_module_part_2(self): + self.assertEqual(modutils.get_module_part('logilab.common.modutils.get_module_part'), + 'logilab.common.modutils') + + def test_knownValues_get_module_part_3(self): + """relative import from given file""" + self.assertEqual(modutils.get_module_part('interface.Interface', + modutils.__file__), 'interface') + + 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(ModutilsTestCase): + """ given an absolute file path return the python module's path as a list """ + + def test_knownValues_modpath_from_file_1(self): + with warnings.catch_warnings(record=True) as warns: + self.assertEqual(modutils.modpath_from_file(modutils.__file__), + ['logilab', 'common', 'modutils']) + self.assertIn('you should avoid using modpath_from_file()', + [str(w.message) for w in warns]) + + 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(ModutilsTestCase): + + def test_do_not_load_twice(self): + sys.path.insert(0, self.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(ModutilsTestCase): + """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): + from pytz import tzinfo + self.assertEqual(path.realpath(modutils.file_from_modpath(['pytz', 'tzinfo'])), + path.realpath(tzinfo.__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']) + + +class get_source_file_tc(ModutilsTestCase): + + def test(self): + from os import path + self.assertEqual(modutils.get_source_file(path.__file__), + path.__file__.replace('.pyc', '.py')) + + def test_raise(self): + self.assertRaises(modutils.NoSourceFile, modutils.get_source_file, 'whatever') + +class is_standard_module_tc(ModutilsTestCase): + """ + 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_4(self): + self.assertEqual(modutils.is_standard_module('marshal'), True) + self.assertEqual(modutils.is_standard_module('pickle'), True) + self.assertEqual(modutils.is_standard_module('email'), True) + self.assertEqual(modutils.is_standard_module('StringIO'), sys.version_info < (3, 0)) + venv_py3 = sys.version_info[0] >= 3 and hasattr(sys, 'real_prefix') + if not venv_py3: + # those modules are symlinked by virtualenv (but not by python's venv) + self.assertEqual(modutils.is_standard_module('hashlib'), True) + self.assertEqual(modutils.is_standard_module('io'), True) + + def test_custom_path(self): + 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_border_cases(self): + # 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(ModutilsTestCase): + + + def test_knownValues_is_relative_1(self): + self.assertEqual(modutils.is_relative('modutils', common.__path__[0]), True) + + def test_knownValues_is_relative_2(self): + self.assertEqual(modutils.is_relative('modutils', tree.__file__), True) + + def test_knownValues_is_relative_3(self): + self.assertEqual(modutils.is_relative('logilab.common.modutils', + common.__path__[0]), False) + +class get_modules_tc(ModutilsTestCase): + + def test_knownValues_get_modules_1(self): # XXXFIXME: TOWRITE + """given a directory return a list of all available python modules, even + in subdirectories + """ + import data.find_test as data + mod_path = ("data", 'find_test') + modules = sorted(modutils.get_modules(path.join(*mod_path), + data.__path__[0])) + self.assertSetEqual(set(modules), + set([ '.'.join(mod_path + (mod, )) for mod in ('module', 'module2', + 'noendingnewline', 'nonregr')])) + + +class get_modules_files_tc(ModutilsTestCase): + + 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 ) + + +def load_tests(loader, tests, ignore): + from logilab.common import modutils + tests.addTests(doctest.DocTestSuite(modutils)) + return tests + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_pytest.py b/pymode/libs/logilab-common-1.4.1/test/unittest_pytest.py new file mode 100644 index 00000000..48e36ce5 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_pytest.py @@ -0,0 +1,86 @@ +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +from os.path import join +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.pytest import * + +class ModuleFunctionTC(TestCase): + def test_this_is_testdir(self): + self.assertTrue(this_is_a_testdir("test")) + self.assertTrue(this_is_a_testdir("tests")) + self.assertTrue(this_is_a_testdir("unittests")) + self.assertTrue(this_is_a_testdir("unittest")) + self.assertFalse(this_is_a_testdir("unit")) + self.assertFalse(this_is_a_testdir("units")) + self.assertFalse(this_is_a_testdir("undksjhqfl")) + self.assertFalse(this_is_a_testdir("this_is_not_a_dir_test")) + self.assertFalse(this_is_a_testdir("this_is_not_a_testdir")) + self.assertFalse(this_is_a_testdir("unittestsarenothere")) + self.assertTrue(this_is_a_testdir(join("coincoin", "unittests"))) + self.assertFalse(this_is_a_testdir(join("unittests", "spongebob"))) + + def test_this_is_testfile(self): + self.assertTrue(this_is_a_testfile("test.py")) + self.assertTrue(this_is_a_testfile("testbabar.py")) + self.assertTrue(this_is_a_testfile("unittest_celestine.py")) + self.assertTrue(this_is_a_testfile("smoketest.py")) + self.assertFalse(this_is_a_testfile("test.pyc")) + self.assertFalse(this_is_a_testfile("zephir_test.py")) + self.assertFalse(this_is_a_testfile("smoketest.pl")) + self.assertFalse(this_is_a_testfile("unittest")) + self.assertTrue(this_is_a_testfile(join("coincoin", "unittest_bibi.py"))) + self.assertFalse(this_is_a_testfile(join("unittest", "spongebob.py"))) + + def test_replace_trace(self): + def tracefn(frame, event, arg): + pass + + oldtrace = sys.gettrace() + with replace_trace(tracefn): + self.assertIs(sys.gettrace(), tracefn) + + self.assertIs(sys.gettrace(), oldtrace) + + def test_pause_trace(self): + def tracefn(frame, event, arg): + pass + + oldtrace = sys.gettrace() + sys.settrace(tracefn) + try: + self.assertIs(sys.gettrace(), tracefn) + with pause_trace(): + self.assertIs(sys.gettrace(), None) + self.assertIs(sys.gettrace(), tracefn) + finally: + sys.settrace(oldtrace) + + def test_nocoverage(self): + def tracefn(frame, event, arg): + pass + + @nocoverage + def myfn(): + self.assertIs(sys.gettrace(), None) + + with replace_trace(tracefn): + myfn() + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_registry.py b/pymode/libs/logilab-common-1.4.1/test/unittest_registry.py new file mode 100644 index 00000000..1c07e4ce --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_registry.py @@ -0,0 +1,220 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of Logilab-Common. +# +# Logilab-Common 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-Common 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-Common. If not, see . +"""unit tests for selectors mechanism""" + +import gc +import logging +import os.path as osp +import sys +from operator import eq, lt, le, gt +from contextlib import contextmanager +import warnings + +logging.basicConfig(level=logging.ERROR) + +from logilab.common.testlib import TestCase, unittest_main + +from logilab.common.registry import * + + +class _1_(Predicate): + def __call__(self, *args, **kwargs): + return 1 + +class _0_(Predicate): + def __call__(self, *args, **kwargs): + return 0 + +def _2_(*args, **kwargs): + return 2 + + +class SelectorsTC(TestCase): + def test_basic_and(self): + selector = _1_() & _1_() + self.assertEqual(selector(None), 2) + selector = _1_() & _0_() + self.assertEqual(selector(None), 0) + selector = _0_() & _1_() + self.assertEqual(selector(None), 0) + + def test_basic_or(self): + selector = _1_() | _1_() + self.assertEqual(selector(None), 1) + selector = _1_() | _0_() + self.assertEqual(selector(None), 1) + selector = _0_() | _1_() + self.assertEqual(selector(None), 1) + selector = _0_() | _0_() + self.assertEqual(selector(None), 0) + + def test_selector_and_function(self): + selector = _1_() & _2_ + self.assertEqual(selector(None), 3) + selector = _2_ & _1_() + self.assertEqual(selector(None), 3) + + def test_three_and(self): + selector = _1_() & _1_() & _1_() + self.assertEqual(selector(None), 3) + selector = _1_() & _0_() & _1_() + self.assertEqual(selector(None), 0) + selector = _0_() & _1_() & _1_() + self.assertEqual(selector(None), 0) + + def test_three_or(self): + selector = _1_() | _1_() | _1_() + self.assertEqual(selector(None), 1) + selector = _1_() | _0_() | _1_() + self.assertEqual(selector(None), 1) + selector = _0_() | _1_() | _1_() + self.assertEqual(selector(None), 1) + selector = _0_() | _0_() | _0_() + self.assertEqual(selector(None), 0) + + def test_composition(self): + selector = (_1_() & _1_()) & (_1_() & _1_()) + self.assertTrue(isinstance(selector, AndPredicate)) + self.assertEqual(len(selector.selectors), 4) + self.assertEqual(selector(None), 4) + selector = (_1_() & _0_()) | (_1_() & _1_()) + self.assertTrue(isinstance(selector, OrPredicate)) + self.assertEqual(len(selector.selectors), 2) + self.assertEqual(selector(None), 2) + + def test_search_selectors(self): + sel = _1_() + self.assertIs(sel.search_selector(_1_), sel) + csel = AndPredicate(sel, Predicate()) + self.assertIs(csel.search_selector(_1_), sel) + csel = AndPredicate(Predicate(), sel) + self.assertIs(csel.search_selector(_1_), sel) + self.assertIs(csel.search_selector((AndPredicate, OrPredicate)), csel) + self.assertIs(csel.search_selector((OrPredicate, AndPredicate)), csel) + self.assertIs(csel.search_selector((_1_, _0_)), sel) + self.assertIs(csel.search_selector((_0_, _1_)), sel) + + def test_inplace_and(self): + selector = _1_() + selector &= _1_() + selector &= _1_() + self.assertEqual(selector(None), 3) + selector = _1_() + selector &= _0_() + selector &= _1_() + self.assertEqual(selector(None), 0) + selector = _0_() + selector &= _1_() + selector &= _1_() + self.assertEqual(selector(None), 0) + selector = _0_() + selector &= _0_() + selector &= _0_() + self.assertEqual(selector(None), 0) + + def test_inplace_or(self): + selector = _1_() + selector |= _1_() + selector |= _1_() + self.assertEqual(selector(None), 1) + selector = _1_() + selector |= _0_() + selector |= _1_() + self.assertEqual(selector(None), 1) + selector = _0_() + selector |= _1_() + selector |= _1_() + self.assertEqual(selector(None), 1) + selector = _0_() + selector |= _0_() + selector |= _0_() + self.assertEqual(selector(None), 0) + + def test_wrap_selectors(self): + class _temp_(Predicate): + def __call__(self, *args, **kwargs): + return 0 + del _temp_ # test weakref + s1 = _1_() & _1_() + s2 = _1_() & _0_() + s3 = _0_() & _1_() + gc.collect() + self.count = 0 + def decorate(f, self=self): + def wrapper(*args, **kwargs): + self.count += 1 + return f(*args, **kwargs) + return wrapper + wrap_predicates(decorate) + self.assertEqual(s1(None), 2) + self.assertEqual(s2(None), 0) + self.assertEqual(s3(None), 0) + self.assertEqual(self.count, 8) + +@contextmanager +def prepended_syspath(path): + sys.path.insert(0, path) + yield + sys.path = sys.path[1:] + +class RegistryStoreTC(TestCase): + + def test_autoload(self): + store = RegistryStore() + store.setdefault('zereg') + with prepended_syspath(self.datadir): + with warnings.catch_warnings(record=True) as warns: + store.register_objects([self.datapath('regobjects.py'), + self.datapath('regobjects2.py')]) + self.assertIn('use register_modnames() instead', + [str(w.message) for w in warns]) + self.assertEqual(['zereg'], list(store.keys())) + self.assertEqual(set(('appobject1', 'appobject2', 'appobject3')), + set(store['zereg'])) + + def test_autoload_modnames(self): + store = RegistryStore() + store.setdefault('zereg') + with prepended_syspath(self.datadir): + store.register_modnames(['regobjects', 'regobjects2']) + self.assertEqual(['zereg'], list(store.keys())) + self.assertEqual(set(('appobject1', 'appobject2', 'appobject3')), + set(store['zereg'])) + + +class RegistrableInstanceTC(TestCase): + + def test_instance_modulename(self): + with warnings.catch_warnings(record=True) as warns: + obj = RegistrableInstance() + self.assertEqual(obj.__module__, 'unittest_registry') + self.assertIn('instantiate RegistrableInstance with __module__=__name__', + [str(w.message) for w in warns]) + # no inheritance + obj = RegistrableInstance(__module__=__name__) + self.assertEqual(obj.__module__, 'unittest_registry') + # with inheritance from another python file + with prepended_syspath(self.datadir): + from regobjects2 import instance, MyRegistrableInstance + instance2 = MyRegistrableInstance(__module__=__name__) + self.assertEqual(instance.__module__, 'regobjects2') + self.assertEqual(instance2.__module__, 'unittest_registry') + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_shellutils.py b/pymode/libs/logilab-common-1.4.1/test/unittest_shellutils.py new file mode 100644 index 00000000..9342ae9b --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_shellutils.py @@ -0,0 +1,235 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""unit tests for logilab.common.shellutils""" + +import sys, os, tempfile, shutil +from os.path import join, dirname, abspath +import datetime, time + +from six.moves import range + +from logilab.common.testlib import TestCase, unittest_main + +from logilab.common.shellutils import (globfind, find, ProgressBar, + RawInput) +from logilab.common.compat import StringIO + + +DATA_DIR = join(dirname(abspath(__file__)), 'data', 'find_test') + + +class FindTC(TestCase): + def test_include(self): + files = set(find(DATA_DIR, '.py')) + self.assertSetEqual(files, + set([join(DATA_DIR, f) for f in ['__init__.py', 'module.py', + 'module2.py', 'noendingnewline.py', + 'nonregr.py', join('sub', 'momo.py')]])) + files = set(find(DATA_DIR, ('.py',), blacklist=('sub',))) + self.assertSetEqual(files, + set([join(DATA_DIR, f) for f in ['__init__.py', 'module.py', + 'module2.py', 'noendingnewline.py', + 'nonregr.py']])) + + def test_exclude(self): + files = set(find(DATA_DIR, ('.py', '.pyc'), exclude=True)) + self.assertSetEqual(files, + set([join(DATA_DIR, f) for f in ['foo.txt', + 'newlines.txt', + 'normal_file.txt', + 'test.ini', + 'test1.msg', + 'test2.msg', + 'spam.txt', + join('sub', 'doc.txt'), + 'write_protected_file.txt', + ]])) + + def test_globfind(self): + files = set(globfind(DATA_DIR, '*.py')) + self.assertSetEqual(files, + set([join(DATA_DIR, f) for f in ['__init__.py', 'module.py', + 'module2.py', 'noendingnewline.py', + 'nonregr.py', join('sub', 'momo.py')]])) + files = set(globfind(DATA_DIR, 'mo*.py')) + self.assertSetEqual(files, + set([join(DATA_DIR, f) for f in ['module.py', 'module2.py', + join('sub', 'momo.py')]])) + files = set(globfind(DATA_DIR, 'mo*.py', blacklist=('sub',))) + self.assertSetEqual(files, + set([join(DATA_DIR, f) for f in ['module.py', 'module2.py']])) + + +class ProgressBarTC(TestCase): + def test_refresh(self): + pgb_stream = StringIO() + expected_stream = StringIO() + pgb = ProgressBar(20, stream=pgb_stream) + self.assertEqual(pgb_stream.getvalue(), expected_stream.getvalue()) # nothing print before refresh + pgb.refresh() + expected_stream.write("\r["+' '*20+"]") + self.assertEqual(pgb_stream.getvalue(), expected_stream.getvalue()) + + def test_refresh_g_size(self): + pgb_stream = StringIO() + expected_stream = StringIO() + pgb = ProgressBar(20, 35, stream=pgb_stream) + pgb.refresh() + expected_stream.write("\r["+' '*35+"]") + self.assertEqual(pgb_stream.getvalue(), expected_stream.getvalue()) + + def test_refresh_l_size(self): + pgb_stream = StringIO() + expected_stream = StringIO() + pgb = ProgressBar(20, 3, stream=pgb_stream) + pgb.refresh() + expected_stream.write("\r["+' '*3+"]") + self.assertEqual(pgb_stream.getvalue(), expected_stream.getvalue()) + + def _update_test(self, nbops, expected, size = None): + pgb_stream = StringIO() + expected_stream = StringIO() + if size is None: + pgb = ProgressBar(nbops, stream=pgb_stream) + size=20 + else: + pgb = ProgressBar(nbops, size, stream=pgb_stream) + last = 0 + for round in expected: + if not hasattr(round, '__int__'): + dots, update = round + else: + dots, update = round, None + pgb.update() + if update or (update is None and dots != last): + last = dots + expected_stream.write("\r["+('='*dots)+(' '*(size-dots))+"]") + self.assertEqual(pgb_stream.getvalue(), expected_stream.getvalue()) + + def test_default(self): + self._update_test(20, range(1, 21)) + + def test_nbops_gt_size(self): + """Test the progress bar for nbops > size""" + def half(total): + for counter in range(1, total+1): + yield counter // 2 + self._update_test(40, half(40)) + + def test_nbops_lt_size(self): + """Test the progress bar for nbops < size""" + def double(total): + for counter in range(1, total+1): + yield counter * 2 + self._update_test(10, double(10)) + + def test_nbops_nomul_size(self): + """Test the progress bar for size % nbops !=0 (non int number of dots per update)""" + self._update_test(3, (6, 13, 20)) + + def test_overflow(self): + self._update_test(5, (8, 16, 25, 33, 42, (42, True)), size=42) + + def test_update_exact(self): + pgb_stream = StringIO() + expected_stream = StringIO() + size=20 + pgb = ProgressBar(100, size, stream=pgb_stream) + last = 0 + for dots in range(10, 105, 15): + pgb.update(dots, exact=True) + dots //= 5 + expected_stream.write("\r["+('='*dots)+(' '*(size-dots))+"]") + self.assertEqual(pgb_stream.getvalue(), expected_stream.getvalue()) + + def test_update_relative(self): + pgb_stream = StringIO() + expected_stream = StringIO() + size=20 + pgb = ProgressBar(100, size, stream=pgb_stream) + last = 0 + for dots in range(5, 105, 5): + pgb.update(5, exact=False) + dots //= 5 + expected_stream.write("\r["+('='*dots)+(' '*(size-dots))+"]") + self.assertEqual(pgb_stream.getvalue(), expected_stream.getvalue()) + + +class RawInputTC(TestCase): + + def auto_input(self, *args): + self.input_args = args + return self.input_answer + + def setUp(self): + null_printer = lambda x: None + self.qa = RawInput(self.auto_input, null_printer) + + def test_ask_default(self): + self.input_answer = '' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(answer, 'yes') + self.input_answer = ' ' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(answer, 'yes') + + def test_ask_case(self): + self.input_answer = 'no' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(answer, 'no') + self.input_answer = 'No' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(answer, 'no') + self.input_answer = 'NO' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(answer, 'no') + self.input_answer = 'nO' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(answer, 'no') + self.input_answer = 'YES' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(answer, 'yes') + + def test_ask_prompt(self): + self.input_answer = '' + answer = self.qa.ask('text', ('yes', 'no'), 'yes') + self.assertEqual(self.input_args[0], 'text [Y(es)/n(o)]: ') + answer = self.qa.ask('text', ('y', 'n'), 'y') + self.assertEqual(self.input_args[0], 'text [Y/n]: ') + answer = self.qa.ask('text', ('n', 'y'), 'y') + self.assertEqual(self.input_args[0], 'text [n/Y]: ') + answer = self.qa.ask('text', ('yes', 'no', 'maybe', '1'), 'yes') + self.assertEqual(self.input_args[0], 'text [Y(es)/n(o)/m(aybe)/1]: ') + + def test_ask_ambiguous(self): + self.input_answer = 'y' + self.assertRaises(Exception, self.qa.ask, 'text', ('yes', 'yep'), 'yes') + + def test_confirm(self): + self.input_answer = 'y' + self.assertEqual(self.qa.confirm('Say yes'), True) + self.assertEqual(self.qa.confirm('Say yes', default_is_yes=False), True) + self.input_answer = 'n' + self.assertEqual(self.qa.confirm('Say yes'), False) + self.assertEqual(self.qa.confirm('Say yes', default_is_yes=False), False) + self.input_answer = '' + self.assertEqual(self.qa.confirm('Say default'), True) + self.assertEqual(self.qa.confirm('Say default', default_is_yes=False), False) + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_table.py b/pymode/libs/logilab-common-1.4.1/test/unittest_table.py new file mode 100644 index 00000000..320b6938 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_table.py @@ -0,0 +1,448 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +""" +Unittests for table management +""" + + +import sys +import os + +from six.moves import range + +from logilab.common.compat import StringIO +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.table import Table, TableStyleSheet, DocbookTableWriter, \ + DocbookRenderer, TableStyle, TableWriter, TableCellRenderer + +class TableTC(TestCase): + """Table TestCase class""" + + def setUp(self): + """Creates a default table""" + # from logilab.common import table + # reload(table) + self.table = Table() + self.table.create_rows(['row1', 'row2', 'row3']) + self.table.create_columns(['col1', 'col2']) + + def test_valeur_scalaire(self): + tab = Table() + tab.create_columns(['col1']) + tab.append_row([1]) + self.assertEqual(tab, [[1]]) + tab.append_row([2]) + self.assertEqual(tab[0, 0], 1) + self.assertEqual(tab[1, 0], 2) + + def test_valeur_ligne(self): + tab = Table() + tab.create_columns(['col1', 'col2']) + tab.append_row([1, 2]) + self.assertEqual(tab, [[1, 2]]) + + def test_valeur_colonne(self): + tab = Table() + tab.create_columns(['col1']) + tab.append_row([1]) + tab.append_row([2]) + self.assertEqual(tab, [[1], [2]]) + self.assertEqual(tab[:, 0], [1, 2]) + + def test_indexation(self): + """we should be able to use [] to access rows""" + self.assertEqual(self.table[0], self.table.data[0]) + self.assertEqual(self.table[1], self.table.data[1]) + + def test_iterable(self): + """test iter(table)""" + it = iter(self.table) + self.assertEqual(next(it), self.table.data[0]) + self.assertEqual(next(it), self.table.data[1]) + + def test_get_rows(self): + """tests Table.get_rows()""" + self.assertEqual(self.table, [[0, 0], [0, 0], [0, 0]]) + self.assertEqual(self.table[:], [[0, 0], [0, 0], [0, 0]]) + self.table.insert_column(1, range(3), 'supp') + self.assertEqual(self.table, [[0, 0, 0], [0, 1, 0], [0, 2, 0]]) + self.assertEqual(self.table[:], [[0, 0, 0], [0, 1, 0], [0, 2, 0]]) + + def test_get_cells(self): + self.table.insert_column(1, range(3), 'supp') + self.assertEqual(self.table[0, 1], 0) + self.assertEqual(self.table[1, 1], 1) + self.assertEqual(self.table[2, 1], 2) + self.assertEqual(self.table['row1', 'supp'], 0) + self.assertEqual(self.table['row2', 'supp'], 1) + self.assertEqual(self.table['row3', 'supp'], 2) + self.assertRaises(KeyError, self.table.__getitem__, ('row1', 'foo')) + self.assertRaises(KeyError, self.table.__getitem__, ('foo', 'bar')) + + def test_shape(self): + """tests table shape""" + self.assertEqual(self.table.shape, (3, 2)) + self.table.insert_column(1, range(3), 'supp') + self.assertEqual(self.table.shape, (3, 3)) + + def test_set_column(self): + """Tests that table.set_column() works fine. + """ + self.table.set_column(0, range(3)) + self.assertEqual(self.table[0, 0], 0) + self.assertEqual(self.table[1, 0], 1) + self.assertEqual(self.table[2, 0], 2) + + def test_set_column_by_id(self): + """Tests that table.set_column_by_id() works fine. + """ + self.table.set_column_by_id('col1', range(3)) + self.assertEqual(self.table[0, 0], 0) + self.assertEqual(self.table[1, 0], 1) + self.assertEqual(self.table[2, 0], 2) + self.assertRaises(KeyError, self.table.set_column_by_id, 'col123', range(3)) + + def test_cells_ids(self): + """tests that we can access cells by giving row/col ids""" + self.assertRaises(KeyError, self.table.set_cell_by_ids, 'row12', 'col1', 12) + self.assertRaises(KeyError, self.table.set_cell_by_ids, 'row1', 'col12', 12) + self.assertEqual(self.table[0, 0], 0) + self.table.set_cell_by_ids('row1', 'col1', 'DATA') + self.assertEqual(self.table[0, 0], 'DATA') + self.assertRaises(KeyError, self.table.set_row_by_id, 'row12', []) + self.table.set_row_by_id('row1', ['1.0', '1.1']) + self.assertEqual(self.table[0, 0], '1.0') + + def test_insert_row(self): + """tests a row insertion""" + tmp_data = ['tmp1', 'tmp2'] + self.table.insert_row(1, tmp_data, 'tmprow') + self.assertEqual(self.table[1], tmp_data) + self.assertEqual(self.table['tmprow'], tmp_data) + self.table.delete_row_by_id('tmprow') + self.assertRaises(KeyError, self.table.delete_row_by_id, 'tmprow') + self.assertEqual(self.table[1], [0, 0]) + self.assertRaises(KeyError, self.table.__getitem__, 'tmprow') + + def test_get_column(self): + """Tests that table.get_column() works fine. + """ + self.table.set_cell(0, 1, 12) + self.table.set_cell(2, 1, 13) + self.assertEqual(self.table[:, 1], [12, 0, 13]) + self.assertEqual(self.table[:, 'col2'], [12, 0, 13]) + + def test_get_columns(self): + """Tests if table.get_columns() works fine. + """ + self.table.set_cell(0, 1, 12) + self.table.set_cell(2, 1, 13) + self.assertEqual(self.table.get_columns(), [[0, 0, 0], [12, 0, 13]]) + + def test_insert_column(self): + """Tests that table.insert_column() works fine. + """ + self.table.insert_column(1, range(3), "inserted_column") + self.assertEqual(self.table[:, 1], [0, 1, 2]) + self.assertEqual(self.table.col_names, + ['col1', 'inserted_column', 'col2']) + + def test_delete_column(self): + """Tests that table.delete_column() works fine. + """ + self.table.delete_column(1) + self.assertEqual(self.table.col_names, ['col1']) + self.assertEqual(self.table[:, 0], [0, 0, 0]) + self.assertRaises(KeyError, self.table.delete_column_by_id, 'col2') + self.table.delete_column_by_id('col1') + self.assertEqual(self.table.col_names, []) + + def test_transpose(self): + """Tests that table.transpose() works fine. + """ + self.table.append_column(range(5, 8), 'col3') + ttable = self.table.transpose() + self.assertEqual(ttable.row_names, ['col1', 'col2', 'col3']) + self.assertEqual(ttable.col_names, ['row1', 'row2', 'row3']) + self.assertEqual(ttable.data, [[0, 0, 0], [0, 0, 0], [5, 6, 7]]) + + def test_sort_table(self): + """Tests the table sort by column + """ + self.table.set_column(0, [3, 1, 2]) + self.table.set_column(1, [1, 2, 3]) + self.table.sort_by_column_index(0) + self.assertEqual(self.table.row_names, ['row2', 'row3', 'row1']) + self.assertEqual(self.table.data, [[1, 2], [2, 3], [3, 1]]) + self.table.sort_by_column_index(1, 'desc') + self.assertEqual(self.table.row_names, ['row3', 'row2', 'row1']) + self.assertEqual(self.table.data, [[2, 3], [1, 2], [3, 1]]) + + def test_sort_by_id(self): + """tests sort_by_column_id()""" + self.table.set_column_by_id('col1', [3, 1, 2]) + self.table.set_column_by_id('col2', [1, 2, 3]) + self.table.sort_by_column_id('col1') + self.assertRaises(KeyError, self.table.sort_by_column_id, 'col123') + self.assertEqual(self.table.row_names, ['row2', 'row3', 'row1']) + self.assertEqual(self.table.data, [[1, 2], [2, 3], [3, 1]]) + self.table.sort_by_column_id('col2', 'desc') + self.assertEqual(self.table.row_names, ['row3', 'row2', 'row1']) + self.assertEqual(self.table.data, [[2, 3], [1, 2], [3, 1]]) + + def test_pprint(self): + """only tests pprint doesn't raise an exception""" + self.table.pprint() + str(self.table) + + +class GroupByTC(TestCase): + """specific test suite for groupby()""" + def setUp(self): + t = Table() + t.create_columns(['date', 'res', 'task', 'usage']) + t.append_row(['date1', 'ing1', 'task1', 0.3]) + t.append_row(['date1', 'ing2', 'task2', 0.3]) + t.append_row(['date2', 'ing3', 'task3', 0.3]) + t.append_row(['date3', 'ing4', 'task2', 0.3]) + t.append_row(['date1', 'ing1', 'task3', 0.3]) + t.append_row(['date3', 'ing1', 'task3', 0.3]) + self.table = t + + def test_single_groupby(self): + """tests groupby() on several columns""" + grouped = self.table.groupby('date') + self.assertEqual(len(grouped), 3) + self.assertEqual(len(grouped['date1']), 3) + self.assertEqual(len(grouped['date2']), 1) + self.assertEqual(len(grouped['date3']), 2) + self.assertEqual(grouped['date1'], [ + ('date1', 'ing1', 'task1', 0.3), + ('date1', 'ing2', 'task2', 0.3), + ('date1', 'ing1', 'task3', 0.3), + ]) + self.assertEqual(grouped['date2'], [('date2', 'ing3', 'task3', 0.3)]) + self.assertEqual(grouped['date3'], [ + ('date3', 'ing4', 'task2', 0.3), + ('date3', 'ing1', 'task3', 0.3), + ]) + + def test_multiple_groupby(self): + """tests groupby() on several columns""" + grouped = self.table.groupby('date', 'task') + self.assertEqual(len(grouped), 3) + self.assertEqual(len(grouped['date1']), 3) + self.assertEqual(len(grouped['date2']), 1) + self.assertEqual(len(grouped['date3']), 2) + self.assertEqual(grouped['date1']['task1'], [('date1', 'ing1', 'task1', 0.3)]) + self.assertEqual(grouped['date2']['task3'], [('date2', 'ing3', 'task3', 0.3)]) + self.assertEqual(grouped['date3']['task2'], [('date3', 'ing4', 'task2', 0.3)]) + date3 = grouped['date3'] + self.assertRaises(KeyError, date3.__getitem__, 'task1') + + + def test_select(self): + """tests Table.select() method""" + rows = self.table.select('date', 'date1') + self.assertEqual(rows, [ + ('date1', 'ing1', 'task1', 0.3), + ('date1', 'ing2', 'task2', 0.3), + ('date1', 'ing1', 'task3', 0.3), + ]) + +class TableStyleSheetTC(TestCase): + """The Stylesheet test case + """ + def setUp(self): + """Builds a simple table to test the stylesheet + """ + self.table = Table() + self.table.create_row('row1') + self.table.create_columns(['a', 'b', 'c']) + self.stylesheet = TableStyleSheet() + # We don't want anything to be printed + self.stdout_backup = sys.stdout + sys.stdout = StringIO() + + def tearDown(self): + sys.stdout = self.stdout_backup + + def test_add_rule(self): + """Tests that the regex pattern works as expected. + """ + rule = '0_2 = sqrt(0_0**2 + 0_1**2)' + self.stylesheet.add_rule(rule) + self.table.set_row(0, [3, 4, 0]) + self.table.apply_stylesheet(self.stylesheet) + self.assertEqual(self.table[0], [3, 4, 5]) + self.assertEqual(len(self.stylesheet.rules), 1) + self.stylesheet.add_rule('some bad rule with bad syntax') + self.assertEqual(len(self.stylesheet.rules), 1, "Ill-formed rule mustn't be added") + self.assertEqual(len(self.stylesheet.instructions), 1, "Ill-formed rule mustn't be added") + + def test_stylesheet_init(self): + """tests Stylesheet.__init__""" + rule = '0_2 = 1' + sheet = TableStyleSheet([rule, 'bad rule']) + self.assertEqual(len(sheet.rules), 1, "Ill-formed rule mustn't be added") + self.assertEqual(len(sheet.instructions), 1, "Ill-formed rule mustn't be added") + + def test_rowavg_rule(self): + """Tests that add_rowavg_rule works as expected + """ + self.table.set_row(0, [10, 20, 0]) + self.stylesheet.add_rowavg_rule((0, 2), 0, 0, 1) + self.table.apply_stylesheet(self.stylesheet) + val = self.table[0, 2] + self.assertEqual(int(val), 15) + + + def test_rowsum_rule(self): + """Tests that add_rowsum_rule works as expected + """ + self.table.set_row(0, [10, 20, 0]) + self.stylesheet.add_rowsum_rule((0, 2), 0, 0, 1) + self.table.apply_stylesheet(self.stylesheet) + val = self.table[0, 2] + self.assertEqual(val, 30) + + + def test_colavg_rule(self): + """Tests that add_colavg_rule works as expected + """ + self.table.set_row(0, [10, 20, 0]) + self.table.append_row([12, 8, 3], 'row2') + self.table.create_row('row3') + self.stylesheet.add_colavg_rule((2, 0), 0, 0, 1) + self.table.apply_stylesheet(self.stylesheet) + val = self.table[2, 0] + self.assertEqual(int(val), 11) + + + def test_colsum_rule(self): + """Tests that add_colsum_rule works as expected + """ + self.table.set_row(0, [10, 20, 0]) + self.table.append_row([12, 8, 3], 'row2') + self.table.create_row('row3') + self.stylesheet.add_colsum_rule((2, 0), 0, 0, 1) + self.table.apply_stylesheet(self.stylesheet) + val = self.table[2, 0] + self.assertEqual(val, 22) + + + +class TableStyleTC(TestCase): + """Test suite for TableSuite""" + def setUp(self): + self.table = Table() + self.table.create_rows(['row1', 'row2', 'row3']) + self.table.create_columns(['col1', 'col2']) + self.style = TableStyle(self.table) + self._tested_attrs = (('size', '1*'), + ('alignment', 'right'), + ('unit', '')) + + def test_getset(self): + """tests style's get and set methods""" + for attrname, default_value in self._tested_attrs: + getter = getattr(self.style, 'get_%s' % attrname) + setter = getattr(self.style, 'set_%s' % attrname) + self.assertRaises(KeyError, getter, 'badcol') + self.assertEqual(getter('col1'), default_value) + setter('FOO', 'col1') + self.assertEqual(getter('col1'), 'FOO') + + def test_getset_index(self): + """tests style's get and set by index methods""" + for attrname, default_value in self._tested_attrs: + getter = getattr(self.style, 'get_%s' % attrname) + setter = getattr(self.style, 'set_%s' % attrname) + igetter = getattr(self.style, 'get_%s_by_index' % attrname) + isetter = getattr(self.style, 'set_%s_by_index' % attrname) + self.assertEqual(getter('__row_column__'), default_value) + isetter('FOO', 0) + self.assertEqual(getter('__row_column__'), 'FOO') + self.assertEqual(igetter(0), 'FOO') + self.assertEqual(getter('col1'), default_value) + isetter('FOO', 1) + self.assertEqual(getter('col1'), 'FOO') + self.assertEqual(igetter(1), 'FOO') + + +class RendererTC(TestCase): + """Test suite for DocbookRenderer""" + def setUp(self): + self.renderer = DocbookRenderer(alignment = True) + self.table = Table() + self.table.create_rows(['row1', 'row2', 'row3']) + self.table.create_columns(['col1', 'col2']) + self.style = TableStyle(self.table) + self.base_renderer = TableCellRenderer() + + def test_cell_content(self): + """test how alignment is rendered""" + entry_xml = self.renderer._render_cell_content('data', self.style, 1) + self.assertEqual(entry_xml, "data\n") + self.style.set_alignment_by_index('left', 1) + entry_xml = self.renderer._render_cell_content('data', self.style, 1) + self.assertEqual(entry_xml, "data\n") + + def test_default_content_rendering(self): + """tests that default rendering just prints the cell's content""" + rendered_cell = self.base_renderer._render_cell_content('data', self.style, 1) + self.assertEqual(rendered_cell, "data") + + def test_replacement_char(self): + """tests that 0 is replaced when asked for""" + cell_content = self.base_renderer._make_cell_content(0, self.style, 1) + self.assertEqual(cell_content, 0) + self.base_renderer.properties['skip_zero'] = '---' + cell_content = self.base_renderer._make_cell_content(0, self.style, 1) + self.assertEqual(cell_content, '---') + + def test_unit(self): + """tests if units are added""" + self.base_renderer.properties['units'] = True + self.style.set_unit_by_index('EUR', 1) + cell_content = self.base_renderer._make_cell_content(12, self.style, 1) + self.assertEqual(cell_content, '12 EUR') + + +class DocbookTableWriterTC(TestCase): + """TestCase for table's writer""" + def setUp(self): + self.stream = StringIO() + self.table = Table() + self.table.create_rows(['row1', 'row2', 'row3']) + self.table.create_columns(['col1', 'col2']) + self.writer = DocbookTableWriter(self.stream, self.table, None) + self.writer.set_renderer(DocbookRenderer()) + + def test_write_table(self): + """make sure write_table() doesn't raise any exception""" + self.writer.write_table() + + def test_abstract_writer(self): + """tests that Abstract Writers can't be used !""" + writer = TableWriter(self.stream, self.table, None) + self.assertRaises(NotImplementedError, writer.write_table) + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_taskqueue.py b/pymode/libs/logilab-common-1.4.1/test/unittest_taskqueue.py new file mode 100644 index 00000000..d8b6a9e7 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_taskqueue.py @@ -0,0 +1,71 @@ +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +from logilab.common.testlib import TestCase, unittest_main + +from logilab.common.tasksqueue import * + +class TaskTC(TestCase): + + def test_eq(self): + self.assertFalse(Task('t1') == Task('t2')) + self.assertTrue(Task('t1') == Task('t1')) + + def test_cmp(self): + self.assertTrue(Task('t1', LOW) < Task('t2', MEDIUM)) + self.assertFalse(Task('t1', LOW) > Task('t2', MEDIUM)) + self.assertTrue(Task('t1', HIGH) > Task('t2', MEDIUM)) + self.assertFalse(Task('t1', HIGH) < Task('t2', MEDIUM)) + + +class PrioritizedTasksQueueTC(TestCase): + + def test_priority(self): + queue = PrioritizedTasksQueue() + queue.put(Task('t1')) + queue.put(Task('t2', MEDIUM)) + queue.put(Task('t3', HIGH)) + queue.put(Task('t4', LOW)) + self.assertEqual(queue.get().id, 't3') + self.assertEqual(queue.get().id, 't2') + self.assertEqual(queue.get().id, 't1') + self.assertEqual(queue.get().id, 't4') + + def test_remove_equivalent(self): + queue = PrioritizedTasksQueue() + queue.put(Task('t1')) + queue.put(Task('t2', MEDIUM)) + queue.put(Task('t1', HIGH)) + queue.put(Task('t3', MEDIUM)) + queue.put(Task('t2', MEDIUM)) + self.assertEqual(queue.qsize(), 3) + self.assertEqual(queue.get().id, 't1') + self.assertEqual(queue.get().id, 't2') + self.assertEqual(queue.get().id, 't3') + self.assertEqual(queue.qsize(), 0) + + def test_remove(self): + queue = PrioritizedTasksQueue() + queue.put(Task('t1')) + queue.put(Task('t2')) + queue.put(Task('t3')) + queue.remove('t2') + self.assertEqual([t.id for t in queue], ['t3', 't1']) + self.assertRaises(ValueError, queue.remove, 't4') + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_testlib.py b/pymode/libs/logilab-common-1.4.1/test/unittest_testlib.py new file mode 100644 index 00000000..fe2e31a8 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_testlib.py @@ -0,0 +1,790 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +"""unittest module for logilab.comon.testlib""" + +from __future__ import print_function + +import os +import sys +from os.path import join, dirname, isdir, isfile, abspath, exists +import tempfile +import shutil + +try: + __file__ +except NameError: + __file__ = sys.argv[0] + +from six.moves import range + +from logilab.common.compat import StringIO +from logilab.common.testlib import (unittest, TestSuite, unittest_main, Tags, + TestCase, mock_object, create_files, InnerTest, with_tempdir, tag, + require_version, require_module) +from logilab.common.pytest import SkipAwareTextTestRunner, NonStrictTestLoader + + +class MockTestCase(TestCase): + def __init__(self): + # Do not call unittest.TestCase's __init__ + pass + + def fail(self, msg): + raise AssertionError(msg) + +class UtilTC(TestCase): + + def test_mockobject(self): + obj = mock_object(foo='bar', baz='bam') + self.assertEqual(obj.foo, 'bar') + self.assertEqual(obj.baz, 'bam') + + def test_create_files(self): + chroot = tempfile.mkdtemp() + path_to = lambda path: join(chroot, path) + dircontent = lambda path: sorted(os.listdir(join(chroot, path))) + try: + self.assertFalse(isdir(path_to('a/'))) + create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], chroot) + # make sure directories exist + self.assertTrue(isdir(path_to('a'))) + self.assertTrue(isdir(path_to('a/b'))) + self.assertTrue(isdir(path_to('a/b/c'))) + self.assertTrue(isdir(path_to('a/b/c/d'))) + # make sure files exist + self.assertTrue(isfile(path_to('a/b/foo.py'))) + self.assertTrue(isfile(path_to('a/b/c/d/e.py'))) + # make sure only asked files were created + self.assertEqual(dircontent('a'), ['b']) + self.assertEqual(dircontent('a/b'), ['c', 'foo.py']) + self.assertEqual(dircontent('a/b/c'), ['d']) + self.assertEqual(dircontent('a/b/c/d'), ['e.py']) + finally: + shutil.rmtree(chroot) + + +class TestlibTC(TestCase): + + def mkdir(self, path): + if not exists(path): + self._dirs.add(path) + os.mkdir(path) + + def setUp(self): + self.tc = MockTestCase() + self._dirs = set() + + def tearDown(self): + while(self._dirs): + shutil.rmtree(self._dirs.pop(), ignore_errors=True) + + def test_dict_equals(self): + """tests TestCase.assertDictEqual""" + d1 = {'a' : 1, 'b' : 2} + d2 = {'a' : 1, 'b' : 3} + d3 = dict(d1) + self.assertRaises(AssertionError, self.tc.assertDictEqual, d1, d2) + self.tc.assertDictEqual(d1, d3) + self.tc.assertDictEqual(d3, d1) + self.tc.assertDictEqual(d1, d1) + + def test_list_equals(self): + """tests TestCase.assertListEqual""" + l1 = list(range(10)) + l2 = list(range(5)) + l3 = list(range(10)) + self.assertRaises(AssertionError, self.tc.assertListEqual, l1, l2) + self.tc.assertListEqual(l1, l1) + self.tc.assertListEqual(l1, l3) + self.tc.assertListEqual(l3, l1) + + def test_equality_for_sets(self): + s1 = set('ab') + s2 = set('a') + self.assertRaises(AssertionError, self.tc.assertSetEqual, s1, s2) + self.tc.assertSetEqual(s1, s1) + self.tc.assertSetEqual(set(), set()) + + def test_text_equality(self): + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", 12) + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", 12) + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", None) + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", None) + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, 3.12, u"toto") + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, 3.12, u"toto") + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, None, u"toto") + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, None, u"toto") + self.tc.assertMultiLineEqual('toto\ntiti', 'toto\ntiti') + self.tc.assertMultiLineEqual('toto\ntiti', 'toto\ntiti') + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, 'toto\ntiti', 'toto\n titi\n') + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, 'toto\ntiti', 'toto\n titi\n') + foo = join(dirname(__file__), 'data', 'foo.txt') + spam = join(dirname(__file__), 'data', 'spam.txt') + with open(foo) as fobj: + text1 = fobj.read() + self.tc.assertMultiLineEqual(text1, text1) + self.tc.assertMultiLineEqual(text1, text1) + with open(spam) as fobj: + text2 = fobj.read() + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, text1, text2) + self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, text1, text2) + + def test_default_datadir(self): + expected_datadir = join(dirname(abspath(__file__)), 'data') + self.assertEqual(self.datadir, expected_datadir) + self.assertEqual(self.datapath('foo'), join(expected_datadir, 'foo')) + + def test_multiple_args_datadir(self): + expected_datadir = join(dirname(abspath(__file__)), 'data') + self.assertEqual(self.datadir, expected_datadir) + self.assertEqual(self.datapath('foo', 'bar'), join(expected_datadir, 'foo', 'bar')) + + def test_custom_datadir(self): + class MyTC(TestCase): + datadir = 'foo' + def test_1(self): pass + + # class' custom datadir + tc = MyTC('test_1') + self.assertEqual(tc.datapath('bar'), join('foo', 'bar')) + + def test_cached_datadir(self): + """test datadir is cached on the class""" + class MyTC(TestCase): + def test_1(self): pass + + expected_datadir = join(dirname(abspath(__file__)), 'data') + tc = MyTC('test_1') + self.assertEqual(tc.datadir, expected_datadir) + # changing module should not change the datadir + MyTC.__module__ = 'os' + self.assertEqual(tc.datadir, expected_datadir) + # even on new instances + tc2 = MyTC('test_1') + self.assertEqual(tc2.datadir, expected_datadir) + + def test_is(self): + obj_1 = [] + obj_2 = [] + self.assertIs(obj_1, obj_1) + self.assertRaises(AssertionError, self.assertIs, obj_1, obj_2) + + def test_isnot(self): + obj_1 = [] + obj_2 = [] + self.assertIsNot(obj_1, obj_2) + self.assertRaises(AssertionError, self.assertIsNot, obj_1, obj_1) + + def test_none(self): + self.assertIsNone(None) + self.assertRaises(AssertionError, self.assertIsNone, object()) + + def test_not_none(self): + self.assertIsNotNone(object()) + self.assertRaises(AssertionError, self.assertIsNotNone, None) + + def test_in(self): + self.assertIn("a", "dsqgaqg") + obj, seq = 'a', ('toto', "azf", "coin") + self.assertRaises(AssertionError, self.assertIn, obj, seq) + + def test_not_in(self): + self.assertNotIn('a', ('toto', "azf", "coin")) + self.assertRaises(AssertionError, self.assertNotIn, 'a', "dsqgaqg") + + +class GenerativeTestsTC(TestCase): + + def setUp(self): + output = StringIO() + self.runner = SkipAwareTextTestRunner(stream=output) + + def test_generative_ok(self): + class FooTC(TestCase): + def test_generative(self): + for i in range(10): + yield self.assertEqual, i, i + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 10) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 0) + + def test_generative_half_bad(self): + class FooTC(TestCase): + def test_generative(self): + for i in range(10): + yield self.assertEqual, i%2, 0 + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 10) + self.assertEqual(len(result.failures), 5) + self.assertEqual(len(result.errors), 0) + + def test_generative_error(self): + class FooTC(TestCase): + def test_generative(self): + for i in range(10): + if i == 5: + raise ValueError('STOP !') + yield self.assertEqual, i, i + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 5) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 1) + + def test_generative_error2(self): + class FooTC(TestCase): + def test_generative(self): + for i in range(10): + if i == 5: + yield self.ouch + yield self.assertEqual, i, i + def ouch(self): raise ValueError('stop !') + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 11) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 1) + + def test_generative_setup(self): + class FooTC(TestCase): + def setUp(self): + raise ValueError('STOP !') + def test_generative(self): + for i in range(10): + yield self.assertEqual, i, i + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 1) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 1) + + def test_generative_inner_skip(self): + class FooTC(TestCase): + def check(self, val): + if val == 5: + self.innerSkip("no 5") + else: + self.assertEqual(val, val) + + def test_generative(self): + for i in range(10): + yield InnerTest("check_%s"%i, self.check, i) + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 10) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.skipped), 1) + + def test_generative_skip(self): + class FooTC(TestCase): + def check(self, val): + if val == 5: + self.skipTest("no 5") + else: + self.assertEqual(val, val) + + def test_generative(self): + for i in range(10): + yield InnerTest("check_%s"%i, self.check, i) + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 10) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.skipped), 1) + + def test_generative_inner_error(self): + class FooTC(TestCase): + def check(self, val): + if val == 5: + raise ValueError("no 5") + else: + self.assertEqual(val, val) + + def test_generative(self): + for i in range(10): + yield InnerTest("check_%s"%i, self.check, i) + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 10) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 1) + self.assertEqual(len(result.skipped), 0) + + def test_generative_inner_failure(self): + class FooTC(TestCase): + def check(self, val): + if val == 5: + self.assertEqual(val, val+1) + else: + self.assertEqual(val, val) + + def test_generative(self): + for i in range(10): + yield InnerTest("check_%s"%i, self.check, i) + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 10) + self.assertEqual(len(result.failures), 1) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.skipped), 0) + + + def test_generative_outer_failure(self): + class FooTC(TestCase): + def test_generative(self): + self.fail() + yield + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 0) + self.assertEqual(len(result.failures), 1) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.skipped), 0) + + def test_generative_outer_skip(self): + class FooTC(TestCase): + def test_generative(self): + self.skipTest('blah') + yield + + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.skipped), 1) + + +class ExitFirstTC(TestCase): + def setUp(self): + output = StringIO() + self.runner = SkipAwareTextTestRunner(stream=output, exitfirst=True) + + def test_failure_exit_first(self): + class FooTC(TestCase): + def test_1(self): pass + def test_2(self): assert False + def test_3(self): pass + tests = [FooTC('test_1'), FooTC('test_2')] + result = self.runner.run(TestSuite(tests)) + self.assertEqual(result.testsRun, 2) + self.assertEqual(len(result.failures), 1) + self.assertEqual(len(result.errors), 0) + + def test_error_exit_first(self): + class FooTC(TestCase): + def test_1(self): pass + def test_2(self): raise ValueError() + def test_3(self): pass + tests = [FooTC('test_1'), FooTC('test_2'), FooTC('test_3')] + result = self.runner.run(TestSuite(tests)) + self.assertEqual(result.testsRun, 2) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.errors), 1) + + def test_generative_exit_first(self): + class FooTC(TestCase): + def test_generative(self): + for i in range(10): + yield self.assertTrue, False + result = self.runner.run(FooTC('test_generative')) + self.assertEqual(result.testsRun, 1) + self.assertEqual(len(result.failures), 1) + self.assertEqual(len(result.errors), 0) + + +class TestLoaderTC(TestCase): + ## internal classes for test purposes ######## + class FooTC(TestCase): + def test_foo1(self): pass + def test_foo2(self): pass + def test_bar1(self): pass + + class BarTC(TestCase): + def test_bar2(self): pass + ############################################## + + def setUp(self): + self.loader = NonStrictTestLoader() + self.module = TestLoaderTC # mock_object(FooTC=TestLoaderTC.FooTC, BarTC=TestLoaderTC.BarTC) + self.output = StringIO() + self.runner = SkipAwareTextTestRunner(stream=self.output) + + def assertRunCount(self, pattern, module, expected_count, skipped=()): + self.loader.test_pattern = pattern + self.loader.skipped_patterns = skipped + if pattern: + suite = self.loader.loadTestsFromNames([pattern], module) + else: + suite = self.loader.loadTestsFromModule(module) + result = self.runner.run(suite) + self.loader.test_pattern = None + self.loader.skipped_patterns = () + self.assertEqual(result.testsRun, expected_count) + + def test_collect_everything(self): + """make sure we don't change the default behaviour + for loadTestsFromModule() and loadTestsFromTestCase + """ + testsuite = self.loader.loadTestsFromModule(self.module) + self.assertEqual(len(testsuite._tests), 2) + suite1, suite2 = testsuite._tests + self.assertEqual(len(suite1._tests) + len(suite2._tests), 4) + + def test_collect_with_classname(self): + self.assertRunCount('FooTC', self.module, 3) + self.assertRunCount('BarTC', self.module, 1) + + def test_collect_with_classname_and_pattern(self): + data = [('FooTC.test_foo1', 1), ('FooTC.test_foo', 2), ('FooTC.test_fo', 2), + ('FooTC.foo1', 1), ('FooTC.foo', 2), ('FooTC.whatever', 0) + ] + for pattern, expected_count in data: + yield self.assertRunCount, pattern, self.module, expected_count + + def test_collect_with_pattern(self): + data = [('test_foo1', 1), ('test_foo', 2), ('test_bar', 2), + ('foo1', 1), ('foo', 2), ('bar', 2), ('ba', 2), + ('test', 4), ('ab', 0), + ] + for pattern, expected_count in data: + yield self.assertRunCount, pattern, self.module, expected_count + + def test_testcase_with_custom_metaclass(self): + class mymetaclass(type): pass + class MyMod: + class MyTestCase(TestCase): + __metaclass__ = mymetaclass + def test_foo1(self): pass + def test_foo2(self): pass + def test_bar(self): pass + data = [('test_foo1', 1), ('test_foo', 2), ('test_bar', 1), + ('foo1', 1), ('foo', 2), ('bar', 1), ('ba', 1), + ('test', 3), ('ab', 0), + ('MyTestCase.test_foo1', 1), ('MyTestCase.test_foo', 2), + ('MyTestCase.test_fo', 2), ('MyTestCase.foo1', 1), + ('MyTestCase.foo', 2), ('MyTestCase.whatever', 0) + ] + for pattern, expected_count in data: + yield self.assertRunCount, pattern, MyMod, expected_count + + def test_collect_everything_and_skipped_patterns(self): + testdata = [ (['foo1'], 3), (['foo'], 2), + (['foo', 'bar'], 0), ] + for skipped, expected_count in testdata: + yield self.assertRunCount, None, self.module, expected_count, skipped + + def test_collect_specific_pattern_and_skip_some(self): + testdata = [ ('bar', ['foo1'], 2), ('bar', [], 2), + ('bar', ['bar'], 0), ] + for runpattern, skipped, expected_count in testdata: + yield self.assertRunCount, runpattern, self.module, expected_count, skipped + + def test_skip_classname(self): + testdata = [ (['BarTC'], 3), (['FooTC'], 1), ] + for skipped, expected_count in testdata: + yield self.assertRunCount, None, self.module, expected_count, skipped + + def test_skip_classname_and_specific_collect(self): + testdata = [ ('bar', ['BarTC'], 1), ('foo', ['FooTC'], 0), ] + for runpattern, skipped, expected_count in testdata: + yield self.assertRunCount, runpattern, self.module, expected_count, skipped + + def test_nonregr_dotted_path(self): + self.assertRunCount('FooTC.test_foo', self.module, 2) + + def test_inner_tests_selection(self): + class MyMod: + class MyTestCase(TestCase): + def test_foo(self): pass + def test_foobar(self): + for i in range(5): + if i%2 == 0: + yield InnerTest('even', lambda: None) + else: + yield InnerTest('odd', lambda: None) + yield lambda: None + + # FIXME InnerTest masked by pattern usage + # data = [('foo', 7), ('test_foobar', 6), ('even', 3), ('odd', 2), ] + data = [('foo', 7), ('test_foobar', 6), ('even', 0), ('odd', 0), ] + for pattern, expected_count in data: + yield self.assertRunCount, pattern, MyMod, expected_count + + def test_nonregr_class_skipped_option(self): + class MyMod: + class MyTestCase(TestCase): + def test_foo(self): pass + def test_bar(self): pass + class FooTC(TestCase): + def test_foo(self): pass + self.assertRunCount('foo', MyMod, 2) + self.assertRunCount(None, MyMod, 3) + self.assertRunCount('foo', MyMod, 1, ['FooTC']) + self.assertRunCount(None, MyMod, 2, ['FooTC']) + + def test__classes_are_ignored(self): + class MyMod: + class _Base(TestCase): + def test_1(self): pass + class MyTestCase(_Base): + def test_2(self): pass + self.assertRunCount(None, MyMod, 2) + + +class DecoratorTC(TestCase): + + @with_tempdir + def test_tmp_dir_normal_1(self): + tempdir = tempfile.gettempdir() + # assert temp directory is empty + self.assertListEqual(list(os.walk(tempdir)), + [(tempdir, [], [])]) + + witness = [] + + @with_tempdir + def createfile(list): + fd1, fn1 = tempfile.mkstemp() + fd2, fn2 = tempfile.mkstemp() + dir = tempfile.mkdtemp() + fd3, fn3 = tempfile.mkstemp(dir=dir) + tempfile.mkdtemp() + list.append(True) + for fd in (fd1, fd2, fd3): + os.close(fd) + + self.assertFalse(witness) + createfile(witness) + self.assertTrue(witness) + + self.assertEqual(tempfile.gettempdir(), tempdir) + + # assert temp directory is empty + self.assertListEqual(list(os.walk(tempdir)), + [(tempdir, [], [])]) + + @with_tempdir + def test_tmp_dir_normal_2(self): + tempdir = tempfile.gettempdir() + # assert temp directory is empty + self.assertListEqual(list(os.walk(tempfile.tempdir)), + [(tempfile.tempdir, [], [])]) + + + class WitnessException(Exception): + pass + + @with_tempdir + def createfile(): + fd1, fn1 = tempfile.mkstemp() + fd2, fn2 = tempfile.mkstemp() + dir = tempfile.mkdtemp() + fd3, fn3 = tempfile.mkstemp(dir=dir) + tempfile.mkdtemp() + for fd in (fd1, fd2, fd3): + os.close(fd) + raise WitnessException() + + self.assertRaises(WitnessException, createfile) + + # assert tempdir didn't change + self.assertEqual(tempfile.gettempdir(), tempdir) + + # assert temp directory is empty + self.assertListEqual(list(os.walk(tempdir)), + [(tempdir, [], [])]) + + def test_tmpdir_generator(self): + orig_tempdir = tempfile.gettempdir() + + @with_tempdir + def gen(): + yield tempfile.gettempdir() + + for tempdir in gen(): + self.assertNotEqual(orig_tempdir, tempdir) + self.assertEqual(orig_tempdir, tempfile.gettempdir()) + + def setUp(self): + self.pyversion = sys.version_info + + def tearDown(self): + sys.version_info = self.pyversion + + def test_require_version_good(self): + """ should return the same function + """ + def func() : + pass + sys.version_info = (2, 5, 5, 'final', 4) + current = sys.version_info[:3] + compare = ('2.4', '2.5', '2.5.4', '2.5.5') + for version in compare: + decorator = require_version(version) + self.assertEqual(func, decorator(func), '%s =< %s : function \ + return by the decorator should be the same.' % (version, + '.'.join([str(element) for element in current]))) + + def test_require_version_bad(self): + """ should return a different function : skipping test + """ + def func() : + pass + sys.version_info = (2, 5, 5, 'final', 4) + current = sys.version_info[:3] + compare = ('2.5.6', '2.6', '2.6.5') + for version in compare: + decorator = require_version(version) + self.assertNotEqual(func, decorator(func), '%s >= %s : function \ + return by the decorator should NOT be the same.' + % ('.'.join([str(element) for element in current]), version)) + + def test_require_version_exception(self): + """ should throw a ValueError exception + """ + def func() : + pass + compare = ('2.5.a', '2.a', 'azerty') + for version in compare: + decorator = require_version(version) + self.assertRaises(ValueError, decorator, func) + + def test_require_module_good(self): + """ should return the same function + """ + def func() : + pass + module = 'sys' + decorator = require_module(module) + self.assertEqual(func, decorator(func), 'module %s exists : function \ + return by the decorator should be the same.' % module) + + def test_require_module_bad(self): + """ should return a different function : skipping test + """ + def func() : + pass + modules = ('bla', 'blo', 'bli') + for module in modules: + try: + __import__(module) + pass + except ImportError: + decorator = require_module(module) + self.assertNotEqual(func, decorator(func), 'module %s does \ + not exist : function return by the decorator should \ + NOT be the same.' % module) + return + print('all modules in %s exist. Could not test %s' % (', '.join(modules), + sys._getframe().f_code.co_name)) + +class TagTC(TestCase): + + def setUp(self): + @tag('testing', 'bob') + def bob(a, b, c): + return (a + b) * c + + self.func = bob + + class TagTestTC(TestCase): + tags = Tags('one', 'two') + + def test_one(self): + self.assertTrue(True) + + @tag('two', 'three') + def test_two(self): + self.assertTrue(True) + + @tag('three', inherit=False) + def test_three(self): + self.assertTrue(True) + self.cls = TagTestTC + + def test_tag_decorator(self): + bob = self.func + + self.assertEqual(bob(2, 3, 7), 35) + self.assertTrue(hasattr(bob, 'tags')) + self.assertSetEqual(bob.tags, set(['testing', 'bob'])) + + def test_tags_class(self): + tags = self.func.tags + + self.assertTrue(tags['testing']) + self.assertFalse(tags['Not inside']) + + def test_tags_match(self): + tags = self.func.tags + + self.assertTrue(tags.match('testing')) + self.assertFalse(tags.match('other')) + + self.assertFalse(tags.match('testing and coin')) + self.assertTrue(tags.match('testing or other')) + + self.assertTrue(tags.match('not other')) + + self.assertTrue(tags.match('not other or (testing and bibi)')) + self.assertTrue(tags.match('other or (testing and bob)')) + + def test_tagged_class(self): + def options(tags): + class Options(object): + tags_pattern = tags + return Options() + + tc = self.cls('test_one') + + runner = SkipAwareTextTestRunner() + self.assertTrue(runner.does_match_tags(tc.test_one)) + self.assertTrue(runner.does_match_tags(tc.test_two)) + self.assertTrue(runner.does_match_tags(tc.test_three)) + + runner = SkipAwareTextTestRunner(options=options('one')) + self.assertTrue(runner.does_match_tags(tc.test_one)) + self.assertTrue(runner.does_match_tags(tc.test_two)) + self.assertFalse(runner.does_match_tags(tc.test_three)) + + runner = SkipAwareTextTestRunner(options=options('two')) + self.assertTrue(runner.does_match_tags(tc.test_one)) + self.assertTrue(runner.does_match_tags(tc.test_two)) + self.assertFalse(runner.does_match_tags(tc.test_three)) + + runner = SkipAwareTextTestRunner(options=options('three')) + self.assertFalse(runner.does_match_tags(tc.test_one)) + self.assertTrue(runner.does_match_tags(tc.test_two)) + self.assertTrue(runner.does_match_tags(tc.test_three)) + + runner = SkipAwareTextTestRunner(options=options('two or three')) + self.assertTrue(runner.does_match_tags(tc.test_one)) + self.assertTrue(runner.does_match_tags(tc.test_two)) + self.assertTrue(runner.does_match_tags(tc.test_three)) + + runner = SkipAwareTextTestRunner(options=options('two and three')) + self.assertFalse(runner.does_match_tags(tc.test_one)) + self.assertTrue(runner.does_match_tags(tc.test_two)) + self.assertFalse(runner.does_match_tags(tc.test_three)) + + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_textutils.py b/pymode/libs/logilab-common-1.4.1/test/unittest_textutils.py new file mode 100644 index 00000000..330d49c2 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_textutils.py @@ -0,0 +1,268 @@ +# -*- coding: utf-8 -*- +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +""" +unit tests for module textutils +squeleton generated by /home/syt/cvs_work/logilab/pyreverse/py2tests.py on Sep 08 at 09:1:31 + +""" +import doctest +import re +from os import linesep + +from logilab.common import textutils as tu +from logilab.common.testlib import TestCase, unittest_main + + +if linesep != '\n': + import re + LINE_RGX = re.compile(linesep) + def ulines(string): + return LINE_RGX.sub('\n', string) +else: + def ulines(string): + return string + +class NormalizeTextTC(TestCase): + + def test_known_values(self): + self.assertEqual(ulines(tu.normalize_text('''some really malformated + text. +With some times some veeeeeeeeeeeeeeerrrrryyyyyyyyyyyyyyyyyyy loooooooooooooooooooooong linnnnnnnnnnnes + +and empty lines! + ''')), + '''some really malformated text. With some times some +veeeeeeeeeeeeeeerrrrryyyyyyyyyyyyyyyyyyy loooooooooooooooooooooong +linnnnnnnnnnnes + +and empty lines!''') + self.assertMultiLineEqual(ulines(tu.normalize_text('''\ +some ReST formated text +======================= +With some times some veeeeeeeeeeeeeeerrrrryyyyyyyyyyyyyyyyyyy loooooooooooooooooooooong linnnnnnnnnnnes +and normal lines! + +another paragraph + ''', rest=True)), + '''\ +some ReST formated text +======================= +With some times some veeeeeeeeeeeeeeerrrrryyyyyyyyyyyyyyyyyyy +loooooooooooooooooooooong linnnnnnnnnnnes +and normal lines! + +another paragraph''') + + def test_nonregr_unsplitable_word(self): + self.assertEqual(ulines(tu.normalize_text('''petit complement : + +http://www.plonefr.net/blog/archive/2005/10/30/tester-la-future-infrastructure-i18n +''', 80)), + '''petit complement : + +http://www.plonefr.net/blog/archive/2005/10/30/tester-la-future-infrastructure-i18n''') + + + def test_nonregr_rest_normalize(self): + self.assertEqual(ulines(tu.normalize_text("""... Il est donc evident que tout le monde doit lire le compte-rendu de RSH et aller discuter avec les autres si c'est utile ou necessaire. + """, rest=True)), """... Il est donc evident que tout le monde doit lire le compte-rendu de RSH et +aller discuter avec les autres si c'est utile ou necessaire.""") + + def test_normalize_rest_paragraph(self): + self.assertEqual(ulines(tu.normalize_rest_paragraph("""**nico**: toto""")), + """**nico**: toto""") + + def test_normalize_rest_paragraph2(self): + self.assertEqual(ulines(tu.normalize_rest_paragraph(""".. _tdm: http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Table-des-matieres/.20_adaa41fb-c125-4919-aece-049601e81c8e_0_0.pdf +.. _extrait: http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Extrait-du-livre/.20_d6eed0be-0d36-4384-be59-2dd09e081012_0_0.pdf""", indent='> ')), + """> .. _tdm: +> http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Table-des-matieres/.20_adaa41fb-c125-4919-aece-049601e81c8e_0_0.pdf +> .. _extrait: +> http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Extrait-du-livre/.20_d6eed0be-0d36-4384-be59-2dd09e081012_0_0.pdf""") + + def test_normalize_paragraph2(self): + self.assertEqual(ulines(tu.normalize_paragraph(""".. _tdm: http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Table-des-matieres/.20_adaa41fb-c125-4919-aece-049601e81c8e_0_0.pdf +.. _extrait: http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Extrait-du-livre/.20_d6eed0be-0d36-4384-be59-2dd09e081012_0_0.pdf""", indent='> ')), + """> .. _tdm: +> http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Table-des-matieres/.20_adaa41fb-c125-4919-aece-049601e81c8e_0_0.pdf +> .. _extrait: +> http://www.editions-eni.fr/Livres/Python-Les-fondamentaux-du-langage---La-programmation-pour-les-scientifiques-Extrait-du-livre/.20_d6eed0be-0d36-4384-be59-2dd09e081012_0_0.pdf""") + +class NormalizeParagraphTC(TestCase): + + def test_known_values(self): + self.assertEqual(ulines(tu.normalize_text("""This package contains test files shared by the logilab-common package. It isn't +necessary to install this package unless you want to execute or look at +the tests.""", indent=' ', line_len=70)), + """\ + This package contains test files shared by the logilab-common + package. It isn't necessary to install this package unless you want + to execute or look at the tests.""") + + +class GetCsvTC(TestCase): + + def test_known(self): + self.assertEqual(tu.splitstrip('a, b,c '), ['a', 'b', 'c']) + +class UnitsTC(TestCase): + + def setUp(self): + self.units = { + 'm': 60, + 'kb': 1024, + 'mb': 1024*1024, + } + + def test_empty_base(self): + self.assertEqual(tu.apply_units('17', {}), 17) + + def test_empty_inter(self): + def inter(value): + return int(float(value)) * 2 + result = tu.apply_units('12.4', {}, inter=inter) + self.assertEqual(result, 12 * 2) + self.assertIsInstance(result, float) + + def test_empty_final(self): + # int('12.4') raise value error + self.assertRaises(ValueError, tu.apply_units, '12.4', {}, final=int) + + def test_empty_inter_final(self): + result = tu.apply_units('12.4', {}, inter=float, final=int) + self.assertEqual(result, 12) + self.assertIsInstance(result, int) + + def test_blank_base(self): + result = tu.apply_units(' 42 ', {}, final=int) + self.assertEqual(result, 42) + + def test_blank_space(self): + result = tu.apply_units(' 1 337 ', {}, final=int) + self.assertEqual(result, 1337) + + def test_blank_coma(self): + result = tu.apply_units(' 4,298.42 ', {}) + self.assertEqual(result, 4298.42) + + def test_blank_mixed(self): + result = tu.apply_units('45, 317, 337', {}, final=int) + self.assertEqual(result, 45317337) + + def test_unit_singleunit_singleletter(self): + result = tu.apply_units('15m', self.units) + self.assertEqual(result, 15 * self.units['m'] ) + + def test_unit_singleunit_multipleletter(self): + result = tu.apply_units('47KB', self.units) + self.assertEqual(result, 47 * self.units['kb'] ) + + def test_unit_singleunit_caseinsensitive(self): + result = tu.apply_units('47kb', self.units) + self.assertEqual(result, 47 * self.units['kb'] ) + + def test_unit_multipleunit(self): + result = tu.apply_units('47KB 1.5MB', self.units) + self.assertEqual(result, 47 * self.units['kb'] + 1.5 * self.units['mb']) + + def test_unit_with_blank(self): + result = tu.apply_units('1 000 KB', self.units) + self.assertEqual(result, 1000 * self.units['kb']) + + def test_unit_wrong_input(self): + self.assertRaises(ValueError, tu.apply_units, '', self.units) + self.assertRaises(ValueError, tu.apply_units, 'wrong input', self.units) + self.assertRaises(ValueError, tu.apply_units, 'wrong13 input', self.units) + self.assertRaises(ValueError, tu.apply_units, 'wrong input42', self.units) + +RGX = re.compile('abcd') +class PrettyMatchTC(TestCase): + + def test_known(self): + string = 'hiuherabcdef' + self.assertEqual(ulines(tu.pretty_match(RGX.search(string), string)), + 'hiuherabcdef\n ^^^^') + def test_known_values_1(self): + rgx = re.compile('(to*)') + string = 'toto' + match = rgx.search(string) + self.assertEqual(ulines(tu.pretty_match(match, string)), '''toto +^^''') + + def test_known_values_2(self): + rgx = re.compile('(to*)') + string = ''' ... ... to to + ... ... ''' + match = rgx.search(string) + self.assertEqual(ulines(tu.pretty_match(match, string)), ''' ... ... to to + ^^ + ... ...''') + + + +class UnquoteTC(TestCase): + def test(self): + self.assertEqual(tu.unquote('"toto"'), 'toto') + self.assertEqual(tu.unquote("'l'inenarrable toto'"), "l'inenarrable toto") + self.assertEqual(tu.unquote("no quote"), "no quote") + + +class ColorizeAnsiTC(TestCase): + def test_known(self): + self.assertEqual(tu.colorize_ansi('hello', 'blue', 'strike'), '\x1b[9;34mhello\x1b[0m') + self.assertEqual(tu.colorize_ansi('hello', style='strike, inverse'), '\x1b[9;7mhello\x1b[0m') + self.assertEqual(tu.colorize_ansi('hello', None, None), 'hello') + self.assertEqual(tu.colorize_ansi('hello', '', ''), 'hello') + def test_raise(self): + self.assertRaises(KeyError, tu.colorize_ansi, 'hello', 'bleu', None) + self.assertRaises(KeyError, tu.colorize_ansi, 'hello', None, 'italique') + + +class UnormalizeTC(TestCase): + def test_unormalize_no_substitute(self): + data = [(u'\u0153nologie', u'oenologie'), + (u'\u0152nologie', u'OEnologie'), + (u'l\xf8to', u'loto'), + (u'été', u'ete'), + (u'àèùéïîôêç', u'aeueiioec'), + (u'ÀÈÙÉÏÎÔÊÇ', u'AEUEIIOEC'), + (u'\xa0', u' '), # NO-BREAK SPACE managed by NFKD decomposition + (u'\u0154', u'R'), + (u'Pointe d\u2019Yves', u"Pointe d'Yves"), + (u'Bordeaux\u2013Mérignac', u'Bordeaux-Merignac'), + ] + for input, output in data: + yield self.assertEqual, tu.unormalize(input), output + + def test_unormalize_substitute(self): + self.assertEqual(tu.unormalize(u'ab \u8000 cd', substitute='_'), + 'ab _ cd') + + def test_unormalize_backward_compat(self): + self.assertRaises(ValueError, tu.unormalize, u"\u8000") + self.assertEqual(tu.unormalize(u"\u8000", substitute=''), u'') + + +def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(tu)) + return tests + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_tree.py b/pymode/libs/logilab-common-1.4.1/test/unittest_tree.py new file mode 100644 index 00000000..ea5af81a --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_tree.py @@ -0,0 +1,247 @@ +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +""" +unit tests for module logilab.common.tree +squeleton generated by /home/syt/bin/py2tests on Jan 20 at 10:43:25 +""" + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.tree import * + +tree = ('root', ( + ('child_1_1', ( + ('child_2_1', ()), ('child_2_2', ( + ('child_3_1', ()), + )))), + ('child_1_2', (('child_2_3', ()),)))) + +def make_tree(tuple): + n = Node(tuple[0]) + for child in tuple[1]: + n.append(make_tree(child)) + return n + +class Node_ClassTest(TestCase): + """ a basic tree node, caracterised by an id""" + def setUp(self): + """ called before each test from this class """ + self.o = make_tree(tree) + + + def test_flatten(self): + result = [r.id for r in self.o.flatten()] + expected = ['root', 'child_1_1', 'child_2_1', 'child_2_2', 'child_3_1', 'child_1_2', 'child_2_3'] + self.assertListEqual(result, expected) + + def test_flatten_with_outlist(self): + resultnodes = [] + self.o.flatten(resultnodes) + result = [r.id for r in resultnodes] + expected = ['root', 'child_1_1', 'child_2_1', 'child_2_2', 'child_3_1', 'child_1_2', 'child_2_3'] + self.assertListEqual(result, expected) + + + def test_known_values_remove(self): + """ + remove a child node + """ + self.o.remove(self.o.get_node_by_id('child_1_1')) + self.assertRaises(NodeNotFound, self.o.get_node_by_id, 'child_1_1') + + def test_known_values_replace(self): + """ + replace a child node with another + """ + self.o.replace(self.o.get_node_by_id('child_1_1'), Node('hoho')) + self.assertRaises(NodeNotFound, self.o.get_node_by_id, 'child_1_1') + self.assertEqual(self.o.get_node_by_id('hoho'), self.o.children[0]) + + def test_known_values_get_sibling(self): + """ + return the sibling node that has given id + """ + self.assertEqual(self.o.children[0].get_sibling('child_1_2'), self.o.children[1], None) + + def test_raise_get_sibling_NodeNotFound(self): + self.assertRaises(NodeNotFound, self.o.children[0].get_sibling, 'houhou') + + def test_known_values_get_node_by_id(self): + """ + return node in whole hierarchy that has given id + """ + self.assertEqual(self.o.get_node_by_id('child_1_1'), self.o.children[0]) + + def test_raise_get_node_by_id_NodeNotFound(self): + self.assertRaises(NodeNotFound, self.o.get_node_by_id, 'houhou') + + def test_known_values_get_child_by_id(self): + """ + return child of given id + """ + self.assertEqual(self.o.get_child_by_id('child_2_1', recurse=1), self.o.children[0].children[0]) + + def test_raise_get_child_by_id_NodeNotFound(self): + self.assertRaises(NodeNotFound, self.o.get_child_by_id, nid='child_2_1') + self.assertRaises(NodeNotFound, self.o.get_child_by_id, 'houhou') + + def test_known_values_get_child_by_path(self): + """ + return child of given path (path is a list of ids) + """ + self.assertEqual(self.o.get_child_by_path(['root', 'child_1_1', 'child_2_1']), self.o.children[0].children[0]) + + def test_raise_get_child_by_path_NodeNotFound(self): + self.assertRaises(NodeNotFound, self.o.get_child_by_path, ['child_1_1', 'child_2_11']) + + def test_known_values_depth_down(self): + """ + return depth of this node in the tree + """ + self.assertEqual(self.o.depth_down(), 4) + self.assertEqual(self.o.get_child_by_id('child_2_1', True).depth_down(), 1) + + def test_known_values_depth(self): + """ + return depth of this node in the tree + """ + self.assertEqual(self.o.depth(), 0) + self.assertEqual(self.o.get_child_by_id('child_2_1', True).depth(), 2) + + def test_known_values_width(self): + """ + return depth of this node in the tree + """ + self.assertEqual(self.o.width(), 3) + self.assertEqual(self.o.get_child_by_id('child_2_1', True).width(), 1) + + def test_known_values_root(self): + """ + return the root node of the tree + """ + self.assertEqual(self.o.get_child_by_id('child_2_1', True).root(), self.o) + + def test_known_values_leaves(self): + """ + return a list with all the leaf nodes descendant from this task + """ + self.assertEqual(self.o.leaves(), [self.o.get_child_by_id('child_2_1', True), + self.o.get_child_by_id('child_3_1', True), + self.o.get_child_by_id('child_2_3', True)]) + + def test_known_values_lineage(self): + c31 = self.o.get_child_by_id('child_3_1', True) + self.assertEqual(c31.lineage(), [self.o.get_child_by_id('child_3_1', True), + self.o.get_child_by_id('child_2_2', True), + self.o.get_child_by_id('child_1_1', True), + self.o]) + + +class post_order_list_FunctionTest(TestCase): + """""" + def setUp(self): + """ called before each test from this class """ + self.o = make_tree(tree) + + def test_known_values_post_order_list(self): + """ + create a list with tree nodes for which the function returned true + in a post order foashion + """ + L = ['child_2_1', 'child_3_1', 'child_2_2', 'child_1_1', 'child_2_3', 'child_1_2', 'root'] + l = [n.id for n in post_order_list(self.o)] + self.assertEqual(l, L, l) + + def test_known_values_post_order_list2(self): + """ + create a list with tree nodes for which the function returned true + in a post order foashion + """ + def filter(node): + if node.id == 'child_2_2': + return 0 + return 1 + L = ['child_2_1', 'child_1_1', 'child_2_3', 'child_1_2', 'root'] + l = [n.id for n in post_order_list(self.o, filter)] + self.assertEqual(l, L, l) + + +class PostfixedDepthFirstIterator_ClassTest(TestCase): + """""" + def setUp(self): + """ called before each test from this class """ + self.o = make_tree(tree) + + def test_known_values_next(self): + L = ['child_2_1', 'child_3_1', 'child_2_2', 'child_1_1', 'child_2_3', 'child_1_2', 'root'] + iter = PostfixedDepthFirstIterator(self.o) + o = next(iter) + i = 0 + while o: + self.assertEqual(o.id, L[i]) + o = next(iter) + i += 1 + + +class pre_order_list_FunctionTest(TestCase): + """""" + def setUp(self): + """ called before each test from this class """ + self.o = make_tree(tree) + + def test_known_values_pre_order_list(self): + """ + create a list with tree nodes for which the function returned true + in a pre order fashion + """ + L = ['root', 'child_1_1', 'child_2_1', 'child_2_2', 'child_3_1', 'child_1_2', 'child_2_3'] + l = [n.id for n in pre_order_list(self.o)] + self.assertEqual(l, L, l) + + def test_known_values_pre_order_list2(self): + """ + create a list with tree nodes for which the function returned true + in a pre order fashion + """ + def filter(node): + if node.id == 'child_2_2': + return 0 + return 1 + L = ['root', 'child_1_1', 'child_2_1', 'child_1_2', 'child_2_3'] + l = [n.id for n in pre_order_list(self.o, filter)] + self.assertEqual(l, L, l) + + +class PrefixedDepthFirstIterator_ClassTest(TestCase): + """""" + def setUp(self): + """ called before each test from this class """ + self.o = make_tree(tree) + + def test_known_values_next(self): + L = ['root', 'child_1_1', 'child_2_1', 'child_2_2', 'child_3_1', 'child_1_2', 'child_2_3'] + iter = PrefixedDepthFirstIterator(self.o) + o = next(iter) + i = 0 + while o: + self.assertEqual(o.id, L[i]) + o = next(iter) + i += 1 + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_umessage.py b/pymode/libs/logilab-common-1.4.1/test/unittest_umessage.py new file mode 100644 index 00000000..2841172a --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_umessage.py @@ -0,0 +1,94 @@ +# encoding: iso-8859-15 +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +import sys +import email +from os.path import join, dirname, abspath + +from six import text_type + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.umessage import UMessage, decode_QP, message_from_string + +DATA = join(dirname(abspath(__file__)), 'data') + +class UMessageTC(TestCase): + + def setUp(self): + if sys.version_info >= (3, 2): + import io + msg1 = email.message_from_file(io.open(join(DATA, 'test1.msg'), encoding='utf8')) + msg2 = email.message_from_file(io.open(join(DATA, 'test2.msg'), encoding='utf8')) + else: + msg1 = email.message_from_file(open(join(DATA, 'test1.msg'))) + msg2 = email.message_from_file(open(join(DATA, 'test2.msg'))) + self.umessage1 = UMessage(msg1) + self.umessage2 = UMessage(msg2) + + def test_get_subject(self): + subj = self.umessage2.get('Subject') + self.assertEqual(type(subj), text_type) + self.assertEqual(subj, u' LA MER') + + def test_get_all(self): + to = self.umessage2.get_all('To') + self.assertEqual(type(to[0]), text_type) + self.assertEqual(to, [u'lment accents ']) + + def test_get_payload_no_multi(self): + payload = self.umessage1.get_payload() + self.assertEqual(type(payload), text_type) + + def test_get_payload_decode(self): + msg = """\ +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: base64 +Subject: =?utf-8?q?b=C3=AFjour?= +From: =?utf-8?q?oim?= +Reply-to: =?utf-8?q?oim?= , =?utf-8?q?BimBam?= +X-CW: data +To: test@logilab.fr +Date: now + +dW4gcGV0aXQgY8O2dWNvdQ== +""" + msg = message_from_string(msg) + self.assertEqual(msg.get_payload(decode=True), u'un petit cucou') + + def test_decode_QP(self): + test_line = '=??b?UmFwaGHrbA==?= DUPONT' + test = decode_QP(test_line) + self.assertEqual(type(test), text_type) + self.assertEqual(test, u'Raphal DUPONT') + + def test_decode_QP_utf8(self): + test_line = '=?utf-8?q?o=C3=AEm?= ' + test = decode_QP(test_line) + self.assertEqual(type(test), text_type) + self.assertEqual(test, u'om ') + + def test_decode_QP_ascii(self): + test_line = 'test ' + test = decode_QP(test_line) + self.assertEqual(type(test), text_type) + self.assertEqual(test, u'test ') + + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_ureports_html.py b/pymode/libs/logilab-common-1.4.1/test/unittest_ureports_html.py new file mode 100644 index 00000000..2298eec7 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_ureports_html.py @@ -0,0 +1,63 @@ +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +'''unit tests for ureports.html_writer +''' + + +from utils import WriterTC +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.ureports.html_writer import * + +class HTMLWriterTC(TestCase, WriterTC): + + def setUp(self): + self.writer = HTMLWriter(1) + + # Section tests ########################################################### + section_base = '''
+

Section title

+

Section\'s description. +Blabla bla

+''' + section_nested = '''
\n

Section title

\n

Section\'s description.\nBlabla bla

\n

Subsection

\n

Sub section description

\n
\n''' + + # List tests ############################################################## + list_base = '''
    \n
  • item1
  • \n
  • item2
  • \n
  • item3
  • \n
  • item4
  • \n
\n''' + + nested_list = '''
    +
  • blabla

      +
    • 1
    • +
    • 2
    • +
    • 3
    • +
    +

  • +
  • an other point
  • +
+''' + + # Table tests ############################################################# + table_base = '''\n\n\n\n\n\n\n\n\n
head1head2
cell1cell2
\n''' + field_table = '''\n\n\n\n\n\n\n\n\n\n\n\n\n
f1v1
f22v22
f333v333
\n''' + advanced_table = '''\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
fieldvalue
f1v1
f22v22
f333v333
toi perdu ? 
\n''' + + + # VerbatimText tests ###################################################### + verbatim_base = '''
blablabla
''' + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_ureports_text.py b/pymode/libs/logilab-common-1.4.1/test/unittest_ureports_text.py new file mode 100644 index 00000000..dd39dd84 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_ureports_text.py @@ -0,0 +1,104 @@ +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +'''unit tests for ureports.text_writer +''' + + +from utils import WriterTC +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.ureports.text_writer import TextWriter + +class TextWriterTC(TestCase, WriterTC): + def setUp(self): + self.writer = TextWriter() + + # Section tests ########################################################### + section_base = ''' +Section title +============= +Section\'s description. +Blabla bla + +''' + section_nested = ''' +Section title +============= +Section\'s description. +Blabla bla + +Subsection +---------- +Sub section description + + +''' + + # List tests ############################################################## + list_base = ''' +* item1 +* item2 +* item3 +* item4''' + + nested_list = ''' +* blabla + - 1 + - 2 + - 3 + +* an other point''' + + # Table tests ############################################################# + table_base = ''' ++------+------+ +|head1 |head2 | ++------+------+ +|cell1 |cell2 | ++------+------+ + +''' + field_table = ''' +f1 : v1 +f22 : v22 +f333: v333 +''' + advanced_table = ''' ++---------------+------+ +|field |value | ++===============+======+ +|f1 |v1 | ++---------------+------+ +|f22 |v22 | ++---------------+------+ +|f333 |v333 | ++---------------+------+ +|`toi perdu ?`_ | | ++---------------+------+ + +''' + + + # VerbatimText tests ###################################################### + verbatim_base = ''':: + + blablabla + +''' + +if __name__ == '__main__': + unittest_main() diff --git a/pymode/libs/logilab-common-1.4.1/test/unittest_xmlutils.py b/pymode/libs/logilab-common-1.4.1/test/unittest_xmlutils.py new file mode 100644 index 00000000..3d82da93 --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/unittest_xmlutils.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.xmlutils import parse_pi_data + + +class ProcessingInstructionDataParsingTest(TestCase): + def test_empty_pi(self): + """ + Tests the parsing of the data of an empty processing instruction. + """ + pi_data = u" \t \n " + data = parse_pi_data(pi_data) + self.assertEqual(data, {}) + + def test_simple_pi_with_double_quotes(self): + """ + Tests the parsing of the data of a simple processing instruction using + double quotes for embedding the value. + """ + pi_data = u""" \t att="value"\n """ + data = parse_pi_data(pi_data) + self.assertEqual(data, {u"att": u"value"}) + + def test_simple_pi_with_simple_quotes(self): + """ + Tests the parsing of the data of a simple processing instruction using + simple quotes for embedding the value. + """ + pi_data = u""" \t att='value'\n """ + data = parse_pi_data(pi_data) + self.assertEqual(data, {u"att": u"value"}) + + def test_complex_pi_with_different_quotes(self): + """ + Tests the parsing of the data of a complex processing instruction using + simple quotes or double quotes for embedding the values. + """ + pi_data = u""" \t att='value'\n att2="value2" att3='value3'""" + data = parse_pi_data(pi_data) + self.assertEqual(data, {u"att": u"value", u"att2": u"value2", + u"att3": u"value3"}) + + def test_pi_with_non_attribute_data(self): + """ + Tests the parsing of the data of a complex processing instruction + containing non-attribute data. + """ + pi_data = u""" \t keyword att1="value1" """ + data = parse_pi_data(pi_data) + self.assertEqual(data, {u"keyword": None, u"att1": u"value1"}) + + +# definitions for automatic unit testing + +if __name__ == '__main__': + unittest_main() + diff --git a/pymode/libs/logilab-common-1.4.1/test/utils.py b/pymode/libs/logilab-common-1.4.1/test/utils.py new file mode 100644 index 00000000..ca1730eb --- /dev/null +++ b/pymode/libs/logilab-common-1.4.1/test/utils.py @@ -0,0 +1,96 @@ +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common 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-common 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-common. If not, see . +'''unit tests utilities for ureports +''' + +from __future__ import print_function + +import sys +from io import StringIO +buffers = [StringIO] +if sys.version_info < (3, 0): + from cStringIO import StringIO as cStringIO + from StringIO import StringIO as pStringIO + buffers += [cStringIO, pStringIO] + +from logilab.common.ureports.nodes import * + +class WriterTC: + def _test_output(self, test_id, layout, msg=None): + for buffercls in buffers: + buffer = buffercls() + self.writer.format(layout, buffer) + got = buffer.getvalue() + expected = getattr(self, test_id) + try: + self.assertMultiLineEqual(got, expected) + except: + print('**** using a %s' % buffer.__class__) + print('**** got for %s' % test_id) + print(got) + print('**** while expected') + print(expected) + print('****') + raise + + def test_section(self): + layout = Section('Section title', + 'Section\'s description.\nBlabla bla') + self._test_output('section_base', layout) + layout.append(Section('Subsection', 'Sub section description')) + self._test_output('section_nested', layout) + + def test_verbatim(self): + layout = VerbatimText('blablabla') + self._test_output('verbatim_base', layout) + + + def test_list(self): + layout = List(children=('item1', 'item2', 'item3', 'item4')) + self._test_output('list_base', layout) + + def test_nested_list(self): + layout = List(children=(Paragraph(("blabla", List(children=('1', "2", "3")))), + "an other point")) + self._test_output('nested_list', layout) + + + def test_table(self): + layout = Table(cols=2, children=('head1', 'head2', 'cell1', 'cell2')) + self._test_output('table_base', layout) + + def test_field_table(self): + table = Table(cols=2, klass='field', id='mytable') + for field, value in (('f1', 'v1'), ('f22', 'v22'), ('f333', 'v333')): + table.append(Text(field)) + table.append(Text(value)) + self._test_output('field_table', table) + + def test_advanced_table(self): + table = Table(cols=2, klass='whatever', id='mytable', rheaders=1) + for field, value in (('field', 'value'), ('f1', 'v1'), ('f22', 'v22'), ('f333', 'v333')): + table.append(Text(field)) + table.append(Text(value)) + table.append(Link('http://www.perdu.com', 'toi perdu ?')) + table.append(Text('')) + self._test_output('advanced_table', table) + + +## def test_image(self): +## layout = Verbatim('blablabla') +## self._test_output('verbatim_base', layout) diff --git a/pymode/libs/logilab/__init__.py b/pymode/libs/logilab/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/pymode/libs/logilab/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pymode/libs/logilab/common/testlib.py b/pymode/libs/logilab/common/testlib.py deleted file mode 100644 index a6b4b1e1..00000000 --- a/pymode/libs/logilab/common/testlib.py +++ /dev/null @@ -1,1338 +0,0 @@ -# -*- coding: utf-8 -*- -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common 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-common 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-common. If not, see . -"""Run tests. - -This will find all modules whose name match a given prefix in the test -directory, and run them. Various command line options provide -additional facilities. - -Command line options: - - -v verbose -- run tests in verbose mode with output to stdout - -q quiet -- don't print anything except if a test fails - -t testdir -- directory where the tests will be found - -x exclude -- add a test to exclude - -p profile -- profiled execution - -d dbc -- enable design-by-contract - -m match -- only run test matching the tag pattern which follow - -If no non-option arguments are present, prefixes used are 'test', -'regrtest', 'smoketest' and 'unittest'. - -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" -# modified copy of some functions from test/regrtest.py from PyXml -# disable camel case warning -# pylint: disable=C0103 - -import sys -import os, os.path as osp -import re -import traceback -import inspect -import difflib -import tempfile -import math -import warnings -from shutil import rmtree -from operator import itemgetter -from itertools import dropwhile -from inspect import isgeneratorfunction - -from six import string_types -from six.moves import builtins, range, configparser, input - -from logilab.common.deprecation import deprecated - -import unittest as unittest_legacy -if not getattr(unittest_legacy, "__package__", None): - try: - import unittest2 as unittest - from unittest2 import SkipTest - except ImportError: - raise ImportError("You have to install python-unittest2 to use %s" % __name__) -else: - import unittest - from unittest import SkipTest - -from functools import wraps - -from logilab.common.debugger import Debugger, colorize_source -from logilab.common.decorators import cached, classproperty -from logilab.common import textutils - - -__all__ = ['main', 'unittest_main', 'find_tests', 'run_test', 'spawn'] - -DEFAULT_PREFIXES = ('test', 'regrtest', 'smoketest', 'unittest', - 'func', 'validation') - -is_generator = deprecated('[lgc 0.63] use inspect.isgeneratorfunction')(isgeneratorfunction) - -# used by unittest to count the number of relevant levels in the traceback -__unittest = 1 - - -def with_tempdir(callable): - """A decorator ensuring no temporary file left when the function return - Work only for temporary file created with the tempfile module""" - if isgeneratorfunction(callable): - def proxy(*args, **kwargs): - old_tmpdir = tempfile.gettempdir() - new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-") - tempfile.tempdir = new_tmpdir - try: - for x in callable(*args, **kwargs): - yield x - finally: - try: - rmtree(new_tmpdir, ignore_errors=True) - finally: - tempfile.tempdir = old_tmpdir - return proxy - - @wraps(callable) - def proxy(*args, **kargs): - - old_tmpdir = tempfile.gettempdir() - new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-") - tempfile.tempdir = new_tmpdir - try: - return callable(*args, **kargs) - finally: - try: - rmtree(new_tmpdir, ignore_errors=True) - finally: - tempfile.tempdir = old_tmpdir - return proxy - -def in_tempdir(callable): - """A decorator moving the enclosed function inside the tempfile.tempfdir - """ - @wraps(callable) - def proxy(*args, **kargs): - - old_cwd = os.getcwd() - os.chdir(tempfile.tempdir) - try: - return callable(*args, **kargs) - finally: - os.chdir(old_cwd) - return proxy - -def within_tempdir(callable): - """A decorator run the enclosed function inside a tmpdir removed after execution - """ - proxy = with_tempdir(in_tempdir(callable)) - proxy.__name__ = callable.__name__ - return proxy - -def find_tests(testdir, - prefixes=DEFAULT_PREFIXES, suffix=".py", - excludes=(), - remove_suffix=True): - """ - Return a list of all applicable test modules. - """ - tests = [] - for name in os.listdir(testdir): - if not suffix or name.endswith(suffix): - for prefix in prefixes: - if name.startswith(prefix): - if remove_suffix and name.endswith(suffix): - name = name[:-len(suffix)] - if name not in excludes: - tests.append(name) - tests.sort() - return tests - - -## PostMortem Debug facilities ##### -def start_interactive_mode(result): - """starts an interactive shell so that the user can inspect errors - """ - debuggers = result.debuggers - descrs = result.error_descrs + result.fail_descrs - if len(debuggers) == 1: - # don't ask for test name if there's only one failure - debuggers[0].start() - else: - while True: - testindex = 0 - print("Choose a test to debug:") - # order debuggers in the same way than errors were printed - print("\n".join(['\t%s : %s' % (i, descr) for i, (_, descr) - in enumerate(descrs)])) - print("Type 'exit' (or ^D) to quit") - print() - try: - todebug = input('Enter a test name: ') - if todebug.strip().lower() == 'exit': - print() - break - else: - try: - testindex = int(todebug) - debugger = debuggers[descrs[testindex][0]] - except (ValueError, IndexError): - print("ERROR: invalid test number %r" % (todebug, )) - else: - debugger.start() - except (EOFError, KeyboardInterrupt): - print() - break - - -# test utils ################################################################## - -class SkipAwareTestResult(unittest._TextTestResult): - - def __init__(self, stream, descriptions, verbosity, - exitfirst=False, pdbmode=False, cvg=None, colorize=False): - super(SkipAwareTestResult, self).__init__(stream, - descriptions, verbosity) - self.skipped = [] - self.debuggers = [] - self.fail_descrs = [] - self.error_descrs = [] - self.exitfirst = exitfirst - self.pdbmode = pdbmode - self.cvg = cvg - self.colorize = colorize - self.pdbclass = Debugger - self.verbose = verbosity > 1 - - def descrs_for(self, flavour): - return getattr(self, '%s_descrs' % flavour.lower()) - - def _create_pdb(self, test_descr, flavour): - self.descrs_for(flavour).append( (len(self.debuggers), test_descr) ) - if self.pdbmode: - self.debuggers.append(self.pdbclass(sys.exc_info()[2])) - - def _iter_valid_frames(self, frames): - """only consider non-testlib frames when formatting traceback""" - lgc_testlib = osp.abspath(__file__) - std_testlib = osp.abspath(unittest.__file__) - invalid = lambda fi: osp.abspath(fi[1]) in (lgc_testlib, std_testlib) - for frameinfo in dropwhile(invalid, frames): - yield frameinfo - - def _exc_info_to_string(self, err, test): - """Converts a sys.exc_info()-style tuple of values into a string. - - This method is overridden here because we want to colorize - lines if --color is passed, and display local variables if - --verbose is passed - """ - exctype, exc, tb = err - output = ['Traceback (most recent call last)'] - frames = inspect.getinnerframes(tb) - colorize = self.colorize - frames = enumerate(self._iter_valid_frames(frames)) - for index, (frame, filename, lineno, funcname, ctx, ctxindex) in frames: - filename = osp.abspath(filename) - if ctx is None: # pyc files or C extensions for instance - source = '' - else: - source = ''.join(ctx) - if colorize: - filename = textutils.colorize_ansi(filename, 'magenta') - source = colorize_source(source) - output.append(' File "%s", line %s, in %s' % (filename, lineno, funcname)) - output.append(' %s' % source.strip()) - if self.verbose: - output.append('%r == %r' % (dir(frame), test.__module__)) - output.append('') - output.append(' ' + ' local variables '.center(66, '-')) - for varname, value in sorted(frame.f_locals.items()): - output.append(' %s: %r' % (varname, value)) - if varname == 'self': # special handy processing for self - for varname, value in sorted(vars(value).items()): - output.append(' self.%s: %r' % (varname, value)) - output.append(' ' + '-' * 66) - output.append('') - output.append(''.join(traceback.format_exception_only(exctype, exc))) - return '\n'.join(output) - - def addError(self, test, err): - """err -> (exc_type, exc, tcbk)""" - exc_type, exc, _ = err - if isinstance(exc, SkipTest): - assert exc_type == SkipTest - self.addSkip(test, exc) - else: - if self.exitfirst: - self.shouldStop = True - descr = self.getDescription(test) - super(SkipAwareTestResult, self).addError(test, err) - self._create_pdb(descr, 'error') - - def addFailure(self, test, err): - if self.exitfirst: - self.shouldStop = True - descr = self.getDescription(test) - super(SkipAwareTestResult, self).addFailure(test, err) - self._create_pdb(descr, 'fail') - - def addSkip(self, test, reason): - self.skipped.append((test, reason)) - if self.showAll: - self.stream.writeln("SKIPPED") - elif self.dots: - self.stream.write('S') - - def printErrors(self): - super(SkipAwareTestResult, self).printErrors() - self.printSkippedList() - - def printSkippedList(self): - # format (test, err) compatible with unittest2 - for test, err in self.skipped: - descr = self.getDescription(test) - self.stream.writeln(self.separator1) - self.stream.writeln("%s: %s" % ('SKIPPED', descr)) - self.stream.writeln("\t%s" % err) - - def printErrorList(self, flavour, errors): - for (_, descr), (test, err) in zip(self.descrs_for(flavour), errors): - self.stream.writeln(self.separator1) - self.stream.writeln("%s: %s" % (flavour, descr)) - self.stream.writeln(self.separator2) - self.stream.writeln(err) - self.stream.writeln('no stdout'.center(len(self.separator2))) - self.stream.writeln('no stderr'.center(len(self.separator2))) - -# Add deprecation warnings about new api used by module level fixtures in unittest2 -# http://www.voidspace.org.uk/python/articles/unittest2.shtml#setupmodule-and-teardownmodule -class _DebugResult(object): # simplify import statement among unittest flavors.. - "Used by the TestSuite to hold previous class when running in debug." - _previousTestClass = None - _moduleSetUpFailed = False - shouldStop = False - -# backward compatibility: TestSuite might be imported from lgc.testlib -TestSuite = unittest.TestSuite - -class keywords(dict): - """Keyword args (**kwargs) support for generative tests.""" - -class starargs(tuple): - """Variable arguments (*args) for generative tests.""" - def __new__(cls, *args): - return tuple.__new__(cls, args) - -unittest_main = unittest.main - - -class InnerTestSkipped(SkipTest): - """raised when a test is skipped""" - pass - -def parse_generative_args(params): - args = [] - varargs = () - kwargs = {} - flags = 0 # 2 <=> starargs, 4 <=> kwargs - for param in params: - if isinstance(param, starargs): - varargs = param - if flags: - raise TypeError('found starargs after keywords !') - flags |= 2 - args += list(varargs) - elif isinstance(param, keywords): - kwargs = param - if flags & 4: - raise TypeError('got multiple keywords parameters') - flags |= 4 - elif flags & 2 or flags & 4: - raise TypeError('found parameters after kwargs or args') - else: - args.append(param) - - return args, kwargs - - -class InnerTest(tuple): - def __new__(cls, name, *data): - instance = tuple.__new__(cls, data) - instance.name = name - return instance - -class Tags(set): - """A set of tag able validate an expression""" - - def __init__(self, *tags, **kwargs): - self.inherit = kwargs.pop('inherit', True) - if kwargs: - raise TypeError("%s are an invalid keyword argument for this function" % kwargs.keys()) - - if len(tags) == 1 and not isinstance(tags[0], string_types): - tags = tags[0] - super(Tags, self).__init__(tags, **kwargs) - - def __getitem__(self, key): - return key in self - - def match(self, exp): - return eval(exp, {}, self) - - def __or__(self, other): - return Tags(*super(Tags, self).__or__(other)) - - -# duplicate definition from unittest2 of the _deprecate decorator -def _deprecate(original_func): - def deprecated_func(*args, **kwargs): - warnings.warn( - ('Please use %s instead.' % original_func.__name__), - DeprecationWarning, 2) - return original_func(*args, **kwargs) - return deprecated_func - -class TestCase(unittest.TestCase): - """A unittest.TestCase extension with some additional methods.""" - maxDiff = None - pdbclass = Debugger - tags = Tags() - - def __init__(self, methodName='runTest'): - super(TestCase, self).__init__(methodName) - self.__exc_info = sys.exc_info - self.__testMethodName = self._testMethodName - self._current_test_descr = None - self._options_ = None - - @classproperty - @cached - def datadir(cls): # pylint: disable=E0213 - """helper attribute holding the standard test's data directory - - NOTE: this is a logilab's standard - """ - mod = sys.modules[cls.__module__] - return osp.join(osp.dirname(osp.abspath(mod.__file__)), 'data') - # cache it (use a class method to cache on class since TestCase is - # instantiated for each test run) - - @classmethod - def datapath(cls, *fname): - """joins the object's datadir and `fname`""" - return osp.join(cls.datadir, *fname) - - def set_description(self, descr): - """sets the current test's description. - This can be useful for generative tests because it allows to specify - a description per yield - """ - self._current_test_descr = descr - - # override default's unittest.py feature - def shortDescription(self): - """override default unittest shortDescription to handle correctly - generative tests - """ - if self._current_test_descr is not None: - return self._current_test_descr - return super(TestCase, self).shortDescription() - - def quiet_run(self, result, func, *args, **kwargs): - try: - func(*args, **kwargs) - except (KeyboardInterrupt, SystemExit): - raise - except unittest.SkipTest as e: - if hasattr(result, 'addSkip'): - result.addSkip(self, str(e)) - else: - warnings.warn("TestResult has no addSkip method, skips not reported", - RuntimeWarning, 2) - result.addSuccess(self) - return False - except: - result.addError(self, self.__exc_info()) - return False - return True - - def _get_test_method(self): - """return the test method""" - return getattr(self, self._testMethodName) - - def optval(self, option, default=None): - """return the option value or default if the option is not define""" - return getattr(self._options_, option, default) - - def __call__(self, result=None, runcondition=None, options=None): - """rewrite TestCase.__call__ to support generative tests - This is mostly a copy/paste from unittest.py (i.e same - variable names, same logic, except for the generative tests part) - """ - from logilab.common.pytest import FILE_RESTART - if result is None: - result = self.defaultTestResult() - result.pdbclass = self.pdbclass - self._options_ = options - # if result.cvg: - # result.cvg.start() - testMethod = self._get_test_method() - if (getattr(self.__class__, "__unittest_skip__", False) or - getattr(testMethod, "__unittest_skip__", False)): - # If the class or method was skipped. - try: - skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') - or getattr(testMethod, '__unittest_skip_why__', '')) - self._addSkip(result, skip_why) - finally: - result.stopTest(self) - return - if runcondition and not runcondition(testMethod): - return # test is skipped - result.startTest(self) - try: - if not self.quiet_run(result, self.setUp): - return - generative = isgeneratorfunction(testMethod) - # generative tests - if generative: - self._proceed_generative(result, testMethod, - runcondition) - else: - status = self._proceed(result, testMethod) - success = (status == 0) - if not self.quiet_run(result, self.tearDown): - return - if not generative and success: - if hasattr(options, "exitfirst") and options.exitfirst: - # add this test to restart file - try: - restartfile = open(FILE_RESTART, 'a') - try: - descr = '.'.join((self.__class__.__module__, - self.__class__.__name__, - self._testMethodName)) - restartfile.write(descr+os.linesep) - finally: - restartfile.close() - except Exception: - print("Error while saving succeeded test into", - osp.join(os.getcwd(), FILE_RESTART), - file=sys.__stderr__) - raise - result.addSuccess(self) - finally: - # if result.cvg: - # result.cvg.stop() - result.stopTest(self) - - def _proceed_generative(self, result, testfunc, runcondition=None): - # cancel startTest()'s increment - result.testsRun -= 1 - success = True - try: - for params in testfunc(): - if runcondition and not runcondition(testfunc, - skipgenerator=False): - if not (isinstance(params, InnerTest) - and runcondition(params)): - continue - if not isinstance(params, (tuple, list)): - params = (params, ) - func = params[0] - args, kwargs = parse_generative_args(params[1:]) - # increment test counter manually - result.testsRun += 1 - status = self._proceed(result, func, args, kwargs) - if status == 0: - result.addSuccess(self) - success = True - else: - success = False - # XXX Don't stop anymore if an error occured - #if status == 2: - # result.shouldStop = True - if result.shouldStop: # either on error or on exitfirst + error - break - except: - # if an error occurs between two yield - result.addError(self, self.__exc_info()) - success = False - return success - - def _proceed(self, result, testfunc, args=(), kwargs=None): - """proceed the actual test - returns 0 on success, 1 on failure, 2 on error - - Note: addSuccess can't be called here because we have to wait - for tearDown to be successfully executed to declare the test as - successful - """ - kwargs = kwargs or {} - try: - testfunc(*args, **kwargs) - except self.failureException: - result.addFailure(self, self.__exc_info()) - return 1 - except KeyboardInterrupt: - raise - except InnerTestSkipped as e: - result.addSkip(self, e) - return 1 - except SkipTest as e: - result.addSkip(self, e) - return 0 - except: - result.addError(self, self.__exc_info()) - return 2 - return 0 - - def defaultTestResult(self): - """return a new instance of the defaultTestResult""" - return SkipAwareTestResult() - - skip = _deprecate(unittest.TestCase.skipTest) - assertEquals = _deprecate(unittest.TestCase.assertEqual) - assertNotEquals = _deprecate(unittest.TestCase.assertNotEqual) - assertAlmostEquals = _deprecate(unittest.TestCase.assertAlmostEqual) - assertNotAlmostEquals = _deprecate(unittest.TestCase.assertNotAlmostEqual) - - def innerSkip(self, msg=None): - """mark a generative test as skipped for the reason""" - msg = msg or 'test was skipped' - raise InnerTestSkipped(msg) - - @deprecated('Please use assertDictEqual instead.') - def assertDictEquals(self, dict1, dict2, msg=None, context=None): - """compares two dicts - - If the two dict differ, the first difference is shown in the error - message - :param dict1: a Python Dictionary - :param dict2: a Python Dictionary - :param msg: custom message (String) in case of failure - """ - dict1 = dict(dict1) - msgs = [] - for key, value in dict2.items(): - try: - if dict1[key] != value: - msgs.append('%r != %r for key %r' % (dict1[key], value, - key)) - del dict1[key] - except KeyError: - msgs.append('missing %r key' % key) - if dict1: - msgs.append('dict2 is lacking %r' % dict1) - if msg: - self.failureException(msg) - elif msgs: - if context is not None: - base = '%s\n' % context - else: - base = '' - self.fail(base + '\n'.join(msgs)) - - @deprecated('Please use assertCountEqual instead.') - def assertUnorderedIterableEquals(self, got, expected, msg=None): - """compares two iterable and shows difference between both - - :param got: the unordered Iterable that we found - :param expected: the expected unordered Iterable - :param msg: custom message (String) in case of failure - """ - got, expected = list(got), list(expected) - self.assertSetEqual(set(got), set(expected), msg) - if len(got) != len(expected): - if msg is None: - msg = ['Iterable have the same elements but not the same number', - '\t\ti\t'] - got_count = {} - expected_count = {} - for element in got: - got_count[element] = got_count.get(element, 0) + 1 - for element in expected: - expected_count[element] = expected_count.get(element, 0) + 1 - # we know that got_count.key() == expected_count.key() - # because of assertSetEqual - for element, count in got_count.iteritems(): - other_count = expected_count[element] - if other_count != count: - msg.append('\t%s\t%s\t%s' % (element, other_count, count)) - - self.fail(msg) - - assertUnorderedIterableEqual = assertUnorderedIterableEquals - assertUnordIterEquals = assertUnordIterEqual = assertUnorderedIterableEqual - - @deprecated('Please use assertSetEqual instead.') - def assertSetEquals(self,got,expected, msg=None): - """compares two sets and shows difference between both - - Don't use it for iterables other than sets. - - :param got: the Set that we found - :param expected: the second Set to be compared to the first one - :param msg: custom message (String) in case of failure - """ - - if not(isinstance(got, set) and isinstance(expected, set)): - warnings.warn("the assertSetEquals function if now intended for set only."\ - "use assertUnorderedIterableEquals instead.", - DeprecationWarning, 2) - return self.assertUnorderedIterableEquals(got, expected, msg) - - items={} - items['missing'] = expected - got - items['unexpected'] = got - expected - if any(items.itervalues()): - if msg is None: - msg = '\n'.join('%s:\n\t%s' % (key, "\n\t".join(str(value) for value in values)) - for key, values in items.iteritems() if values) - self.fail(msg) - - @deprecated('Please use assertListEqual instead.') - def assertListEquals(self, list_1, list_2, msg=None): - """compares two lists - - If the two list differ, the first difference is shown in the error - message - - :param list_1: a Python List - :param list_2: a second Python List - :param msg: custom message (String) in case of failure - """ - _l1 = list_1[:] - for i, value in enumerate(list_2): - try: - if _l1[0] != value: - from pprint import pprint - pprint(list_1) - pprint(list_2) - self.fail('%r != %r for index %d' % (_l1[0], value, i)) - del _l1[0] - except IndexError: - if msg is None: - msg = 'list_1 has only %d elements, not %s '\ - '(at least %r missing)'% (i, len(list_2), value) - self.fail(msg) - if _l1: - if msg is None: - msg = 'list_2 is lacking %r' % _l1 - self.fail(msg) - - @deprecated('Non-standard. Please use assertMultiLineEqual instead.') - def assertLinesEquals(self, string1, string2, msg=None, striplines=False): - """compare two strings and assert that the text lines of the strings - are equal. - - :param string1: a String - :param string2: a String - :param msg: custom message (String) in case of failure - :param striplines: Boolean to trigger line stripping before comparing - """ - lines1 = string1.splitlines() - lines2 = string2.splitlines() - if striplines: - lines1 = [l.strip() for l in lines1] - lines2 = [l.strip() for l in lines2] - self.assertListEqual(lines1, lines2, msg) - assertLineEqual = assertLinesEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertXMLWellFormed(self, stream, msg=None, context=2): - """asserts the XML stream is well-formed (no DTD conformance check) - - :param context: number of context lines in standard message - (show all data if negative). - Only available with element tree - """ - try: - from xml.etree.ElementTree import parse - self._assertETXMLWellFormed(stream, parse, msg) - except ImportError: - from xml.sax import make_parser, SAXParseException - parser = make_parser() - try: - parser.parse(stream) - except SAXParseException as ex: - if msg is None: - stream.seek(0) - for _ in range(ex.getLineNumber()): - line = stream.readline() - pointer = ('' * (ex.getLineNumber() - 1)) + '^' - msg = 'XML stream not well formed: %s\n%s%s' % (ex, line, pointer) - self.fail(msg) - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertXMLStringWellFormed(self, xml_string, msg=None, context=2): - """asserts the XML string is well-formed (no DTD conformance check) - - :param context: number of context lines in standard message - (show all data if negative). - Only available with element tree - """ - try: - from xml.etree.ElementTree import fromstring - except ImportError: - from elementtree.ElementTree import fromstring - self._assertETXMLWellFormed(xml_string, fromstring, msg) - - def _assertETXMLWellFormed(self, data, parse, msg=None, context=2): - """internal function used by /assertXML(String)?WellFormed/ functions - - :param data: xml_data - :param parse: appropriate parser function for this data - :param msg: error message - :param context: number of context lines in standard message - (show all data if negative). - Only available with element tree - """ - from xml.parsers.expat import ExpatError - try: - from xml.etree.ElementTree import ParseError - except ImportError: - # compatibility for 1: - if len(tup)<=1: - self.fail( "tuple %s has no attributes (%s expected)"%(tup, - dict(element.attrib))) - self.assertDictEqual(element.attrib, tup[1]) - # check children - if len(element) or len(tup)>2: - if len(tup)<=2: - self.fail( "tuple %s has no children (%i expected)"%(tup, - len(element))) - if len(element) != len(tup[2]): - self.fail( "tuple %s has %i children%s (%i expected)"%(tup, - len(tup[2]), - ('', 's')[len(tup[2])>1], len(element))) - for index in range(len(tup[2])): - self.assertXMLEqualsTuple(element[index], tup[2][index]) - #check text - if element.text or len(tup)>3: - if len(tup)<=3: - self.fail( "tuple %s has no text value (%r expected)"%(tup, - element.text)) - self.assertTextEquals(element.text, tup[3]) - #check tail - if element.tail or len(tup)>4: - if len(tup)<=4: - self.fail( "tuple %s has no tail value (%r expected)"%(tup, - element.tail)) - self.assertTextEquals(element.tail, tup[4]) - - def _difftext(self, lines1, lines2, junk=None, msg_prefix='Texts differ'): - junk = junk or (' ', '\t') - # result is a generator - result = difflib.ndiff(lines1, lines2, charjunk=lambda x: x in junk) - read = [] - for line in result: - read.append(line) - # lines that don't start with a ' ' are diff ones - if not line.startswith(' '): - self.fail('\n'.join(['%s\n'%msg_prefix]+read + list(result))) - - @deprecated('Non-standard. Please use assertMultiLineEqual instead.') - def assertTextEquals(self, text1, text2, junk=None, - msg_prefix='Text differ', striplines=False): - """compare two multiline strings (using difflib and splitlines()) - - :param text1: a Python BaseString - :param text2: a second Python Basestring - :param junk: List of Caracters - :param msg_prefix: String (message prefix) - :param striplines: Boolean to trigger line stripping before comparing - """ - msg = [] - if not isinstance(text1, string_types): - msg.append('text1 is not a string (%s)'%(type(text1))) - if not isinstance(text2, string_types): - msg.append('text2 is not a string (%s)'%(type(text2))) - if msg: - self.fail('\n'.join(msg)) - lines1 = text1.strip().splitlines(True) - lines2 = text2.strip().splitlines(True) - if striplines: - lines1 = [line.strip() for line in lines1] - lines2 = [line.strip() for line in lines2] - self._difftext(lines1, lines2, junk, msg_prefix) - assertTextEqual = assertTextEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertStreamEquals(self, stream1, stream2, junk=None, - msg_prefix='Stream differ'): - """compare two streams (using difflib and readlines())""" - # if stream2 is stream2, readlines() on stream1 will also read lines - # in stream2, so they'll appear different, although they're not - if stream1 is stream2: - return - # make sure we compare from the beginning of the stream - stream1.seek(0) - stream2.seek(0) - # compare - self._difftext(stream1.readlines(), stream2.readlines(), junk, - msg_prefix) - - assertStreamEqual = assertStreamEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertFileEquals(self, fname1, fname2, junk=(' ', '\t')): - """compares two files using difflib""" - self.assertStreamEqual(open(fname1), open(fname2), junk, - msg_prefix='Files differs\n-:%s\n+:%s\n'%(fname1, fname2)) - - assertFileEqual = assertFileEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertDirEquals(self, path_a, path_b): - """compares two files using difflib""" - assert osp.exists(path_a), "%s doesn't exists" % path_a - assert osp.exists(path_b), "%s doesn't exists" % path_b - - all_a = [ (ipath[len(path_a):].lstrip('/'), idirs, ifiles) - for ipath, idirs, ifiles in os.walk(path_a)] - all_a.sort(key=itemgetter(0)) - - all_b = [ (ipath[len(path_b):].lstrip('/'), idirs, ifiles) - for ipath, idirs, ifiles in os.walk(path_b)] - all_b.sort(key=itemgetter(0)) - - iter_a, iter_b = iter(all_a), iter(all_b) - partial_iter = True - ipath_a, idirs_a, ifiles_a = data_a = None, None, None - while True: - try: - ipath_a, idirs_a, ifiles_a = datas_a = next(iter_a) - partial_iter = False - ipath_b, idirs_b, ifiles_b = datas_b = next(iter_b) - partial_iter = True - - - self.assertTrue(ipath_a == ipath_b, - "unexpected %s in %s while looking %s from %s" % - (ipath_a, path_a, ipath_b, path_b)) - - - errors = {} - sdirs_a = set(idirs_a) - sdirs_b = set(idirs_b) - errors["unexpected directories"] = sdirs_a - sdirs_b - errors["missing directories"] = sdirs_b - sdirs_a - - sfiles_a = set(ifiles_a) - sfiles_b = set(ifiles_b) - errors["unexpected files"] = sfiles_a - sfiles_b - errors["missing files"] = sfiles_b - sfiles_a - - - msgs = [ "%s: %s"% (name, items) - for name, items in errors.items() if items] - - if msgs: - msgs.insert(0, "%s and %s differ :" % ( - osp.join(path_a, ipath_a), - osp.join(path_b, ipath_b), - )) - self.fail("\n".join(msgs)) - - for files in (ifiles_a, ifiles_b): - files.sort() - - for index, path in enumerate(ifiles_a): - self.assertFileEquals(osp.join(path_a, ipath_a, path), - osp.join(path_b, ipath_b, ifiles_b[index])) - - except StopIteration: - break - - assertDirEqual = assertDirEquals - - def assertIsInstance(self, obj, klass, msg=None, strict=False): - """check if an object is an instance of a class - - :param obj: the Python Object to be checked - :param klass: the target class - :param msg: a String for a custom message - :param strict: if True, check that the class of is ; - else check with 'isinstance' - """ - if strict: - warnings.warn('[API] Non-standard. Strict parameter has vanished', - DeprecationWarning, stacklevel=2) - if msg is None: - if strict: - msg = '%r is not of class %s but of %s' - else: - msg = '%r is not an instance of %s but of %s' - msg = msg % (obj, klass, type(obj)) - if strict: - self.assertTrue(obj.__class__ is klass, msg) - else: - self.assertTrue(isinstance(obj, klass), msg) - - @deprecated('Please use assertIsNone instead.') - def assertNone(self, obj, msg=None): - """assert obj is None - - :param obj: Python Object to be tested - """ - if msg is None: - msg = "reference to %r when None expected"%(obj,) - self.assertTrue( obj is None, msg ) - - @deprecated('Please use assertIsNotNone instead.') - def assertNotNone(self, obj, msg=None): - """assert obj is not None""" - if msg is None: - msg = "unexpected reference to None" - self.assertTrue( obj is not None, msg ) - - @deprecated('Non-standard. Please use assertAlmostEqual instead.') - def assertFloatAlmostEquals(self, obj, other, prec=1e-5, - relative=False, msg=None): - """compares if two floats have a distance smaller than expected - precision. - - :param obj: a Float - :param other: another Float to be comparted to - :param prec: a Float describing the precision - :param relative: boolean switching to relative/absolute precision - :param msg: a String for a custom message - """ - if msg is None: - msg = "%r != %r" % (obj, other) - if relative: - prec = prec*math.fabs(obj) - self.assertTrue(math.fabs(obj - other) < prec, msg) - - def failUnlessRaises(self, excClass, callableObj=None, *args, **kwargs): - """override default failUnlessRaises method to return the raised - exception instance. - - Fail unless an exception of class excClass is thrown - by callableObj when invoked with arguments args and keyword - arguments kwargs. If a different type of exception is - thrown, it will not be caught, and the test case will be - deemed to have suffered an error, exactly as for an - unexpected exception. - - CAUTION! There are subtle differences between Logilab and unittest2 - - exc is not returned in standard version - - context capabilities in standard version - - try/except/else construction (minor) - - :param excClass: the Exception to be raised - :param callableObj: a callable Object which should raise - :param args: a List of arguments for - :param kwargs: a List of keyword arguments for - """ - # XXX cube vcslib : test_branches_from_app - if callableObj is None: - _assert = super(TestCase, self).assertRaises - return _assert(excClass, callableObj, *args, **kwargs) - try: - callableObj(*args, **kwargs) - except excClass as exc: - class ProxyException: - def __init__(self, obj): - self._obj = obj - def __getattr__(self, attr): - warn_msg = ("This exception was retrieved with the old testlib way " - "`exc = self.assertRaises(Exc, callable)`, please use " - "the context manager instead'") - warnings.warn(warn_msg, DeprecationWarning, 2) - return self._obj.__getattribute__(attr) - return ProxyException(exc) - else: - if hasattr(excClass, '__name__'): - excName = excClass.__name__ - else: - excName = str(excClass) - raise self.failureException("%s not raised" % excName) - - assertRaises = failUnlessRaises - - if sys.version_info >= (3,2): - assertItemsEqual = unittest.TestCase.assertCountEqual - else: - assertCountEqual = unittest.TestCase.assertItemsEqual - if sys.version_info < (2,7): - def assertIsNotNone(self, value, *args, **kwargs): - self.assertNotEqual(None, value, *args, **kwargs) - -TestCase.assertItemsEqual = deprecated('assertItemsEqual is deprecated, use assertCountEqual')( - TestCase.assertItemsEqual) - -import doctest - -class SkippedSuite(unittest.TestSuite): - def test(self): - """just there to trigger test execution""" - self.skipped_test('doctest module has no DocTestSuite class') - - -class DocTestFinder(doctest.DocTestFinder): - - def __init__(self, *args, **kwargs): - self.skipped = kwargs.pop('skipped', ()) - doctest.DocTestFinder.__init__(self, *args, **kwargs) - - def _get_test(self, obj, name, module, globs, source_lines): - """override default _get_test method to be able to skip tests - according to skipped attribute's value - """ - if getattr(obj, '__name__', '') in self.skipped: - return None - return doctest.DocTestFinder._get_test(self, obj, name, module, - globs, source_lines) - - -class DocTest(TestCase): - """trigger module doctest - I don't know how to make unittest.main consider the DocTestSuite instance - without this hack - """ - skipped = () - def __call__(self, result=None, runcondition=None, options=None):\ - # pylint: disable=W0613 - try: - finder = DocTestFinder(skipped=self.skipped) - suite = doctest.DocTestSuite(self.module, test_finder=finder) - # XXX iirk - doctest.DocTestCase._TestCase__exc_info = sys.exc_info - except AttributeError: - suite = SkippedSuite() - # doctest may gork the builtins dictionnary - # This happen to the "_" entry used by gettext - old_builtins = builtins.__dict__.copy() - try: - return suite.run(result) - finally: - builtins.__dict__.clear() - builtins.__dict__.update(old_builtins) - run = __call__ - - def test(self): - """just there to trigger test execution""" - -MAILBOX = None - -class MockSMTP: - """fake smtplib.SMTP""" - - def __init__(self, host, port): - self.host = host - self.port = port - global MAILBOX - self.reveived = MAILBOX = [] - - def set_debuglevel(self, debuglevel): - """ignore debug level""" - - def sendmail(self, fromaddr, toaddres, body): - """push sent mail in the mailbox""" - self.reveived.append((fromaddr, toaddres, body)) - - def quit(self): - """ignore quit""" - - -class MockConfigParser(configparser.ConfigParser): - """fake ConfigParser.ConfigParser""" - - def __init__(self, options): - configparser.ConfigParser.__init__(self) - for section, pairs in options.iteritems(): - self.add_section(section) - for key, value in pairs.iteritems(): - self.set(section, key, value) - def write(self, _): - raise NotImplementedError() - - -class MockConnection: - """fake DB-API 2.0 connexion AND cursor (i.e. cursor() return self)""" - - def __init__(self, results): - self.received = [] - self.states = [] - self.results = results - - def cursor(self): - """Mock cursor method""" - return self - def execute(self, query, args=None): - """Mock execute method""" - self.received.append( (query, args) ) - def fetchone(self): - """Mock fetchone method""" - return self.results[0] - def fetchall(self): - """Mock fetchall method""" - return self.results - def commit(self): - """Mock commiy method""" - self.states.append( ('commit', len(self.received)) ) - def rollback(self): - """Mock rollback method""" - self.states.append( ('rollback', len(self.received)) ) - def close(self): - """Mock close method""" - pass - - -def mock_object(**params): - """creates an object using params to set attributes - >>> option = mock_object(verbose=False, index=range(5)) - >>> option.verbose - False - >>> option.index - [0, 1, 2, 3, 4] - """ - return type('Mock', (), params)() - - -def create_files(paths, chroot): - """Creates directories and files found in . - - :param paths: list of relative paths to files or directories - :param chroot: the root directory in which paths will be created - - >>> from os.path import isdir, isfile - >>> isdir('/tmp/a') - False - >>> create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], '/tmp') - >>> isdir('/tmp/a') - True - >>> isdir('/tmp/a/b/c') - True - >>> isfile('/tmp/a/b/c/d/e.py') - True - >>> isfile('/tmp/a/b/foo.py') - True - """ - dirs, files = set(), set() - for path in paths: - path = osp.join(chroot, path) - filename = osp.basename(path) - # path is a directory path - if filename == '': - dirs.add(path) - # path is a filename path - else: - dirs.add(osp.dirname(path)) - files.add(path) - for dirpath in dirs: - if not osp.isdir(dirpath): - os.makedirs(dirpath) - for filepath in files: - open(filepath, 'w').close() - - -class AttrObject: # XXX cf mock_object - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - -def tag(*args, **kwargs): - """descriptor adding tag to a function""" - def desc(func): - assert not hasattr(func, 'tags') - func.tags = Tags(*args, **kwargs) - return func - return desc - -def require_version(version): - """ Compare version of python interpreter to the given one. Skip the test - if older. - """ - def check_require_version(f): - version_elements = version.split('.') - try: - compare = tuple([int(v) for v in version_elements]) - except ValueError: - raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version) - current = sys.version_info[:3] - if current < compare: - def new_f(self, *args, **kwargs): - self.skipTest('Need at least %s version of python. Current version is %s.' % (version, '.'.join([str(element) for element in current]))) - new_f.__name__ = f.__name__ - return new_f - else: - return f - return check_require_version - -def require_module(module): - """ Check if the given module is loaded. Skip the test if not. - """ - def check_require_module(f): - try: - __import__(module) - return f - except ImportError: - def new_f(self, *args, **kwargs): - self.skipTest('%s can not be imported.' % module) - new_f.__name__ = f.__name__ - return new_f - return check_require_module - diff --git a/pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth b/pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth deleted file mode 100644 index d268b884..00000000 --- a/pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('logilab',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and sys.modules.setdefault('logilab', types.ModuleType('logilab'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/METADATA b/pymode/libs/logilab_common-1.0.2.dist-info/METADATA deleted file mode 100644 index 9a00a498..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/METADATA +++ /dev/null @@ -1,169 +0,0 @@ -Metadata-Version: 2.0 -Name: logilab-common -Version: 1.0.2 -Summary: collection of low-level Python packages and modules used by Logilab projects -Home-page: http://www.logilab.org/project/logilab-common -Author: Logilab -Author-email: contact@logilab.fr -License: LGPL -Platform: UNKNOWN -Classifier: Topic :: Utilities -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Requires-Dist: setuptools -Requires-Dist: six (>=1.4.0) - -Logilab's common library -======================== - -What's this ? -------------- - -This package contains some modules used by different Logilab projects. - -It is released under the GNU Lesser General Public License. - -There is no documentation available yet but the source code should be clean and -well documented. - -Designed to ease: - -* handling command line options and configuration files -* writing interactive command line tools -* manipulation of files and character strings -* manipulation of common structures such as graph, tree, and pattern such as visitor -* generating text and HTML reports -* more... - - -Installation ------------- - -Extract the tarball, jump into the created directory and run :: - - python setup.py install - -For installation options, see :: - - python setup.py install --help - - -Provided modules ----------------- - -Here is a brief description of the available modules. - -Modules providing high-level features -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `cache`, a cache implementation with a least recently used algorithm. - -* `changelog`, a tiny library to manipulate our simplified ChangeLog file format. - -* `clcommands`, high-level classes to define command line programs handling - different subcommands. It is based on `configuration` to get easy command line - / configuration file handling. - -* `configuration`, some classes to handle unified configuration from both - command line (using optparse) and configuration file (using ConfigParser). - -* `proc`, interface to Linux /proc. - -* `umessage`, unicode email support. - -* `ureports`, micro-reports, a way to create simple reports using python objects - without care of the final formatting. ReST and html formatters are provided. - - -Modules providing low-level functions and structures -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `compat`, provides a transparent compatibility layer between different python - versions. - -* `date`, a set of date manipulation functions. - -* `daemon`, a daemon function and mix-in class to properly start an Unix daemon - process. - -* `decorators`, function decorators such as cached, timed... - -* `deprecation`, decorator, metaclass & all to mark functions / classes as - deprecated or moved - -* `fileutils`, some file / file path manipulation utilities. - -* `graph`, graph manipulations functions such as cycle detection, bases for dot - file generation. - -* `modutils`, python module manipulation functions. - -* `shellutils`, some powerful shell like functions to replace shell scripts with - python scripts. - -* `tasksqueue`, a prioritized tasks queue implementation. - -* `textutils`, some text manipulation functions (ansi colorization, line wrapping, - rest support...). - -* `tree`, base class to represent tree structure, and some others to make it - works with the visitor implementation (see below). - -* `visitor`, a generic visitor pattern implementation. - - -Modules extending some standard modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `debugger`, `pdb` customization. - -* `logging_ext`, extensions to `logging` module such as a colorized formatter - and an easier initialization function. - -* `optik_ext`, defines some new option types (regexp, csv, color, date, etc.) - for `optik` / `optparse` - - -Modules extending some external modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `sphinx_ext`, Sphinx_ plugin defining a `autodocstring` directive. - -* `vcgutils` , utilities functions to generate file readable with Georg Sander's - vcg tool (Visualization of Compiler Graphs). - - -To be deprecated modules -~~~~~~~~~~~~~~~~~~~~~~~~ - -Those `logilab.common` modules will much probably be deprecated in future -versions: - -* `testlib`: use `unittest2`_ instead -* `pytest`: use `discover`_ instead -* `interface`: use `zope.interface`_ if you really want this -* `table`, `xmlutils`: is that used? -* `sphinxutils`: we won't go that way imo (i == syt) - - -Comments, support, bug reports ------------------------------- - -Project page https://www.logilab.org/project/logilab-common - -Use the python-projects@lists.logilab.org mailing list. - -You can subscribe to this mailing list at -https://lists.logilab.org/mailman/listinfo/python-projects - -Archives are available at -https://lists.logilab.org/pipermail/python-projects/ - - -.. _Sphinx: http://sphinx.pocoo.org/ -.. _`unittest2`: http://pypi.python.org/pypi/unittest2 -.. _`discover`: http://pypi.python.org/pypi/discover -.. _`zope.interface`: http://pypi.python.org/pypi/zope.interface - - diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/RECORD b/pymode/libs/logilab_common-1.0.2.dist-info/RECORD deleted file mode 100644 index e6e4730a..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/RECORD +++ /dev/null @@ -1,87 +0,0 @@ -logilab_common-1.0.2-py2.7-nspkg.pth,sha256=ZY-Jf8tK2WQu_mjLvZuFpvpX9uwdpX3yDS1AuRncCZA,308 -logilab/common/__init__.py,sha256=UiR9rv7f7WsAnIHsxa3UApVCJGTzXbZoC-c4EQJpcvg,5390 -logilab/common/cache.py,sha256=wmY87WSoyERDhAlfIKKUipYavlZPpm3sGAQMpzbDHTM,3621 -logilab/common/changelog.py,sha256=Ea_4j22rWJJ33VSCj4Lz0pBGP0wP7LMP2Zo4DR7iZIo,8075 -logilab/common/clcommands.py,sha256=abMNAsB6ADT7Ns5MsxNtAMOlTQGJLCMO9MUkNYdsVG8,11237 -logilab/common/compat.py,sha256=rMGytWS1DCo35MdKUocU1LfLbZA0RyK79Gyu7lvd6Rg,2593 -logilab/common/configuration.py,sha256=s4rg7Qa1_4bpWlTg-bEaHYUcrgvuoDt75ZJgRnlFsME,42160 -logilab/common/daemon.py,sha256=Eqwo_oKjrHtS9SLrtSfeghRTCjqvveGho43s7vMkd7A,3337 -logilab/common/date.py,sha256=nnUN-4onEaWSR8r4PvtmJyn5ukfFzasjEcOGzEdrvqQ,11230 -logilab/common/debugger.py,sha256=Bw2-yI9KrvSgPLDksda4F8nuK_DvxnSCS-ymPSVc778,7094 -logilab/common/decorators.py,sha256=4DD3iNgEQPVz5hPp-SbbgD-ZObXhaeazGqKleyHdXaw,8868 -logilab/common/deprecation.py,sha256=MAxc_Ds9H_j6C7d4VQqMQPB1j-Ib8vy7iBWoQa8aRHs,7417 -logilab/common/fileutils.py,sha256=kCk_8odmAKnYPHPhUruuV-6og8N9kT8fplV-pvwwd4A,12738 -logilab/common/graph.py,sha256=GTSN-kP40EHjnHXk1vxO-56rEszo-esu1S3hf-SOddw,10247 -logilab/common/interface.py,sha256=dXl6kiuXSpefxauu7J6CUv0soe09wjT4_vXbeWQFgJ8,2593 -logilab/common/logging_ext.py,sha256=Yi8k2fGqr_tt-YApT1JjroNpXETxfj84HKmgTgO22Nw,6975 -logilab/common/modutils.py,sha256=w2LVy_vzhGoyBRrKivx0hqx8n326KrtTUezelEwDAcc,24002 -logilab/common/optik_ext.py,sha256=_aZgWKTKCC8_vYIpstNCOk8wewwZ4jfrpvXWrmPzn5Y,13451 -logilab/common/optparser.py,sha256=QgDoAyVoRy7U1fG9BSZ0O7LQsyNayo1HAelZaKlb4kY,3386 -logilab/common/proc.py,sha256=RGMlPuc11FfrIsqzqNFO3Q6buqt8dvMwXfXKXfwAHks,9352 -logilab/common/pytest.py,sha256=ac7hVpAb06TstSjPV586h1wW21Y__XH5bjrwX55dDOE,46736 -logilab/common/registry.py,sha256=0qIJfNJiqM1HkI-twKHfXiTPU5HKSGRrS-P0Dsj56qw,41550 -logilab/common/shellutils.py,sha256=ZFZ19eX0TCcDrsbOWiy7sr1oqnhQsLixv9n8HakcJiM,14363 -logilab/common/sphinx_ext.py,sha256=pbKN0ObMDY_jy9ehP_7NOKMo40LbQLjf0xntmxHnGr8,3329 -logilab/common/sphinxutils.py,sha256=piY1R04GNR-i1mIb4PRhbGbmbDZPhDsn1FBAiA_Bbrg,4444 -logilab/common/table.py,sha256=5NEx4Ju-jk2CV6W-jxTpOoYArt2BlRpaTZZUBGwu1kg,31408 -logilab/common/tasksqueue.py,sha256=wFE0C0FiuHGBoCnvU-_Kno1eM_Em6yYxYvND6emRN34,2987 -logilab/common/testlib.py,sha256=2Ra9OPs5QpQv7hoZod3M2yYCUdtqSaN3LAvVyiQyA1k,50506 -logilab/common/textutils.py,sha256=TgPGqkN3JsJuR7VxnkoWaOWfkwHiVNB9gpId_3S2xO4,17277 -logilab/common/tree.py,sha256=Y-sa_pfI17cCb-vkyJMaBW3XKVNrreexBgBMPpQJDy0,10606 -logilab/common/umessage.py,sha256=2BuxspHkPEXhlf-XVDye25Mt0RUELneay-K1KNLcS9c,6551 -logilab/common/urllib2ext.py,sha256=FOpxVrbAPtY_6ssq3Qui3zxzckAqLJe9kGkp8tLR0Ic,3416 -logilab/common/vcgutils.py,sha256=tNfi6jxZ4xdUvrjw1cKOodecRlcD0U3MQvTb5HrY5fE,7673 -logilab/common/visitor.py,sha256=5Oc9Y88Kx4wiZ6JAFYFeXwKrMS8jNph9ENVWG3oim1E,3444 -logilab/common/xmlutils.py,sha256=2e4FM-X1PLKBaTG6etLHsAIrtZQiDEA9U7WqM3KjNks,2273 -logilab/common/ureports/__init__.py,sha256=b3_8f4mAm6T3O_-klutleWZ99XjlR-AELfuLEyCbzQ8,6113 -logilab/common/ureports/docbook_writer.py,sha256=KSkIk0W4C4E6DR-Ul_Y9jgnd4_tgVVu15LnU8p2RoeM,5706 -logilab/common/ureports/html_writer.py,sha256=Ee_x9rXjx2NZp290e-0C7nu7VYuKpkCsrl79m4HLI5g,4956 -logilab/common/ureports/nodes.py,sha256=t2NQiL6LQV94D8ugitklVnZRVbz6kP5QkUrl8zGsmMQ,5838 -logilab/common/ureports/text_writer.py,sha256=cMBHbA36_1NrKKnx5LBKczGQmBRg4aObkpr1d581ORU,5212 -../../bin/pytest,sha256=vkYcOC21mDzGBrz4-ajilr8TGxa9tRabxQhyYyXeEDE,124 -logilab_common-1.0.2.dist-info/DESCRIPTION.rst,sha256=bMLyPRBRS-tSzW5zhchxcLlPbYHRv0XEMqs6Oln2z5U,4426 -logilab_common-1.0.2.dist-info/METADATA,sha256=3_iFYhN84fXSjkdjzHv3grHBY2xIZVLSkmuBeTSnLQE,4934 -logilab_common-1.0.2.dist-info/metadata.json,sha256=dTwpZUieC7dZFkKiNdtgVExm2w1B44k4ZDSaCP3ASXo,742 -logilab_common-1.0.2.dist-info/namespace_packages.txt,sha256=xXemaIbd-285ANf3yiCDkMHRTZSuLvlqL_MTLEJKMuk,8 -logilab_common-1.0.2.dist-info/RECORD,, -logilab_common-1.0.2.dist-info/top_level.txt,sha256=xXemaIbd-285ANf3yiCDkMHRTZSuLvlqL_MTLEJKMuk,8 -logilab_common-1.0.2.dist-info/WHEEL,sha256=54bVun1KfEBTJ68SHUmbxNPj80VxlQ0sHi4gZdGZXEY,92 -logilab/common/logging_ext.pyc,, -logilab/common/date.pyc,, -logilab/common/modutils.pyc,, -logilab/common/ureports/__init__.pyc,, -logilab/common/sphinxutils.pyc,, -logilab/common/ureports/text_writer.pyc,, -logilab/common/optik_ext.pyc,, -logilab/common/visitor.pyc,, -logilab/common/debugger.pyc,, -logilab/common/compat.pyc,, -logilab/common/decorators.pyc,, -logilab/common/textutils.pyc,, -logilab/common/ureports/docbook_writer.pyc,, -logilab/common/shellutils.pyc,, -logilab/common/changelog.pyc,, -logilab/common/interface.pyc,, -logilab/common/ureports/nodes.pyc,, -logilab/common/pytest.pyc,, -logilab/common/sphinx_ext.pyc,, -logilab/common/xmlutils.pyc,, -logilab/common/__init__.pyc,, -logilab/common/tree.pyc,, -logilab/common/umessage.pyc,, -logilab/common/registry.pyc,, -logilab/common/proc.pyc,, -logilab/common/urllib2ext.pyc,, -logilab/common/testlib.pyc,, -logilab/common/clcommands.pyc,, -logilab/common/ureports/html_writer.pyc,, -logilab/common/vcgutils.pyc,, -logilab/common/daemon.pyc,, -logilab/common/table.pyc,, -logilab/common/optparser.pyc,, -logilab/common/deprecation.pyc,, -logilab/common/tasksqueue.pyc,, -logilab/common/fileutils.pyc,, -logilab/common/graph.pyc,, -logilab/common/cache.pyc,, -logilab/common/configuration.pyc,, diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL b/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL deleted file mode 100644 index 45a0cd88..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) -Root-Is-Purelib: true -Tag: py2-none-any - diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/metadata.json b/pymode/libs/logilab_common-1.0.2.dist-info/metadata.json deleted file mode 100644 index 54212666..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"license": "LGPL", "name": "logilab-common", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "test_requires": [{"requires": ["pytz"]}], "summary": "collection of low-level Python packages and modules used by Logilab projects", "run_requires": [{"requires": ["setuptools", "six (>=1.4.0)"]}], "version": "1.0.2", "extensions": {"python.details": {"project_urls": {"Home": "http://www.logilab.org/project/logilab-common"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "contact@logilab.fr", "name": "Logilab"}]}}, "classifiers": ["Topic :: Utilities", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3"], "extras": []} \ No newline at end of file diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt b/pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt deleted file mode 100644 index 3ac267a9..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt +++ /dev/null @@ -1 +0,0 @@ -logilab diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt b/pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt deleted file mode 100644 index 3ac267a9..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -logilab diff --git a/pymode/libs/pylint b/pymode/libs/pylint new file mode 120000 index 00000000..0d144c06 --- /dev/null +++ b/pymode/libs/pylint @@ -0,0 +1 @@ +../../submodules/pylint/pylint \ No newline at end of file diff --git a/pymode/libs/pylint/__init__.py b/pymode/libs/pylint/__init__.py deleted file mode 100644 index ba882ea6..00000000 --- a/pymode/libs/pylint/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -import sys - -from .__pkginfo__ import version as __version__ - -def run_pylint(): - """run pylint""" - from pylint.lint import Run - Run(sys.argv[1:]) - - -def run_epylint(): - """run pylint""" - from pylint.epylint import Run - Run() - -def run_pyreverse(): - """run pyreverse""" - from pylint.pyreverse.main import Run - Run(sys.argv[1:]) - -def run_symilar(): - """run symilar""" - from pylint.checkers.similar import Run - Run(sys.argv[1:]) diff --git a/pymode/libs/pylint/__main__.py b/pymode/libs/pylint/__main__.py deleted file mode 100644 index f1ecf1b9..00000000 --- a/pymode/libs/pylint/__main__.py +++ /dev/null @@ -1,7 +0,0 @@ - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -#!/usr/bin/env python -import pylint -pylint.run_pylint() diff --git a/pymode/libs/pylint/__pkginfo__.py b/pymode/libs/pylint/__pkginfo__.py deleted file mode 100644 index 099da4ba..00000000 --- a/pymode/libs/pylint/__pkginfo__.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -# pylint: disable=W0622,C0103 -"""pylint packaging information""" - -from __future__ import absolute_import - -from os.path import join -from sys import version_info as py_version - -from pkg_resources import parse_version -from setuptools import __version__ as setuptools_version - -modname = distname = 'pylint' - -numversion = (1, 7, 2) -version = '.'.join([str(num) for num in numversion]) - -install_requires = [ - 'astroid>=1.5.1', - 'six', - 'isort >= 4.2.5', - 'mccabe', -] - -dependency_links = [] - -extras_require = {} -extras_require[':sys_platform=="win32"'] = ['colorama'] - - -def has_environment_marker_range_operators_support(): - """Code extracted from 'pytest/setup.py' - https://github.com/pytest-dev/pytest/blob/7538680c/setup.py#L31 - The first known release to support environment marker with range operators - it is 17.1, see: https://setuptools.readthedocs.io/en/latest/history.html#id113 - """ - return parse_version(setuptools_version) >= parse_version('17.1') - - -if has_environment_marker_range_operators_support(): - extras_require[':python_version=="2.7"'] = ['configparser', 'backports.functools_lru_cache'] - extras_require[':python_version<"3.4"'] = ['singledispatch'] -else: - if (py_version.major, py_version.minor) == (2, 7): - install_requires.extend(['configparser', 'backports.functools_lru_cache']) - if py_version < (3, 4): - install_requires.extend(['singledispatch']) - - -license = 'GPL' -description = "python code static checker" -web = 'https://github.com/PyCQA/pylint' -mailinglist = "mailto:code-quality@python.org" -author = 'Python Code Quality Authority' -author_email = 'code-quality@python.org' - -classifiers = ['Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - 'Topic :: Software Development :: Debuggers', - 'Topic :: Software Development :: Quality Assurance', - 'Topic :: Software Development :: Testing' - ] - - -long_desc = """\ - Pylint is a Python source code analyzer which looks for programming - errors, helps enforcing a coding standard and sniffs for some code - smells (as defined in Martin Fowler's Refactoring book) - . - Pylint can be seen as another PyChecker since nearly all tests you - can do with PyChecker can also be done with Pylint. However, Pylint - offers some more features, like checking length of lines of code, - checking if variable names are well-formed according to your coding - standard, or checking if declared interfaces are truly implemented, - and much more. - . - Additionally, it is possible to write plugins to add your own checks. - . - Pylint is shipped with "pyreverse" (UML diagram generator) - and "symilar" (an independent similarities checker).""" - -scripts = [join('bin', filename) - for filename in ('pylint', "symilar", "epylint", - "pyreverse")] - -include_dirs = [join('pylint', 'test')] diff --git a/pymode/libs/pylint/checkers/__init__.py b/pymode/libs/pylint/checkers/__init__.py deleted file mode 100644 index 78921870..00000000 --- a/pymode/libs/pylint/checkers/__init__.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""utilities methods and classes for checkers - -Base id of standard checkers (used in msg and report ids): -01: base -02: classes -03: format -04: import -05: misc -06: variables -07: exceptions -08: similar -09: design_analysis -10: newstyle -11: typecheck -12: logging -13: string_format -14: string_constant -15: stdlib -16: python3 -17: refactoring -18-50: not yet used: reserved for future internal checkers. -51-99: perhaps used: reserved for external checkers - -The raw_metrics checker has no number associated since it doesn't emit any -messages nor reports. XXX not true, emit a 07 report ! - -""" - -import sys -import tokenize -import warnings - -from pylint.config import OptionsProviderMixIn -from pylint.reporters import diff_string -from pylint.utils import register_plugins -from pylint.interfaces import UNDEFINED - - -def table_lines_from_stats(stats, old_stats, columns): - """get values listed in from and , - and return a formated list of values, designed to be given to a - ureport.Table object - """ - lines = [] - for m_type in columns: - new = stats[m_type] - format = str # pylint: disable=redefined-builtin - if isinstance(new, float): - format = lambda num: '%.3f' % num - old = old_stats.get(m_type) - if old is not None: - diff_str = diff_string(old, new) - old = format(old) - else: - old, diff_str = 'NC', 'NC' - lines += (m_type.replace('_', ' '), format(new), old, diff_str) - return lines - - -class BaseChecker(OptionsProviderMixIn): - """base class for checkers""" - # checker name (you may reuse an existing one) - name = None - # options level (0 will be displaying in --help, 1 in --long-help) - level = 1 - # ordered list of options to control the ckecker behaviour - options = () - # messages issued by this checker - msgs = {} - # reports issued by this checker - reports = () - # mark this checker as enabled or not. - enabled = True - - def __init__(self, linter=None): - """checker instances should have the linter as argument - - linter is an object implementing ILinter - """ - self.name = self.name.lower() - OptionsProviderMixIn.__init__(self) - self.linter = linter - - def add_message(self, msg_id, line=None, node=None, args=None, confidence=UNDEFINED): - """add a message of a given type""" - self.linter.add_message(msg_id, line, node, args, confidence) - - # dummy methods implementing the IChecker interface - - def open(self): - """called before visiting project (i.e set of modules)""" - - def close(self): - """called after visiting project (i.e set of modules)""" - - -class BaseTokenChecker(BaseChecker): - """Base class for checkers that want to have access to the token stream.""" - - def process_tokens(self, tokens): - """Should be overridden by subclasses.""" - raise NotImplementedError() - - -def initialize(linter): - """initialize linter with checkers in this package """ - register_plugins(linter, __path__[0]) - -__all__ = ('BaseChecker', 'initialize') diff --git a/pymode/libs/pylint/checkers/async.py b/pymode/libs/pylint/checkers/async.py deleted file mode 100644 index 6d759b24..00000000 --- a/pymode/libs/pylint/checkers/async.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Checker for anything related to the async protocol (PEP 492).""" - -import sys - -import astroid -from astroid import exceptions - -from pylint import checkers -from pylint.checkers import utils as checker_utils -from pylint import interfaces -from pylint import utils - - -class AsyncChecker(checkers.BaseChecker): - __implements__ = interfaces.IAstroidChecker - name = 'async' - msgs = { - 'E1700': ('Yield inside async function', - 'yield-inside-async-function', - 'Used when an `yield` or `yield from` statement is ' - 'found inside an async function.', - {'minversion': (3, 5)}), - 'E1701': ("Async context manager '%s' doesn't implement __aenter__ and __aexit__.", - 'not-async-context-manager', - 'Used when an async context manager is used with an object ' - 'that does not implement the async context management protocol.', - {'minversion': (3, 5)}), - } - - def open(self): - self._ignore_mixin_members = utils.get_global_option(self, 'ignore-mixin-members') - - @checker_utils.check_messages('yield-inside-async-function') - def visit_asyncfunctiondef(self, node): - for child in node.nodes_of_class(astroid.Yield): - if child.scope() is node and (sys.version_info[:2] == (3, 5) or - isinstance(child, astroid.YieldFrom)): - self.add_message('yield-inside-async-function', node=child) - - @checker_utils.check_messages('not-async-context-manager') - def visit_asyncwith(self, node): - for ctx_mgr, _ in node.items: - infered = checker_utils.safe_infer(ctx_mgr) - if infered is None or infered is astroid.YES: - continue - - if isinstance(infered, astroid.Instance): - try: - infered.getattr('__aenter__') - infered.getattr('__aexit__') - except exceptions.NotFoundError: - if isinstance(infered, astroid.Instance): - # If we do not know the bases of this class, - # just skip it. - if not checker_utils.has_known_bases(infered): - continue - # Just ignore mixin classes. - if self._ignore_mixin_members: - if infered.name[-5:].lower() == 'mixin': - continue - else: - continue - - self.add_message('not-async-context-manager', - node=node, args=(infered.name, )) - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(AsyncChecker(linter)) diff --git a/pymode/libs/pylint/checkers/base.py b/pymode/libs/pylint/checkers/base.py deleted file mode 100644 index a0d8c431..00000000 --- a/pymode/libs/pylint/checkers/base.py +++ /dev/null @@ -1,1660 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2012-2014 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2014 Brett Cannon -# Copyright (c) 2015 Radu Ciorba -# Copyright (c) 2015 Michael Kefeder -# Copyright (c) 2015 Dmitry Pribysh -# Copyright (c) 2015 Stephane Wirtel -# Copyright (c) 2015 Nick Bastin -# Copyright (c) 2016 Alex Jurkiewicz -# Copyright (c) 2016 Yannack -# Copyright (c) 2016 Laura Médioni -# Copyright (c) 2016 Ashley Whetter - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""basic checker for Python code""" - -import collections -import itertools -import sys -import re - -import six -from six.moves import zip # pylint: disable=redefined-builtin - -import astroid -import astroid.bases -import astroid.scoped_nodes - -from pylint import checkers -from pylint import exceptions -from pylint import interfaces -from pylint.checkers import utils -from pylint import reporters -from pylint.reporters.ureports import nodes as reporter_nodes - - -# regex for class/function/variable/constant name -CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$') -MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$') -CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$') -COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$') -DEFAULT_NAME_RGX = re.compile('(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$') -CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$') -# do not require a doc string on private/system methods -NO_REQUIRED_DOC_RGX = re.compile('^_') -REVERSED_PROTOCOL_METHOD = '__reversed__' -SEQUENCE_PROTOCOL_METHODS = ('__getitem__', '__len__') -REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, - (REVERSED_PROTOCOL_METHOD, )) -TYPECHECK_COMPARISON_OPERATORS = frozenset(('is', 'is not', '==', - '!=', 'in', 'not in')) -LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set) -UNITTEST_CASE = 'unittest.case' -BUILTINS = six.moves.builtins.__name__ -TYPE_QNAME = "%s.type" % BUILTINS -PY33 = sys.version_info >= (3, 3) -PY3K = sys.version_info >= (3, 0) -PY35 = sys.version_info >= (3, 5) - -# Name categories that are always consistent with all naming conventions. -EXEMPT_NAME_CATEGORIES = {'exempt', 'ignore'} - -# A mapping from builtin-qname -> symbol, to be used when generating messages -# about dangerous default values as arguments -DEFAULT_ARGUMENT_SYMBOLS = dict( - zip(['.'.join([BUILTINS, x]) for x in ('set', 'dict', 'list')], - ['set()', '{}', '[]']) -) -REVERSED_COMPS = {'<': '>', '<=': '>=', '>': '<', '>=': '<='} - - -def _redefines_import(node): - """ Detect that the given node (AssName) is inside an - exception handler and redefines an import from the tryexcept body. - Returns True if the node redefines an import, False otherwise. - """ - current = node - while current and not isinstance(current.parent, astroid.ExceptHandler): - current = current.parent - if not current or not utils.error_of_type(current.parent, ImportError): - return False - try_block = current.parent.parent - for import_node in try_block.nodes_of_class((astroid.ImportFrom, astroid.Import)): - for name, alias in import_node.names: - if alias: - if alias == node.name: - return True - elif name == node.name: - return True - return False - - -def in_loop(node): - """return True if the node is inside a kind of for loop""" - parent = node.parent - while parent is not None: - if isinstance(parent, (astroid.For, astroid.ListComp, astroid.SetComp, - astroid.DictComp, astroid.GeneratorExp)): - return True - parent = parent.parent - return False - - -def in_nested_list(nested_list, obj): - """return true if the object is an element of or of a nested - list - """ - for elmt in nested_list: - if isinstance(elmt, (list, tuple)): - if in_nested_list(elmt, obj): - return True - elif elmt == obj: - return True - return False - - -def _loop_exits_early(loop): - """Returns true if a loop has a break statement in its body.""" - loop_nodes = (astroid.For, astroid.While) - # Loop over body explicitly to avoid matching break statements - # in orelse. - for child in loop.body: - if isinstance(child, loop_nodes): - # break statement may be in orelse of child loop. - # pylint: disable=superfluous-parens - for orelse in (child.orelse or ()): - for _ in orelse.nodes_of_class(astroid.Break, skip_klass=loop_nodes): - return True - continue - for _ in child.nodes_of_class(astroid.Break, skip_klass=loop_nodes): - return True - return False - - -def _is_multi_naming_match(match, node_type, confidence): - return (match is not None and - match.lastgroup is not None and - match.lastgroup not in EXEMPT_NAME_CATEGORIES - and (node_type != 'method' or confidence != interfaces.INFERENCE_FAILURE)) - - -if sys.version_info < (3, 0): - BUILTIN_PROPERTY = '__builtin__.property' -else: - BUILTIN_PROPERTY = 'builtins.property' - - -def _get_properties(config): - """Returns a tuple of property classes and names. - - Property classes are fully qualified, such as 'abc.abstractproperty' and - property names are the actual names, such as 'abstract_property'. - """ - property_classes = set((BUILTIN_PROPERTY,)) - property_names = set() # Not returning 'property', it has its own check. - if config is not None: - property_classes.update(config.property_classes) - property_names.update((prop.rsplit('.', 1)[-1] - for prop in config.property_classes)) - return property_classes, property_names - - -def _determine_function_name_type(node, config=None): - """Determine the name type whose regex the a function's name should match. - - :param node: A function node. - :type node: astroid.node_classes.NodeNG - :param config: Configuration from which to pull additional property classes. - :type config: :class:`optparse.Values` - - :returns: One of ('function', 'method', 'attr') - :rtype: str - """ - property_classes, property_names = _get_properties(config) - if not node.is_method(): - return 'function' - if node.decorators: - decorators = node.decorators.nodes - else: - decorators = [] - for decorator in decorators: - # If the function is a property (decorated with @property - # or @abc.abstractproperty), the name type is 'attr'. - if (isinstance(decorator, astroid.Name) or - (isinstance(decorator, astroid.Attribute) and - decorator.attrname in property_names)): - infered = utils.safe_infer(decorator) - if infered and infered.qname() in property_classes: - return 'attr' - # If the function is decorated using the prop_method.{setter,getter} - # form, treat it like an attribute as well. - elif (isinstance(decorator, astroid.Attribute) and - decorator.attrname in ('setter', 'deleter')): - return 'attr' - return 'method' - - -def _has_abstract_methods(node): - """ - Determine if the given `node` has abstract methods. - - The methods should be made abstract by decorating them - with `abc` decorators. - """ - return len(utils.unimplemented_abstract_methods(node)) > 0 - - -def report_by_type_stats(sect, stats, old_stats): - """make a report of - - * percentage of different types documented - * percentage of different types with a bad name - """ - # percentage of different types documented and/or with a bad name - nice_stats = {} - for node_type in ('module', 'class', 'method', 'function'): - try: - total = stats[node_type] - except KeyError: - raise exceptions.EmptyReportError() - nice_stats[node_type] = {} - if total != 0: - try: - documented = total - stats['undocumented_'+node_type] - percent = (documented * 100.) / total - nice_stats[node_type]['percent_documented'] = '%.2f' % percent - except KeyError: - nice_stats[node_type]['percent_documented'] = 'NC' - try: - percent = (stats['badname_'+node_type] * 100.) / total - nice_stats[node_type]['percent_badname'] = '%.2f' % percent - except KeyError: - nice_stats[node_type]['percent_badname'] = 'NC' - lines = ('type', 'number', 'old number', 'difference', - '%documented', '%badname') - for node_type in ('module', 'class', 'method', 'function'): - new = stats[node_type] - old = old_stats.get(node_type, None) - if old is not None: - diff_str = reporters.diff_string(old, new) - else: - old, diff_str = 'NC', 'NC' - lines += (node_type, str(new), str(old), diff_str, - nice_stats[node_type].get('percent_documented', '0'), - nice_stats[node_type].get('percent_badname', '0')) - sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1)) - - -def redefined_by_decorator(node): - """return True if the object is a method redefined via decorator. - - For example: - @property - def x(self): return self._x - @x.setter - def x(self, value): self._x = value - """ - if node.decorators: - for decorator in node.decorators.nodes: - if (isinstance(decorator, astroid.Attribute) and - getattr(decorator.expr, 'name', None) == node.name): - return True - return False - - -class _BasicChecker(checkers.BaseChecker): - __implements__ = interfaces.IAstroidChecker - name = 'basic' - - -class BasicErrorChecker(_BasicChecker): - msgs = { - 'E0100': ('__init__ method is a generator', - 'init-is-generator', - 'Used when the special class method __init__ is turned into a ' - 'generator by a yield in its body.'), - 'E0101': ('Explicit return in __init__', - 'return-in-init', - 'Used when the special class method __init__ has an explicit ' - 'return value.'), - 'E0102': ('%s already defined line %s', - 'function-redefined', - 'Used when a function / class / method is redefined.'), - 'E0103': ('%r not properly in loop', - 'not-in-loop', - 'Used when break or continue keywords are used outside a loop.'), - 'E0104': ('Return outside function', - 'return-outside-function', - 'Used when a "return" statement is found outside a function or ' - 'method.'), - 'E0105': ('Yield outside function', - 'yield-outside-function', - 'Used when a "yield" statement is found outside a function or ' - 'method.'), - 'E0106': ('Return with argument inside generator', - 'return-arg-in-generator', - 'Used when a "return" statement with an argument is found ' - 'outside in a generator function or method (e.g. with some ' - '"yield" statements).', - {'maxversion': (3, 3)}), - 'E0107': ("Use of the non-existent %s operator", - 'nonexistent-operator', - "Used when you attempt to use the C-style pre-increment or" - "pre-decrement operator -- and ++, which doesn't exist in Python."), - 'E0108': ('Duplicate argument name %s in function definition', - 'duplicate-argument-name', - 'Duplicate argument names in function definitions are syntax' - ' errors.'), - 'E0110': ('Abstract class %r with abstract methods instantiated', - 'abstract-class-instantiated', - 'Used when an abstract class with `abc.ABCMeta` as metaclass ' - 'has abstract methods and is instantiated.'), - 'W0120': ('Else clause on loop without a break statement', - 'useless-else-on-loop', - 'Loops should only have an else clause if they can exit early ' - 'with a break statement, otherwise the statements under else ' - 'should be on the same scope as the loop itself.'), - 'E0112': ('More than one starred expression in assignment', - 'too-many-star-expressions', - 'Emitted when there are more than one starred ' - 'expressions (`*x`) in an assignment. This is a SyntaxError.', - {'minversion': (3, 0)}), - 'E0113': ('Starred assignment target must be in a list or tuple', - 'invalid-star-assignment-target', - 'Emitted when a star expression is used as a starred ' - 'assignment target.', - {'minversion': (3, 0)}), - 'E0114': ('Can use starred expression only in assignment target', - 'star-needs-assignment-target', - 'Emitted when a star expression is not used in an ' - 'assignment target.', - {'minversion': (3, 0)}), - 'E0115': ('Name %r is nonlocal and global', - 'nonlocal-and-global', - 'Emitted when a name is both nonlocal and global.', - {'minversion': (3, 0)}), - 'E0116': ("'continue' not supported inside 'finally' clause", - 'continue-in-finally', - 'Emitted when the `continue` keyword is found ' - 'inside a finally clause, which is a SyntaxError.'), - 'E0117': ("nonlocal name %s found without binding", - 'nonlocal-without-binding', - 'Emitted when a nonlocal variable does not have an attached ' - 'name somewhere in the parent scopes', - {'minversion': (3, 0)}), - 'E0118': ("Name %r is used prior to global declaration", - 'used-prior-global-declaration', - 'Emitted when a name is used prior a global declaration, ' - 'which results in an error since Python 3.6.', - {'minversion': (3, 6)}), - } - - @utils.check_messages('function-redefined') - def visit_classdef(self, node): - self._check_redefinition('class', node) - - @utils.check_messages('too-many-star-expressions', - 'invalid-star-assignment-target') - def visit_assign(self, node): - starred = list(node.targets[0].nodes_of_class(astroid.Starred)) - if len(starred) > 1: - self.add_message('too-many-star-expressions', node=node) - - # Check *a = b - if isinstance(node.targets[0], astroid.Starred): - self.add_message('invalid-star-assignment-target', node=node) - - @utils.check_messages('star-needs-assignment-target') - def visit_starred(self, node): - """Check that a Starred expression is used in an assignment target.""" - if isinstance(node.parent, astroid.Call): - # f(*args) is converted to Call(args=[Starred]), so ignore - # them for this check. - return - if PY35 and isinstance(node.parent, - (astroid.List, astroid.Tuple, - astroid.Set, astroid.Dict)): - # PEP 448 unpacking. - return - - stmt = node.statement() - if not isinstance(stmt, astroid.Assign): - return - - if stmt.value is node or stmt.value.parent_of(node): - self.add_message('star-needs-assignment-target', node=node) - - @utils.check_messages('init-is-generator', 'return-in-init', - 'function-redefined', 'return-arg-in-generator', - 'duplicate-argument-name', 'nonlocal-and-global', - 'used-prior-global-declaration') - def visit_functiondef(self, node): - self._check_nonlocal_and_global(node) - self._check_name_used_prior_global(node) - if (not redefined_by_decorator(node) and - not utils.is_registered_in_singledispatch_function(node)): - self._check_redefinition(node.is_method() and 'method' or 'function', node) - # checks for max returns, branch, return in __init__ - returns = node.nodes_of_class(astroid.Return, - skip_klass=(astroid.FunctionDef, - astroid.ClassDef)) - if node.is_method() and node.name == '__init__': - if node.is_generator(): - self.add_message('init-is-generator', node=node) - else: - values = [r.value for r in returns] - # Are we returning anything but None from constructors - if any(v for v in values if not utils.is_none(v)): - self.add_message('return-in-init', node=node) - elif node.is_generator(): - # make sure we don't mix non-None returns and yields - if not PY33: - for retnode in returns: - if isinstance(retnode.value, astroid.Const) and \ - retnode.value.value is not None: - self.add_message('return-arg-in-generator', node=node, - line=retnode.fromlineno) - # Check for duplicate names - args = set() - for name in node.argnames(): - if name in args: - self.add_message('duplicate-argument-name', node=node, args=(name,)) - else: - args.add(name) - - visit_asyncfunctiondef = visit_functiondef - - def _check_name_used_prior_global(self, node): - - scope_globals = { - name: child - for child in node.nodes_of_class(astroid.Global) - for name in child.names - if child.scope() is node - } - - for node_name in node.nodes_of_class(astroid.Name): - if node_name.scope() is not node: - continue - - name = node_name.name - corresponding_global = scope_globals.get(name) - if not corresponding_global: - continue - - global_lineno = corresponding_global.fromlineno - if global_lineno and global_lineno > node_name.fromlineno: - self.add_message('used-prior-global-declaration', - node=node_name, args=(name, )) - - def _check_nonlocal_and_global(self, node): - """Check that a name is both nonlocal and global.""" - def same_scope(current): - return current.scope() is node - - from_iter = itertools.chain.from_iterable - nonlocals = set(from_iter( - child.names for child in node.nodes_of_class(astroid.Nonlocal) - if same_scope(child))) - global_vars = set(from_iter( - child.names for child in node.nodes_of_class(astroid.Global) - if same_scope(child))) - for name in nonlocals.intersection(global_vars): - self.add_message('nonlocal-and-global', - args=(name, ), node=node) - - @utils.check_messages('return-outside-function') - def visit_return(self, node): - if not isinstance(node.frame(), astroid.FunctionDef): - self.add_message('return-outside-function', node=node) - - @utils.check_messages('yield-outside-function') - def visit_yield(self, node): - self._check_yield_outside_func(node) - - @utils.check_messages('yield-outside-function') - def visit_yieldfrom(self, node): - self._check_yield_outside_func(node) - - @utils.check_messages('not-in-loop', 'continue-in-finally') - def visit_continue(self, node): - self._check_in_loop(node, 'continue') - - @utils.check_messages('not-in-loop') - def visit_break(self, node): - self._check_in_loop(node, 'break') - - @utils.check_messages('useless-else-on-loop') - def visit_for(self, node): - self._check_else_on_loop(node) - - @utils.check_messages('useless-else-on-loop') - def visit_while(self, node): - self._check_else_on_loop(node) - - @utils.check_messages('nonexistent-operator') - def visit_unaryop(self, node): - """check use of the non-existent ++ and -- operator operator""" - if ((node.op in '+-') and - isinstance(node.operand, astroid.UnaryOp) and - (node.operand.op == node.op)): - self.add_message('nonexistent-operator', node=node, args=node.op*2) - - def _check_nonlocal_without_binding(self, node, name): - current_scope = node.scope() - while True: - if current_scope.parent is None: - break - - if not isinstance(current_scope, astroid.FunctionDef): - self.add_message('nonlocal-without-binding', args=(name, ), - node=node) - return - else: - if name not in current_scope.locals: - current_scope = current_scope.parent.scope() - continue - else: - # Okay, found it. - return - - self.add_message('nonlocal-without-binding', args=(name, ), node=node) - - @utils.check_messages('nonlocal-without-binding') - def visit_nonlocal(self, node): - for name in node.names: - self._check_nonlocal_without_binding(node, name) - - @utils.check_messages('abstract-class-instantiated') - def visit_call(self, node): - """ Check instantiating abstract class with - abc.ABCMeta as metaclass. - """ - try: - infered = next(node.func.infer()) - except astroid.InferenceError: - return - - if not isinstance(infered, astroid.ClassDef): - return - - klass = utils.node_frame_class(node) - if klass is infered: - # Don't emit the warning if the class is instantiated - # in its own body or if the call is not an instance - # creation. If the class is instantiated into its own - # body, we're expecting that it knows what it is doing. - return - - # __init__ was called - metaclass = infered.metaclass() - abstract_methods = _has_abstract_methods(infered) - if metaclass is None: - # Python 3.4 has `abc.ABC`, which won't be detected - # by ClassNode.metaclass() - for ancestor in infered.ancestors(): - if ancestor.qname() == 'abc.ABC' and abstract_methods: - self.add_message('abstract-class-instantiated', - args=(infered.name, ), - node=node) - break - return - if metaclass.qname() == 'abc.ABCMeta' and abstract_methods: - self.add_message('abstract-class-instantiated', - args=(infered.name, ), - node=node) - - def _check_yield_outside_func(self, node): - if not isinstance(node.frame(), (astroid.FunctionDef, astroid.Lambda)): - self.add_message('yield-outside-function', node=node) - - def _check_else_on_loop(self, node): - """Check that any loop with an else clause has a break statement.""" - if node.orelse and not _loop_exits_early(node): - self.add_message('useless-else-on-loop', node=node, - # This is not optimal, but the line previous - # to the first statement in the else clause - # will usually be the one that contains the else:. - line=node.orelse[0].lineno - 1) - - def _check_in_loop(self, node, node_name): - """check that a node is inside a for or while loop""" - _node = node.parent - while _node: - if isinstance(_node, (astroid.For, astroid.While)): - if node not in _node.orelse: - return - - if isinstance(_node, (astroid.ClassDef, astroid.FunctionDef)): - break - if (isinstance(_node, astroid.TryFinally) - and node in _node.finalbody - and isinstance(node, astroid.Continue)): - self.add_message('continue-in-finally', node=node) - - _node = _node.parent - - self.add_message('not-in-loop', node=node, args=node_name) - - def _check_redefinition(self, redeftype, node): - """check for redefinition of a function / method / class name""" - defined_self = node.parent.frame()[node.name] - if defined_self is not node and not astroid.are_exclusive(node, defined_self): - self.add_message('function-redefined', node=node, - args=(redeftype, defined_self.fromlineno)) - - -class BasicChecker(_BasicChecker): - """checks for : - * doc strings - * number of arguments, local variables, branches, returns and statements in -functions, methods - * required module attributes - * dangerous default values as arguments - * redefinition of function / method / class - * uses of the global statement - """ - - __implements__ = interfaces.IAstroidChecker - - name = 'basic' - msgs = { - 'W0101': ('Unreachable code', - 'unreachable', - 'Used when there is some code behind a "return" or "raise" ' - 'statement, which will never be accessed.'), - 'W0102': ('Dangerous default value %s as argument', - 'dangerous-default-value', - 'Used when a mutable value as list or dictionary is detected in ' - 'a default value for an argument.'), - 'W0104': ('Statement seems to have no effect', - 'pointless-statement', - 'Used when a statement doesn\'t have (or at least seems to) ' - 'any effect.'), - 'W0105': ('String statement has no effect', - 'pointless-string-statement', - 'Used when a string is used as a statement (which of course ' - 'has no effect). This is a particular case of W0104 with its ' - 'own message so you can easily disable it if you\'re using ' - 'those strings as documentation, instead of comments.'), - 'W0106': ('Expression "%s" is assigned to nothing', - 'expression-not-assigned', - 'Used when an expression that is not a function call is assigned ' - 'to nothing. Probably something else was intended.'), - 'W0108': ('Lambda may not be necessary', - 'unnecessary-lambda', - 'Used when the body of a lambda expression is a function call ' - 'on the same argument list as the lambda itself; such lambda ' - 'expressions are in all but a few cases replaceable with the ' - 'function being called in the body of the lambda.'), - 'W0109': ("Duplicate key %r in dictionary", - 'duplicate-key', - 'Used when a dictionary expression binds the same key multiple ' - 'times.'), - 'W0122': ('Use of exec', - 'exec-used', - 'Used when you use the "exec" statement (function for Python ' - '3), to discourage its usage. That doesn\'t ' - 'mean you cannot use it !'), - 'W0123': ('Use of eval', - 'eval-used', - 'Used when you use the "eval" function, to discourage its ' - 'usage. Consider using `ast.literal_eval` for safely evaluating ' - 'strings containing Python expressions ' - 'from untrusted sources. '), - 'W0150': ("%s statement in finally block may swallow exception", - 'lost-exception', - 'Used when a break or a return statement is found inside the ' - 'finally clause of a try...finally block: the exceptions raised ' - 'in the try clause will be silently swallowed instead of being ' - 're-raised.'), - 'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?', - 'assert-on-tuple', - 'A call of assert on a tuple will always evaluate to true if ' - 'the tuple is not empty, and will always evaluate to false if ' - 'it is.'), - 'W0124': ('Following "as" with another context manager looks like a tuple.', - 'confusing-with-statement', - 'Emitted when a `with` statement component returns multiple values ' - 'and uses name binding with `as` only for a part of those values, ' - 'as in with ctx() as a, b. This can be misleading, since it\'s not ' - 'clear if the context manager returns a tuple or if the node without ' - 'a name binding is another context manager.'), - 'W0125': ('Using a conditional statement with a constant value', - 'using-constant-test', - 'Emitted when a conditional statement (If or ternary if) ' - 'uses a constant value for its test. This might not be what ' - 'the user intended to do.'), - 'E0111': ('The first reversed() argument is not a sequence', - 'bad-reversed-sequence', - 'Used when the first argument to reversed() builtin ' - 'isn\'t a sequence (does not implement __reversed__, ' - 'nor __getitem__ and __len__'), - - } - - reports = (('RP0101', 'Statistics by type', report_by_type_stats),) - - def __init__(self, linter): - _BasicChecker.__init__(self, linter) - self.stats = None - self._tryfinallys = None - - def open(self): - """initialize visit variables and statistics - """ - self._tryfinallys = [] - self.stats = self.linter.add_stats(module=0, function=0, - method=0, class_=0) - - @utils.check_messages('using-constant-test') - def visit_if(self, node): - self._check_using_constant_test(node, node.test) - - @utils.check_messages('using-constant-test') - def visit_ifexp(self, node): - self._check_using_constant_test(node, node.test) - - @utils.check_messages('using-constant-test') - def visit_comprehension(self, node): - if node.ifs: - for if_test in node.ifs: - self._check_using_constant_test(node, if_test) - - def _check_using_constant_test(self, node, test): - const_nodes = ( - astroid.Module, - astroid.scoped_nodes.GeneratorExp, - astroid.Lambda, astroid.FunctionDef, astroid.ClassDef, - astroid.bases.Generator, astroid.UnboundMethod, - astroid.BoundMethod, astroid.Module) - structs = (astroid.Dict, astroid.Tuple, astroid.Set) - - # These nodes are excepted, since they are not constant - # values, requiring a computation to happen. The only type - # of node in this list which doesn't have this property is - # Getattr, which is excepted because the conditional statement - # can be used to verify that the attribute was set inside a class, - # which is definitely a valid use case. - except_nodes = (astroid.Attribute, astroid.Call, - astroid.BinOp, astroid.BoolOp, astroid.UnaryOp, - astroid.Subscript) - inferred = None - emit = isinstance(test, (astroid.Const, ) + structs + const_nodes) - if not isinstance(test, except_nodes): - inferred = utils.safe_infer(test) - - if emit or isinstance(inferred, const_nodes): - self.add_message('using-constant-test', node=node) - - def visit_module(self, _): - """check module name, docstring and required arguments - """ - self.stats['module'] += 1 - - def visit_classdef(self, node): # pylint: disable=unused-argument - """check module name, docstring and redefinition - increment branch counter - """ - self.stats['class'] += 1 - - @utils.check_messages('pointless-statement', 'pointless-string-statement', - 'expression-not-assigned') - def visit_expr(self, node): - """check for various kind of statements without effect""" - expr = node.value - if isinstance(expr, astroid.Const) and isinstance(expr.value, - six.string_types): - # treat string statement in a separated message - # Handle PEP-257 attribute docstrings. - # An attribute docstring is defined as being a string right after - # an assignment at the module level, class level or __init__ level. - scope = expr.scope() - if isinstance(scope, (astroid.ClassDef, astroid.Module, astroid.FunctionDef)): - if isinstance(scope, astroid.FunctionDef) and scope.name != '__init__': - pass - else: - sibling = expr.previous_sibling() - if (sibling is not None and sibling.scope() is scope and - isinstance(sibling, astroid.Assign)): - return - self.add_message('pointless-string-statement', node=node) - return - # ignore if this is : - # * a direct function call - # * the unique child of a try/except body - # * a yield (which are wrapped by a discard node in _ast XXX) - # warn W0106 if we have any underlying function call (we can't predict - # side effects), else pointless-statement - if (isinstance(expr, (astroid.Yield, astroid.Await, astroid.Call)) or - (isinstance(node.parent, astroid.TryExcept) and - node.parent.body == [node])): - return - if any(expr.nodes_of_class(astroid.Call)): - self.add_message('expression-not-assigned', node=node, - args=expr.as_string()) - else: - self.add_message('pointless-statement', node=node) - - @staticmethod - def _filter_vararg(node, call_args): - # Return the arguments for the given call which are - # not passed as vararg. - for arg in call_args: - if isinstance(arg, astroid.Starred): - if (isinstance(arg.value, astroid.Name) - and arg.value.name != node.args.vararg): - yield arg - else: - yield arg - - @staticmethod - def _has_variadic_argument(args, variadic_name): - if not args: - return True - for arg in args: - if isinstance(arg.value, astroid.Name): - if arg.value.name != variadic_name: - return True - else: - return True - return False - - @utils.check_messages('unnecessary-lambda') - def visit_lambda(self, node): - """check whether or not the lambda is suspicious - """ - # if the body of the lambda is a call expression with the same - # argument list as the lambda itself, then the lambda is - # possibly unnecessary and at least suspicious. - if node.args.defaults: - # If the arguments of the lambda include defaults, then a - # judgment cannot be made because there is no way to check - # that the defaults defined by the lambda are the same as - # the defaults defined by the function called in the body - # of the lambda. - return - call = node.body - if not isinstance(call, astroid.Call): - # The body of the lambda must be a function call expression - # for the lambda to be unnecessary. - return - if (isinstance(node.body.func, astroid.Attribute) and - isinstance(node.body.func.expr, astroid.Call)): - # Chained call, the intermediate call might - # return something else (but we don't check that, yet). - return - - ordinary_args = list(node.args.args) - new_call_args = list(self._filter_vararg(node, call.args)) - if node.args.kwarg: - if self._has_variadic_argument(call.kwargs, node.args.kwarg): - return - elif call.kwargs or call.keywords: - return - - if node.args.vararg: - if self._has_variadic_argument(call.starargs, node.args.vararg): - return - elif call.starargs: - return - - # The "ordinary" arguments must be in a correspondence such that: - # ordinary_args[i].name == call.args[i].name. - if len(ordinary_args) != len(new_call_args): - return - for arg, passed_arg in zip(ordinary_args, new_call_args): - if not isinstance(passed_arg, astroid.Name): - return - if arg.name != passed_arg.name: - return - - self.add_message('unnecessary-lambda', line=node.fromlineno, - node=node) - - @utils.check_messages('dangerous-default-value') - def visit_functiondef(self, node): - """check function name, docstring, arguments, redefinition, - variable names, max locals - """ - self.stats[node.is_method() and 'method' or 'function'] += 1 - self._check_dangerous_default(node) - - visit_asyncfunctiondef = visit_functiondef - - def _check_dangerous_default(self, node): - # check for dangerous default values as arguments - is_iterable = lambda n: isinstance(n, (astroid.List, - astroid.Set, - astroid.Dict)) - for default in node.args.defaults: - try: - value = next(default.infer()) - except astroid.InferenceError: - continue - - if (isinstance(value, astroid.Instance) and - value.qname() in DEFAULT_ARGUMENT_SYMBOLS): - - if value is default: - msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] - elif isinstance(value, astroid.Instance) or is_iterable(value): - # We are here in the following situation(s): - # * a dict/set/list/tuple call which wasn't inferred - # to a syntax node ({}, () etc.). This can happen - # when the arguments are invalid or unknown to - # the inference. - # * a variable from somewhere else, which turns out to be a list - # or a dict. - if is_iterable(default): - msg = value.pytype() - elif isinstance(default, astroid.Call): - msg = '%s() (%s)' % (value.name, value.qname()) - else: - msg = '%s (%s)' % (default.as_string(), value.qname()) - else: - # this argument is a name - msg = '%s (%s)' % (default.as_string(), - DEFAULT_ARGUMENT_SYMBOLS[value.qname()]) - self.add_message('dangerous-default-value', - node=node, - args=(msg, )) - - @utils.check_messages('unreachable', 'lost-exception') - def visit_return(self, node): - """1 - check is the node has a right sibling (if so, that's some - unreachable code) - 2 - check is the node is inside the finally clause of a try...finally - block - """ - self._check_unreachable(node) - # Is it inside final body of a try...finally bloc ? - self._check_not_in_finally(node, 'return', (astroid.FunctionDef,)) - - @utils.check_messages('unreachable') - def visit_continue(self, node): - """check is the node has a right sibling (if so, that's some unreachable - code) - """ - self._check_unreachable(node) - - @utils.check_messages('unreachable', 'lost-exception') - def visit_break(self, node): - """1 - check is the node has a right sibling (if so, that's some - unreachable code) - 2 - check is the node is inside the finally clause of a try...finally - block - """ - # 1 - Is it right sibling ? - self._check_unreachable(node) - # 2 - Is it inside final body of a try...finally bloc ? - self._check_not_in_finally(node, 'break', (astroid.For, astroid.While,)) - - @utils.check_messages('unreachable') - def visit_raise(self, node): - """check if the node has a right sibling (if so, that's some unreachable - code) - """ - self._check_unreachable(node) - - @utils.check_messages('exec-used') - def visit_exec(self, node): - """just print a warning on exec statements""" - self.add_message('exec-used', node=node) - - @utils.check_messages('eval-used', 'exec-used', 'bad-reversed-sequence') - def visit_call(self, node): - """visit a CallFunc node -> check if this is not a blacklisted builtin - call and check for * or ** use - """ - if isinstance(node.func, astroid.Name): - name = node.func.name - # ignore the name if it's not a builtin (i.e. not defined in the - # locals nor globals scope) - if not (name in node.frame() or - name in node.root()): - if name == 'exec': - self.add_message('exec-used', node=node) - elif name == 'reversed': - self._check_reversed(node) - elif name == 'eval': - self.add_message('eval-used', node=node) - - @utils.check_messages('assert-on-tuple') - def visit_assert(self, node): - """check the use of an assert statement on a tuple.""" - if node.fail is None and isinstance(node.test, astroid.Tuple) and \ - len(node.test.elts) == 2: - self.add_message('assert-on-tuple', node=node) - - @utils.check_messages('duplicate-key') - def visit_dict(self, node): - """check duplicate key in dictionary""" - keys = set() - for k, _ in node.items: - if isinstance(k, astroid.Const): - key = k.value - if key in keys: - self.add_message('duplicate-key', node=node, args=key) - keys.add(key) - - def visit_tryfinally(self, node): - """update try...finally flag""" - self._tryfinallys.append(node) - - def leave_tryfinally(self, node): # pylint: disable=unused-argument - """update try...finally flag""" - self._tryfinallys.pop() - - def _check_unreachable(self, node): - """check unreachable code""" - unreach_stmt = node.next_sibling() - if unreach_stmt is not None: - self.add_message('unreachable', node=unreach_stmt) - - def _check_not_in_finally(self, node, node_name, breaker_classes=()): - """check that a node is not inside a finally clause of a - try...finally statement. - If we found before a try...finally bloc a parent which its type is - in breaker_classes, we skip the whole check.""" - # if self._tryfinallys is empty, we're not a in try...finally bloc - if not self._tryfinallys: - return - # the node could be a grand-grand...-children of the try...finally - _parent = node.parent - _node = node - while _parent and not isinstance(_parent, breaker_classes): - if hasattr(_parent, 'finalbody') and _node in _parent.finalbody: - self.add_message('lost-exception', node=node, args=node_name) - return - _node = _parent - _parent = _node.parent - - def _check_reversed(self, node): - """ check that the argument to `reversed` is a sequence """ - try: - argument = utils.safe_infer(utils.get_argument_from_call(node, position=0)) - except utils.NoSuchArgumentError: - pass - else: - if argument is astroid.YES: - return - if argument is None: - # Nothing was infered. - # Try to see if we have iter(). - if isinstance(node.args[0], astroid.Call): - try: - func = next(node.args[0].func.infer()) - except astroid.InferenceError: - return - if (getattr(func, 'name', None) == 'iter' and - utils.is_builtin_object(func)): - self.add_message('bad-reversed-sequence', node=node) - return - - if isinstance(argument, astroid.Instance): - if (argument._proxied.name == 'dict' and - utils.is_builtin_object(argument._proxied)): - self.add_message('bad-reversed-sequence', node=node) - return - elif any(ancestor.name == 'dict' and utils.is_builtin_object(ancestor) - for ancestor in argument._proxied.ancestors()): - # Mappings aren't accepted by reversed(), unless - # they provide explicitly a __reversed__ method. - try: - argument.locals[REVERSED_PROTOCOL_METHOD] - except KeyError: - self.add_message('bad-reversed-sequence', node=node) - return - - for methods in REVERSED_METHODS: - for meth in methods: - try: - argument.getattr(meth) - except astroid.NotFoundError: - break - else: - break - else: - self.add_message('bad-reversed-sequence', node=node) - elif not isinstance(argument, (astroid.List, astroid.Tuple)): - # everything else is not a proper sequence for reversed() - self.add_message('bad-reversed-sequence', node=node) - - @utils.check_messages('confusing-with-statement') - def visit_with(self, node): - if not PY3K: - # in Python 2 a "with" statement with multiple managers coresponds - # to multiple nested AST "With" nodes - pairs = [] - parent_node = node.parent - if isinstance(parent_node, astroid.With): - # we only care about the direct parent, since this method - # gets called for each with node anyway - pairs.extend(parent_node.items) - pairs.extend(node.items) - else: - # in PY3K a "with" statement with multiple managers coresponds - # to one AST "With" node with multiple items - pairs = node.items - if pairs: - for prev_pair, pair in zip(pairs, pairs[1:]): - if (isinstance(prev_pair[1], astroid.AssignName) and - (pair[1] is None and not isinstance(pair[0], astroid.Call))): - # don't emit a message if the second is a function call - # there's no way that can be mistaken for a name assignment - if PY3K or node.lineno == node.parent.lineno: - # if the line number doesn't match - # we assume it's a nested "with" - self.add_message('confusing-with-statement', node=node) - - -_NAME_TYPES = { - 'module': (MOD_NAME_RGX, 'module'), - 'const': (CONST_NAME_RGX, 'constant'), - 'class': (CLASS_NAME_RGX, 'class'), - 'function': (DEFAULT_NAME_RGX, 'function'), - 'method': (DEFAULT_NAME_RGX, 'method'), - 'attr': (DEFAULT_NAME_RGX, 'attribute'), - 'argument': (DEFAULT_NAME_RGX, 'argument'), - 'variable': (DEFAULT_NAME_RGX, 'variable'), - 'class_attribute': (CLASS_ATTRIBUTE_RGX, 'class attribute'), - 'inlinevar': (COMP_VAR_RGX, 'inline iteration'), -} - - -def _create_naming_options(): - name_options = [] - for name_type, (rgx, human_readable_name) in six.iteritems(_NAME_TYPES): - name_type = name_type.replace('_', '-') - name_options.append(( - '%s-rgx' % (name_type,), - {'default': rgx, 'type': 'regexp', 'metavar': '', - 'help': 'Regular expression matching correct %s names' % (human_readable_name,)})) - name_options.append(( - '%s-name-hint' % (name_type,), - {'default': rgx.pattern, 'type': 'string', 'metavar': '', - 'help': 'Naming hint for %s names' % (human_readable_name,)})) - return tuple(name_options) - - -class NameChecker(_BasicChecker): - - msgs = { - 'C0102': ('Black listed name "%s"', - 'blacklisted-name', - 'Used when the name is listed in the black list (unauthorized ' - 'names).'), - 'C0103': ('Invalid %s name "%s"%s', - 'invalid-name', - 'Used when the name doesn\'t match the regular expression ' - 'associated to its type (constant, variable, class...).'), - 'W0111': ('Name %s will become a keyword in Python %s', - 'assign-to-new-keyword', - 'Used when assignment will become invalid in future ' - 'Python release due to introducing new keyword'), - } - - options = (('good-names', - {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'), - 'type' :'csv', 'metavar' : '', - 'help' : 'Good variable names which should always be accepted,' - ' separated by a comma'} - ), - ('bad-names', - {'default' : ('foo', 'bar', 'baz', 'toto', 'tutu', 'tata'), - 'type' :'csv', 'metavar' : '', - 'help' : 'Bad variable names which should always be refused, ' - 'separated by a comma'} - ), - ('name-group', - {'default' : (), - 'type' :'csv', 'metavar' : '', - 'help' : ('Colon-delimited sets of names that determine each' - ' other\'s naming style when the name regexes' - ' allow several styles.')} - ), - ('include-naming-hint', - {'default': False, 'type' : 'yn', 'metavar' : '', - 'help': 'Include a hint for the correct naming format with invalid-name'} - ), - ('property-classes', - {'default': ('abc.abstractproperty',), - 'type': 'csv', - 'metavar': '', - 'help': 'List of decorators that produce properties, such as ' - 'abc.abstractproperty. Add to this list to register ' - 'other decorators that produce valid properties.'} - ), - ) + _create_naming_options() - - KEYWORD_ONSET = { - (3, 0): {'True', 'False'}, - (3, 7): {'async', 'await'} - } - - def __init__(self, linter): - _BasicChecker.__init__(self, linter) - self._name_category = {} - self._name_group = {} - self._bad_names = {} - - def open(self): - self.stats = self.linter.add_stats(badname_module=0, - badname_class=0, badname_function=0, - badname_method=0, badname_attr=0, - badname_const=0, - badname_variable=0, - badname_inlinevar=0, - badname_argument=0, - badname_class_attribute=0) - for group in self.config.name_group: - for name_type in group.split(':'): - self._name_group[name_type] = 'group_%s' % (group,) - - @utils.check_messages('blacklisted-name', 'invalid-name') - def visit_module(self, node): - self._check_name('module', node.name.split('.')[-1], node) - self._bad_names = {} - - def leave_module(self, node): # pylint: disable=unused-argument - for all_groups in six.itervalues(self._bad_names): - if len(all_groups) < 2: - continue - groups = collections.defaultdict(list) - min_warnings = sys.maxsize - for group in six.itervalues(all_groups): - groups[len(group)].append(group) - min_warnings = min(len(group), min_warnings) - if len(groups[min_warnings]) > 1: - by_line = sorted(groups[min_warnings], - key=lambda group: min(warning[0].lineno for warning in group)) - warnings = itertools.chain(*by_line[1:]) - else: - warnings = groups[min_warnings][0] - for args in warnings: - self._raise_name_warning(*args) - - @utils.check_messages('blacklisted-name', 'invalid-name') - def visit_classdef(self, node): - self._check_name('class', node.name, node) - for attr, anodes in six.iteritems(node.instance_attrs): - if not any(node.instance_attr_ancestors(attr)): - self._check_name('attr', attr, anodes[0]) - - @utils.check_messages('blacklisted-name', 'invalid-name') - def visit_functiondef(self, node): - # Do not emit any warnings if the method is just an implementation - # of a base class method. - confidence = interfaces.HIGH - if node.is_method(): - if utils.overrides_a_method(node.parent.frame(), node.name): - return - confidence = (interfaces.INFERENCE if utils.has_known_bases(node.parent.frame()) - else interfaces.INFERENCE_FAILURE) - - self._check_name(_determine_function_name_type(node, - config=self.config), - node.name, node, confidence) - # Check argument names - args = node.args.args - if args is not None: - self._recursive_check_names(args, node) - - visit_asyncfunctiondef = visit_functiondef - - @utils.check_messages('blacklisted-name', 'invalid-name') - def visit_global(self, node): - for name in node.names: - self._check_name('const', name, node) - - @utils.check_messages('blacklisted-name', 'invalid-name') - def visit_assignname(self, node): - """check module level assigned names""" - keyword_first_version = self._name_became_keyword_in_version( - node.name, self.KEYWORD_ONSET - ) - if keyword_first_version is not None: - self.add_message('assign-to-new-keyword', - node=node, args=(node.name, keyword_first_version), - confidence=interfaces.HIGH) - - frame = node.frame() - ass_type = node.assign_type() - if isinstance(ass_type, astroid.Comprehension): - self._check_name('inlinevar', node.name, node) - elif isinstance(frame, astroid.Module): - if isinstance(ass_type, astroid.Assign) and not in_loop(ass_type): - if isinstance(utils.safe_infer(ass_type.value), astroid.ClassDef): - self._check_name('class', node.name, node) - else: - if not _redefines_import(node): - # Don't emit if the name redefines an import - # in an ImportError except handler. - self._check_name('const', node.name, node) - elif isinstance(ass_type, astroid.ExceptHandler): - self._check_name('variable', node.name, node) - elif isinstance(frame, astroid.FunctionDef): - # global introduced variable aren't in the function locals - if node.name in frame and node.name not in frame.argnames(): - if not _redefines_import(node): - self._check_name('variable', node.name, node) - elif isinstance(frame, astroid.ClassDef): - if not list(frame.local_attr_ancestors(node.name)): - self._check_name('class_attribute', node.name, node) - - def _recursive_check_names(self, args, node): - """check names in a possibly recursive list """ - for arg in args: - if isinstance(arg, astroid.AssignName): - self._check_name('argument', arg.name, node) - else: - self._recursive_check_names(arg.elts, node) - - def _find_name_group(self, node_type): - return self._name_group.get(node_type, node_type) - - def _raise_name_warning(self, node, node_type, name, confidence): - type_label = _NAME_TYPES[node_type][1] - hint = '' - if self.config.include_naming_hint: - hint = ' (hint: %s)' % (getattr(self.config, node_type + '_name_hint')) - self.add_message('invalid-name', node=node, args=(type_label, name, hint), - confidence=confidence) - self.stats['badname_' + node_type] += 1 - - def _check_name(self, node_type, name, node, confidence=interfaces.HIGH): - """check for a name using the type's regexp""" - if utils.is_inside_except(node): - clobbering, _ = utils.clobber_in_except(node) - if clobbering: - return - if name in self.config.good_names: - return - if name in self.config.bad_names: - self.stats['badname_' + node_type] += 1 - self.add_message('blacklisted-name', node=node, args=name) - return - regexp = getattr(self.config, node_type + '_rgx') - match = regexp.match(name) - - if _is_multi_naming_match(match, node_type, confidence): - name_group = self._find_name_group(node_type) - bad_name_group = self._bad_names.setdefault(name_group, {}) - warnings = bad_name_group.setdefault(match.lastgroup, []) - warnings.append((node, node_type, name, confidence)) - - if match is None: - self._raise_name_warning(node, node_type, name, confidence) - - @staticmethod - def _name_became_keyword_in_version(name, rules): - for version, keywords in rules.items(): - if name in keywords and sys.version_info < version: - return '.'.join(map(str, version)) - return None - - -class DocStringChecker(_BasicChecker): - msgs = { - 'C0111': ('Missing %s docstring', # W0131 - 'missing-docstring', - 'Used when a module, function, class or method has no docstring.' - 'Some special methods like __init__ doesn\'t necessary require a ' - 'docstring.'), - 'C0112': ('Empty %s docstring', # W0132 - 'empty-docstring', - 'Used when a module, function, class or method has an empty ' - 'docstring (it would be too easy ;).'), - } - options = (('no-docstring-rgx', - {'default' : NO_REQUIRED_DOC_RGX, - 'type' : 'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match ' - 'function or class names that do not require a ' - 'docstring.'} - ), - ('docstring-min-length', - {'default' : -1, - 'type' : 'int', 'metavar' : '', - 'help': ('Minimum line length for functions/classes that' - ' require docstrings, shorter ones are exempt.')} - ), - ) - - def open(self): - self.stats = self.linter.add_stats(undocumented_module=0, - undocumented_function=0, - undocumented_method=0, - undocumented_class=0) - - @utils.check_messages('missing-docstring', 'empty-docstring') - def visit_module(self, node): - self._check_docstring('module', node) - - @utils.check_messages('missing-docstring', 'empty-docstring') - def visit_classdef(self, node): - if self.config.no_docstring_rgx.match(node.name) is None: - self._check_docstring('class', node) - - @staticmethod - def _is_setter_or_deleter(node): - names = {'setter', 'deleter'} - for decorator in node.decorators.nodes: - if (isinstance(decorator, astroid.Attribute) - and decorator.attrname in names): - return True - return False - - @utils.check_messages('missing-docstring', 'empty-docstring') - def visit_functiondef(self, node): - if self.config.no_docstring_rgx.match(node.name) is None: - ftype = 'method' if node.is_method() else 'function' - if node.decorators and self._is_setter_or_deleter(node): - return - - if isinstance(node.parent.frame(), astroid.ClassDef): - overridden = False - confidence = (interfaces.INFERENCE if utils.has_known_bases(node.parent.frame()) - else interfaces.INFERENCE_FAILURE) - # check if node is from a method overridden by its ancestor - for ancestor in node.parent.frame().ancestors(): - if node.name in ancestor and \ - isinstance(ancestor[node.name], astroid.FunctionDef): - overridden = True - break - self._check_docstring(ftype, node, - report_missing=not overridden, - confidence=confidence) - else: - self._check_docstring(ftype, node) - - visit_asyncfunctiondef = visit_functiondef - - def _check_docstring(self, node_type, node, report_missing=True, - confidence=interfaces.HIGH): - """check the node has a non empty docstring""" - docstring = node.doc - if docstring is None: - if not report_missing: - return - if node.body: - lines = node.body[-1].lineno - node.body[0].lineno + 1 - else: - lines = 0 - - if node_type == 'module' and not lines: - # If the module has no body, there's no reason - # to require a docstring. - return - max_lines = self.config.docstring_min_length - - if node_type != 'module' and max_lines > -1 and lines < max_lines: - return - self.stats['undocumented_'+node_type] += 1 - if (node.body and isinstance(node.body[0], astroid.Expr) and - isinstance(node.body[0].value, astroid.Call)): - # Most likely a string with a format call. Let's see. - func = utils.safe_infer(node.body[0].value.func) - if (isinstance(func, astroid.BoundMethod) - and isinstance(func.bound, astroid.Instance)): - # Strings in Python 3, others in Python 2. - if PY3K and func.bound.name == 'str': - return - elif func.bound.name in ('str', 'unicode', 'bytes'): - return - self.add_message('missing-docstring', node=node, args=(node_type,), - confidence=confidence) - elif not docstring.strip(): - self.stats['undocumented_'+node_type] += 1 - self.add_message('empty-docstring', node=node, args=(node_type,), - confidence=confidence) - - -class PassChecker(_BasicChecker): - """check if the pass statement is really necessary""" - msgs = {'W0107': ('Unnecessary pass statement', - 'unnecessary-pass', - 'Used when a "pass" statement that can be avoided is ' - 'encountered.'), - } - - @utils.check_messages('unnecessary-pass') - def visit_pass(self, node): - if len(node.parent.child_sequence(node)) > 1: - self.add_message('unnecessary-pass', node=node) - - -class LambdaForComprehensionChecker(_BasicChecker): - """check for using a lambda where a comprehension would do. - - See - where GvR says comprehensions would be clearer. - """ - - msgs = {'W0110': ('map/filter on lambda could be replaced by comprehension', - 'deprecated-lambda', - 'Used when a lambda is the first argument to "map" or ' - '"filter". It could be clearer as a list ' - 'comprehension or generator expression.', - {'maxversion': (3, 0)}), - } - - @utils.check_messages('deprecated-lambda') - def visit_call(self, node): - """visit a CallFunc node, check if map or filter are called with a - lambda - """ - if not node.args: - return - if not isinstance(node.args[0], astroid.Lambda): - return - infered = utils.safe_infer(node.func) - if (utils.is_builtin_object(infered) - and infered.name in ['map', 'filter']): - self.add_message('deprecated-lambda', node=node) - - -def _is_one_arg_pos_call(call): - """Is this a call with exactly 1 argument, - where that argument is positional? - """ - return (isinstance(call, astroid.Call) - and len(call.args) == 1 and not call.keywords) - - -class ComparisonChecker(_BasicChecker): - """Checks for comparisons - - - singleton comparison: 'expr == True', 'expr == False' and 'expr == None' - - yoda condition: 'const "comp" right' where comp can be '==', '!=', '<', - '<=', '>' or '>=', and right can be a variable, an attribute, a method or - a function - """ - msgs = {'C0121': ('Comparison to %s should be %s', - 'singleton-comparison', - 'Used when an expression is compared to singleton ' - 'values like True, False or None.'), - 'C0122': ('Comparison should be %s', - 'misplaced-comparison-constant', - 'Used when the constant is placed on the left side ' - 'of a comparison. It is usually clearer in intent to ' - 'place it in the right hand side of the comparison.'), - 'C0123': ('Using type() instead of isinstance() for a typecheck.', - 'unidiomatic-typecheck', - 'The idiomatic way to perform an explicit typecheck in ' - 'Python is to use isinstance(x, Y) rather than ' - 'type(x) == Y, type(x) is Y. Though there are unusual ' - 'situations where these give different results.', - {'old_names': [('W0154', 'unidiomatic-typecheck')]}), - 'R0123': ('Comparison to literal', - 'literal-comparison', - 'Used when comparing an object to a literal, which is usually ' - 'what you do not want to do, since you can compare to a different ' - 'literal than what was expected altogether.'), - } - - def _check_singleton_comparison(self, singleton, root_node): - if singleton.value is True: - suggestion = "just 'expr' or 'expr is True'" - self.add_message('singleton-comparison', - node=root_node, - args=(True, suggestion)) - elif singleton.value is False: - suggestion = "'not expr' or 'expr is False'" - self.add_message('singleton-comparison', - node=root_node, - args=(False, suggestion)) - elif singleton.value is None: - self.add_message('singleton-comparison', - node=root_node, - args=(None, "'expr is None'")) - - def _check_literal_comparison(self, literal, node): - """Check if we compare to a literal, which is usually what we do not want to do.""" - nodes = (astroid.List, - astroid.Tuple, - astroid.Dict, - astroid.Set) - is_other_literal = isinstance(literal, nodes) - is_const = False - if isinstance(literal, astroid.Const): - if literal.value in (True, False, None): - # Not interested in this values. - return - is_const = isinstance(literal.value, (bytes, str, int, float)) - - if is_const or is_other_literal: - self.add_message('literal-comparison', node=node) - - def _check_misplaced_constant(self, node, left, right, operator): - if isinstance(right, astroid.Const): - return - operator = REVERSED_COMPS.get(operator, operator) - suggestion = '%s %s %r' % (right.as_string(), operator, left.value) - self.add_message('misplaced-comparison-constant', node=node, - args=(suggestion,)) - - @utils.check_messages('singleton-comparison', 'misplaced-comparison-constant', - 'unidiomatic-typecheck', 'literal-comparison') - def visit_compare(self, node): - self._check_unidiomatic_typecheck(node) - # NOTE: this checker only works with binary comparisons like 'x == 42' - # but not 'x == y == 42' - if len(node.ops) != 1: - return - - left = node.left - operator, right = node.ops[0] - if (operator in ('<', '<=', '>', '>=', '!=', '==') - and isinstance(left, astroid.Const)): - self._check_misplaced_constant(node, left, right, operator) - - if operator == '==': - if isinstance(left, astroid.Const): - self._check_singleton_comparison(left, node) - elif isinstance(right, astroid.Const): - self._check_singleton_comparison(right, node) - if operator in ('is', 'is not'): - self._check_literal_comparison(right, node) - - def _check_unidiomatic_typecheck(self, node): - operator, right = node.ops[0] - if operator in TYPECHECK_COMPARISON_OPERATORS: - left = node.left - if _is_one_arg_pos_call(left): - self._check_type_x_is_y(node, left, operator, right) - - def _check_type_x_is_y(self, node, left, operator, right): - """Check for expressions like type(x) == Y.""" - left_func = utils.safe_infer(left.func) - if not (isinstance(left_func, astroid.ClassDef) - and left_func.qname() == TYPE_QNAME): - return - - if operator in ('is', 'is not') and _is_one_arg_pos_call(right): - right_func = utils.safe_infer(right.func) - if (isinstance(right_func, astroid.ClassDef) - and right_func.qname() == TYPE_QNAME): - # type(x) == type(a) - right_arg = utils.safe_infer(right.args[0]) - if not isinstance(right_arg, LITERAL_NODE_TYPES): - # not e.g. type(x) == type([]) - return - self.add_message('unidiomatic-typecheck', node=node) - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(BasicErrorChecker(linter)) - linter.register_checker(BasicChecker(linter)) - linter.register_checker(NameChecker(linter)) - linter.register_checker(DocStringChecker(linter)) - linter.register_checker(PassChecker(linter)) - linter.register_checker(LambdaForComprehensionChecker(linter)) - linter.register_checker(ComparisonChecker(linter)) diff --git a/pymode/libs/pylint/checkers/classes.py b/pymode/libs/pylint/checkers/classes.py deleted file mode 100644 index 9975220b..00000000 --- a/pymode/libs/pylint/checkers/classes.py +++ /dev/null @@ -1,1402 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2006-2016 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2012, 2014 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2015 Dmitry Pribysh -# Copyright (c) 2016 Moises Lopez - https://www.vauxoo.com/ -# Copyright (c) 2016 Łukasz Rogalski - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""classes checker for Python code -""" -from __future__ import generators - -import collections -import sys - -import six - -import astroid -from astroid.bases import Generator, BUILTINS -from astroid.exceptions import InconsistentMroError, DuplicateBasesError -from astroid import decorators -from astroid import objects -from astroid.scoped_nodes import function_to_method -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - PYMETHODS, SPECIAL_METHODS_PARAMS, - overrides_a_method, check_messages, is_attr_private, - is_attr_protected, node_frame_class, is_builtin_object, - decorated_with_property, unimplemented_abstract_methods, - decorated_with, class_is_abstract, - safe_infer, has_known_bases, is_iterable, is_comprehension) -from pylint.utils import get_global_option - - -if sys.version_info >= (3, 0): - NEXT_METHOD = '__next__' -else: - NEXT_METHOD = 'next' -INVALID_BASE_CLASSES = {'bool', 'range', 'slice', 'memoryview'} - - -# Dealing with useless override detection, with regard -# to parameters vs arguments - -_CallSignature = collections.namedtuple( - '_CallSignature', 'args kws starred_args starred_kws') -_ParameterSignature = collections.namedtuple( - '_ParameterSignature', - 'args kwonlyargs varargs kwargs', -) - - -def _signature_from_call(call): - kws = {} - args = [] - starred_kws = [] - starred_args = [] - for keyword in call.keywords or []: - arg, value = keyword.arg, keyword.value - if arg is None and isinstance(value, astroid.Name): - # Starred node and we are interested only in names, - # otherwise some transformation might occur for the parameter. - starred_kws.append(value.name) - elif isinstance(value, astroid.Name): - kws[arg] = value.name - else: - kws[arg] = None - - for arg in call.args: - if isinstance(arg, astroid.Starred) and isinstance(arg.value, astroid.Name): - # Positional variadic and a name, otherwise some transformation - # might have occurred. - starred_args.append(arg.value.name) - elif isinstance(arg, astroid.Name): - args.append(arg.name) - else: - args.append(None) - - return _CallSignature(args, kws, starred_args, starred_kws) - - -def _signature_from_arguments(arguments): - kwarg = arguments.kwarg - vararg = arguments.vararg - args = [arg.name for arg in arguments.args if arg.name != 'self'] - kwonlyargs = [arg.name for arg in arguments.kwonlyargs] - return _ParameterSignature(args, kwonlyargs, vararg, kwarg) - - -def _definition_equivalent_to_call(definition, call): - '''Check if a definition signature is equivalent to a call.''' - if definition.kwargs: - same_kw_variadics = definition.kwargs in call.starred_kws - else: - same_kw_variadics = not call.starred_kws - if definition.varargs: - same_args_variadics = definition.varargs in call.starred_args - else: - same_args_variadics = not call.starred_args - same_kwonlyargs = all(kw in call.kws for kw in definition.kwonlyargs) - same_args = definition.args == call.args - - no_additional_kwarg_arguments = True - if call.kws: - for keyword in call.kws: - is_arg = keyword in call.args - is_kwonly = keyword in definition.kwonlyargs - if not is_arg and not is_kwonly: - # Maybe this argument goes into **kwargs, - # or it is an extraneous argument. - # In any case, the signature is different than - # the call site, which stops our search. - no_additional_kwarg_arguments = False - break - - return all(( - same_args, - same_kwonlyargs, - same_args_variadics, - same_kw_variadics, - no_additional_kwarg_arguments, - )) - -# Deal with parameters overridding in two methods. - -def _positional_parameters(method): - positional = method.args.args - if method.type in ('classmethod', 'method'): - positional = positional[1:] - return positional - - -def _has_different_parameters(original, overridden, dummy_parameter_regex): - zipped = six.moves.zip_longest(original, overridden) - for original_param, overridden_param in zipped: - params = (original_param, overridden_param) - if not all(params): - return True - - names = [param.name for param in params] - if any(map(dummy_parameter_regex.match, names)): - continue - if original_param.name != overridden_param.name: - return True - return False - - -def _different_parameters(original, overridden, dummy_parameter_regex): - """Determine if the two methods have different parameters - - They are considered to have different parameters if: - - * they have different positional parameters, including different names - - * one of the methods is having variadics, while the other is not - - * they have different keyword only parameters. - - """ - original_parameters = _positional_parameters(original) - overridden_parameters = _positional_parameters(overridden) - - different_positional = _has_different_parameters( - original_parameters, - overridden_parameters, - dummy_parameter_regex) - different_kwonly = _has_different_parameters( - original.args.kwonlyargs, - overridden.args.kwonlyargs, - dummy_parameter_regex) - if original.name in PYMETHODS: - # Ignore the difference for special methods. If the parameter - # numbers are different, then that is going to be caught by - # unexpected-special-method-signature. - # If the names are different, it doesn't matter, since they can't - # be used as keyword arguments anyway. - different_positional = different_kwonly = False - - # Both or none should have extra variadics, otherwise the method - # loses or gains capabilities that are not reflected into the parent method, - # leading to potential inconsistencies in the code. - different_kwarg = sum( - 1 for param in (original.args.kwarg, overridden.args.kwarg) - if not param) == 1 - different_vararg = sum( - 1 for param in (original.args.vararg, overridden.args.vararg) - if not param) == 1 - - return any(( - different_positional, - different_kwarg, - different_vararg, - different_kwonly - )) - - -def _is_invalid_base_class(cls): - return cls.name in INVALID_BASE_CLASSES and is_builtin_object(cls) - - -def _has_data_descriptor(cls, attr): - attributes = cls.getattr(attr) - for attribute in attributes: - try: - for inferred in attribute.infer(): - if isinstance(inferred, astroid.Instance): - try: - inferred.getattr('__get__') - inferred.getattr('__set__') - except astroid.NotFoundError: - continue - else: - return True - except astroid.InferenceError: - # Can't infer, avoid emitting a false positive in this case. - return True - return False - - -def _called_in_methods(func, klass, methods): - """ Check if the func was called in any of the given methods, - belonging to the *klass*. Returns True if so, False otherwise. - """ - if not isinstance(func, astroid.FunctionDef): - return False - for method in methods: - try: - infered = klass.getattr(method) - except astroid.NotFoundError: - continue - for infer_method in infered: - for callfunc in infer_method.nodes_of_class(astroid.Call): - try: - bound = next(callfunc.func.infer()) - except (astroid.InferenceError, StopIteration): - continue - if not isinstance(bound, astroid.BoundMethod): - continue - func_obj = bound._proxied - if isinstance(func_obj, astroid.UnboundMethod): - func_obj = func_obj._proxied - if func_obj.name == func.name: - return True - return False - - -def _is_attribute_property(name, klass): - """ Check if the given attribute *name* is a property - in the given *klass*. - - It will look for `property` calls or for functions - with the given name, decorated by `property` or `property` - subclasses. - Returns ``True`` if the name is a property in the given klass, - ``False`` otherwise. - """ - - try: - attributes = klass.getattr(name) - except astroid.NotFoundError: - return False - property_name = "{0}.property".format(BUILTINS) - for attr in attributes: - try: - infered = next(attr.infer()) - except astroid.InferenceError: - continue - if (isinstance(infered, astroid.FunctionDef) and - decorated_with_property(infered)): - return True - if infered.pytype() == property_name: - return True - return False - - -def _has_bare_super_call(fundef_node): - for call in fundef_node.nodes_of_class(astroid.Call): - func = call.func - if (isinstance(func, astroid.Name) and - func.name == 'super' and - not call.args): - return True - return False - - -def _safe_infer_call_result(node, caller, context=None): - """ - Safely infer the return value of a function. - - Returns None if inference failed or if there is some ambiguity (more than - one node has been inferred). Otherwise returns infered value. - """ - try: - inferit = node.infer_call_result(caller, context=context) - value = next(inferit) - except astroid.InferenceError: - return # inference failed - except StopIteration: - return # no values infered - try: - next(inferit) - return # there is ambiguity on the inferred node - except astroid.InferenceError: - return # there is some kind of ambiguity - except StopIteration: - return value - - -MSGS = { - 'F0202': ('Unable to check methods signature (%s / %s)', - 'method-check-failed', - 'Used when Pylint has been unable to check methods signature ' - 'compatibility for an unexpected reason. Please report this kind ' - 'if you don\'t make sense of it.'), - - 'E0202': ('An attribute defined in %s line %s hides this method', - 'method-hidden', - 'Used when a class defines a method which is hidden by an ' - 'instance attribute from an ancestor class or set by some ' - 'client code.'), - 'E0203': ('Access to member %r before its definition line %s', - 'access-member-before-definition', - 'Used when an instance member is accessed before it\'s actually ' - 'assigned.'), - 'W0201': ('Attribute %r defined outside __init__', - 'attribute-defined-outside-init', - 'Used when an instance attribute is defined outside the __init__ ' - 'method.'), - - 'W0212': ('Access to a protected member %s of a client class', # E0214 - 'protected-access', - 'Used when a protected member (i.e. class member with a name ' - 'beginning with an underscore) is access outside the class or a ' - 'descendant of the class where it\'s defined.'), - - 'E0211': ('Method has no argument', - 'no-method-argument', - 'Used when a method which should have the bound instance as ' - 'first argument has no argument defined.'), - 'E0213': ('Method should have "self" as first argument', - 'no-self-argument', - 'Used when a method has an attribute different the "self" as ' - 'first argument. This is considered as an error since this is ' - 'a so common convention that you shouldn\'t break it!'), - 'C0202': ('Class method %s should have %s as first argument', - 'bad-classmethod-argument', - 'Used when a class method has a first argument named differently ' - 'than the value specified in valid-classmethod-first-arg option ' - '(default to "cls"), recommended to easily differentiate them ' - 'from regular instance methods.'), - 'C0203': ('Metaclass method %s should have %s as first argument', - 'bad-mcs-method-argument', - 'Used when a metaclass method has a first argument named ' - 'differently than the value specified in valid-classmethod-first' - '-arg option (default to "cls"), recommended to easily ' - 'differentiate them from regular instance methods.'), - 'C0204': ('Metaclass class method %s should have %s as first argument', - 'bad-mcs-classmethod-argument', - 'Used when a metaclass class method has a first argument named ' - 'differently than the value specified in valid-metaclass-' - 'classmethod-first-arg option (default to "mcs"), recommended to ' - 'easily differentiate them from regular instance methods.'), - - 'W0211': ('Static method with %r as first argument', - 'bad-staticmethod-argument', - 'Used when a static method has "self" or a value specified in ' - 'valid-classmethod-first-arg option or ' - 'valid-metaclass-classmethod-first-arg option as first argument.' - ), - 'R0201': ('Method could be a function', - 'no-self-use', - 'Used when a method doesn\'t use its bound instance, and so could ' - 'be written as a function.' - ), - 'W0221': ('Parameters differ from %s %r method', - 'arguments-differ', - 'Used when a method has a different number of arguments than in ' - 'the implemented interface or in an overridden method.'), - 'W0222': ('Signature differs from %s %r method', - 'signature-differs', - 'Used when a method signature is different than in the ' - 'implemented interface or in an overridden method.'), - 'W0223': ('Method %r is abstract in class %r but is not overridden', - 'abstract-method', - 'Used when an abstract method (i.e. raise NotImplementedError) is ' - 'not overridden in concrete class.' - ), - 'W0231': ('__init__ method from base class %r is not called', - 'super-init-not-called', - 'Used when an ancestor class method has an __init__ method ' - 'which is not called by a derived class.'), - 'W0232': ('Class has no __init__ method', - 'no-init', - 'Used when a class has no __init__ method, neither its parent ' - 'classes.'), - 'W0233': ('__init__ method from a non direct base class %r is called', - 'non-parent-init-called', - 'Used when an __init__ method is called on a class which is not ' - 'in the direct ancestors for the analysed class.'), - 'W0235': ('Useless super delegation in method %r', - 'useless-super-delegation', - 'Used whenever we can detect that an overridden method is useless, ' - 'relying on super() delegation to do the same thing as another method ' - 'from the MRO.'), - 'E0236': ('Invalid object %r in __slots__, must contain ' - 'only non empty strings', - 'invalid-slots-object', - 'Used when an invalid (non-string) object occurs in __slots__.'), - 'E0237': ('Assigning to attribute %r not defined in class slots', - 'assigning-non-slot', - 'Used when assigning to an attribute not defined ' - 'in the class slots.'), - 'E0238': ('Invalid __slots__ object', - 'invalid-slots', - 'Used when an invalid __slots__ is found in class. ' - 'Only a string, an iterable or a sequence is permitted.'), - 'E0239': ('Inheriting %r, which is not a class.', - 'inherit-non-class', - 'Used when a class inherits from something which is not a ' - 'class.'), - 'E0240': ('Inconsistent method resolution order for class %r', - 'inconsistent-mro', - 'Used when a class has an inconsistent method resolution order.'), - 'E0241': ('Duplicate bases for class %r', - 'duplicate-bases', - 'Used when a class has duplicate bases.'), - 'R0202': ('Consider using a decorator instead of calling classmethod', - 'no-classmethod-decorator', - 'Used when a class method is defined without using the decorator ' - 'syntax.'), - 'R0203': ('Consider using a decorator instead of calling staticmethod', - 'no-staticmethod-decorator', - 'Used when a static method is defined without using the decorator ' - 'syntax.'), - 'C0205': ('Class __slots__ should be a non-string iterable', - 'single-string-used-for-slots', - 'Used when a class __slots__ is a simple string, rather ' - 'than an iterable.'), - } - - -class ScopeAccessMap(object): - """Store the accessed variables per scope.""" - - def __init__(self): - self._scopes = collections.defaultdict( - lambda: collections.defaultdict(list) - ) - - def set_accessed(self, node): - """Set the given node as accessed.""" - - frame = node_frame_class(node) - if frame is None: - # The node does not live in a class. - return - self._scopes[frame][node.attrname].append(node) - - def accessed(self, scope): - """Get the accessed variables for the given scope.""" - return self._scopes.get(scope, {}) - - -class ClassChecker(BaseChecker): - """checks for : - * methods without self as first argument - * overridden methods signature - * access only to existent members via self - * attributes not defined in the __init__ method - * unreachable code - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'classes' - # messages - msgs = MSGS - priority = -2 - # configuration options - options = (('defining-attr-methods', - {'default' : ('__init__', '__new__', 'setUp'), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of method names used to declare (i.e. assign) \ -instance attributes.'} - ), - ('valid-classmethod-first-arg', - {'default' : ('cls',), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of valid names for the first argument in \ -a class method.'} - ), - ('valid-metaclass-classmethod-first-arg', - {'default' : ('mcs',), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of valid names for the first argument in \ -a metaclass class method.'} - ), - ('exclude-protected', - { - 'default': ( - # namedtuple public API. - '_asdict', '_fields', '_replace', '_source', '_make'), - 'type': 'csv', - 'metavar': '', - 'help': ('List of member names, which should be excluded ' - 'from the protected access warning.')} - )) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self._accessed = ScopeAccessMap() - self._first_attrs = [] - self._meth_could_be_func = None - - @decorators.cachedproperty - def _dummy_rgx(self): - return get_global_option( - self, 'dummy-variables-rgx', default=None) - - @decorators.cachedproperty - def _ignore_mixin(self): - return get_global_option( - self, 'ignore-mixin-members', default=True) - - def visit_classdef(self, node): - """init visit variable _accessed - """ - self._check_bases_classes(node) - # if not an exception or a metaclass - if node.type == 'class' and has_known_bases(node): - try: - node.local_attr('__init__') - except astroid.NotFoundError: - self.add_message('no-init', args=node, node=node) - self._check_slots(node) - self._check_proper_bases(node) - self._check_consistent_mro(node) - - def _check_consistent_mro(self, node): - """Detect that a class has a consistent mro or duplicate bases.""" - try: - node.mro() - except InconsistentMroError: - self.add_message('inconsistent-mro', args=node.name, node=node) - except DuplicateBasesError: - self.add_message('duplicate-bases', args=node.name, node=node) - except NotImplementedError: - # Old style class, there's no mro so don't do anything. - pass - - def _check_proper_bases(self, node): - """ - Detect that a class inherits something which is not - a class or a type. - """ - for base in node.bases: - ancestor = safe_infer(base) - if ancestor in (astroid.YES, None): - continue - if (isinstance(ancestor, astroid.Instance) and - ancestor.is_subtype_of('%s.type' % (BUILTINS,))): - continue - - if (not isinstance(ancestor, astroid.ClassDef) or - _is_invalid_base_class(ancestor)): - self.add_message('inherit-non-class', - args=base.as_string(), node=node) - - def leave_classdef(self, cnode): - """close a class node: - check that instance attributes are defined in __init__ and check - access to existent members - """ - # check access to existent members on non metaclass classes - if self._ignore_mixin and cnode.name[-5:].lower() == 'mixin': - # We are in a mixin class. No need to try to figure out if - # something is missing, since it is most likely that it will - # miss. - return - - accessed = self._accessed.accessed(cnode) - if cnode.type != 'metaclass': - self._check_accessed_members(cnode, accessed) - # checks attributes are defined in an allowed method such as __init__ - if not self.linter.is_message_enabled('attribute-defined-outside-init'): - return - defining_methods = self.config.defining_attr_methods - current_module = cnode.root() - for attr, nodes in six.iteritems(cnode.instance_attrs): - # skip nodes which are not in the current module and it may screw up - # the output, while it's not worth it - nodes = [n for n in nodes if not - isinstance(n.statement(), (astroid.Delete, astroid.AugAssign)) - and n.root() is current_module] - if not nodes: - continue # error detected by typechecking - # check if any method attr is defined in is a defining method - if any(node.frame().name in defining_methods - for node in nodes): - continue - - # check attribute is defined in a parent's __init__ - for parent in cnode.instance_attr_ancestors(attr): - attr_defined = False - # check if any parent method attr is defined in is a defining method - for node in parent.instance_attrs[attr]: - if node.frame().name in defining_methods: - attr_defined = True - if attr_defined: - # we're done :) - break - else: - # check attribute is defined as a class attribute - try: - cnode.local_attr(attr) - except astroid.NotFoundError: - for node in nodes: - if node.frame().name not in defining_methods: - # If the attribute was set by a callfunc in any - # of the defining methods, then don't emit - # the warning. - if _called_in_methods(node.frame(), cnode, - defining_methods): - continue - self.add_message('attribute-defined-outside-init', - args=attr, node=node) - - def visit_functiondef(self, node): - """check method arguments, overriding""" - # ignore actual functions - if not node.is_method(): - return - - self._check_useless_super_delegation(node) - - klass = node.parent.frame() - self._meth_could_be_func = True - # check first argument is self if this is actually a method - self._check_first_arg_for_type(node, klass.type == 'metaclass') - if node.name == '__init__': - self._check_init(node) - return - # check signature if the method overloads inherited method - for overridden in klass.local_attr_ancestors(node.name): - # get astroid for the searched method - try: - meth_node = overridden[node.name] - except KeyError: - # we have found the method but it's not in the local - # dictionary. - # This may happen with astroid build from living objects - continue - if not isinstance(meth_node, astroid.FunctionDef): - continue - self._check_signature(node, meth_node, 'overridden', klass) - break - if node.decorators: - for decorator in node.decorators.nodes: - if isinstance(decorator, astroid.Attribute) and \ - decorator.attrname in ('getter', 'setter', 'deleter'): - # attribute affectation will call this method, not hiding it - return - if isinstance(decorator, astroid.Name) and decorator.name == 'property': - # attribute affectation will either call a setter or raise - # an attribute error, anyway not hiding the function - return - # check if the method is hidden by an attribute - try: - overridden = klass.instance_attr(node.name)[0] # XXX - overridden_frame = overridden.frame() - if (isinstance(overridden_frame, astroid.FunctionDef) - and overridden_frame.type == 'method'): - overridden_frame = overridden_frame.parent.frame() - if (isinstance(overridden_frame, astroid.ClassDef) - and klass.is_subtype_of(overridden_frame.qname())): - args = (overridden.root().name, overridden.fromlineno) - self.add_message('method-hidden', args=args, node=node) - except astroid.NotFoundError: - pass - - visit_asyncfunctiondef = visit_functiondef - - def _check_useless_super_delegation(self, function): - '''Check if the given function node is an useless method override - - We consider it *useless* if it uses the super() builtin, but having - nothing additional whatsoever than not implementing the method at all. - If the method uses super() to delegate an operation to the rest of the MRO, - and if the method called is the same as the current one, the arguments - passed to super() are the same as the parameters that were passed to - this method, then the method could be removed altogether, by letting - other implementation to take precedence. - ''' - - if not function.is_method(): - return - - if function.decorators: - # With decorators is a change of use - return - - body = function.body - if len(body) != 1: - # Multiple statements, which means this overridden method - # could do multiple things we are not aware of. - return - - statement = body[0] - if not isinstance(statement, (astroid.Expr, astroid.Return)): - # Doing something else than what we are interested into. - return - - call = statement.value - if not isinstance(call, astroid.Call): - return - if not isinstance(call.func, astroid.Attribute): - # Not a super() attribute access. - return - - # Should be a super call. - try: - super_call = next(call.func.expr.infer()) - except astroid.InferenceError: - return - else: - if not isinstance(super_call, objects.Super): - return - - # The name should be the same. - if call.func.attrname != function.name: - return - - # Should be a super call with the MRO pointer being the current class - # and the type being the current instance. - current_scope = function.parent.scope() - if super_call.mro_pointer != current_scope: - return - if not isinstance(super_call.type, astroid.Instance): - return - if super_call.type.name != current_scope.name: - return - - # Detect if the parameters are the same as the call's arguments. - params = _signature_from_arguments(function.args) - args = _signature_from_call(call) - if _definition_equivalent_to_call(params, args): - self.add_message('useless-super-delegation', node=function, - args=(function.name, )) - - def _check_slots(self, node): - if '__slots__' not in node.locals: - return - for slots in node.igetattr('__slots__'): - # check if __slots__ is a valid type - if slots is astroid.YES: - continue - if not is_iterable(slots) and not is_comprehension(slots): - self.add_message('invalid-slots', node=node) - continue - - if isinstance(slots, astroid.Const): - # a string, ignore the following checks - self.add_message('single-string-used-for-slots', node=node) - continue - if not hasattr(slots, 'itered'): - # we can't obtain the values, maybe a .deque? - continue - - if isinstance(slots, astroid.Dict): - values = [item[0] for item in slots.items] - else: - values = slots.itered() - if values is astroid.YES: - return - - for elt in values: - try: - self._check_slots_elt(elt) - except astroid.InferenceError: - continue - - def _check_slots_elt(self, elt): - for infered in elt.infer(): - if infered is astroid.YES: - continue - if (not isinstance(infered, astroid.Const) or - not isinstance(infered.value, six.string_types)): - self.add_message('invalid-slots-object', - args=infered.as_string(), - node=elt) - continue - if not infered.value: - self.add_message('invalid-slots-object', - args=infered.as_string(), - node=elt) - - def leave_functiondef(self, node): - """on method node, check if this method couldn't be a function - - ignore class, static and abstract methods, initializer, - methods overridden from a parent class. - """ - if node.is_method(): - if node.args.args is not None: - self._first_attrs.pop() - if not self.linter.is_message_enabled('no-self-use'): - return - class_node = node.parent.frame() - if (self._meth_could_be_func and node.type == 'method' - and node.name not in PYMETHODS - and not (node.is_abstract() or - overrides_a_method(class_node, node.name) or - decorated_with_property(node) or - (six.PY3 and _has_bare_super_call(node)))): - self.add_message('no-self-use', node=node) - - def visit_attribute(self, node): - """check if the getattr is an access to a class member - if so, register it. Also check for access to protected - class member from outside its class (but ignore __special__ - methods) - """ - # Check self - if self._uses_mandatory_method_param(node): - self._accessed.set_accessed(node) - return - if not self.linter.is_message_enabled('protected-access'): - return - - self._check_protected_attribute_access(node) - - def visit_assignattr(self, node): - if (isinstance(node.assign_type(), astroid.AugAssign) and - self._uses_mandatory_method_param(node)): - self._accessed.set_accessed(node) - self._check_in_slots(node) - - def _check_in_slots(self, node): - """ Check that the given assattr node - is defined in the class slots. - """ - infered = safe_infer(node.expr) - if infered and isinstance(infered, astroid.Instance): - klass = infered._proxied - if '__slots__' not in klass.locals or not klass.newstyle: - return - - slots = klass.slots() - if slots is None: - return - # If any ancestor doesn't use slots, the slots - # defined for this class are superfluous. - if any('__slots__' not in ancestor.locals and - ancestor.name != 'object' - for ancestor in klass.ancestors()): - return - - if not any(slot.value == node.attrname for slot in slots): - # If we have a '__dict__' in slots, then - # assigning any name is valid. - if not any(slot.value == '__dict__' for slot in slots): - if _is_attribute_property(node.attrname, klass): - # Properties circumvent the slots mechanism, - # so we should not emit a warning for them. - return - if (node.attrname in klass.locals - and _has_data_descriptor(klass, node.attrname)): - # Descriptors circumvent the slots mechanism as well. - return - self.add_message('assigning-non-slot', - args=(node.attrname, ), node=node) - - @check_messages('protected-access', 'no-classmethod-decorator', - 'no-staticmethod-decorator') - def visit_assign(self, assign_node): - self._check_classmethod_declaration(assign_node) - node = assign_node.targets[0] - if not isinstance(node, astroid.AssignAttr): - return - - if self._uses_mandatory_method_param(node): - return - self._check_protected_attribute_access(node) - - def _check_classmethod_declaration(self, node): - """Checks for uses of classmethod() or staticmethod() - - When a @classmethod or @staticmethod decorator should be used instead. - A message will be emitted only if the assignment is at a class scope - and only if the classmethod's argument belongs to the class where it - is defined. - `node` is an assign node. - """ - if not isinstance(node.value, astroid.Call): - return - - # check the function called is "classmethod" or "staticmethod" - func = node.value.func - if (not isinstance(func, astroid.Name) or - func.name not in ('classmethod', 'staticmethod')): - return - - msg = ('no-classmethod-decorator' if func.name == 'classmethod' else - 'no-staticmethod-decorator') - # assignment must be at a class scope - parent_class = node.scope() - if not isinstance(parent_class, astroid.ClassDef): - return - - # Check if the arg passed to classmethod is a class member - classmeth_arg = node.value.args[0] - if not isinstance(classmeth_arg, astroid.Name): - return - - method_name = classmeth_arg.name - if any(method_name == member.name - for member in parent_class.mymethods()): - self.add_message(msg, node=node.targets[0]) - - def _check_protected_attribute_access(self, node): - '''Given an attribute access node (set or get), check if attribute - access is legitimate. Call _check_first_attr with node before calling - this method. Valid cases are: - * self._attr in a method or cls._attr in a classmethod. Checked by - _check_first_attr. - * Klass._attr inside "Klass" class. - * Klass2._attr inside "Klass" class when Klass2 is a base class of - Klass. - ''' - attrname = node.attrname - - if (is_attr_protected(attrname) and - attrname not in self.config.exclude_protected): - - klass = node_frame_class(node) - - # XXX infer to be more safe and less dirty ?? - # in classes, check we are not getting a parent method - # through the class object or through super - callee = node.expr.as_string() - - # We are not in a class, no remaining valid case - if klass is None: - self.add_message('protected-access', node=node, args=attrname) - return - - # If the expression begins with a call to super, that's ok. - if isinstance(node.expr, astroid.Call) and \ - isinstance(node.expr.func, astroid.Name) and \ - node.expr.func.name == 'super': - return - - # If the expression begins with a call to type(self), that's ok. - if self._is_type_self_call(node.expr): - return - - # We are in a class, one remaining valid cases, Klass._attr inside - # Klass - if not (callee == klass.name or callee in klass.basenames): - # Detect property assignments in the body of the class. - # This is acceptable: - # - # class A: - # b = property(lambda: self._b) - - stmt = node.parent.statement() - if (isinstance(stmt, astroid.Assign) - and len(stmt.targets) == 1 - and isinstance(stmt.targets[0], astroid.AssignName)): - name = stmt.targets[0].name - if _is_attribute_property(name, klass): - return - - self.add_message('protected-access', node=node, args=attrname) - - def _is_type_self_call(self, expr): - return (isinstance(expr, astroid.Call) and - isinstance(expr.func, astroid.Name) and - expr.func.name == 'type' and len(expr.args) == 1 and - self._is_mandatory_method_param(expr.args[0])) - - def visit_name(self, node): - """check if the name handle an access to a class member - if so, register it - """ - if self._first_attrs and (node.name == self._first_attrs[-1] or - not self._first_attrs[-1]): - self._meth_could_be_func = False - - def _check_accessed_members(self, node, accessed): - """check that accessed members are defined""" - # XXX refactor, probably much simpler now that E0201 is in type checker - excs = ('AttributeError', 'Exception', 'BaseException') - for attr, nodes in six.iteritems(accessed): - try: - # is it a class attribute ? - node.local_attr(attr) - # yes, stop here - continue - except astroid.NotFoundError: - pass - # is it an instance attribute of a parent class ? - try: - next(node.instance_attr_ancestors(attr)) - # yes, stop here - continue - except StopIteration: - pass - # is it an instance attribute ? - try: - defstmts = node.instance_attr(attr) - except astroid.NotFoundError: - pass - else: - # filter out augment assignment nodes - defstmts = [stmt for stmt in defstmts if stmt not in nodes] - if not defstmts: - # only augment assignment for this node, no-member should be - # triggered by the typecheck checker - continue - # filter defstmts to only pick the first one when there are - # several assignments in the same scope - scope = defstmts[0].scope() - defstmts = [stmt for i, stmt in enumerate(defstmts) - if i == 0 or stmt.scope() is not scope] - # if there are still more than one, don't attempt to be smarter - # than we can be - if len(defstmts) == 1: - defstmt = defstmts[0] - # check that if the node is accessed in the same method as - # it's defined, it's accessed after the initial assignment - frame = defstmt.frame() - lno = defstmt.fromlineno - for _node in nodes: - if _node.frame() is frame and _node.fromlineno < lno \ - and not astroid.are_exclusive(_node.statement(), defstmt, excs): - self.add_message('access-member-before-definition', - node=_node, args=(attr, lno)) - - def _check_first_arg_for_type(self, node, metaclass=0): - """check the name of first argument, expect: - - * 'self' for a regular method - * 'cls' for a class method or a metaclass regular method (actually - valid-classmethod-first-arg value) - * 'mcs' for a metaclass class method (actually - valid-metaclass-classmethod-first-arg) - * not one of the above for a static method - """ - # don't care about functions with unknown argument (builtins) - if node.args.args is None: - return - first_arg = node.args.args and node.argnames()[0] - self._first_attrs.append(first_arg) - first = self._first_attrs[-1] - # static method - if node.type == 'staticmethod': - if (first_arg == 'self' or - first_arg in self.config.valid_classmethod_first_arg or - first_arg in self.config.valid_metaclass_classmethod_first_arg): - self.add_message('bad-staticmethod-argument', args=first, node=node) - return - self._first_attrs[-1] = None - # class / regular method with no args - elif not node.args.args: - self.add_message('no-method-argument', node=node) - # metaclass - elif metaclass: - # metaclass __new__ or classmethod - if node.type == 'classmethod': - self._check_first_arg_config( - first, - self.config.valid_metaclass_classmethod_first_arg, node, - 'bad-mcs-classmethod-argument', node.name) - # metaclass regular method - else: - self._check_first_arg_config( - first, - self.config.valid_classmethod_first_arg, node, - 'bad-mcs-method-argument', - node.name) - # regular class - else: - # class method - if node.type == 'classmethod': - self._check_first_arg_config( - first, - self.config.valid_classmethod_first_arg, node, - 'bad-classmethod-argument', - node.name) - # regular method without self as argument - elif first != 'self': - self.add_message('no-self-argument', node=node) - - def _check_first_arg_config(self, first, config, node, message, - method_name): - if first not in config: - if len(config) == 1: - valid = repr(config[0]) - else: - valid = ', '.join(repr(v) for v in config[:-1]) - valid = '%s or %r' % (valid, config[-1]) - self.add_message(message, args=(method_name, valid), node=node) - - def _check_bases_classes(self, node): - """check that the given class node implements abstract methods from - base classes - """ - def is_abstract(method): - return method.is_abstract(pass_is_abstract=False) - - # check if this class abstract - if class_is_abstract(node): - return - - methods = sorted( - unimplemented_abstract_methods(node, is_abstract).items(), - key=lambda item: item[0], - ) - for name, method in methods: - owner = method.parent.frame() - if owner is node: - continue - # owner is not this class, it must be a parent class - # check that the ancestor's method is not abstract - if name in node.locals: - # it is redefined as an attribute or with a descriptor - continue - self.add_message('abstract-method', node=node, - args=(name, owner.name)) - - def _check_init(self, node): - """check that the __init__ method call super or ancestors'__init__ - method - """ - if (not self.linter.is_message_enabled('super-init-not-called') and - not self.linter.is_message_enabled('non-parent-init-called')): - return - klass_node = node.parent.frame() - to_call = _ancestors_to_call(klass_node) - not_called_yet = dict(to_call) - for stmt in node.nodes_of_class(astroid.Call): - expr = stmt.func - if not isinstance(expr, astroid.Attribute) \ - or expr.attrname != '__init__': - continue - # skip the test if using super - if isinstance(expr.expr, astroid.Call) and \ - isinstance(expr.expr.func, astroid.Name) and \ - expr.expr.func.name == 'super': - return - try: - for klass in expr.expr.infer(): - if klass is astroid.YES: - continue - # The infered klass can be super(), which was - # assigned to a variable and the `__init__` - # was called later. - # - # base = super() - # base.__init__(...) - - if (isinstance(klass, astroid.Instance) and - isinstance(klass._proxied, astroid.ClassDef) and - is_builtin_object(klass._proxied) and - klass._proxied.name == 'super'): - return - elif isinstance(klass, objects.Super): - return - try: - del not_called_yet[klass] - except KeyError: - if klass not in to_call: - self.add_message('non-parent-init-called', - node=expr, args=klass.name) - except astroid.InferenceError: - continue - for klass, method in six.iteritems(not_called_yet): - cls = node_frame_class(method) - if klass.name == 'object' or (cls and cls.name == 'object'): - continue - self.add_message('super-init-not-called', args=klass.name, node=node) - - def _check_signature(self, method1, refmethod, class_type, cls): - """check that the signature of the two given methods match - """ - if not (isinstance(method1, astroid.FunctionDef) - and isinstance(refmethod, astroid.FunctionDef)): - self.add_message('method-check-failed', - args=(method1, refmethod), node=method1) - return - - instance = cls.instantiate_class() - method1 = function_to_method(method1, instance) - refmethod = function_to_method(refmethod, instance) - - # Don't care about functions with unknown argument (builtins). - if method1.args.args is None or refmethod.args.args is None: - return - - # Ignore private to class methods. - if is_attr_private(method1.name): - return - # Ignore setters, they have an implicit extra argument, - # which shouldn't be taken in consideration. - if method1.decorators: - for decorator in method1.decorators.nodes: - if (isinstance(decorator, astroid.Attribute) and - decorator.attrname == 'setter'): - return - - if _different_parameters( - refmethod, method1, - dummy_parameter_regex=self._dummy_rgx): - self.add_message('arguments-differ', - args=(class_type, method1.name), - node=method1) - elif len(method1.args.defaults) < len(refmethod.args.defaults): - self.add_message('signature-differs', - args=(class_type, method1.name), - node=method1) - - def _uses_mandatory_method_param(self, node): - """Check that attribute lookup name use first attribute variable name - - Name is `self` for method, `cls` for classmethod and `mcs` for metaclass. - """ - return self._is_mandatory_method_param(node.expr) - - def _is_mandatory_method_param(self, node): - """Check if astroid.Name corresponds to first attribute variable name - - Name is `self` for method, `cls` for classmethod and `mcs` for metaclass. - """ - return (self._first_attrs and isinstance(node, astroid.Name) - and node.name == self._first_attrs[-1]) - - -class SpecialMethodsChecker(BaseChecker): - """Checker which verifies that special methods - are implemented correctly. - """ - __implements__ = (IAstroidChecker, ) - name = 'classes' - msgs = { - 'E0301': ('__iter__ returns non-iterator', - 'non-iterator-returned', - 'Used when an __iter__ method returns something which is not an ' - 'iterable (i.e. has no `%s` method)' % NEXT_METHOD, - {'old_names': [('W0234', 'non-iterator-returned'), - ('E0234', 'non-iterator-returned')]}), - 'E0302': ('The special method %r expects %s param(s), %d %s given', - 'unexpected-special-method-signature', - 'Emitted when a special method was defined with an ' - 'invalid number of parameters. If it has too few or ' - 'too many, it might not work at all.', - {'old_names': [('E0235', 'bad-context-manager')]}), - 'E0303': ('__len__ does not return non-negative integer', - 'invalid-length-returned', - 'Used when an __len__ method returns something which is not a ' - 'non-negative integer', {}), - } - priority = -2 - - @check_messages('unexpected-special-method-signature', - 'non-iterator-returned', 'invalid-length-returned') - def visit_functiondef(self, node): - if not node.is_method(): - return - if node.name == '__iter__': - self._check_iter(node) - if node.name == '__len__': - self._check_len(node) - if node.name in PYMETHODS: - self._check_unexpected_method_signature(node) - - visit_asyncfunctiondef = visit_functiondef - - def _check_unexpected_method_signature(self, node): - expected_params = SPECIAL_METHODS_PARAMS[node.name] - - if expected_params is None: - # This can support a variable number of parameters. - return - if not node.args.args and not node.args.vararg: - # Method has no parameter, will be caught - # by no-method-argument. - return - - if decorated_with(node, [BUILTINS + ".staticmethod"]): - # We expect to not take in consideration self. - all_args = node.args.args - else: - all_args = node.args.args[1:] - mandatory = len(all_args) - len(node.args.defaults) - optional = len(node.args.defaults) - current_params = mandatory + optional - - if isinstance(expected_params, tuple): - # The expected number of parameters can be any value from this - # tuple, although the user should implement the method - # to take all of them in consideration. - emit = mandatory not in expected_params - expected_params = "between %d or %d" % expected_params - else: - # If the number of mandatory parameters doesn't - # suffice, the expected parameters for this - # function will be deduced from the optional - # parameters. - rest = expected_params - mandatory - if rest == 0: - emit = False - elif rest < 0: - emit = True - elif rest > 0: - emit = not ((optional - rest) >= 0 or node.args.vararg) - - if emit: - verb = "was" if current_params <= 1 else "were" - self.add_message('unexpected-special-method-signature', - args=(node.name, expected_params, current_params, verb), - node=node) - - @staticmethod - def _is_iterator(node): - if node is astroid.YES: - # Just ignore YES objects. - return True - if isinstance(node, Generator): - # Generators can be itered. - return True - - if isinstance(node, astroid.Instance): - try: - node.local_attr(NEXT_METHOD) - return True - except astroid.NotFoundError: - pass - elif isinstance(node, astroid.ClassDef): - metaclass = node.metaclass() - if metaclass and isinstance(metaclass, astroid.ClassDef): - try: - metaclass.local_attr(NEXT_METHOD) - return True - except astroid.NotFoundError: - pass - return False - - def _check_iter(self, node): - infered = _safe_infer_call_result(node, node) - if infered is not None: - if not self._is_iterator(infered): - self.add_message('non-iterator-returned', node=node) - - def _check_len(self, node): - inferred = _safe_infer_call_result(node, node) - if not inferred: - return - - if not isinstance(inferred, astroid.Const): - self.add_message('invalid-length-returned', node=node) - return - - value = inferred.value - if not isinstance(value, six.integer_types) or value < 0: - self.add_message('invalid-length-returned', node=node) - - -def _ancestors_to_call(klass_node, method='__init__'): - """return a dictionary where keys are the list of base classes providing - the queried method, and so that should/may be called from the method node - """ - to_call = {} - for base_node in klass_node.ancestors(recurs=False): - try: - to_call[base_node] = next(base_node.igetattr(method)) - except astroid.InferenceError: - continue - return to_call - - -def node_method(node, method_name): - """get astroid for on the given class node, ensuring it - is a Function node - """ - for node_attr in node.local_attr(method_name): - if isinstance(node_attr, astroid.Function): - return node_attr - raise astroid.NotFoundError(method_name) - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(ClassChecker(linter)) - linter.register_checker(SpecialMethodsChecker(linter)) diff --git a/pymode/libs/pylint/checkers/design_analysis.py b/pymode/libs/pylint/checkers/design_analysis.py deleted file mode 100644 index c34ec4dc..00000000 --- a/pymode/libs/pylint/checkers/design_analysis.py +++ /dev/null @@ -1,334 +0,0 @@ -# Copyright (c) 2006, 2009-2010, 2012-2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""check for signs of poor design""" - -from collections import defaultdict - -from astroid import If, BoolOp -from astroid import decorators - -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages -from pylint import utils - - -MSGS = { - 'R0901': ('Too many ancestors (%s/%s)', - 'too-many-ancestors', - 'Used when class has too many parent classes, try to reduce \ - this to get a simpler (and so easier to use) class.'), - 'R0902': ('Too many instance attributes (%s/%s)', - 'too-many-instance-attributes', - 'Used when class has too many instance attributes, try to reduce \ - this to get a simpler (and so easier to use) class.'), - 'R0903': ('Too few public methods (%s/%s)', - 'too-few-public-methods', - 'Used when class has too few public methods, so be sure it\'s \ - really worth it.'), - 'R0904': ('Too many public methods (%s/%s)', - 'too-many-public-methods', - 'Used when class has too many public methods, try to reduce \ - this to get a simpler (and so easier to use) class.'), - - 'R0911': ('Too many return statements (%s/%s)', - 'too-many-return-statements', - 'Used when a function or method has too many return statement, \ - making it hard to follow.'), - 'R0912': ('Too many branches (%s/%s)', - 'too-many-branches', - 'Used when a function or method has too many branches, \ - making it hard to follow.'), - 'R0913': ('Too many arguments (%s/%s)', - 'too-many-arguments', - 'Used when a function or method takes too many arguments.'), - 'R0914': ('Too many local variables (%s/%s)', - 'too-many-locals', - 'Used when a function or method has too many local variables.'), - 'R0915': ('Too many statements (%s/%s)', - 'too-many-statements', - 'Used when a function or method has too many statements. You \ - should then split it in smaller functions / methods.'), - 'R0916': ('Too many boolean expressions in if statement (%s/%s)', - 'too-many-boolean-expressions', - 'Used when a if statement contains too many boolean ' - 'expressions'), - } - - -def _count_boolean_expressions(bool_op): - """Counts the number of boolean expressions in BoolOp `bool_op` (recursive) - - example: a and (b or c or (d and e)) ==> 5 boolean expressions - """ - nb_bool_expr = 0 - for bool_expr in bool_op.get_children(): - if isinstance(bool_expr, BoolOp): - nb_bool_expr += _count_boolean_expressions(bool_expr) - else: - nb_bool_expr += 1 - return nb_bool_expr - - -class MisdesignChecker(BaseChecker): - """checks for sign of poor/misdesign: - * number of methods, attributes, local variables... - * size, complexity of functions, methods - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'design' - # messages - msgs = MSGS - priority = -2 - # configuration options - options = (('max-args', - {'default' : 5, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of arguments for function / method'} - ), - ('max-locals', - {'default' : 15, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of locals for function / method body'} - ), - ('max-returns', - {'default' : 6, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of return / yield for function / ' - 'method body'} - ), - ('max-branches', - {'default' : 12, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of branch for function / method body'} - ), - ('max-statements', - {'default' : 50, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of statements in function / method ' - 'body'} - ), - ('max-parents', - {'default' : 7, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Maximum number of parents for a class (see R0901).'} - ), - ('max-attributes', - {'default' : 7, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Maximum number of attributes for a class \ -(see R0902).'} - ), - ('min-public-methods', - {'default' : 2, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Minimum number of public methods for a class \ -(see R0903).'} - ), - ('max-public-methods', - {'default' : 20, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Maximum number of public methods for a class \ -(see R0904).'} - ), - ('max-bool-expr', - {'default': 5, - 'type': 'int', - 'metavar': '', - 'help': 'Maximum number of boolean expressions in a if ' - 'statement'} - ), - ) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self.stats = None - self._returns = None - self._branches = None - self._stmts = 0 - - def open(self): - """initialize visit variables""" - self.stats = self.linter.add_stats() - self._returns = [] - self._branches = defaultdict(int) - - @decorators.cachedproperty - def _ignored_argument_names(self): - return utils.get_global_option(self, 'ignored-argument-names', default=None) - - @check_messages('too-many-ancestors', 'too-many-instance-attributes', - 'too-few-public-methods', 'too-many-public-methods') - def visit_classdef(self, node): - """check size of inheritance hierarchy and number of instance attributes - """ - nb_parents = len(list(node.ancestors())) - if nb_parents > self.config.max_parents: - self.add_message('too-many-ancestors', node=node, - args=(nb_parents, self.config.max_parents)) - - if len(node.instance_attrs) > self.config.max_attributes: - self.add_message('too-many-instance-attributes', node=node, - args=(len(node.instance_attrs), - self.config.max_attributes)) - - @check_messages('too-few-public-methods', 'too-many-public-methods') - def leave_classdef(self, node): - """check number of public methods""" - my_methods = sum(1 for method in node.mymethods() - if not method.name.startswith('_')) - all_methods = sum(1 for method in node.methods() - if not method.name.startswith('_')) - - # Does the class contain less than n public methods ? - # This checks only the methods defined in the current class, - # since the user might not have control over the classes - # from the ancestors. It avoids some false positives - # for classes such as unittest.TestCase, which provides - # a lot of assert methods. It doesn't make sense to warn - # when the user subclasses TestCase to add his own tests. - if my_methods > self.config.max_public_methods: - self.add_message('too-many-public-methods', node=node, - args=(my_methods, - self.config.max_public_methods)) - # stop here for exception, metaclass and interface classes - if node.type != 'class': - return - - # Does the class contain more than n public methods ? - # This checks all the methods defined by ancestors and - # by the current class. - if all_methods < self.config.min_public_methods: - self.add_message('too-few-public-methods', node=node, - args=(all_methods, - self.config.min_public_methods)) - - @check_messages('too-many-return-statements', 'too-many-branches', - 'too-many-arguments', 'too-many-locals', - 'too-many-statements') - def visit_functiondef(self, node): - """check function name, docstring, arguments, redefinition, - variable names, max locals - """ - # init branch and returns counters - self._returns.append(0) - # check number of arguments - args = node.args.args - ignored_argument_names = self._ignored_argument_names - if args is not None: - ignored_args_num = 0 - if ignored_argument_names: - ignored_args_num = sum(1 for arg in args if ignored_argument_names.match(arg.name)) - - argnum = len(args) - ignored_args_num - if argnum > self.config.max_args: - self.add_message('too-many-arguments', node=node, - args=(len(args), self.config.max_args)) - else: - ignored_args_num = 0 - # check number of local variables - locnum = len(node.locals) - ignored_args_num - if locnum > self.config.max_locals: - self.add_message('too-many-locals', node=node, - args=(locnum, self.config.max_locals)) - # init statements counter - self._stmts = 1 - - visit_asyncfunctiondef = visit_functiondef - - @check_messages('too-many-return-statements', 'too-many-branches', - 'too-many-arguments', 'too-many-locals', - 'too-many-statements') - def leave_functiondef(self, node): - """most of the work is done here on close: - checks for max returns, branch, return in __init__ - """ - returns = self._returns.pop() - if returns > self.config.max_returns: - self.add_message('too-many-return-statements', node=node, - args=(returns, self.config.max_returns)) - branches = self._branches[node] - if branches > self.config.max_branches: - self.add_message('too-many-branches', node=node, - args=(branches, self.config.max_branches)) - # check number of statements - if self._stmts > self.config.max_statements: - self.add_message('too-many-statements', node=node, - args=(self._stmts, self.config.max_statements)) - - leave_asyncfunctiondef = leave_functiondef - - def visit_return(self, _): - """count number of returns""" - if not self._returns: - return # return outside function, reported by the base checker - self._returns[-1] += 1 - - def visit_default(self, node): - """default visit method -> increments the statements counter if - necessary - """ - if node.is_statement: - self._stmts += 1 - - def visit_tryexcept(self, node): - """increments the branches counter""" - branches = len(node.handlers) - if node.orelse: - branches += 1 - self._inc_branch(node, branches) - self._stmts += branches - - def visit_tryfinally(self, node): - """increments the branches counter""" - self._inc_branch(node, 2) - self._stmts += 2 - - @check_messages('too-many-boolean-expressions') - def visit_if(self, node): - """increments the branches counter and checks boolean expressions""" - self._check_boolean_expressions(node) - branches = 1 - # don't double count If nodes coming from some 'elif' - if node.orelse and (len(node.orelse) > 1 or - not isinstance(node.orelse[0], If)): - branches += 1 - self._inc_branch(node, branches) - self._stmts += branches - - def _check_boolean_expressions(self, node): - """Go through "if" node `node` and counts its boolean expressions - - if the "if" node test is a BoolOp node - """ - condition = node.test - if not isinstance(condition, BoolOp): - return - nb_bool_expr = _count_boolean_expressions(condition) - if nb_bool_expr > self.config.max_bool_expr: - self.add_message('too-many-boolean-expressions', node=condition, - args=(nb_bool_expr, self.config.max_bool_expr)) - - def visit_while(self, node): - """increments the branches counter""" - branches = 1 - if node.orelse: - branches += 1 - self._inc_branch(node, branches) - - visit_for = visit_while - - def _inc_branch(self, node, branchesnum=1): - """increments the branches counter""" - self._branches[node.scope()] += branchesnum - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(MisdesignChecker(linter)) diff --git a/pymode/libs/pylint/checkers/exceptions.py b/pymode/libs/pylint/checkers/exceptions.py deleted file mode 100644 index dde3ae62..00000000 --- a/pymode/libs/pylint/checkers/exceptions.py +++ /dev/null @@ -1,389 +0,0 @@ -# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2011, 2013-2014 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2015 Steven Myint - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Checks for various exception related errors.""" - -import inspect -import sys - -import six -from six.moves import builtins - -import astroid -from pylint import checkers -from pylint.checkers import utils -from pylint import interfaces - - -def _builtin_exceptions(): - def predicate(obj): - return isinstance(obj, type) and issubclass(obj, BaseException) - - members = inspect.getmembers(six.moves.builtins, predicate) - return {exc.__name__ for (_, exc) in members} - - -def _annotated_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. - Returns an iterator which yields tuples in the format - ('original node', 'infered node'). - """ - if isinstance(stmt, (astroid.List, astroid.Tuple)): - for elt in stmt.elts: - inferred = utils.safe_infer(elt) - if inferred and inferred is not astroid.YES: - yield elt, inferred - return - for infered in stmt.infer(context): - if infered is astroid.YES: - continue - yield stmt, infered - - -PY3K = sys.version_info >= (3, 0) -OVERGENERAL_EXCEPTIONS = ('Exception',) -BUILTINS_NAME = builtins.__name__ - -MSGS = { - 'E0701': ('Bad except clauses order (%s)', - 'bad-except-order', - 'Used when except clauses are not in the correct order (from the ' - 'more specific to the more generic). If you don\'t fix the order, ' - 'some exceptions may not be caught by the most specific handler.'), - 'E0702': ('Raising %s while only classes or instances are allowed', - 'raising-bad-type', - 'Used when something which is neither a class, an instance or a \ - string is raised (i.e. a `TypeError` will be raised).'), - 'E0703': ('Exception context set to something which is not an ' - 'exception, nor None', - 'bad-exception-context', - 'Used when using the syntax "raise ... from ...", ' - 'where the exception context is not an exception, ' - 'nor None.', - {'minversion': (3, 0)}), - 'E0704': ('The raise statement is not inside an except clause', - 'misplaced-bare-raise', - 'Used when a bare raise is not used inside an except clause. ' - 'This generates an error, since there are no active exceptions ' - 'to be reraised. An exception to this rule is represented by ' - 'a bare raise inside a finally clause, which might work, as long ' - 'as an exception is raised inside the try block, but it is ' - 'nevertheless a code smell that must not be relied upon.'), - 'E0710': ('Raising a new style class which doesn\'t inherit from BaseException', - 'raising-non-exception', - 'Used when a new style class which doesn\'t inherit from \ - BaseException is raised.'), - 'E0711': ('NotImplemented raised - should raise NotImplementedError', - 'notimplemented-raised', - 'Used when NotImplemented is raised instead of \ - NotImplementedError'), - 'E0712': ('Catching an exception which doesn\'t inherit from Exception: %s', - 'catching-non-exception', - 'Used when a class which doesn\'t inherit from \ - Exception is used as an exception in an except clause.'), - 'W0702': ('No exception type(s) specified', - 'bare-except', - 'Used when an except clause doesn\'t specify exceptions type to \ - catch.'), - 'W0703': ('Catching too general exception %s', - 'broad-except', - 'Used when an except catches a too general exception, \ - possibly burying unrelated errors.'), - 'W0705': ('Catching previously caught exception type %s', - 'duplicate-except', - 'Used when an except catches a type that was already caught by ' - 'a previous handler.'), - 'W0710': ('Exception doesn\'t inherit from standard "Exception" class', - 'nonstandard-exception', - 'Used when a custom exception class is raised but doesn\'t \ - inherit from the builtin "Exception" class.', - {'maxversion': (3, 0)}), - 'W0711': ('Exception to catch is the result of a binary "%s" operation', - 'binary-op-exception', - 'Used when the exception to catch is of the form \ - "except A or B:". If intending to catch multiple, \ - rewrite as "except (A, B):"'), - } - - -class BaseVisitor(object): - """Base class for visitors defined in this module.""" - - def __init__(self, checker, node): - self._checker = checker - self._node = node - - def visit(self, node): - name = node.__class__.__name__.lower() - dispatch_meth = getattr(self, 'visit_' + name, None) - if dispatch_meth: - dispatch_meth(node) - else: - self.visit_default(node) - - def visit_default(self, node): # pylint: disable=unused-argument - """Default implementation for all the nodes.""" - - -class ExceptionRaiseRefVisitor(BaseVisitor): - """Visit references (anything that is not an AST leaf).""" - - def visit_name(self, name): - if name.name == 'NotImplemented': - self._checker.add_message( - 'notimplemented-raised', - node=self._node) - - def visit_call(self, call): - if isinstance(call.func, astroid.Name): - self.visit_name(call.func) - - -class ExceptionRaiseLeafVisitor(BaseVisitor): - """Visitor for handling leaf kinds of a raise value.""" - - def visit_const(self, const): - if not isinstance(const.value, str): - # raising-string will be emitted from python3 porting checker. - self._checker.add_message('raising-bad-type', node=self._node, - args=const.value.__class__.__name__) - - def visit_instance(self, instance): - # pylint: disable=protected-access - cls = instance._proxied - self.visit_classdef(cls) - - # Exception instances have a particular class type - visit_exceptioninstance = visit_instance - - def visit_classdef(self, cls): - if (not utils.inherit_from_std_ex(cls) and - utils.has_known_bases(cls)): - if cls.newstyle: - self._checker.add_message('raising-non-exception', node=self._node) - else: - self._checker.add_message('nonstandard-exception', node=self._node) - - def visit_tuple(self, tuple_node): - if PY3K or not tuple_node.elts: - self._checker.add_message('raising-bad-type', - node=self._node, - args='tuple') - return - - # On Python 2, using the following is not an error: - # raise (ZeroDivisionError, None) - # raise (ZeroDivisionError, ) - # What's left to do is to check that the first - # argument is indeed an exception. Verifying the other arguments - # is not the scope of this check. - first = tuple_node.elts[0] - inferred = utils.safe_infer(first) - if not inferred or inferred is astroid.Uninferable: - return - - if (isinstance(inferred, astroid.Instance) - and inferred.__class__.__name__ != 'Instance'): - # TODO: explain why - self.visit_default(tuple_node) - else: - self.visit(inferred) - - def visit_default(self, node): - name = getattr(node, 'name', node.__class__.__name__) - self._checker.add_message('raising-bad-type', - node=self._node, - args=name) - - -class ExceptionsChecker(checkers.BaseChecker): - """Exception related checks.""" - - __implements__ = interfaces.IAstroidChecker - - name = 'exceptions' - msgs = MSGS - priority = -4 - options = (('overgeneral-exceptions', - {'default' : OVERGENERAL_EXCEPTIONS, - 'type' : 'csv', 'metavar' : '', - 'help' : 'Exceptions that will emit a warning ' - 'when being caught. Defaults to "%s"' % ( - ', '.join(OVERGENERAL_EXCEPTIONS),)} - ), - ) - - def open(self): - self._builtin_exceptions = _builtin_exceptions() - super(ExceptionsChecker, self).open() - - @utils.check_messages('nonstandard-exception', 'misplaced-bare-raise', - 'raising-bad-type', 'raising-non-exception', - 'notimplemented-raised', 'bad-exception-context') - def visit_raise(self, node): - if node.exc is None: - self._check_misplaced_bare_raise(node) - return - - if PY3K and node.cause: - self._check_bad_exception_context(node) - - expr = node.exc - try: - inferred_value = next(expr.infer()) - except astroid.InferenceError: - inferred_value = None - - ExceptionRaiseRefVisitor(self, node).visit(expr) - - if inferred_value: - ExceptionRaiseLeafVisitor(self, node).visit(inferred_value) - - def _check_misplaced_bare_raise(self, node): - # Filter out if it's present in __exit__. - scope = node.scope() - if (isinstance(scope, astroid.FunctionDef) - and scope.is_method() - and scope.name == '__exit__'): - return - - current = node - # Stop when a new scope is generated or when the raise - # statement is found inside a TryFinally. - ignores = (astroid.ExceptHandler, astroid.FunctionDef, astroid.TryFinally) - while current and not isinstance(current.parent, ignores): - current = current.parent - - expected = (astroid.ExceptHandler,) - if not current or not isinstance(current.parent, expected): - self.add_message('misplaced-bare-raise', node=node) - - def _check_bad_exception_context(self, node): - """Verify that the exception context is properly set. - - An exception context can be only `None` or an exception. - """ - cause = utils.safe_infer(node.cause) - if cause in (astroid.YES, None): - return - - if isinstance(cause, astroid.Const): - if cause.value is not None: - self.add_message('bad-exception-context', - node=node) - elif (not isinstance(cause, astroid.ClassDef) and - not utils.inherit_from_std_ex(cause)): - self.add_message('bad-exception-context', - node=node) - - def _check_catching_non_exception(self, handler, exc, part): - if isinstance(exc, astroid.Tuple): - # Check if it is a tuple of exceptions. - inferred = [utils.safe_infer(elt) for elt in exc.elts] - if any(node is astroid.YES for node in inferred): - # Don't emit if we don't know every component. - return - if all(node and utils.inherit_from_std_ex(node) - for node in inferred): - return - - if not isinstance(exc, astroid.ClassDef): - # Don't emit the warning if the infered stmt - # is None, but the exception handler is something else, - # maybe it was redefined. - if (isinstance(exc, astroid.Const) and - exc.value is None): - if ((isinstance(handler.type, astroid.Const) and - handler.type.value is None) or - handler.type.parent_of(exc)): - # If the exception handler catches None or - # the exception component, which is None, is - # defined by the entire exception handler, then - # emit a warning. - self.add_message('catching-non-exception', - node=handler.type, - args=(part.as_string(), )) - else: - self.add_message('catching-non-exception', - node=handler.type, - args=(part.as_string(), )) - return - - if (not utils.inherit_from_std_ex(exc) and - exc.name not in self._builtin_exceptions): - if utils.has_known_bases(exc): - self.add_message('catching-non-exception', - node=handler.type, - args=(exc.name, )) - - @utils.check_messages('bare-except', 'broad-except', - 'binary-op-exception', 'bad-except-order', - 'catching-non-exception', 'duplicate-except') - def visit_tryexcept(self, node): - """check for empty except""" - exceptions_classes = [] - nb_handlers = len(node.handlers) - for index, handler in enumerate(node.handlers): - if handler.type is None: - if not utils.is_raising(handler.body): - self.add_message('bare-except', node=handler) - # check if a "except:" is followed by some other - # except - if index < (nb_handlers - 1): - msg = 'empty except clause should always appear last' - self.add_message('bad-except-order', node=node, args=msg) - - elif isinstance(handler.type, astroid.BoolOp): - self.add_message('binary-op-exception', - node=handler, args=handler.type.op) - else: - try: - excs = list(_annotated_unpack_infer(handler.type)) - except astroid.InferenceError: - continue - - for part, exc in excs: - if exc is astroid.YES: - continue - if (isinstance(exc, astroid.Instance) - and utils.inherit_from_std_ex(exc)): - # pylint: disable=protected-access - exc = exc._proxied - - self._check_catching_non_exception(handler, exc, part) - - if not isinstance(exc, astroid.ClassDef): - continue - - exc_ancestors = [anc for anc in exc.ancestors() - if isinstance(anc, astroid.ClassDef)] - - for previous_exc in exceptions_classes: - if previous_exc in exc_ancestors: - msg = '%s is an ancestor class of %s' % ( - previous_exc.name, exc.name) - self.add_message('bad-except-order', - node=handler.type, args=msg) - if (exc.name in self.config.overgeneral_exceptions - and exc.root().name == utils.EXCEPTIONS_MODULE - and not utils.is_raising(handler.body)): - self.add_message('broad-except', - args=exc.name, node=handler.type) - - if exc in exceptions_classes: - self.add_message('duplicate-except', - args=exc.name, node=handler.type) - - exceptions_classes += [exc for _, exc in excs] - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(ExceptionsChecker(linter)) diff --git a/pymode/libs/pylint/checkers/format.py b/pymode/libs/pylint/checkers/format.py deleted file mode 100644 index 691b41bc..00000000 --- a/pymode/libs/pylint/checkers/format.py +++ /dev/null @@ -1,1069 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2015 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Michal Nowikowski -# Copyright (c) 2015 Mike Frysinger -# Copyright (c) 2015 Mihai Balint -# Copyright (c) 2015 Fabio Natali -# Copyright (c) 2015 Harut -# Copyright (c) 2016 Ashley Whetter - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Python code format's checker. - -By default try to follow Guido's style guide : - -http://www.python.org/doc/essays/styleguide.html - -Some parts of the process_token method is based from The Tab Nanny std module. -""" - -import keyword -import sys -import tokenize -from functools import reduce # pylint: disable=redefined-builtin - -import six -from six.moves import zip, map, filter # pylint: disable=redefined-builtin - -from astroid import nodes - -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker -from pylint.checkers import BaseTokenChecker -from pylint.checkers.utils import check_messages -from pylint.utils import WarningScope, OPTION_RGX - -_ASYNC_TOKEN = 'async' -_CONTINUATION_BLOCK_OPENERS = ['elif', 'except', 'for', 'if', 'while', 'def', 'class'] -_KEYWORD_TOKENS = ['assert', 'del', 'elif', 'except', 'for', 'if', 'in', 'not', - 'raise', 'return', 'while', 'yield'] -if sys.version_info < (3, 0): - _KEYWORD_TOKENS.append('print') - -_SPACED_OPERATORS = ['==', '<', '>', '!=', '<>', '<=', '>=', - '+=', '-=', '*=', '**=', '/=', '//=', '&=', '|=', '^=', - '%=', '>>=', '<<='] -_OPENING_BRACKETS = ['(', '[', '{'] -_CLOSING_BRACKETS = [')', ']', '}'] -_TAB_LENGTH = 8 - -_EOL = frozenset([tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT]) -_JUNK_TOKENS = (tokenize.COMMENT, tokenize.NL) - -# Whitespace checking policy constants -_MUST = 0 -_MUST_NOT = 1 -_IGNORE = 2 - -# Whitespace checking config constants -_DICT_SEPARATOR = 'dict-separator' -_TRAILING_COMMA = 'trailing-comma' -_EMPTY_LINE = 'empty-line' -_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR, _EMPTY_LINE] -_DEFAULT_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR] - -MSGS = { - 'C0301': ('Line too long (%s/%s)', - 'line-too-long', - 'Used when a line is longer than a given number of characters.'), - 'C0302': ('Too many lines in module (%s/%s)', # was W0302 - 'too-many-lines', - 'Used when a module has too much lines, reducing its readability.' - ), - 'C0303': ('Trailing whitespace', - 'trailing-whitespace', - 'Used when there is whitespace between the end of a line and the ' - 'newline.'), - 'C0304': ('Final newline missing', - 'missing-final-newline', - 'Used when the last line in a file is missing a newline.'), - 'C0305': ('Trailing newlines', - 'trailing-newlines', - 'Used when there are trailing blank lines in a file.'), - 'W0311': ('Bad indentation. Found %s %s, expected %s', - 'bad-indentation', - 'Used when an unexpected number of indentation\'s tabulations or ' - 'spaces has been found.'), - 'C0330': ('Wrong %s indentation%s%s.\n%s%s', - 'bad-continuation', - 'TODO'), - 'W0312': ('Found indentation with %ss instead of %ss', - 'mixed-indentation', - 'Used when there are some mixed tabs and spaces in a module.'), - 'W0301': ('Unnecessary semicolon', # was W0106 - 'unnecessary-semicolon', - 'Used when a statement is ended by a semi-colon (";"), which \ - isn\'t necessary (that\'s python, not C ;).'), - 'C0321': ('More than one statement on a single line', - 'multiple-statements', - 'Used when more than on statement are found on the same line.', - {'scope': WarningScope.NODE}), - 'C0325' : ('Unnecessary parens after %r keyword', - 'superfluous-parens', - 'Used when a single item in parentheses follows an if, for, or ' - 'other keyword.'), - 'C0326': ('%s space %s %s %s\n%s', - 'bad-whitespace', - ('Used when a wrong number of spaces is used around an operator, ' - 'bracket or block opener.'), - {'old_names': [('C0323', 'no-space-after-operator'), - ('C0324', 'no-space-after-comma'), - ('C0322', 'no-space-before-operator')]}), - 'W0332': ('Use of "l" as long integer identifier', - 'lowercase-l-suffix', - 'Used when a lower case "l" is used to mark a long integer. You ' - 'should use a upper case "L" since the letter "l" looks too much ' - 'like the digit "1"', - {'maxversion': (3, 0)}), - 'C0327': ('Mixed line endings LF and CRLF', - 'mixed-line-endings', - 'Used when there are mixed (LF and CRLF) newline signs in a file.'), - 'C0328': ('Unexpected line ending format. There is \'%s\' while it should be \'%s\'.', - 'unexpected-line-ending-format', - 'Used when there is different newline than expected.'), - } - - -def _underline_token(token): - length = token[3][1] - token[2][1] - offset = token[2][1] - referenced_line = token[4] - # If the referenced line does not end with a newline char, fix it - if referenced_line[-1] != '\n': - referenced_line += '\n' - return referenced_line + (' ' * offset) + ('^' * length) - -def _column_distance(token1, token2): - if token1 == token2: - return 0 - if token2[3] < token1[3]: - token1, token2 = token2, token1 - if token1[3][0] != token2[2][0]: - return None - return token2[2][1] - token1[3][1] - - -def _last_token_on_line_is(tokens, line_end, token): - return (line_end > 0 and tokens.token(line_end-1) == token or - line_end > 1 and tokens.token(line_end-2) == token - and tokens.type(line_end-1) == tokenize.COMMENT) - - -def _token_followed_by_eol(tokens, position): - return (tokens.type(position+1) == tokenize.NL or - tokens.type(position+1) == tokenize.COMMENT and - tokens.type(position+2) == tokenize.NL) - - -def _get_indent_length(line): - """Return the length of the indentation on the given token's line.""" - result = 0 - for char in line: - if char == ' ': - result += 1 - elif char == '\t': - result += _TAB_LENGTH - else: - break - return result - - -def _get_indent_hint_line(bar_positions, bad_position): - """Return a line with |s for each of the positions in the given lists.""" - if not bar_positions: - return ('', '') - delta_message = '' - markers = [(pos, '|') for pos in bar_positions] - if len(markers) == 1: - # if we have only one marker we'll provide an extra hint on how to fix - expected_position = markers[0][0] - delta = abs(expected_position - bad_position) - direction = 'add' if expected_position > bad_position else 'remove' - delta_message = _CONTINUATION_HINT_MESSAGE % ( - direction, delta, 's' if delta > 1 else '') - markers.append((bad_position, '^')) - markers.sort() - line = [' '] * (markers[-1][0] + 1) - for position, marker in markers: - line[position] = marker - return (''.join(line), delta_message) - - -class _ContinuedIndent(object): - __slots__ = ('valid_outdent_offsets', - 'valid_continuation_offsets', - 'context_type', - 'token', - 'position') - - def __init__(self, - context_type, - token, - position, - valid_outdent_offsets, - valid_continuation_offsets): - self.valid_outdent_offsets = valid_outdent_offsets - self.valid_continuation_offsets = valid_continuation_offsets - self.context_type = context_type - self.position = position - self.token = token - - -# The contexts for hanging indents. -# A hanging indented dictionary value after : -HANGING_DICT_VALUE = 'dict-value' -# Hanging indentation in an expression. -HANGING = 'hanging' -# Hanging indentation in a block header. -HANGING_BLOCK = 'hanging-block' -# Continued indentation inside an expression. -CONTINUED = 'continued' -# Continued indentation in a block header. -CONTINUED_BLOCK = 'continued-block' - -SINGLE_LINE = 'single' -WITH_BODY = 'multi' - -_CONTINUATION_MSG_PARTS = { - HANGING_DICT_VALUE: ('hanging', ' in dict value'), - HANGING: ('hanging', ''), - HANGING_BLOCK: ('hanging', ' before block'), - CONTINUED: ('continued', ''), - CONTINUED_BLOCK: ('continued', ' before block'), -} - -_CONTINUATION_HINT_MESSAGE = ' (%s %d space%s)' # Ex: (remove 2 spaces) - -def _Offsets(*args): - """Valid indentation offsets for a continued line.""" - return dict((a, None) for a in args) - - -def _BeforeBlockOffsets(single, with_body): - """Valid alternative indent offsets for continued lines before blocks. - - :param int single: Valid offset for statements on a single logical line. - :param int with_body: Valid offset for statements on several lines. - - :returns: A dictionary mapping indent offsets to a string representing - whether the indent if for a line or block. - :rtype: dict - """ - return {single: SINGLE_LINE, with_body: WITH_BODY} - - -class TokenWrapper(object): - """A wrapper for readable access to token information.""" - - def __init__(self, tokens): - self._tokens = tokens - - def token(self, idx): - return self._tokens[idx][1] - - def type(self, idx): - return self._tokens[idx][0] - - def start_line(self, idx): - return self._tokens[idx][2][0] - - def start_col(self, idx): - return self._tokens[idx][2][1] - - def line(self, idx): - return self._tokens[idx][4] - - -class ContinuedLineState(object): - """Tracker for continued indentation inside a logical line.""" - - def __init__(self, tokens, config): - self._line_start = -1 - self._cont_stack = [] - self._is_block_opener = False - self.retained_warnings = [] - self._config = config - self._tokens = TokenWrapper(tokens) - - @property - def has_content(self): - return bool(self._cont_stack) - - @property - def _block_indent_size(self): - return len(self._config.indent_string.replace('\t', ' ' * _TAB_LENGTH)) - - @property - def _continuation_size(self): - return self._config.indent_after_paren - - def handle_line_start(self, pos): - """Record the first non-junk token at the start of a line.""" - if self._line_start > -1: - return - - check_token_position = pos - if self._tokens.token(pos) == _ASYNC_TOKEN: - check_token_position += 1 - self._is_block_opener = self._tokens.token( - check_token_position - ) in _CONTINUATION_BLOCK_OPENERS - self._line_start = pos - - def next_physical_line(self): - """Prepares the tracker for a new physical line (NL).""" - self._line_start = -1 - self._is_block_opener = False - - def next_logical_line(self): - """Prepares the tracker for a new logical line (NEWLINE). - - A new logical line only starts with block indentation. - """ - self.next_physical_line() - self.retained_warnings = [] - self._cont_stack = [] - - def add_block_warning(self, token_position, state, valid_offsets): - self.retained_warnings.append((token_position, state, valid_offsets)) - - def get_valid_offsets(self, idx): - """Returns the valid offsets for the token at the given position.""" - # The closing brace on a dict or the 'for' in a dict comprehension may - # reset two indent levels because the dict value is ended implicitly - stack_top = -1 - if self._tokens.token(idx) in ('}', 'for') and self._cont_stack[-1].token == ':': - stack_top = -2 - indent = self._cont_stack[stack_top] - if self._tokens.token(idx) in _CLOSING_BRACKETS: - valid_offsets = indent.valid_outdent_offsets - else: - valid_offsets = indent.valid_continuation_offsets - return indent, valid_offsets.copy() - - def _hanging_indent_after_bracket(self, bracket, position): - """Extracts indentation information for a hanging indent.""" - indentation = _get_indent_length(self._tokens.line(position)) - if self._is_block_opener and self._continuation_size == self._block_indent_size: - return _ContinuedIndent( - HANGING_BLOCK, - bracket, - position, - _Offsets(indentation + self._continuation_size, indentation), - _BeforeBlockOffsets(indentation + self._continuation_size, - indentation + self._continuation_size * 2)) - if bracket == ':': - # If the dict key was on the same line as the open brace, the new - # correct indent should be relative to the key instead of the - # current indent level - paren_align = self._cont_stack[-1].valid_outdent_offsets - next_align = self._cont_stack[-1].valid_continuation_offsets.copy() - next_align_keys = list(next_align.keys()) - next_align[next_align_keys[0] + self._continuation_size] = True - # Note that the continuation of - # d = { - # 'a': 'b' - # 'c' - # } - # is handled by the special-casing for hanging continued string indents. - return _ContinuedIndent(HANGING_DICT_VALUE, bracket, position, paren_align, next_align) - return _ContinuedIndent( - HANGING, - bracket, - position, - _Offsets(indentation, indentation + self._continuation_size), - _Offsets(indentation + self._continuation_size)) - - def _continuation_inside_bracket(self, bracket, pos): - """Extracts indentation information for a continued indent.""" - indentation = _get_indent_length(self._tokens.line(pos)) - token_start = self._tokens.start_col(pos) - next_token_start = self._tokens.start_col(pos + 1) - if self._is_block_opener and next_token_start - indentation == self._block_indent_size: - return _ContinuedIndent( - CONTINUED_BLOCK, - bracket, - pos, - _Offsets(token_start), - _BeforeBlockOffsets(next_token_start, next_token_start + self._continuation_size)) - return _ContinuedIndent( - CONTINUED, - bracket, - pos, - _Offsets(token_start), - _Offsets(next_token_start)) - - def pop_token(self): - self._cont_stack.pop() - - def push_token(self, token, position): - """Pushes a new token for continued indentation on the stack. - - Tokens that can modify continued indentation offsets are: - * opening brackets - * 'lambda' - * : inside dictionaries - - push_token relies on the caller to filter out those - interesting tokens. - - :param int token: The concrete token - :param int position: The position of the token in the stream. - """ - if _token_followed_by_eol(self._tokens, position): - self._cont_stack.append( - self._hanging_indent_after_bracket(token, position)) - else: - self._cont_stack.append( - self._continuation_inside_bracket(token, position)) - - -class FormatChecker(BaseTokenChecker): - """checks for : - * unauthorized constructions - * strict indentation - * line length - """ - - __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker) - - # configuration section name - name = 'format' - # messages - msgs = MSGS - # configuration options - # for available dict keys/values see the optik parser 'add_option' method - options = (('max-line-length', - {'default' : 100, 'type' : "int", 'metavar' : '', - 'help' : 'Maximum number of characters on a single line.'}), - ('ignore-long-lines', - {'type': 'regexp', 'metavar': '', - 'default': r'^\s*(# )??$', - 'help': ('Regexp for a line that is allowed to be longer than ' - 'the limit.')}), - ('single-line-if-stmt', - {'default': False, 'type' : 'yn', 'metavar' : '', - 'help' : ('Allow the body of an if to be on the same ' - 'line as the test if there is no else.')}), - ('single-line-class-stmt', - {'default': False, 'type' : 'yn', 'metavar' : '', - 'help' : ('Allow the body of a class to be on the same ' - 'line as the declaration if body contains ' - 'single statement.')}), - ('no-space-check', - {'default': ','.join(_DEFAULT_NO_SPACE_CHECK_CHOICES), - 'metavar': ','.join(_NO_SPACE_CHECK_CHOICES), - 'type': 'multiple_choice', - 'choices': _NO_SPACE_CHECK_CHOICES, - 'help': ('List of optional constructs for which whitespace ' - 'checking is disabled. ' - '`'+ _DICT_SEPARATOR + '` is used to allow tabulation ' - 'in dicts, etc.: {1 : 1,\\n222: 2}. ' - '`'+ _TRAILING_COMMA + '` allows a space between comma ' - 'and closing bracket: (a, ). ' - '`'+ _EMPTY_LINE + '` allows space-only lines.')}), - ('max-module-lines', - {'default' : 1000, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of lines in a module'} - ), - ('indent-string', - {'default' : ' ', 'type' : "non_empty_string", 'metavar' : '', - 'help' : 'String used as indentation unit. This is usually ' - '" " (4 spaces) or "\\t" (1 tab).'}), - ('indent-after-paren', - {'type': 'int', 'metavar': '', 'default': 4, - 'help': 'Number of spaces of indent required inside a hanging ' - ' or continued line.'}), - ('expected-line-ending-format', - {'type': 'choice', 'metavar': '', 'default': '', - 'choices': ['', 'LF', 'CRLF'], - 'help': ('Expected format of line ending, ' - 'e.g. empty (any line ending), LF or CRLF.')}), - ) - - def __init__(self, linter=None): - BaseTokenChecker.__init__(self, linter) - self._lines = None - self._visited_lines = None - self._bracket_stack = [None] - - def _pop_token(self): - self._bracket_stack.pop() - self._current_line.pop_token() - - def _push_token(self, token, idx): - self._bracket_stack.append(token) - self._current_line.push_token(token, idx) - - def new_line(self, tokens, line_end, line_start): - """a new line has been encountered, process it if necessary""" - if _last_token_on_line_is(tokens, line_end, ';'): - self.add_message('unnecessary-semicolon', line=tokens.start_line(line_end)) - - line_num = tokens.start_line(line_start) - line = tokens.line(line_start) - if tokens.type(line_start) not in _JUNK_TOKENS: - self._lines[line_num] = line.split('\n')[0] - self.check_lines(line, line_num) - - def process_module(self, module): - self._keywords_with_parens = set() - if 'print_function' in module.future_imports: - self._keywords_with_parens.add('print') - - def _check_keyword_parentheses(self, tokens, start): - """Check that there are not unnecessary parens after a keyword. - - Parens are unnecessary if there is exactly one balanced outer pair on a - line, and it is followed by a colon, and contains no commas (i.e. is not a - tuple). - - Args: - tokens: list of Tokens; the entire list of Tokens. - start: int; the position of the keyword in the token list. - """ - # If the next token is not a paren, we're fine. - if self._inside_brackets(':') and tokens[start][1] == 'for': - self._pop_token() - if tokens[start+1][1] != '(': - return - - found_and_or = False - depth = 0 - keyword_token = tokens[start][1] - line_num = tokens[start][2][0] - - for i in range(start, len(tokens) - 1): - token = tokens[i] - - # If we hit a newline, then assume any parens were for continuation. - if token[0] == tokenize.NL: - return - - if token[1] == '(': - depth += 1 - elif token[1] == ')': - depth -= 1 - if depth: - continue - # ')' can't happen after if (foo), since it would be a syntax error. - if (tokens[i+1][1] in (':', ')', ']', '}', 'in') or - tokens[i+1][0] in (tokenize.NEWLINE, - tokenize.ENDMARKER, - tokenize.COMMENT)): - # The empty tuple () is always accepted. - if i == start + 2: - return - if keyword_token == 'not': - if not found_and_or: - self.add_message('superfluous-parens', line=line_num, - args=keyword_token) - elif keyword_token in ('return', 'yield'): - self.add_message('superfluous-parens', line=line_num, - args=keyword_token) - elif keyword_token not in self._keywords_with_parens: - if not (tokens[i+1][1] == 'in' and found_and_or): - self.add_message('superfluous-parens', line=line_num, - args=keyword_token) - return - elif depth == 1: - # This is a tuple, which is always acceptable. - if token[1] == ',': - return - # 'and' and 'or' are the only boolean operators with lower precedence - # than 'not', so parens are only required when they are found. - elif token[1] in ('and', 'or'): - found_and_or = True - # A yield inside an expression must always be in parentheses, - # quit early without error. - elif token[1] == 'yield': - return - # A generator expression always has a 'for' token in it, and - # the 'for' token is only legal inside parens when it is in a - # generator expression. The parens are necessary here, so bail - # without an error. - elif token[1] == 'for': - return - - def _opening_bracket(self, tokens, i): - self._push_token(tokens[i][1], i) - # Special case: ignore slices - if tokens[i][1] == '[' and tokens[i+1][1] == ':': - return - - if (i > 0 and (tokens[i-1][0] == tokenize.NAME and - not (keyword.iskeyword(tokens[i-1][1])) - or tokens[i-1][1] in _CLOSING_BRACKETS)): - self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT)) - else: - self._check_space(tokens, i, (_IGNORE, _MUST_NOT)) - - def _closing_bracket(self, tokens, i): - if self._inside_brackets(':'): - self._pop_token() - self._pop_token() - # Special case: ignore slices - if tokens[i-1][1] == ':' and tokens[i][1] == ']': - return - policy_before = _MUST_NOT - if tokens[i][1] in _CLOSING_BRACKETS and tokens[i-1][1] == ',': - if _TRAILING_COMMA in self.config.no_space_check: - policy_before = _IGNORE - - self._check_space(tokens, i, (policy_before, _IGNORE)) - - def _has_valid_type_annotation(self, tokens, i): - """Extended check of PEP-484 type hint presence""" - if not self._inside_brackets('('): - return False - bracket_level = 0 - for token in tokens[i-1::-1]: - if token[1] == ':': - return True - if token[1] == '(': - return False - if token[1] == ']': - bracket_level += 1 - elif token[1] == '[': - bracket_level -= 1 - elif token[1] == ',': - if not bracket_level: - return False - elif token[0] not in (tokenize.NAME, tokenize.STRING): - return False - return False - - def _check_equals_spacing(self, tokens, i): - """Check the spacing of a single equals sign.""" - if self._has_valid_type_annotation(tokens, i): - self._check_space(tokens, i, (_MUST, _MUST)) - elif self._inside_brackets('(') or self._inside_brackets('lambda'): - self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT)) - else: - self._check_space(tokens, i, (_MUST, _MUST)) - - def _open_lambda(self, tokens, i): # pylint:disable=unused-argument - self._push_token('lambda', i) - - def _handle_colon(self, tokens, i): - # Special case: ignore slices - if self._inside_brackets('['): - return - if (self._inside_brackets('{') and - _DICT_SEPARATOR in self.config.no_space_check): - policy = (_IGNORE, _IGNORE) - else: - policy = (_MUST_NOT, _MUST) - self._check_space(tokens, i, policy) - - if self._inside_brackets('lambda'): - self._pop_token() - elif self._inside_brackets('{'): - self._push_token(':', i) - - def _handle_comma(self, tokens, i): - # Only require a following whitespace if this is - # not a hanging comma before a closing bracket. - if tokens[i+1][1] in _CLOSING_BRACKETS: - self._check_space(tokens, i, (_MUST_NOT, _IGNORE)) - else: - self._check_space(tokens, i, (_MUST_NOT, _MUST)) - if self._inside_brackets(':'): - self._pop_token() - - def _check_surrounded_by_space(self, tokens, i): - """Check that a binary operator is surrounded by exactly one space.""" - self._check_space(tokens, i, (_MUST, _MUST)) - - def _check_space(self, tokens, i, policies): - def _policy_string(policy): - if policy == _MUST: - return 'Exactly one', 'required' - return 'No', 'allowed' - - def _name_construct(token): - if token[1] == ',': - return 'comma' - if token[1] == ':': - return ':' - if token[1] in '()[]{}': - return 'bracket' - if token[1] in ('<', '>', '<=', '>=', '!=', '=='): - return 'comparison' - if self._inside_brackets('('): - return 'keyword argument assignment' - return 'assignment' - - good_space = [True, True] - token = tokens[i] - pairs = [(tokens[i-1], token), (token, tokens[i+1])] - - for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)): - if token_pair[other_idx][0] in _EOL or policy == _IGNORE: - continue - - distance = _column_distance(*token_pair) - if distance is None: - continue - good_space[other_idx] = ( - (policy == _MUST and distance == 1) or - (policy == _MUST_NOT and distance == 0)) - - warnings = [] - if not any(good_space) and policies[0] == policies[1]: - warnings.append((policies[0], 'around')) - else: - for ok, policy, position in zip(good_space, policies, ('before', 'after')): - if not ok: - warnings.append((policy, position)) - for policy, position in warnings: - construct = _name_construct(token) - count, state = _policy_string(policy) - self.add_message('bad-whitespace', line=token[2][0], - args=(count, state, position, construct, - _underline_token(token))) - - def _inside_brackets(self, left): - return self._bracket_stack[-1] == left - - def _prepare_token_dispatcher(self): - raw = [ - (_KEYWORD_TOKENS, - self._check_keyword_parentheses), - - (_OPENING_BRACKETS, self._opening_bracket), - - (_CLOSING_BRACKETS, self._closing_bracket), - - (['='], self._check_equals_spacing), - - (_SPACED_OPERATORS, self._check_surrounded_by_space), - - ([','], self._handle_comma), - - ([':'], self._handle_colon), - - (['lambda'], self._open_lambda), - - ] - - dispatch = {} - for tokens, handler in raw: - for token in tokens: - dispatch[token] = handler - return dispatch - - def process_tokens(self, tokens): - """process tokens and search for : - - _ non strict indentation (i.e. not always using the parameter as - indent unit) - _ too long lines (i.e. longer than ) - _ optionally bad construct (if given, bad_construct must be a compiled - regular expression). - """ - self._bracket_stack = [None] - indents = [0] - check_equal = False - line_num = 0 - self._lines = {} - self._visited_lines = {} - token_handlers = self._prepare_token_dispatcher() - self._last_line_ending = None - last_blank_line_num = 0 - - self._current_line = ContinuedLineState(tokens, self.config) - for idx, (tok_type, token, start, _, line) in enumerate(tokens): - if start[0] != line_num: - line_num = start[0] - # A tokenizer oddity: if an indented line contains a multi-line - # docstring, the line member of the INDENT token does not contain - # the full line; therefore we check the next token on the line. - if tok_type == tokenize.INDENT: - self.new_line(TokenWrapper(tokens), idx-1, idx+1) - else: - self.new_line(TokenWrapper(tokens), idx-1, idx) - - if tok_type == tokenize.NEWLINE: - # a program statement, or ENDMARKER, will eventually follow, - # after some (possibly empty) run of tokens of the form - # (NL | COMMENT)* (INDENT | DEDENT+)? - # If an INDENT appears, setting check_equal is wrong, and will - # be undone when we see the INDENT. - check_equal = True - self._process_retained_warnings(TokenWrapper(tokens), idx) - self._current_line.next_logical_line() - self._check_line_ending(token, line_num) - elif tok_type == tokenize.INDENT: - check_equal = False - self.check_indent_level(token, indents[-1]+1, line_num) - indents.append(indents[-1]+1) - elif tok_type == tokenize.DEDENT: - # there's nothing we need to check here! what's important is - # that when the run of DEDENTs ends, the indentation of the - # program statement (or ENDMARKER) that triggered the run is - # equal to what's left at the top of the indents stack - check_equal = True - if len(indents) > 1: - del indents[-1] - elif tok_type == tokenize.NL: - if not line.strip('\r\n'): - last_blank_line_num = line_num - self._check_continued_indentation(TokenWrapper(tokens), idx+1) - self._current_line.next_physical_line() - elif tok_type != tokenize.COMMENT: - self._current_line.handle_line_start(idx) - # This is the first concrete token following a NEWLINE, so it - # must be the first token of the next program statement, or an - # ENDMARKER; the "line" argument exposes the leading whitespace - # for this statement; in the case of ENDMARKER, line is an empty - # string, so will properly match the empty string with which the - # "indents" stack was seeded - if check_equal: - check_equal = False - self.check_indent_level(line, indents[-1], line_num) - - if tok_type == tokenize.NUMBER and token.endswith('l'): - self.add_message('lowercase-l-suffix', line=line_num) - - try: - handler = token_handlers[token] - except KeyError: - pass - else: - handler(tokens, idx) - - line_num -= 1 # to be ok with "wc -l" - if line_num > self.config.max_module_lines: - # Get the line where the too-many-lines (or its message id) - # was disabled or default to 1. - symbol = self.linter.msgs_store.check_message_id('too-many-lines') - names = (symbol.msgid, 'too-many-lines') - line = next(filter(None, - map(self.linter._pragma_lineno.get, names)), 1) - self.add_message('too-many-lines', - args=(line_num, self.config.max_module_lines), - line=line) - - # See if there are any trailing lines. Do not complain about empty - # files like __init__.py markers. - if line_num == last_blank_line_num and line_num > 0: - self.add_message('trailing-newlines', line=line_num) - - def _check_line_ending(self, line_ending, line_num): - # check if line endings are mixed - if self._last_line_ending is not None: - if line_ending != self._last_line_ending: - self.add_message('mixed-line-endings', line=line_num) - - self._last_line_ending = line_ending - - # check if line ending is as expected - expected = self.config.expected_line_ending_format - if expected: - # reduce multiple \n\n\n\n to one \n - line_ending = reduce(lambda x, y: x + y if x != y else x, line_ending, "") - line_ending = 'LF' if line_ending == '\n' else 'CRLF' - if line_ending != expected: - self.add_message('unexpected-line-ending-format', args=(line_ending, expected), - line=line_num) - - def _process_retained_warnings(self, tokens, current_pos): - single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos, ':') - - for indent_pos, state, offsets in self._current_line.retained_warnings: - block_type = offsets[tokens.start_col(indent_pos)] - hints = dict((k, v) for k, v in six.iteritems(offsets) - if v != block_type) - if single_line_block_stmt and block_type == WITH_BODY: - self._add_continuation_message(state, hints, tokens, indent_pos) - elif not single_line_block_stmt and block_type == SINGLE_LINE: - self._add_continuation_message(state, hints, tokens, indent_pos) - - def _check_continued_indentation(self, tokens, next_idx): - def same_token_around_nl(token_type): - return (tokens.type(next_idx) == token_type and - tokens.type(next_idx-2) == token_type) - - # Do not issue any warnings if the next line is empty. - if not self._current_line.has_content or tokens.type(next_idx) == tokenize.NL: - return - - state, valid_offsets = self._current_line.get_valid_offsets(next_idx) - # Special handling for hanging comments and strings. If the last line ended - # with a comment (string) and the new line contains only a comment, the line - # may also be indented to the start of the previous token. - if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl(tokenize.STRING): - valid_offsets[tokens.start_col(next_idx-2)] = True - - # We can only decide if the indentation of a continued line before opening - # a new block is valid once we know of the body of the block is on the - # same line as the block opener. Since the token processing is single-pass, - # emitting those warnings is delayed until the block opener is processed. - if (state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK) - and tokens.start_col(next_idx) in valid_offsets): - self._current_line.add_block_warning(next_idx, state, valid_offsets) - elif tokens.start_col(next_idx) not in valid_offsets: - - self._add_continuation_message(state, valid_offsets, tokens, next_idx) - - def _add_continuation_message(self, state, offsets, tokens, position): - readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context_type] - hint_line, delta_message = _get_indent_hint_line(offsets, tokens.start_col(position)) - self.add_message( - 'bad-continuation', - line=tokens.start_line(position), - args=(readable_type, readable_position, delta_message, - tokens.line(position), hint_line)) - - @check_messages('multiple-statements') - def visit_default(self, node): - """check the node line number and check it if not yet done""" - if not node.is_statement: - return - if not node.root().pure_python: - return # XXX block visit of child nodes - prev_sibl = node.previous_sibling() - if prev_sibl is not None: - prev_line = prev_sibl.fromlineno - else: - # The line on which a finally: occurs in a try/finally - # is not directly represented in the AST. We infer it - # by taking the last line of the body and adding 1, which - # should be the line of finally: - if (isinstance(node.parent, nodes.TryFinally) - and node in node.parent.finalbody): - prev_line = node.parent.body[0].tolineno + 1 - else: - prev_line = node.parent.statement().fromlineno - line = node.fromlineno - assert line, node - if prev_line == line and self._visited_lines.get(line) != 2: - self._check_multi_statement_line(node, line) - return - if line in self._visited_lines: - return - try: - tolineno = node.blockstart_tolineno - except AttributeError: - tolineno = node.tolineno - assert tolineno, node - lines = [] - for line in range(line, tolineno + 1): - self._visited_lines[line] = 1 - try: - lines.append(self._lines[line].rstrip()) - except KeyError: - lines.append('') - - def _check_multi_statement_line(self, node, line): - """Check for lines containing multiple statements.""" - # Do not warn about multiple nested context managers - # in with statements. - if isinstance(node, nodes.With): - return - # For try... except... finally..., the two nodes - # appear to be on the same line due to how the AST is built. - if (isinstance(node, nodes.TryExcept) and - isinstance(node.parent, nodes.TryFinally)): - return - if (isinstance(node.parent, nodes.If) and not node.parent.orelse - and self.config.single_line_if_stmt): - return - if (isinstance(node.parent, nodes.ClassDef) and len(node.parent.body) == 1 - and self.config.single_line_class_stmt): - return - self.add_message('multiple-statements', node=node) - self._visited_lines[line] = 2 - - def check_lines(self, lines, i): - """check lines have less than a maximum number of characters - """ - max_chars = self.config.max_line_length - ignore_long_line = self.config.ignore_long_lines - - def check_line(line, i): - if not line.endswith('\n'): - self.add_message('missing-final-newline', line=i) - else: - # exclude \f (formfeed) from the rstrip - stripped_line = line.rstrip('\t\n\r\v ') - if not stripped_line and _EMPTY_LINE in self.config.no_space_check: - # allow empty lines - pass - elif line[len(stripped_line):] not in ('\n', '\r\n'): - self.add_message('trailing-whitespace', line=i) - # Don't count excess whitespace in the line length. - line = stripped_line - mobj = OPTION_RGX.search(line) - if mobj and mobj.group(1).split('=', 1)[0].strip() == 'disable': - line = line.split('#')[0].rstrip() - - if len(line) > max_chars and not ignore_long_line.search(line): - self.add_message('line-too-long', line=i, args=(len(line), max_chars)) - return i + 1 - - unsplit_ends = { - u'\v', - u'\x0b', - u'\f', - u'\x0c', - u'\x1c', - u'\x1d', - u'\x1e', - u'\x85', - u'\u2028', - u'\u2029' - } - unsplit = [] - for line in lines.splitlines(True): - if line[-1] in unsplit_ends: - unsplit.append(line) - continue - - if unsplit: - unsplit.append(line) - line = ''.join(unsplit) - unsplit = [] - - i = check_line(line, i) - - if unsplit: - check_line(''.join(unsplit), i) - - def check_indent_level(self, string, expected, line_num): - """return the indent level of the string - """ - indent = self.config.indent_string - if indent == '\\t': # \t is not interpreted in the configuration file - indent = '\t' - level = 0 - unit_size = len(indent) - while string[:unit_size] == indent: - string = string[unit_size:] - level += 1 - suppl = '' - while string and string[0] in ' \t': - if string[0] != indent[0]: - if string[0] == '\t': - args = ('tab', 'space') - else: - args = ('space', 'tab') - self.add_message('mixed-indentation', args=args, line=line_num) - return level - suppl += string[0] - string = string[1:] - if level != expected or suppl: - i_type = 'spaces' - if indent[0] == '\t': - i_type = 'tabs' - self.add_message('bad-indentation', line=line_num, - args=(level * unit_size + len(suppl), i_type, - expected * unit_size)) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(FormatChecker(linter)) diff --git a/pymode/libs/pylint/checkers/imports.py b/pymode/libs/pylint/checkers/imports.py deleted file mode 100644 index 4cae39da..00000000 --- a/pymode/libs/pylint/checkers/imports.py +++ /dev/null @@ -1,768 +0,0 @@ -# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2012-2014 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015 Dmitry Pribysh -# Copyright (c) 2015 Noam Yorav-Raphael -# Copyright (c) 2015 Cezar -# Copyright (c) 2015 James Morgensen -# Copyright (c) 2016 Moises Lopez - https://www.vauxoo.com/ -# Copyright (c) 2016 Ashley Whetter - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""imports checkers for Python code""" - -import collections -from distutils import sysconfig -import os -import sys -import copy - -import six - -import astroid -from astroid import are_exclusive -from astroid.modutils import (get_module_part, is_standard_module) -import isort - -from pylint.interfaces import IAstroidChecker -from pylint.utils import get_global_option -from pylint.exceptions import EmptyReportError -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - check_messages, - node_ignores_exception, - is_from_fallback_block -) -from pylint.graph import get_cycles, DotBackend -from pylint.reporters.ureports.nodes import VerbatimText, Paragraph - - -def _qualified_names(modname): - """Split the names of the given module into subparts - - For example, - _qualified_names('pylint.checkers.ImportsChecker') - returns - ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker'] - """ - names = modname.split('.') - return ['.'.join(names[0:i+1]) for i in range(len(names))] - - -def _get_import_name(importnode, modname): - """Get a prepared module name from the given import node - - In the case of relative imports, this will return the - absolute qualified module name, which might be useful - for debugging. Otherwise, the initial module name - is returned unchanged. - """ - if isinstance(importnode, astroid.ImportFrom): - if importnode.level: - root = importnode.root() - if isinstance(root, astroid.Module): - modname = root.relative_to_absolute_name( - modname, level=importnode.level) - return modname - - -def _get_first_import(node, context, name, base, level, alias): - """return the node where [base.] is imported or None if not found - """ - fullname = '%s.%s' % (base, name) if base else name - - first = None - found = False - for first in context.body: - if first is node: - continue - if first.scope() is node.scope() and first.fromlineno > node.fromlineno: - continue - if isinstance(first, astroid.Import): - if any(fullname == iname[0] for iname in first.names): - found = True - break - elif isinstance(first, astroid.ImportFrom): - if level == first.level: - for imported_name, imported_alias in first.names: - if fullname == '%s.%s' % (first.modname, imported_name): - found = True - break - if name != '*' and name == imported_name and not (alias or imported_alias): - found = True - break - if found: - break - if found and not are_exclusive(first, node): - return first - - -def _ignore_import_failure(node, modname, ignored_modules): - for submodule in _qualified_names(modname): - if submodule in ignored_modules: - return True - - return node_ignores_exception(node, ImportError) - -# utilities to represents import dependencies as tree and dot graph ########### - -def _make_tree_defs(mod_files_list): - """get a list of 2-uple (module, list_of_files_which_import_this_module), - it will return a dictionary to represent this as a tree - """ - tree_defs = {} - for mod, files in mod_files_list: - node = (tree_defs, ()) - for prefix in mod.split('.'): - node = node[0].setdefault(prefix, [{}, []]) - node[1] += files - return tree_defs - - -def _repr_tree_defs(data, indent_str=None): - """return a string which represents imports as a tree""" - lines = [] - nodes = data.items() - for i, (mod, (sub, files)) in enumerate(sorted(nodes, key=lambda x: x[0])): - if not files: - files = '' - else: - files = '(%s)' % ','.join(sorted(files)) - if indent_str is None: - lines.append('%s %s' % (mod, files)) - sub_indent_str = ' ' - else: - lines.append(r'%s\-%s %s' % (indent_str, mod, files)) - if i == len(nodes)-1: - sub_indent_str = '%s ' % indent_str - else: - sub_indent_str = '%s| ' % indent_str - if sub: - lines.append(_repr_tree_defs(sub, sub_indent_str)) - return '\n'.join(lines) - - -def _dependencies_graph(filename, dep_info): - """write dependencies as a dot (graphviz) file - """ - done = {} - printer = DotBackend(filename[:-4], rankdir='LR') - printer.emit('URL="." node[shape="box"]') - for modname, dependencies in sorted(six.iteritems(dep_info)): - done[modname] = 1 - printer.emit_node(modname) - for depmodname in dependencies: - if depmodname not in done: - done[depmodname] = 1 - printer.emit_node(depmodname) - for depmodname, dependencies in sorted(six.iteritems(dep_info)): - for modname in dependencies: - printer.emit_edge(modname, depmodname) - printer.generate(filename) - - -def _make_graph(filename, dep_info, sect, gtype): - """generate a dependencies graph and add some information about it in the - report's section - """ - _dependencies_graph(filename, dep_info) - sect.append(Paragraph('%simports graph has been written to %s' - % (gtype, filename))) - - -# the import checker itself ################################################### - -MSGS = { - 'E0401': ('Unable to import %s', - 'import-error', - 'Used when pylint has been unable to import a module.', - {'old_names': [('F0401', 'import-error')]}), - 'E0402': ('Attempted relative import beyond top-level package', - 'relative-beyond-top-level', - 'Used when a relative import tries to access too many levels ' - 'in the current package.'), - 'R0401': ('Cyclic import (%s)', - 'cyclic-import', - 'Used when a cyclic import between two or more modules is \ - detected.'), - - 'W0401': ('Wildcard import %s', - 'wildcard-import', - 'Used when `from module import *` is detected.'), - 'W0402': ('Uses of a deprecated module %r', - 'deprecated-module', - 'Used a module marked as deprecated is imported.'), - 'W0403': ('Relative import %r, should be %r', - 'relative-import', - 'Used when an import relative to the package directory is ' - 'detected.', - {'maxversion': (3, 0)}), - 'W0404': ('Reimport %r (imported line %s)', - 'reimported', - 'Used when a module is reimported multiple times.'), - 'W0406': ('Module import itself', - 'import-self', - 'Used when a module is importing itself.'), - - 'W0410': ('__future__ import is not the first non docstring statement', - 'misplaced-future', - 'Python 2.5 and greater require __future__ import to be the \ - first non docstring statement in the module.'), - - 'C0410': ('Multiple imports on one line (%s)', - 'multiple-imports', - 'Used when import statement importing multiple modules is ' - 'detected.'), - 'C0411': ('%s should be placed before %s', - 'wrong-import-order', - 'Used when PEP8 import order is not respected (standard imports ' - 'first, then third-party libraries, then local imports)'), - 'C0412': ('Imports from package %s are not grouped', - 'ungrouped-imports', - 'Used when imports are not grouped by packages'), - 'C0413': ('Import "%s" should be placed at the top of the ' - 'module', - 'wrong-import-position', - 'Used when code and imports are mixed'), - } - - -DEFAULT_STANDARD_LIBRARY = () -DEFAULT_KNOWN_THIRD_PARTY = ('enchant',) - - -class ImportsChecker(BaseChecker): - """checks for - * external modules dependencies - * relative / wildcard imports - * cyclic imports - * uses of deprecated modules - """ - - __implements__ = IAstroidChecker - - name = 'imports' - msgs = MSGS - priority = -2 - - if six.PY2: - deprecated_modules = ('regsub', 'TERMIOS', 'Bastion', 'rexec') - elif sys.version_info < (3, 5): - deprecated_modules = ('optparse', ) - else: - deprecated_modules = ('optparse', 'tkinter.tix') - options = (('deprecated-modules', - {'default' : deprecated_modules, - 'type' : 'csv', - 'metavar' : '', - 'help' : 'Deprecated modules which should not be used,' - ' separated by a comma'} - ), - ('import-graph', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'Create a graph of every (i.e. internal and' - ' external) dependencies in the given file' - ' (report RP0402 must not be disabled)'} - ), - ('ext-import-graph', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'Create a graph of external dependencies in the' - ' given file (report RP0402 must not be disabled)'} - ), - ('int-import-graph', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'Create a graph of internal dependencies in the' - ' given file (report RP0402 must not be disabled)'} - ), - ('known-standard-library', - {'default': DEFAULT_STANDARD_LIBRARY, - 'type': 'csv', - 'metavar': '', - 'help': 'Force import order to recognize a module as part of' - ' the standard compatibility libraries.'} - ), - ('known-third-party', - {'default': DEFAULT_KNOWN_THIRD_PARTY, - 'type': 'csv', - 'metavar': '', - 'help': 'Force import order to recognize a module as part of' - ' a third party library.'} - ), - ('analyse-fallback-blocks', - {'default': False, - 'type': 'yn', - 'metavar': '', - 'help': 'Analyse import fallback blocks. This can be used to ' - 'support both Python 2 and 3 compatible code, which means that ' - 'the block might have code that exists only in one or another ' - 'interpreter, leading to false positives when analysed.'}, - ), - ('allow-wildcard-with-all', - {'default': False, - 'type': 'yn', - 'metavar': '', - 'help': 'Allow wildcard imports from modules that define __all__.'}), - ) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self.stats = None - self.import_graph = None - self._imports_stack = [] - self._first_non_import_node = None - self.__int_dep_info = self.__ext_dep_info = None - self.reports = (('RP0401', 'External dependencies', - self._report_external_dependencies), - ('RP0402', 'Modules dependencies graph', - self._report_dependencies_graph), - ) - - self._site_packages = self._compute_site_packages() - - @staticmethod - def _compute_site_packages(): - def _normalized_path(path): - return os.path.normcase(os.path.abspath(path)) - - paths = set() - real_prefix = getattr(sys, 'real_prefix', None) - for prefix in filter(None, (real_prefix, sys.prefix)): - path = sysconfig.get_python_lib(prefix=prefix) - path = _normalized_path(path) - paths.add(path) - - # Handle Debian's derivatives /usr/local. - if os.path.isfile("/etc/debian_version"): - for prefix in filter(None, (real_prefix, sys.prefix)): - libpython = os.path.join(prefix, "local", "lib", - "python" + sysconfig.get_python_version(), - "dist-packages") - paths.add(libpython) - return paths - - def open(self): - """called before visiting project (i.e set of modules)""" - self.linter.add_stats(dependencies={}) - self.linter.add_stats(cycles=[]) - self.stats = self.linter.stats - self.import_graph = collections.defaultdict(set) - self._excluded_edges = collections.defaultdict(set) - self._ignored_modules = get_global_option( - self, 'ignored-modules', default=[]) - - def _import_graph_without_ignored_edges(self): - filtered_graph = copy.deepcopy(self.import_graph) - for node in filtered_graph: - filtered_graph[node].difference_update(self._excluded_edges[node]) - return filtered_graph - - def close(self): - """called before visiting project (i.e set of modules)""" - if self.linter.is_message_enabled('cyclic-import'): - graph = self._import_graph_without_ignored_edges() - vertices = list(graph) - for cycle in get_cycles(graph, vertices=vertices): - self.add_message('cyclic-import', args=' -> '.join(cycle)) - - @check_messages('wrong-import-position', 'multiple-imports', - 'relative-import', 'reimported', 'deprecated-module') - def visit_import(self, node): - """triggered when an import statement is seen""" - self._check_reimport(node) - - modnode = node.root() - names = [name for name, _ in node.names] - if len(names) >= 2: - self.add_message('multiple-imports', args=', '.join(names), node=node) - - for name in names: - self._check_deprecated_module(node, name) - imported_module = self._get_imported_module(node, name) - if isinstance(node.parent, astroid.Module): - # Allow imports nested - self._check_position(node) - if isinstance(node.scope(), astroid.Module): - self._record_import(node, imported_module) - - if imported_module is None: - continue - - self._check_relative_import(modnode, node, imported_module, name) - self._add_imported_module(node, imported_module.name) - - @check_messages(*(MSGS.keys())) - def visit_importfrom(self, node): - """triggered when a from statement is seen""" - basename = node.modname - imported_module = self._get_imported_module(node, basename) - - self._check_misplaced_future(node) - self._check_deprecated_module(node, basename) - self._check_wildcard_imports(node, imported_module) - self._check_same_line_imports(node) - self._check_reimport(node, basename=basename, level=node.level) - - if isinstance(node.parent, astroid.Module): - # Allow imports nested - self._check_position(node) - if isinstance(node.scope(), astroid.Module): - self._record_import(node, imported_module) - if imported_module is None: - return - modnode = node.root() - self._check_relative_import(modnode, node, imported_module, basename) - - for name, _ in node.names: - if name != '*': - self._add_imported_module(node, '%s.%s' % (imported_module.name, name)) - - @check_messages('wrong-import-order', 'ungrouped-imports', - 'wrong-import-position') - def leave_module(self, node): - # Check imports are grouped by category (standard, 3rd party, local) - std_imports, ext_imports, loc_imports = self._check_imports_order(node) - - # Check imports are grouped by package within a given category - met = set() - current_package = None - for import_node, import_name in std_imports + ext_imports + loc_imports: - package, _, _ = import_name.partition('.') - if current_package and current_package != package and package in met: - self.add_message('ungrouped-imports', node=import_node, - args=package) - current_package = package - met.add(package) - - self._imports_stack = [] - self._first_non_import_node = None - - def compute_first_non_import_node(self, node): - # if the node does not contain an import instruction, and if it is the - # first node of the module, keep a track of it (all the import positions - # of the module will be compared to the position of this first - # instruction) - if self._first_non_import_node: - return - if not isinstance(node.parent, astroid.Module): - return - nested_allowed = [astroid.TryExcept, astroid.TryFinally] - is_nested_allowed = [ - allowed for allowed in nested_allowed if isinstance(node, allowed)] - if is_nested_allowed and \ - any(node.nodes_of_class((astroid.Import, astroid.ImportFrom))): - return - if isinstance(node, astroid.Assign): - # Add compatibility for module level dunder names - # https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names - valid_targets = [ - isinstance(target, astroid.AssignName) and - target.name.startswith('__') and target.name.endswith('__') - for target in node.targets] - if all(valid_targets): - return - self._first_non_import_node = node - - visit_tryfinally = visit_tryexcept = visit_assignattr = visit_assign = \ - visit_ifexp = visit_comprehension = visit_expr = visit_if = \ - compute_first_non_import_node - - def visit_functiondef(self, node): - # If it is the first non import instruction of the module, record it. - if self._first_non_import_node: - return - - # Check if the node belongs to an `If` or a `Try` block. If they - # contain imports, skip recording this node. - if not isinstance(node.parent.scope(), astroid.Module): - return - - root = node - while not isinstance(root.parent, astroid.Module): - root = root.parent - - if isinstance(root, (astroid.If, astroid.TryFinally, astroid.TryExcept)): - if any(root.nodes_of_class((astroid.Import, astroid.ImportFrom))): - return - - self._first_non_import_node = node - - visit_classdef = visit_for = visit_while = visit_functiondef - - def _check_misplaced_future(self, node): - basename = node.modname - if basename == '__future__': - # check if this is the first non-docstring statement in the module - prev = node.previous_sibling() - if prev: - # consecutive future statements are possible - if not (isinstance(prev, astroid.ImportFrom) - and prev.modname == '__future__'): - self.add_message('misplaced-future', node=node) - return - - def _check_same_line_imports(self, node): - # Detect duplicate imports on the same line. - names = (name for name, _ in node.names) - counter = collections.Counter(names) - for name, count in counter.items(): - if count > 1: - self.add_message('reimported', node=node, - args=(name, node.fromlineno)) - - def _check_position(self, node): - """Check `node` import or importfrom node position is correct - - Send a message if `node` comes before another instruction - """ - # if a first non-import instruction has already been encountered, - # it means the import comes after it and therefore is not well placed - if self._first_non_import_node: - self.add_message('wrong-import-position', node=node, - args=node.as_string()) - - def _record_import(self, node, importedmodnode): - """Record the package `node` imports from""" - importedname = importedmodnode.name if importedmodnode else None - if not importedname: - if isinstance(node, astroid.ImportFrom): - importedname = node.modname - else: - importedname = node.names[0][0].split('.')[0] - if isinstance(node, astroid.ImportFrom) and (node.level or 0) >= 1: - # We need the impotedname with first point to detect local package - # Example of node: - # 'from .my_package1 import MyClass1' - # the output should be '.my_package1' instead of 'my_package1' - # Example of node: - # 'from . import my_package2' - # the output should be '.my_package2' instead of '{pyfile}' - importedname = '.' + importedname - self._imports_stack.append((node, importedname)) - - @staticmethod - def _is_fallback_import(node, imports): - imports = [import_node for (import_node, _) in imports] - return any(astroid.are_exclusive(import_node, node) - for import_node in imports) - - def _check_imports_order(self, _module_node): - """Checks imports of module `node` are grouped by category - - Imports must follow this order: standard, 3rd party, local - """ - extern_imports = [] - local_imports = [] - std_imports = [] - extern_not_nested = [] - local_not_nested = [] - isort_obj = isort.SortImports( - file_contents='', known_third_party=self.config.known_third_party, - known_standard_library=self.config.known_standard_library, - ) - for node, modname in self._imports_stack: - if modname.startswith('.'): - package = '.' + modname.split('.')[1] - else: - package = modname.split('.')[0] - nested = not isinstance(node.parent, astroid.Module) - import_category = isort_obj.place_module(package) - if import_category in ('FUTURE', 'STDLIB'): - std_imports.append((node, package)) - wrong_import = extern_not_nested or local_not_nested - if self._is_fallback_import(node, wrong_import): - continue - if wrong_import and not nested: - self.add_message('wrong-import-order', node=node, - args=('standard import "%s"' % node.as_string(), - '"%s"' % wrong_import[0][0].as_string())) - elif import_category in ('FIRSTPARTY', 'THIRDPARTY'): - extern_imports.append((node, package)) - if not nested: - extern_not_nested.append((node, package)) - wrong_import = local_not_nested - if wrong_import and not nested: - self.add_message('wrong-import-order', node=node, - args=('external import "%s"' % node.as_string(), - '"%s"' % wrong_import[0][0].as_string())) - elif import_category == 'LOCALFOLDER': - local_imports.append((node, package)) - if not nested: - local_not_nested.append((node, package)) - return std_imports, extern_imports, local_imports - - def _get_imported_module(self, importnode, modname): - try: - return importnode.do_import_module(modname) - except astroid.TooManyLevelsError: - if _ignore_import_failure(importnode, modname, self._ignored_modules): - return None - - self.add_message('relative-beyond-top-level', node=importnode) - - except astroid.AstroidBuildingException: - if _ignore_import_failure(importnode, modname, self._ignored_modules): - return None - if not self.config.analyse_fallback_blocks and is_from_fallback_block(importnode): - return None - - dotted_modname = _get_import_name(importnode, modname) - self.add_message('import-error', args=repr(dotted_modname), - node=importnode) - - def _check_relative_import(self, modnode, importnode, importedmodnode, - importedasname): - """check relative import. node is either an Import or From node, modname - the imported module name. - """ - if not self.linter.is_message_enabled('relative-import'): - return - if importedmodnode.file is None: - return False # built-in module - if modnode is importedmodnode: - return False # module importing itself - if modnode.absolute_import_activated() or getattr(importnode, 'level', None): - return False - if importedmodnode.name != importedasname: - # this must be a relative import... - self.add_message('relative-import', - args=(importedasname, importedmodnode.name), - node=importnode) - - def _add_imported_module(self, node, importedmodname): - """notify an imported module, used to analyze dependencies""" - module_file = node.root().file - context_name = node.root().name - base = os.path.splitext(os.path.basename(module_file))[0] - - # Determine if we have a `from .something import` in a package's - # __init__. This means the module will never be able to import - # itself using this condition (the level will be bigger or - # if the same module is named as the package, it will be different - # anyway). - if isinstance(node, astroid.ImportFrom): - if node.level and node.level > 0 and base == '__init__': - return - - try: - importedmodname = get_module_part(importedmodname, - module_file) - except ImportError: - pass - - if context_name == importedmodname: - self.add_message('import-self', node=node) - elif not is_standard_module(importedmodname): - # handle dependencies - importedmodnames = self.stats['dependencies'].setdefault( - importedmodname, set()) - if context_name not in importedmodnames: - importedmodnames.add(context_name) - - # update import graph - self.import_graph[context_name].add(importedmodname) - if not self.linter.is_message_enabled('cyclic-import'): - self._excluded_edges[context_name].add(importedmodname) - - def _check_deprecated_module(self, node, mod_path): - """check if the module is deprecated""" - for mod_name in self.config.deprecated_modules: - if mod_path == mod_name or mod_path.startswith(mod_name + '.'): - self.add_message('deprecated-module', node=node, args=mod_path) - - def _check_reimport(self, node, basename=None, level=None): - """check if the import is necessary (i.e. not already done)""" - if not self.linter.is_message_enabled('reimported'): - return - - frame = node.frame() - root = node.root() - contexts = [(frame, level)] - if root is not frame: - contexts.append((root, None)) - - for known_context, known_level in contexts: - for name, alias in node.names: - first = _get_first_import( - node, known_context, - name, basename, - known_level, alias) - if first is not None: - self.add_message('reimported', node=node, - args=(name, first.fromlineno)) - - def _report_external_dependencies(self, sect, _, _dummy): - """return a verbatim layout for displaying dependencies""" - dep_info = _make_tree_defs(six.iteritems(self._external_dependencies_info())) - if not dep_info: - raise EmptyReportError() - tree_str = _repr_tree_defs(dep_info) - sect.append(VerbatimText(tree_str)) - - def _report_dependencies_graph(self, sect, _, _dummy): - """write dependencies as a dot (graphviz) file""" - dep_info = self.stats['dependencies'] - if not dep_info or not (self.config.import_graph - or self.config.ext_import_graph - or self.config.int_import_graph): - raise EmptyReportError() - filename = self.config.import_graph - if filename: - _make_graph(filename, dep_info, sect, '') - filename = self.config.ext_import_graph - if filename: - _make_graph(filename, self._external_dependencies_info(), - sect, 'external ') - filename = self.config.int_import_graph - if filename: - _make_graph(filename, self._internal_dependencies_info(), - sect, 'internal ') - - def _external_dependencies_info(self): - """return cached external dependencies information or build and - cache them - """ - if self.__ext_dep_info is None: - package = self.linter.current_name - self.__ext_dep_info = result = {} - for importee, importers in six.iteritems(self.stats['dependencies']): - if not importee.startswith(package): - result[importee] = importers - return self.__ext_dep_info - - def _internal_dependencies_info(self): - """return cached internal dependencies information or build and - cache them - """ - if self.__int_dep_info is None: - package = self.linter.current_name - self.__int_dep_info = result = {} - for importee, importers in six.iteritems(self.stats['dependencies']): - if importee.startswith(package): - result[importee] = importers - return self.__int_dep_info - - def _check_wildcard_imports(self, node, imported_module): - wildcard_import_is_allowed = ( - self._wildcard_import_is_allowed(imported_module) - ) - for name, _ in node.names: - if name == '*' and not wildcard_import_is_allowed: - self.add_message('wildcard-import', args=node.modname, node=node) - - def _wildcard_import_is_allowed(self, imported_module): - return (self.config.allow_wildcard_with_all - and imported_module is not None - and '__all__' in imported_module.locals) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(ImportsChecker(linter)) diff --git a/pymode/libs/pylint/checkers/logging.py b/pymode/libs/pylint/checkers/logging.py deleted file mode 100644 index d9c1fd78..00000000 --- a/pymode/libs/pylint/checkers/logging.py +++ /dev/null @@ -1,271 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Claudiu Popa -# Copyright (c) 2016 Ashley Whetter -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""checker for use of Python logging -""" -import string - -import six - -import astroid - -from pylint import checkers -from pylint import interfaces -from pylint.checkers import utils -from pylint.checkers.utils import check_messages - - - -MSGS = { - 'W1201': ('Specify string format arguments as logging function parameters', - 'logging-not-lazy', - 'Used when a logging statement has a call form of ' - '"logging.(format_string % (format_args...))". ' - 'Such calls should leave string interpolation to the logging ' - 'method itself and be written ' - '"logging.(format_string, format_args...)" ' - 'so that the program may avoid incurring the cost of the ' - 'interpolation in those cases in which no message will be ' - 'logged. For more, see ' - 'http://www.python.org/dev/peps/pep-0282/.'), - 'W1202': ('Use % formatting in logging functions and pass the % ' - 'parameters as arguments', - 'logging-format-interpolation', - 'Used when a logging statement has a call form of ' - '"logging.(format_string.format(format_args...))"' - '. Such calls should use % formatting instead, but leave ' - 'interpolation to the logging function by passing the parameters ' - 'as arguments.'), - 'E1200': ('Unsupported logging format character %r (%#02x) at index %d', - 'logging-unsupported-format', - 'Used when an unsupported format character is used in a logging\ - statement format string.'), - 'E1201': ('Logging format string ends in middle of conversion specifier', - 'logging-format-truncated', - 'Used when a logging statement format string terminates before\ - the end of a conversion specifier.'), - 'E1205': ('Too many arguments for logging format string', - 'logging-too-many-args', - 'Used when a logging format string is given too many arguments.'), - 'E1206': ('Not enough arguments for logging format string', - 'logging-too-few-args', - 'Used when a logging format string is given too few arguments.'), - } - - -CHECKED_CONVENIENCE_FUNCTIONS = { - 'critical', 'debug', 'error', 'exception', 'fatal', 'info', 'warn', 'warning' -} - - -def is_method_call(func, types=(), methods=()): - """Determines if a BoundMethod node represents a method call. - - Args: - func (astroid.BoundMethod): The BoundMethod AST node to check. - types (Optional[String]): Optional sequence of caller type names to restrict check. - methods (Optional[String]): Optional sequence of method names to restrict check. - - Returns: - bool: true if the node represents a method call for the given type and - method names, False otherwise. - """ - return (isinstance(func, astroid.BoundMethod) - and isinstance(func.bound, astroid.Instance) - and (func.bound.name in types if types else True) - and (func.name in methods if methods else True)) - - -class LoggingChecker(checkers.BaseChecker): - """Checks use of the logging module.""" - - __implements__ = interfaces.IAstroidChecker - name = 'logging' - msgs = MSGS - - options = (('logging-modules', - {'default': ('logging',), - 'type': 'csv', - 'metavar': '', - 'help': 'Logging modules to check that the string format ' - 'arguments are in logging function parameter format'} - ), - ) - - def visit_module(self, node): # pylint: disable=unused-argument - """Clears any state left in this checker from last module checked.""" - # The code being checked can just as easily "import logging as foo", - # so it is necessary to process the imports and store in this field - # what name the logging module is actually given. - self._logging_names = set() - logging_mods = self.config.logging_modules - - self._logging_modules = set(logging_mods) - self._from_imports = {} - for logging_mod in logging_mods: - parts = logging_mod.rsplit('.', 1) - if len(parts) > 1: - self._from_imports[parts[0]] = parts[1] - - def visit_importfrom(self, node): - """Checks to see if a module uses a non-Python logging module.""" - try: - logging_name = self._from_imports[node.modname] - for module, as_name in node.names: - if module == logging_name: - self._logging_names.add(as_name or module) - except KeyError: - pass - - def visit_import(self, node): - """Checks to see if this module uses Python's built-in logging.""" - for module, as_name in node.names: - if module in self._logging_modules: - self._logging_names.add(as_name or module) - - @check_messages(*(MSGS.keys())) - def visit_call(self, node): - """Checks calls to logging methods.""" - def is_logging_name(): - return (isinstance(node.func, astroid.Attribute) and - isinstance(node.func.expr, astroid.Name) and - node.func.expr.name in self._logging_names) - - def is_logger_class(): - try: - for inferred in node.func.infer(): - if isinstance(inferred, astroid.BoundMethod): - parent = inferred._proxied.parent - if (isinstance(parent, astroid.ClassDef) and - (parent.qname() == 'logging.Logger' or - any(ancestor.qname() == 'logging.Logger' - for ancestor in parent.ancestors()))): - return True, inferred._proxied.name - except astroid.exceptions.InferenceError: - pass - return False, None - - if is_logging_name(): - name = node.func.attrname - else: - result, name = is_logger_class() - if not result: - return - self._check_log_method(node, name) - - def _check_log_method(self, node, name): - """Checks calls to logging.log(level, format, *format_args).""" - if name == 'log': - if node.starargs or node.kwargs or len(node.args) < 2: - # Either a malformed call, star args, or double-star args. Beyond - # the scope of this checker. - return - format_pos = 1 - elif name in CHECKED_CONVENIENCE_FUNCTIONS: - if node.starargs or node.kwargs or not node.args: - # Either no args, star args, or double-star args. Beyond the - # scope of this checker. - return - format_pos = 0 - else: - return - - if isinstance(node.args[format_pos], astroid.BinOp) and node.args[format_pos].op == '%': - self.add_message('logging-not-lazy', node=node) - elif isinstance(node.args[format_pos], astroid.Call): - self._check_call_func(node.args[format_pos]) - elif isinstance(node.args[format_pos], astroid.Const): - self._check_format_string(node, format_pos) - - def _check_call_func(self, node): - """Checks that function call is not format_string.format(). - - Args: - node (astroid.node_classes.CallFunc): - CallFunc AST node to be checked. - """ - func = utils.safe_infer(node.func) - types = ('str', 'unicode') - methods = ('format',) - if is_method_call(func, types, methods) and not is_complex_format_str(func.bound): - self.add_message('logging-format-interpolation', node=node) - - def _check_format_string(self, node, format_arg): - """Checks that format string tokens match the supplied arguments. - - Args: - node (astroid.node_classes.NodeNG): AST node to be checked. - format_arg (int): Index of the format string in the node arguments. - """ - num_args = _count_supplied_tokens(node.args[format_arg + 1:]) - if not num_args: - # If no args were supplied, then all format strings are valid - - # don't check any further. - return - format_string = node.args[format_arg].value - if not isinstance(format_string, six.string_types): - # If the log format is constant non-string (e.g. logging.debug(5)), - # ensure there are no arguments. - required_num_args = 0 - else: - try: - keyword_args, required_num_args = \ - utils.parse_format_string(format_string) - if keyword_args: - # Keyword checking on logging strings is complicated by - # special keywords - out of scope. - return - except utils.UnsupportedFormatCharacter as ex: - char = format_string[ex.index] - self.add_message('logging-unsupported-format', node=node, - args=(char, ord(char), ex.index)) - return - except utils.IncompleteFormatString: - self.add_message('logging-format-truncated', node=node) - return - if num_args > required_num_args: - self.add_message('logging-too-many-args', node=node) - elif num_args < required_num_args: - self.add_message('logging-too-few-args', node=node) - - -def is_complex_format_str(node): - """Checks if node represents a string with complex formatting specs. - - Args: - node (astroid.node_classes.NodeNG): AST node to check - Returns: - bool: True if inferred string uses complex formatting, False otherwise - """ - inferred = utils.safe_infer(node) - if inferred is None or not isinstance(inferred.value, six.string_types): - return True - for _, _, format_spec, _ in string.Formatter().parse(inferred.value): - if format_spec: - return True - return False - - -def _count_supplied_tokens(args): - """Counts the number of tokens in an args list. - - The Python log functions allow for special keyword arguments: func, - exc_info and extra. To handle these cases correctly, we only count - arguments that aren't keywords. - - Args: - args (list): AST nodes that are arguments for a log format string. - - Returns: - int: Number of AST nodes that aren't keywords. - """ - return sum(1 for arg in args if not isinstance(arg, astroid.Keyword)) - - -def register(linter): - """Required method to auto-register this checker.""" - linter.register_checker(LoggingChecker(linter)) diff --git a/pymode/libs/pylint/checkers/misc.py b/pymode/libs/pylint/checkers/misc.py deleted file mode 100644 index 104f0dfc..00000000 --- a/pymode/libs/pylint/checkers/misc.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (c) 2006, 2009-2013 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2014 Alexandru Coman -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - - -"""Check source code is ascii only or has an encoding declaration (PEP 263)""" - -# pylint: disable=W0511 - -import re - -import six - -from pylint.interfaces import IRawChecker -from pylint.checkers import BaseChecker - - -MSGS = { - 'W0511': ('%s', - 'fixme', - 'Used when a warning note as FIXME or XXX is detected.'), - 'W0512': ('Cannot decode using encoding "%s", unexpected byte at position %d', - 'invalid-encoded-data', - 'Used when a source line cannot be decoded using the specified ' - 'source file encoding.', - {'maxversion': (3, 0)}), -} - - -class EncodingChecker(BaseChecker): - - """checks for: - * warning notes in the code like FIXME, XXX - * encoding issues. - """ - __implements__ = IRawChecker - - # configuration section name - name = 'miscellaneous' - msgs = MSGS - - options = (('notes', - {'type': 'csv', 'metavar': '', - 'default': ('FIXME', 'XXX', 'TODO'), - 'help': ('List of note tags to take in consideration, ' - 'separated by a comma.')}),) - - def _check_note(self, notes, lineno, line): - # First, simply check if the notes are in the line at all. This is an - # optimisation to prevent using the regular expression on every line, - # but rather only on lines which may actually contain one of the notes. - # This prevents a pathological problem with lines that are hundreds - # of thousands of characters long. - for note in self.config.notes: - if note in line: - break - else: - return - - match = notes.search(line) - if not match: - return - self.add_message('fixme', args=line[match.start(1):].rstrip(), line=lineno) - - def _check_encoding(self, lineno, line, file_encoding): - try: - return six.text_type(line, file_encoding) - except UnicodeDecodeError as ex: - self.add_message('invalid-encoded-data', line=lineno, - args=(file_encoding, ex.args[2])) - - def process_module(self, module): - """inspect the source file to find encoding problem or fixmes like - notes - """ - if self.config.notes: - notes = re.compile( - r'.*?#\s*(%s)(:*\s*.*)' % "|".join(self.config.notes)) - else: - notes = None - if module.file_encoding: - encoding = module.file_encoding - else: - encoding = 'ascii' - - with module.stream() as stream: - for lineno, line in enumerate(stream): - line = self._check_encoding(lineno + 1, line, encoding) - if line is not None and notes: - self._check_note(notes, lineno + 1, line) - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(EncodingChecker(linter)) diff --git a/pymode/libs/pylint/checkers/newstyle.py b/pymode/libs/pylint/checkers/newstyle.py deleted file mode 100644 index 6071ea5c..00000000 --- a/pymode/libs/pylint/checkers/newstyle.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright (c) 2006, 2008-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""check for new / old style related problems -""" -import sys - -import astroid - -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - check_messages, - node_frame_class, - has_known_bases -) - -MSGS = { - 'E1001': ('Use of __slots__ on an old style class', - 'slots-on-old-class', - 'Used when an old style class uses the __slots__ attribute.', - {'maxversion': (3, 0)}), - 'E1002': ('Use of super on an old style class', - 'super-on-old-class', - 'Used when an old style class uses the super builtin.', - {'maxversion': (3, 0)}), - 'E1003': ('Bad first argument %r given to super()', - 'bad-super-call', - 'Used when another argument than the current class is given as \ - first argument of the super builtin.'), - 'E1004': ('Missing argument to super()', - 'missing-super-argument', - 'Used when the super builtin didn\'t receive an \ - argument.', - {'maxversion': (3, 0)}), - 'W1001': ('Use of "property" on an old style class', - 'property-on-old-class', - 'Used when Pylint detect the use of the builtin "property" \ - on an old style class while this is relying on new style \ - classes features.', - {'maxversion': (3, 0)}), - 'C1001': ('Old-style class defined.', - 'old-style-class', - 'Used when a class is defined that does not inherit from another ' - 'class and does not inherit explicitly from "object".', - {'maxversion': (3, 0)}) - } - - -class NewStyleConflictChecker(BaseChecker): - """checks for usage of new style capabilities on old style classes and - other new/old styles conflicts problems - * use of property, __slots__, super - * "super" usage - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'newstyle' - # messages - msgs = MSGS - priority = -2 - # configuration options - options = () - - @check_messages('slots-on-old-class', 'old-style-class') - def visit_classdef(self, node): - """ Check __slots__ in old style classes and old - style class definition. - """ - if '__slots__' in node and not node.newstyle: - confidence = (INFERENCE if has_known_bases(node) - else INFERENCE_FAILURE) - self.add_message('slots-on-old-class', node=node, - confidence=confidence) - # The node type could be class, exception, metaclass, or - # interface. Presumably, the non-class-type nodes would always - # have an explicit base class anyway. - if not node.bases and node.type == 'class' and not node.metaclass(): - # We use confidence HIGH here because this message should only ever - # be emitted for classes at the root of the inheritance hierarchyself. - self.add_message('old-style-class', node=node, confidence=HIGH) - - @check_messages('property-on-old-class') - def visit_call(self, node): - """check property usage""" - parent = node.parent.frame() - if (isinstance(parent, astroid.ClassDef) and - not parent.newstyle and - isinstance(node.func, astroid.Name)): - confidence = (INFERENCE if has_known_bases(parent) - else INFERENCE_FAILURE) - name = node.func.name - if name == 'property': - self.add_message('property-on-old-class', node=node, - confidence=confidence) - - @check_messages('super-on-old-class', 'bad-super-call', 'missing-super-argument') - def visit_functiondef(self, node): - """check use of super""" - # ignore actual functions or method within a new style class - if not node.is_method(): - return - klass = node.parent.frame() - for stmt in node.nodes_of_class(astroid.Call): - if node_frame_class(stmt) != node_frame_class(node): - # Don't look down in other scopes. - continue - - expr = stmt.func - if not isinstance(expr, astroid.Attribute): - continue - - call = expr.expr - # skip the test if using super - if not (isinstance(call, astroid.Call) and - isinstance(call.func, astroid.Name) and - call.func.name == 'super'): - continue - - if not klass.newstyle and has_known_bases(klass): - # super should not be used on an old style class - self.add_message('super-on-old-class', node=node) - else: - # super first arg should be the class - if not call.args: - if sys.version_info[0] == 3: - # unless Python 3 - continue - else: - self.add_message('missing-super-argument', node=call) - continue - - # calling super(type(self), self) can lead to recursion loop - # in derived classes - arg0 = call.args[0] - if isinstance(arg0, astroid.Call) and \ - isinstance(arg0.func, astroid.Name) and \ - arg0.func.name == 'type': - self.add_message('bad-super-call', node=call, args=('type', )) - continue - - # calling super(self.__class__, self) can lead to recursion loop - # in derived classes - if len(call.args) >= 2 and \ - isinstance(call.args[1], astroid.Name) and \ - call.args[1].name == 'self' and \ - isinstance(arg0, astroid.Attribute) and \ - arg0.attrname == '__class__': - self.add_message('bad-super-call', node=call, args=('self.__class__', )) - continue - - try: - supcls = call.args and next(call.args[0].infer(), None) - except astroid.InferenceError: - continue - - if klass is not supcls: - name = None - # if supcls is not YES, then supcls was infered - # and use its name. Otherwise, try to look - # for call.args[0].name - if supcls: - name = supcls.name - elif call.args and hasattr(call.args[0], 'name'): - name = call.args[0].name - if name: - self.add_message('bad-super-call', node=call, args=(name, )) - - visit_asyncfunctiondef = visit_functiondef - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(NewStyleConflictChecker(linter)) diff --git a/pymode/libs/pylint/checkers/python3.py b/pymode/libs/pylint/checkers/python3.py deleted file mode 100644 index 971072ee..00000000 --- a/pymode/libs/pylint/checkers/python3.py +++ /dev/null @@ -1,861 +0,0 @@ -# Copyright (c) 2014-2015 Brett Cannon -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015 Pavel Roskin -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Check Python 2 code for Python 2/3 source-compatible issues.""" -from __future__ import absolute_import, print_function - -import re -import sys -import tokenize - -from collections import namedtuple - -import six - -import astroid -from astroid import bases - -from pylint import checkers, interfaces -from pylint.interfaces import INFERENCE_FAILURE, INFERENCE -from pylint.utils import WarningScope -from pylint.checkers import utils - - -_ZERO = re.compile("^0+$") - -def _is_old_octal(literal): - if _ZERO.match(literal): - return False - if re.match(r'0\d+', literal): - try: - int(literal, 8) - except ValueError: - return False - return True - -def _check_dict_node(node): - inferred_types = set() - try: - inferred = node.infer() - for inferred_node in inferred: - inferred_types.add(inferred_node) - except astroid.InferenceError: - pass - return (not inferred_types - or any(isinstance(x, astroid.Dict) for x in inferred_types)) - -def _is_builtin(node): - return getattr(node, 'name', None) in ('__builtin__', 'builtins') - -_ACCEPTS_ITERATOR = {'iter', 'list', 'tuple', 'sorted', 'set', 'sum', 'any', - 'all', 'enumerate', 'dict'} - -def _in_iterating_context(node): - """Check if the node is being used as an iterator. - - Definition is taken from lib2to3.fixer_util.in_special_context(). - """ - parent = node.parent - # Since a call can't be the loop variant we only need to know if the node's - # parent is a 'for' loop to know it's being used as the iterator for the - # loop. - if isinstance(parent, astroid.For): - return True - # Need to make sure the use of the node is in the iterator part of the - # comprehension. - elif isinstance(parent, astroid.Comprehension): - if parent.iter == node: - return True - # Various built-ins can take in an iterable or list and lead to the same - # value. - elif isinstance(parent, astroid.Call): - if isinstance(parent.func, astroid.Name): - parent_scope = parent.func.lookup(parent.func.name)[0] - if _is_builtin(parent_scope) and parent.func.name in _ACCEPTS_ITERATOR: - return True - elif isinstance(parent.func, astroid.Attribute): - if parent.func.attrname == 'join': - return True - # If the call is in an unpacking, there's no need to warn, - # since it can be considered iterating. - elif (isinstance(parent, astroid.Assign) and - isinstance(parent.targets[0], (astroid.List, astroid.Tuple))): - if len(parent.targets[0].elts) > 1: - return True - return False - - -def _is_conditional_import(node): - """Checks if a import node is in the context of a conditional. - """ - parent = node.parent - return isinstance(parent, (astroid.TryExcept, astroid.ExceptHandler, - astroid.If, astroid.IfExp)) - -Branch = namedtuple('Branch', ['node', 'is_py2_only']) - -class Python3Checker(checkers.BaseChecker): - - __implements__ = interfaces.IAstroidChecker - enabled = False - name = 'python3' - - msgs = { - # Errors for what will syntactically break in Python 3, warnings for - # everything else. - 'E1601': ('print statement used', - 'print-statement', - 'Used when a print statement is used ' - '(`print` is a function in Python 3)', - {'maxversion': (3, 0)}), - 'E1602': ('Parameter unpacking specified', - 'parameter-unpacking', - 'Used when parameter unpacking is specified for a function' - "(Python 3 doesn't allow it)", - {'maxversion': (3, 0)}), - 'E1603': ('Implicit unpacking of exceptions is not supported ' - 'in Python 3', - 'unpacking-in-except', - 'Python3 will not allow implicit unpacking of ' - 'exceptions in except clauses. ' - 'See http://www.python.org/dev/peps/pep-3110/', - {'maxversion': (3, 0), - 'old_names': [('W0712', 'unpacking-in-except')]}), - 'E1604': ('Use raise ErrorClass(args) instead of ' - 'raise ErrorClass, args.', - 'old-raise-syntax', - "Used when the alternate raise syntax " - "'raise foo, bar' is used " - "instead of 'raise foo(bar)'.", - {'maxversion': (3, 0), - 'old_names': [('W0121', 'old-raise-syntax')]}), - 'E1605': ('Use of the `` operator', - 'backtick', - 'Used when the deprecated "``" (backtick) operator is used ' - 'instead of the str() function.', - {'scope': WarningScope.NODE, - 'maxversion': (3, 0), - 'old_names': [('W0333', 'backtick')]}), - 'E1609': ('Import * only allowed at module level', - 'import-star-module-level', - 'Used when the import star syntax is used somewhere ' - 'else than the module level.', - {'maxversion': (3, 0)}), - 'W1601': ('apply built-in referenced', - 'apply-builtin', - 'Used when the apply built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1602': ('basestring built-in referenced', - 'basestring-builtin', - 'Used when the basestring built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1603': ('buffer built-in referenced', - 'buffer-builtin', - 'Used when the buffer built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1604': ('cmp built-in referenced', - 'cmp-builtin', - 'Used when the cmp built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1605': ('coerce built-in referenced', - 'coerce-builtin', - 'Used when the coerce built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1606': ('execfile built-in referenced', - 'execfile-builtin', - 'Used when the execfile built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1607': ('file built-in referenced', - 'file-builtin', - 'Used when the file built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1608': ('long built-in referenced', - 'long-builtin', - 'Used when the long built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1609': ('raw_input built-in referenced', - 'raw_input-builtin', - 'Used when the raw_input built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1610': ('reduce built-in referenced', - 'reduce-builtin', - 'Used when the reduce built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1611': ('StandardError built-in referenced', - 'standarderror-builtin', - 'Used when the StandardError built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1612': ('unicode built-in referenced', - 'unicode-builtin', - 'Used when the unicode built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1613': ('xrange built-in referenced', - 'xrange-builtin', - 'Used when the xrange built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1614': ('__coerce__ method defined', - 'coerce-method', - 'Used when a __coerce__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1615': ('__delslice__ method defined', - 'delslice-method', - 'Used when a __delslice__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1616': ('__getslice__ method defined', - 'getslice-method', - 'Used when a __getslice__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1617': ('__setslice__ method defined', - 'setslice-method', - 'Used when a __setslice__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1618': ('import missing `from __future__ import absolute_import`', - 'no-absolute-import', - 'Used when an import is not accompanied by ' - '``from __future__ import absolute_import`` ' - '(default behaviour in Python 3)', - {'maxversion': (3, 0)}), - 'W1619': ('division w/o __future__ statement', - 'old-division', - 'Used for non-floor division w/o a float literal or ' - '``from __future__ import division`` ' - '(Python 3 returns a float for int division unconditionally)', - {'maxversion': (3, 0)}), - 'W1620': ('Calling a dict.iter*() method', - 'dict-iter-method', - 'Used for calls to dict.iterkeys(), itervalues() or iteritems() ' - '(Python 3 lacks these methods)', - {'maxversion': (3, 0)}), - 'W1621': ('Calling a dict.view*() method', - 'dict-view-method', - 'Used for calls to dict.viewkeys(), viewvalues() or viewitems() ' - '(Python 3 lacks these methods)', - {'maxversion': (3, 0)}), - 'W1622': ('Called a next() method on an object', - 'next-method-called', - "Used when an object's next() method is called " - '(Python 3 uses the next() built-in function)', - {'maxversion': (3, 0)}), - 'W1623': ("Assigning to a class's __metaclass__ attribute", - 'metaclass-assignment', - "Used when a metaclass is specified by assigning to __metaclass__ " - '(Python 3 specifies the metaclass as a class statement argument)', - {'maxversion': (3, 0)}), - 'W1624': ('Indexing exceptions will not work on Python 3', - 'indexing-exception', - 'Indexing exceptions will not work on Python 3. Use ' - '`exception.args[index]` instead.', - {'maxversion': (3, 0), - 'old_names': [('W0713', 'indexing-exception')]}), - 'W1625': ('Raising a string exception', - 'raising-string', - 'Used when a string exception is raised. This will not ' - 'work on Python 3.', - {'maxversion': (3, 0), - 'old_names': [('W0701', 'raising-string')]}), - 'W1626': ('reload built-in referenced', - 'reload-builtin', - 'Used when the reload built-in function is referenced ' - '(missing from Python 3). You can use instead imp.reload ' - 'or importlib.reload.', - {'maxversion': (3, 0)}), - 'W1627': ('__oct__ method defined', - 'oct-method', - 'Used when a __oct__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1628': ('__hex__ method defined', - 'hex-method', - 'Used when a __hex__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1629': ('__nonzero__ method defined', - 'nonzero-method', - 'Used when a __nonzero__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1630': ('__cmp__ method defined', - 'cmp-method', - 'Used when a __cmp__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - # 'W1631': replaced by W1636 - 'W1632': ('input built-in referenced', - 'input-builtin', - 'Used when the input built-in is referenced ' - '(backwards-incompatible semantics in Python 3)', - {'maxversion': (3, 0)}), - 'W1633': ('round built-in referenced', - 'round-builtin', - 'Used when the round built-in is referenced ' - '(backwards-incompatible semantics in Python 3)', - {'maxversion': (3, 0)}), - 'W1634': ('intern built-in referenced', - 'intern-builtin', - 'Used when the intern built-in is referenced ' - '(Moved to sys.intern in Python 3)', - {'maxversion': (3, 0)}), - 'W1635': ('unichr built-in referenced', - 'unichr-builtin', - 'Used when the unichr built-in is referenced ' - '(Use chr in Python 3)', - {'maxversion': (3, 0)}), - 'W1636': ('map built-in referenced when not iterating', - 'map-builtin-not-iterating', - 'Used when the map built-in is referenced in a non-iterating ' - 'context (returns an iterator in Python 3)', - {'maxversion': (3, 0), - 'old_names': [('W1631', 'implicit-map-evaluation')]}), - 'W1637': ('zip built-in referenced when not iterating', - 'zip-builtin-not-iterating', - 'Used when the zip built-in is referenced in a non-iterating ' - 'context (returns an iterator in Python 3)', - {'maxversion': (3, 0)}), - 'W1638': ('range built-in referenced when not iterating', - 'range-builtin-not-iterating', - 'Used when the range built-in is referenced in a non-iterating ' - 'context (returns an iterator in Python 3)', - {'maxversion': (3, 0)}), - 'W1639': ('filter built-in referenced when not iterating', - 'filter-builtin-not-iterating', - 'Used when the filter built-in is referenced in a non-iterating ' - 'context (returns an iterator in Python 3)', - {'maxversion': (3, 0)}), - 'W1640': ('Using the cmp argument for list.sort / sorted', - 'using-cmp-argument', - 'Using the cmp argument for list.sort or the sorted ' - 'builtin should be avoided, since it was removed in ' - 'Python 3. Using either `key` or `functools.cmp_to_key` ' - 'should be preferred.', - {'maxversion': (3, 0)}), - 'W1641': ('Implementing __eq__ without also implementing __hash__', - 'eq-without-hash', - 'Used when a class implements __eq__ but not __hash__. In Python 2, objects ' - 'get object.__hash__ as the default implementation, in Python 3 objects get ' - 'None as their default __hash__ implementation if they also implement __eq__.', - {'maxversion': (3, 0)}), - 'W1642': ('__div__ method defined', - 'div-method', - 'Used when a __div__ method is defined. Using `__truediv__` and setting' - '__div__ = __truediv__ should be preferred.' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1643': ('__idiv__ method defined', - 'idiv-method', - 'Used when a __idiv__ method is defined. Using `__itruediv__` and setting' - '__idiv__ = __itruediv__ should be preferred.' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1644': ('__rdiv__ method defined', - 'rdiv-method', - 'Used when a __rdiv__ method is defined. Using `__rtruediv__` and setting' - '__rdiv__ = __rtruediv__ should be preferred.' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1645': ('Exception.message removed in Python 3', - 'exception-message-attribute', - 'Used when the message attribute is accessed on an Exception. Use ' - 'str(exception) instead.', - {'maxversion': (3, 0)}), - 'W1646': ('non-text encoding used in str.decode', - 'invalid-str-codec', - 'Used when using str.encode or str.decode with a non-text encoding. Use ' - 'codecs module to handle arbitrary codecs.', - {'maxversion': (3, 0)}), - 'W1647': ('sys.maxint removed in Python 3', - 'sys-max-int', - 'Used when accessing sys.maxint. Use sys.maxsize instead.', - {'maxversion': (3, 0)}), - 'W1648': ('Module moved in Python 3', - 'bad-python3-import', - 'Used when importing a module that no longer exists in Python 3.', - {'maxversion': (3, 0)}), - 'W1649': ('Accessing a function method on the string module', - 'deprecated-string-function', - 'Used when accessing a string function that has been deprecated in Python 3.', - {'maxversion': (3, 0)}), - 'W1650': ('Using str.translate with deprecated deletechars parameters', - 'deprecated-str-translate-call', - 'Used when using the deprecated deletechars parameters from str.translate. Use' - 're.sub to remove the desired characters ', - {'maxversion': (3, 0)}), - } - - _bad_builtins = frozenset([ - 'apply', - 'basestring', - 'buffer', - 'cmp', - 'coerce', - 'execfile', - 'file', - 'input', # Not missing, but incompatible semantics - 'intern', - 'long', - 'raw_input', - 'reduce', - 'round', # Not missing, but incompatible semantics - 'StandardError', - 'unichr', - 'unicode', - 'xrange', - 'reload', - ]) - - _unused_magic_methods = frozenset([ - '__coerce__', - '__delslice__', - '__getslice__', - '__setslice__', - '__oct__', - '__hex__', - '__nonzero__', - '__cmp__', - '__div__', - '__idiv__', - '__rdiv__', - ]) - - _invalid_encodings = frozenset([ - 'base64_codec', - 'base64', - 'base_64', - 'bz2_codec', - 'bz2', - 'hex_codec', - 'hex', - 'quopri_codec', - 'quopri', - 'quotedprintable', - 'quoted_printable', - 'uu_codec', - 'uu', - 'zlib_codec', - 'zlib', - 'zip', - 'rot13', - 'rot_13', - ]) - - _bad_python3_module_map = { - 'sys-max-int': { - 'sys': frozenset(['maxint']) - }, - 'bad-python3-import': frozenset([ - 'anydbm', 'BaseHTTPServer', '__builtin__', 'CGIHTTPServer', 'ConfigParser', 'copy_reg', - 'cPickle', 'cProfile', 'cStringIO', 'Cookie', 'cookielib', 'dbhash', 'dbm', 'dumbdbm', - 'dumbdb', 'Dialog', 'DocXMLRPCServer', 'FileDialog', 'FixTk', 'gdbm', 'htmlentitydefs', - 'HTMLParser', 'httplib', 'markupbase', 'Queue', 'repr', 'robotparser', 'ScrolledText', - 'SimpleDialog', 'SimpleHTTPServer', 'SimpleXMLRPCServer', 'StringIO', 'dummy_thread', - 'SocketServer', 'test.test_support', 'Tkinter', 'Tix', 'Tkconstants', 'tkColorChooser', - 'tkCommonDialog', 'Tkdnd', 'tkFileDialog', 'tkFont', 'tkMessageBox', 'tkSimpleDialog', - 'turtle', 'UserList', 'UserString', 'whichdb', '_winreg', 'xmlrpclib', 'audiodev', - 'Bastion', 'bsddb185', 'bsddb3', 'Canvas', 'cfmfile', 'cl', 'commands', 'compiler', - 'dircache', 'dl', 'exception', 'fpformat', 'htmllib', 'ihooks', 'imageop', 'imputil', - 'linuxaudiodev', 'md5', 'mhlib', 'mimetools', 'MimeWriter', 'mimify', 'multifile', - 'mutex', 'new', 'popen2', 'posixfile', 'pure', 'rexec', 'rfc822', 'sha', 'sgmllib', - 'sre', 'stat', 'stringold', 'sunaudio', 'sv', 'test.testall', 'thread', 'timing', - 'toaiff', 'user', 'urllib2', 'urlparse' - ]), - 'deprecated-string-function': { - 'string': frozenset([ - 'maketrans', 'atof', 'atoi', 'atol', 'capitalize', 'expandtabs', 'find', 'rfind', - 'index', 'rindex', 'count', 'lower', 'split', 'rsplit', 'splitfields', 'join', - 'joinfields', 'lstrip', 'rstrip', 'strip', 'swapcase', 'translate', 'upper', - 'ljust', 'rjust', 'center', 'zfill', 'replace' - ]) - } - } - - if (3, 4) <= sys.version_info < (3, 4, 4): - # Python 3.4.0 -> 3.4.3 has a bug which breaks `repr_tree()`: - # https://bugs.python.org/issue23572 - _python_2_tests = frozenset() - else: - _python_2_tests = frozenset( - [astroid.extract_node(x).repr_tree() for x in [ - 'sys.version_info[0] == 2', - 'sys.version_info[0] < 3', - 'sys.version_info == (2, 7)', - 'sys.version_info <= (2, 7)', - 'sys.version_info < (3, 0)', - ]]) - - def __init__(self, *args, **kwargs): - self._future_division = False - self._future_absolute_import = False - self._modules_warned_about = set() - self._branch_stack = [] - super(Python3Checker, self).__init__(*args, **kwargs) - - def add_message(self, msg_id, always_warn=False, # pylint: disable=arguments-differ - *args, **kwargs): - if always_warn or not (self._branch_stack and self._branch_stack[-1].is_py2_only): - super(Python3Checker, self).add_message(msg_id, *args, **kwargs) - - def _is_py2_test(self, node): - if isinstance(node.test, astroid.Attribute) and isinstance(node.test.expr, astroid.Name): - if node.test.expr.name == 'six' and node.test.attrname == 'PY2': - return True - elif (isinstance(node.test, astroid.Compare) and - node.test.repr_tree() in self._python_2_tests): - return True - return False - - def visit_if(self, node): - self._branch_stack.append(Branch(node, self._is_py2_test(node))) - - def leave_if(self, node): - assert self._branch_stack.pop().node == node - - def visit_ifexp(self, node): - self._branch_stack.append(Branch(node, self._is_py2_test(node))) - - def leave_ifexp(self, node): - assert self._branch_stack.pop().node == node - - def visit_module(self, node): # pylint: disable=unused-argument - """Clear checker state after previous module.""" - self._future_division = False - self._future_absolute_import = False - - def visit_functiondef(self, node): - if node.is_method() and node.name in self._unused_magic_methods: - method_name = node.name - if node.name.startswith('__'): - method_name = node.name[2:-2] - self.add_message(method_name + '-method', node=node) - - @utils.check_messages('parameter-unpacking') - def visit_arguments(self, node): - for arg in node.args: - if isinstance(arg, astroid.Tuple): - self.add_message('parameter-unpacking', node=arg) - - def visit_name(self, node): - """Detect when a "bad" built-in is referenced.""" - found_node = node.lookup(node.name)[0] - if _is_builtin(found_node): - if node.name in self._bad_builtins: - message = node.name.lower() + '-builtin' - self.add_message(message, node=node) - - @utils.check_messages('print-statement') - def visit_print(self, node): - self.add_message('print-statement', node=node, always_warn=True) - - def _warn_if_deprecated(self, node, module, attributes, report_on_modules=True): - for message, module_map in six.iteritems(self._bad_python3_module_map): - if module in module_map and module not in self._modules_warned_about: - if isinstance(module_map, frozenset): - if report_on_modules: - self._modules_warned_about.add(module) - self.add_message(message, node=node) - elif attributes and module_map[module].intersection(attributes): - self.add_message(message, node=node) - - def visit_importfrom(self, node): - if node.modname == '__future__': - for name, _ in node.names: - if name == 'division': - self._future_division = True - elif name == 'absolute_import': - self._future_absolute_import = True - else: - if not self._future_absolute_import: - if self.linter.is_message_enabled('no-absolute-import'): - self.add_message('no-absolute-import', node=node) - if not _is_conditional_import(node): - self._warn_if_deprecated(node, node.modname, {x[0] for x in node.names}) - - if node.names[0][0] == '*': - if self.linter.is_message_enabled('import-star-module-level'): - if not isinstance(node.scope(), astroid.Module): - self.add_message('import-star-module-level', node=node) - - def visit_import(self, node): - if not self._future_absolute_import: - self.add_message('no-absolute-import', node=node) - if not _is_conditional_import(node): - for name, _ in node.names: - self._warn_if_deprecated(node, name, None) - - @utils.check_messages('metaclass-assignment') - def visit_classdef(self, node): - if '__metaclass__' in node.locals: - self.add_message('metaclass-assignment', node=node) - locals_and_methods = set(node.locals).union(x.name for x in node.mymethods()) - if '__eq__' in locals_and_methods and '__hash__' not in locals_and_methods: - self.add_message('eq-without-hash', node=node) - - @utils.check_messages('old-division') - def visit_binop(self, node): - if not self._future_division and node.op == '/': - for arg in (node.left, node.right): - if isinstance(arg, astroid.Const) and isinstance(arg.value, float): - break - else: - self.add_message('old-division', node=node) - - def _check_cmp_argument(self, node): - # Check that the `cmp` argument is used - kwargs = [] - if (isinstance(node.func, astroid.Attribute) - and node.func.attrname == 'sort'): - inferred = utils.safe_infer(node.func.expr) - if not inferred: - return - - builtins_list = "{}.list".format(bases.BUILTINS) - if (isinstance(inferred, astroid.List) - or inferred.qname() == builtins_list): - kwargs = node.keywords - - elif (isinstance(node.func, astroid.Name) - and node.func.name == 'sorted'): - inferred = utils.safe_infer(node.func) - if not inferred: - return - - builtins_sorted = "{}.sorted".format(bases.BUILTINS) - if inferred.qname() == builtins_sorted: - kwargs = node.keywords - - for kwarg in kwargs or []: - if kwarg.arg == 'cmp': - self.add_message('using-cmp-argument', node=node) - return - - @staticmethod - def _is_constant_string_or_name(node): - if isinstance(node, astroid.Const): - return isinstance(node.value, six.string_types) - return isinstance(node, astroid.Name) - - @staticmethod - def _is_none(node): - return isinstance(node, astroid.Const) and node.value is None - - @staticmethod - def _has_only_n_positional_args(node, number_of_args): - return len(node.args) == number_of_args and all(node.args) and not node.keywords - - @staticmethod - def _could_be_string(inferred_types): - confidence = INFERENCE if inferred_types else INFERENCE_FAILURE - for inferred_type in inferred_types: - if inferred_type is astroid.Uninferable: - confidence = INFERENCE_FAILURE - elif not (isinstance(inferred_type, astroid.Const) and - isinstance(inferred_type.value, six.string_types)): - return None - return confidence - - def visit_call(self, node): - self._check_cmp_argument(node) - - if isinstance(node.func, astroid.Attribute): - inferred_types = set() - try: - for inferred_receiver in node.func.expr.infer(): - inferred_types.add(inferred_receiver) - if isinstance(inferred_receiver, astroid.Module): - self._warn_if_deprecated(node, inferred_receiver.name, - {node.func.attrname}, - report_on_modules=False) - except astroid.InferenceError: - pass - if node.args: - is_str_confidence = self._could_be_string(inferred_types) - if is_str_confidence: - if (node.func.attrname in ('encode', 'decode') and - len(node.args) >= 1 and node.args[0]): - first_arg = node.args[0] - self._validate_encoding(first_arg, node) - if (node.func.attrname == 'translate' and - self._has_only_n_positional_args(node, 2) and - self._is_none(node.args[0]) and - self._is_constant_string_or_name(node.args[1])): - # The above statement looking for calls of the form: - # - # foo.translate(None, 'abc123') - # - # or - # - # foo.translate(None, some_variable) - # - # This check is somewhat broad and _may_ have some false positives, but - # after checking several large codebases it did not have any false - # positives while finding several real issues. This call pattern seems - # rare enough that the trade off is worth it. - self.add_message('deprecated-str-translate-call', - node=node, - confidence=is_str_confidence) - return - if node.keywords: - return - if node.func.attrname == 'next': - self.add_message('next-method-called', node=node) - else: - if _check_dict_node(node.func.expr): - if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems'): - self.add_message('dict-iter-method', node=node) - elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems'): - self.add_message('dict-view-method', node=node) - elif isinstance(node.func, astroid.Name): - found_node = node.func.lookup(node.func.name)[0] - if _is_builtin(found_node): - if node.func.name in ('filter', 'map', 'range', 'zip'): - if not _in_iterating_context(node): - checker = '{}-builtin-not-iterating'.format(node.func.name) - self.add_message(checker, node=node) - if node.func.name == 'open' and node.keywords: - kwargs = node.keywords - for kwarg in kwargs or []: - if kwarg.arg == 'encoding': - self._validate_encoding(kwarg.value, node) - break - - def _validate_encoding(self, encoding, node): - if isinstance(encoding, astroid.Const): - value = encoding.value - if value in self._invalid_encodings: - self.add_message('invalid-str-codec', - node=node) - - @utils.check_messages('indexing-exception') - def visit_subscript(self, node): - """ Look for indexing exceptions. """ - try: - for inferred in node.value.infer(): - if not isinstance(inferred, astroid.Instance): - continue - if utils.inherit_from_std_ex(inferred): - self.add_message('indexing-exception', node=node) - except astroid.InferenceError: - return - - def visit_assignattr(self, node): - if isinstance(node.assign_type(), astroid.AugAssign): - self.visit_attribute(node) - - def visit_delattr(self, node): - self.visit_attribute(node) - - @utils.check_messages('exception-message-attribute') - def visit_attribute(self, node): - """ Look for accessing message on exceptions. """ - try: - for inferred in node.expr.infer(): - if (isinstance(inferred, astroid.Instance) and - utils.inherit_from_std_ex(inferred)): - if node.attrname == 'message': - self.add_message('exception-message-attribute', node=node) - if isinstance(inferred, astroid.Module): - self._warn_if_deprecated(node, inferred.name, {node.attrname}, - report_on_modules=False) - except astroid.InferenceError: - return - - @utils.check_messages('unpacking-in-except') - def visit_excepthandler(self, node): - """Visit an except handler block and check for exception unpacking.""" - if isinstance(node.name, (astroid.Tuple, astroid.List)): - self.add_message('unpacking-in-except', node=node) - - @utils.check_messages('backtick') - def visit_repr(self, node): - self.add_message('backtick', node=node) - - @utils.check_messages('raising-string', 'old-raise-syntax') - def visit_raise(self, node): - """Visit a raise statement and check for raising - strings or old-raise-syntax. - """ - if (node.exc is not None and - node.inst is not None and - node.tback is None): - self.add_message('old-raise-syntax', node=node) - - # Ignore empty raise. - if node.exc is None: - return - expr = node.exc - if self._check_raise_value(node, expr): - return - else: - try: - value = next(astroid.unpack_infer(expr)) - except astroid.InferenceError: - return - self._check_raise_value(node, value) - - def _check_raise_value(self, node, expr): - if isinstance(expr, astroid.Const): - value = expr.value - if isinstance(value, str): - self.add_message('raising-string', node=node) - return True - - -class Python3TokenChecker(checkers.BaseTokenChecker): - __implements__ = interfaces.ITokenChecker - name = 'python3' - enabled = False - - msgs = { - 'E1606': ('Use of long suffix', - 'long-suffix', - 'Used when "l" or "L" is used to mark a long integer. ' - 'This will not work in Python 3, since `int` and `long` ' - 'types have merged.', - {'maxversion': (3, 0)}), - 'E1607': ('Use of the <> operator', - 'old-ne-operator', - 'Used when the deprecated "<>" operator is used instead ' - 'of "!=". This is removed in Python 3.', - {'maxversion': (3, 0), - 'old_names': [('W0331', 'old-ne-operator')]}), - 'E1608': ('Use of old octal literal', - 'old-octal-literal', - 'Used when encountering the old octal syntax, ' - 'removed in Python 3. To use the new syntax, ' - 'prepend 0o on the number.', - {'maxversion': (3, 0)}), - } - - def process_tokens(self, tokens): - for idx, (tok_type, token, start, _, _) in enumerate(tokens): - if tok_type == tokenize.NUMBER: - if token.lower().endswith('l'): - # This has a different semantic than lowercase-l-suffix. - self.add_message('long-suffix', line=start[0]) - elif _is_old_octal(token): - self.add_message('old-octal-literal', line=start[0]) - if tokens[idx][1] == '<>': - self.add_message('old-ne-operator', line=tokens[idx][2][0]) - - -def register(linter): - linter.register_checker(Python3Checker(linter)) - linter.register_checker(Python3TokenChecker(linter)) diff --git a/pymode/libs/pylint/checkers/raw_metrics.py b/pymode/libs/pylint/checkers/raw_metrics.py deleted file mode 100644 index 2cceee7e..00000000 --- a/pymode/libs/pylint/checkers/raw_metrics.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) 2007, 2010, 2013, 2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). - http://www.logilab.fr/ -- mailto:contact@logilab.fr - -Raw metrics checker -""" - -import tokenize - -from pylint.interfaces import ITokenChecker -from pylint.exceptions import EmptyReportError -from pylint.checkers import BaseTokenChecker -from pylint.reporters import diff_string -from pylint.reporters.ureports.nodes import Table - - -def report_raw_stats(sect, stats, old_stats): - """calculate percentage of code / doc / comment / empty - """ - total_lines = stats['total_lines'] - if not total_lines: - raise EmptyReportError() - sect.description = '%s lines have been analyzed' % total_lines - lines = ('type', 'number', '%', 'previous', 'difference') - for node_type in ('code', 'docstring', 'comment', 'empty'): - key = node_type + '_lines' - total = stats[key] - percent = float(total * 100) / total_lines - old = old_stats.get(key, None) - if old is not None: - diff_str = diff_string(old, total) - else: - old, diff_str = 'NC', 'NC' - lines += (node_type, str(total), '%.2f' % percent, - str(old), diff_str) - sect.append(Table(children=lines, cols=5, rheaders=1)) - - -class RawMetricsChecker(BaseTokenChecker): - """does not check anything but gives some raw metrics : - * total number of lines - * total number of code lines - * total number of docstring lines - * total number of comments lines - * total number of empty lines - """ - - __implements__ = (ITokenChecker,) - - # configuration section name - name = 'metrics' - # configuration options - options = () - # messages - msgs = {} - # reports - reports = (('RP0701', 'Raw metrics', report_raw_stats),) - - def __init__(self, linter): - BaseTokenChecker.__init__(self, linter) - self.stats = None - - def open(self): - """init statistics""" - self.stats = self.linter.add_stats(total_lines=0, code_lines=0, - empty_lines=0, docstring_lines=0, - comment_lines=0) - - def process_tokens(self, tokens): - """update stats""" - i = 0 - tokens = list(tokens) - while i < len(tokens): - i, lines_number, line_type = get_type(tokens, i) - self.stats['total_lines'] += lines_number - self.stats[line_type] += lines_number - - -JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER) - -def get_type(tokens, start_index): - """return the line type : docstring, comment, code, empty""" - i = start_index - tok_type = tokens[i][0] - start = tokens[i][2] - pos = start - line_type = None - while i < len(tokens) and tokens[i][2][0] == start[0]: - tok_type = tokens[i][0] - pos = tokens[i][3] - if line_type is None: - if tok_type == tokenize.STRING: - line_type = 'docstring_lines' - elif tok_type == tokenize.COMMENT: - line_type = 'comment_lines' - elif tok_type in JUNK: - pass - else: - line_type = 'code_lines' - i += 1 - if line_type is None: - line_type = 'empty_lines' - elif i < len(tokens) and tokens[i][0] == tokenize.NEWLINE: - i += 1 - return i, pos[0] - start[0] + 1, line_type - - -def register(linter): - """ required method to auto register this checker """ - linter.register_checker(RawMetricsChecker(linter)) diff --git a/pymode/libs/pylint/checkers/refactoring.py b/pymode/libs/pylint/checkers/refactoring.py deleted file mode 100644 index 418e63ef..00000000 --- a/pymode/libs/pylint/checkers/refactoring.py +++ /dev/null @@ -1,715 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2016 Moisés López -# Copyright (c) 2016 Claudiu Popa -# Copyright (c) 2016 Alexander Todorov - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Looks for code which can be refactored.""" - -import collections -import itertools -import tokenize - -import astroid -from astroid import decorators -import six - -from pylint import interfaces -from pylint import checkers -from pylint import utils as lint_utils -from pylint.checkers import utils - - -def _all_elements_are_true(gen): - values = list(gen) - return values and all(values) - - -def _if_statement_is_always_returning(if_node): - def _has_return_node(elems, scope): - for node in elems: - if isinstance(node, astroid.If): - yield _if_statement_is_always_returning(node) - elif isinstance(node, astroid.Return): - yield node.scope() is scope - - scope = if_node.scope() - body_returns = _all_elements_are_true( - _has_return_node(if_node.body, scope=scope) - ) - if if_node.orelse: - orelse_returns = _all_elements_are_true( - _has_return_node(if_node.orelse, scope=scope) - ) - else: - orelse_returns = False - - return body_returns and orelse_returns - - -class RefactoringChecker(checkers.BaseTokenChecker): - """Looks for code which can be refactored - - This checker also mixes the astroid and the token approaches - in order to create knowledge about whether a "else if" node - is a true "else if" node, or a "elif" node. - """ - - __implements__ = (interfaces.ITokenChecker, interfaces.IAstroidChecker) - - name = 'refactoring' - - msgs = { - 'R1701': ("Consider merging these isinstance calls to isinstance(%s, (%s))", - "consider-merging-isinstance", - "Used when multiple consecutive isinstance calls can be merged into one."), - 'R1706': ("Consider using ternary (%s if %s else %s)", - "consider-using-ternary", - "Used when one of known pre-python 2.5 ternary syntax is used."), - 'R1702': ('Too many nested blocks (%s/%s)', - 'too-many-nested-blocks', - 'Used when a function or a method has too many nested ' - 'blocks. This makes the code less understandable and ' - 'maintainable.', - {'old_names': [('R0101', 'too-many-nested-blocks')]}), - 'R1703': ('The if statement can be replaced with %s', - 'simplifiable-if-statement', - 'Used when an if statement can be replaced with ' - '\'bool(test)\'. ', - {'old_names': [('R0102', 'simplifiable-if-statement')]}), - 'R1704': ('Redefining argument with the local name %r', - 'redefined-argument-from-local', - 'Used when a local name is redefining an argument, which might ' - 'suggest a potential error. This is taken in account only for ' - 'a handful of name binding operations, such as for iteration, ' - 'with statement assignment and exception handler assignment.' - ), - 'R1705': ('Unnecessary "else" after "return"', - 'no-else-return', - 'Used in order to highlight an unnecessary block of ' - 'code following an if containing a return statement. ' - 'As such, it will warn when it encounters an else ' - 'following a chain of ifs, all of them containing a ' - 'return statement.' - ), - 'R1707': ('Disallow trailing comma tuple', - 'trailing-comma-tuple', - 'In Python, a tuple is actually created by the comma symbol, ' - 'not by the parentheses. Unfortunately, one can actually create a ' - 'tuple by misplacing a trailing comma, which can lead to potential ' - 'weird bugs in your code. You should always use parentheses ' - 'explicitly for creating a tuple.', - {'minversion': (3, 0)}), - } - options = (('max-nested-blocks', - {'default': 5, 'type': 'int', 'metavar': '', - 'help': 'Maximum number of nested blocks for function / ' - 'method body'} - ),) - - priority = 0 - - def __init__(self, linter=None): - checkers.BaseTokenChecker.__init__(self, linter) - self._init() - - def _init(self): - self._nested_blocks = [] - self._elifs = [] - self._if_counter = 0 - self._nested_blocks_msg = None - - @decorators.cachedproperty - def _dummy_rgx(self): - return lint_utils.get_global_option( - self, 'dummy-variables-rgx', default=None) - - @staticmethod - def _is_bool_const(node): - return (isinstance(node.value, astroid.Const) - and isinstance(node.value.value, bool)) - - def _is_actual_elif(self, node): - """Check if the given node is an actual elif - - This is a problem we're having with the builtin ast module, - which splits `elif` branches into a separate if statement. - Unfortunately we need to know the exact type in certain - cases. - """ - - if isinstance(node.parent, astroid.If): - orelse = node.parent.orelse - # current if node must directly follow a "else" - if orelse and orelse == [node]: - if self._elifs[self._if_counter]: - return True - return False - - def _check_simplifiable_if(self, node): - """Check if the given if node can be simplified. - - The if statement can be reduced to a boolean expression - in some cases. For instance, if there are two branches - and both of them return a boolean value that depends on - the result of the statement's test, then this can be reduced - to `bool(test)` without losing any functionality. - """ - - if self._is_actual_elif(node): - # Not interested in if statements with multiple branches. - return - if len(node.orelse) != 1 or len(node.body) != 1: - return - - # Check if both branches can be reduced. - first_branch = node.body[0] - else_branch = node.orelse[0] - if isinstance(first_branch, astroid.Return): - if not isinstance(else_branch, astroid.Return): - return - first_branch_is_bool = self._is_bool_const(first_branch) - else_branch_is_bool = self._is_bool_const(else_branch) - reduced_to = "'return bool(test)'" - elif isinstance(first_branch, astroid.Assign): - if not isinstance(else_branch, astroid.Assign): - return - first_branch_is_bool = self._is_bool_const(first_branch) - else_branch_is_bool = self._is_bool_const(else_branch) - reduced_to = "'var = bool(test)'" - else: - return - - if not first_branch_is_bool or not else_branch_is_bool: - return - if not first_branch.value.value: - # This is a case that can't be easily simplified and - # if it can be simplified, it will usually result in a - # code that's harder to understand and comprehend. - # Let's take for instance `arg and arg <= 3`. This could theoretically be - # reduced to `not arg or arg > 3`, but the net result is that now the - # condition is harder to understand, because it requires understanding of - # an extra clause: - # * first, there is the negation of truthness with `not arg` - # * the second clause is `arg > 3`, which occurs when arg has a - # a truth value, but it implies that `arg > 3` is equivalent - # with `arg and arg > 3`, which means that the user must - # think about this assumption when evaluating `arg > 3`. - # The original form is easier to grasp. - return - - self.add_message('simplifiable-if-statement', node=node, - args=(reduced_to,)) - - def process_tokens(self, tokens): - # Process tokens and look for 'if' or 'elif' - for index, token in enumerate(tokens): - token_string = token[1] - if token_string == 'elif': - self._elifs.append(True) - elif token_string == 'if': - self._elifs.append(False) - elif six.PY3 and token.exact_type == tokenize.COMMA: - self._check_one_element_trailing_comma_tuple(tokens, token, index) - - def _check_one_element_trailing_comma_tuple(self, tokens, token, index): - left_tokens = itertools.islice(tokens, index + 1, None) - same_line_remaining_tokens = list( - other_token for other_token in left_tokens - if other_token.start[0] == token.start[0] - ) - is_last_element = all( - other_token.type in (tokenize.NEWLINE, tokenize.COMMENT) - for other_token in same_line_remaining_tokens - ) - - if not same_line_remaining_tokens or not is_last_element: - return - - assign_token = tokens[index-2:index-1] - if assign_token and '=' in assign_token[0].string: - if self.linter.is_message_enabled('trailing-comma-tuple'): - self.add_message('trailing-comma-tuple', - line=token.start[0]) - - def leave_module(self, _): - self._init() - - @utils.check_messages('too-many-nested-blocks') - def visit_tryexcept(self, node): - self._check_nested_blocks(node) - - visit_tryfinally = visit_tryexcept - visit_while = visit_tryexcept - - def _check_redefined_argument_from_local(self, name_node): - if self._dummy_rgx and self._dummy_rgx.match(name_node.name): - return - if not name_node.lineno: - # Unknown position, maybe it is a manually built AST? - return - - scope = name_node.scope() - if not isinstance(scope, astroid.FunctionDef): - return - - for defined_argument in scope.args.nodes_of_class(astroid.AssignName): - if defined_argument.name == name_node.name: - self.add_message('redefined-argument-from-local', - node=name_node, - args=(name_node.name, )) - - @utils.check_messages('redefined-argument-from-local', - 'too-many-nested-blocks') - def visit_for(self, node): - self._check_nested_blocks(node) - - for name in node.target.nodes_of_class(astroid.AssignName): - self._check_redefined_argument_from_local(name) - - @utils.check_messages('redefined-argument-from-local') - def visit_excepthandler(self, node): - if node.name and isinstance(node.name, astroid.AssignName): - self._check_redefined_argument_from_local(node.name) - - @utils.check_messages('redefined-argument-from-local') - def visit_with(self, node): - for _, names in node.items: - if not names: - continue - for name in names.nodes_of_class(astroid.AssignName): - self._check_redefined_argument_from_local(name) - - def visit_ifexp(self, _): - self._if_counter += 1 - - def visit_comprehension(self, node): - self._if_counter += len(node.ifs) - - def _check_superfluous_else_return(self, node): - if not node.orelse: - # Not interested in if statements without else. - return - - if _if_statement_is_always_returning(node) and not self._is_actual_elif(node): - self.add_message('no-else-return', node=node) - - @utils.check_messages('too-many-nested-blocks', 'simplifiable-if-statement', - 'no-else-return',) - def visit_if(self, node): - self._check_simplifiable_if(node) - self._check_nested_blocks(node) - self._check_superfluous_else_return(node) - self._if_counter += 1 - - @utils.check_messages('too-many-nested-blocks') - def leave_functiondef(self, _): - # check left-over nested blocks stack - self._emit_nested_blocks_message_if_needed(self._nested_blocks) - # new scope = reinitialize the stack of nested blocks - self._nested_blocks = [] - - def _check_nested_blocks(self, node): - """Update and check the number of nested blocks - """ - # only check block levels inside functions or methods - if not isinstance(node.scope(), astroid.FunctionDef): - return - # messages are triggered on leaving the nested block. Here we save the - # stack in case the current node isn't nested in the previous one - nested_blocks = self._nested_blocks[:] - if node.parent == node.scope(): - self._nested_blocks = [node] - else: - # go through ancestors from the most nested to the less - for ancestor_node in reversed(self._nested_blocks): - if ancestor_node == node.parent: - break - self._nested_blocks.pop() - # if the node is a elif, this should not be another nesting level - if isinstance(node, astroid.If) and self._elifs[self._if_counter]: - if self._nested_blocks: - self._nested_blocks.pop() - self._nested_blocks.append(node) - - # send message only once per group of nested blocks - if len(nested_blocks) > len(self._nested_blocks): - self._emit_nested_blocks_message_if_needed(nested_blocks) - - def _emit_nested_blocks_message_if_needed(self, nested_blocks): - if len(nested_blocks) > self.config.max_nested_blocks: - self.add_message('too-many-nested-blocks', node=nested_blocks[0], - args=(len(nested_blocks), self.config.max_nested_blocks)) - - @staticmethod - def _duplicated_isinstance_types(node): - """Get the duplicated types from the underlying isinstance calls. - - :param astroid.BoolOp node: Node which should contain a bunch of isinstance calls. - :returns: Dictionary of the comparison objects from the isinstance calls, - to duplicate values from consecutive calls. - :rtype: dict - """ - duplicated_objects = set() - all_types = collections.defaultdict(set) - - for call in node.values: - if not isinstance(call, astroid.Call) or len(call.args) != 2: - continue - - inferred = utils.safe_infer(call.func) - if not inferred or not utils.is_builtin_object(inferred): - continue - - if inferred.name != 'isinstance': - continue - - isinstance_object = call.args[0].as_string() - isinstance_types = call.args[1] - - if isinstance_object in all_types: - duplicated_objects.add(isinstance_object) - - if isinstance(isinstance_types, astroid.Tuple): - elems = [class_type.as_string() for class_type in isinstance_types.itered()] - else: - elems = [isinstance_types.as_string()] - all_types[isinstance_object].update(elems) - - # Remove all keys which not duplicated - return {key: value for key, value in all_types.items() - if key in duplicated_objects} - - @utils.check_messages('consider-merging-isinstance') - def visit_boolop(self, node): - '''Check isinstance calls which can be merged together.''' - if node.op != 'or': - return - - first_args = self._duplicated_isinstance_types(node) - for duplicated_name, class_names in first_args.items(): - names = sorted(name for name in class_names) - self.add_message('consider-merging-isinstance', - node=node, - args=(duplicated_name, ', '.join(names))) - - @utils.check_messages('consider-using-ternary') - def visit_assign(self, node): - if self._is_and_or_ternary(node.value): - cond, truth_value, false_value = self._and_or_ternary_arguments(node.value) - elif self._is_seq_based_ternary(node.value): - cond, truth_value, false_value = self._seq_based_ternary_params(node.value) - else: - return - - self.add_message( - 'consider-using-ternary', node=node, - args=(truth_value.as_string(), - cond.as_string(), - false_value.as_string()),) - - visit_return = visit_assign - - @staticmethod - def _is_and_or_ternary(node): - """ - Returns true if node is 'condition and true_value else false_value' form. - - All of: condition, true_value and false_value should not be a complex boolean expression - """ - return (isinstance(node, astroid.BoolOp) - and node.op == 'or' and len(node.values) == 2 - and isinstance(node.values[0], astroid.BoolOp) - and not isinstance(node.values[1], astroid.BoolOp) - and node.values[0].op == 'and' - and not isinstance(node.values[0].values[1], astroid.BoolOp) - and len(node.values[0].values) == 2) - - @staticmethod - def _and_or_ternary_arguments(node): - false_value = node.values[1] - condition, true_value = node.values[0].values - return condition, true_value, false_value - - @staticmethod - def _is_seq_based_ternary(node): - """Returns true if node is '[false_value,true_value][condition]' form""" - return (isinstance(node, astroid.Subscript) - and isinstance(node.value, (astroid.Tuple, astroid.List)) - and len(node.value.elts) == 2 and isinstance(node.slice, astroid.Index)) - - @staticmethod - def _seq_based_ternary_params(node): - false_value, true_value = node.value.elts - condition = node.slice.value - return condition, true_value, false_value - - -class RecommandationChecker(checkers.BaseChecker): - __implements__ = (interfaces.IAstroidChecker,) - name = 'refactoring' - msgs = {'C0200': ('Consider using enumerate instead of iterating with range and len', - 'consider-using-enumerate', - 'Emitted when code that iterates with range and len is ' - 'encountered. Such code can be simplified by using the ' - 'enumerate builtin.'), - 'C0201': ('Consider iterating the dictionary directly instead of calling .keys()', - 'consider-iterating-dictionary', - 'Emitted when the keys of a dictionary are iterated through the .keys() ' - 'method. It is enough to just iterate through the dictionary itself, as ' - 'in "for key in dictionary".'), - } - - @staticmethod - def _is_builtin(node, function): - inferred = utils.safe_infer(node) - if not inferred: - return False - return utils.is_builtin_object(inferred) and inferred.name == function - - @utils.check_messages('consider-iterating-dictionary') - def visit_call(self, node): - inferred = utils.safe_infer(node.func) - if not inferred: - return - - if not isinstance(inferred, astroid.BoundMethod): - return - if not isinstance(inferred.bound, astroid.Dict) or inferred.name != 'keys': - return - - if isinstance(node.parent, (astroid.For, astroid.Comprehension)): - self.add_message('consider-iterating-dictionary', node=node) - - @utils.check_messages('consider-using-enumerate') - def visit_for(self, node): - """Emit a convention whenever range and len are used for indexing.""" - # Verify that we have a `range(len(...))` call and that the object - # which is iterated is used as a subscript in the body of the for. - - # Is it a proper range call? - if not isinstance(node.iter, astroid.Call): - return - if not self._is_builtin(node.iter.func, 'range'): - return - if len(node.iter.args) != 1: - return - - # Is it a proper len call? - if not isinstance(node.iter.args[0], astroid.Call): - return - second_func = node.iter.args[0].func - if not self._is_builtin(second_func, 'len'): - return - len_args = node.iter.args[0].args - if not len_args or len(len_args) != 1: - return - iterating_object = len_args[0] - if not isinstance(iterating_object, astroid.Name): - return - - # Verify that the body of the for loop uses a subscript - # with the object that was iterated. This uses some heuristics - # in order to make sure that the same object is used in the - # for body. - for child in node.body: - for subscript in child.nodes_of_class(astroid.Subscript): - if not isinstance(subscript.value, astroid.Name): - continue - if not isinstance(subscript.slice, astroid.Index): - continue - if not isinstance(subscript.slice.value, astroid.Name): - continue - if subscript.slice.value.name != node.target.name: - continue - if iterating_object.name != subscript.value.name: - continue - if subscript.value.scope() != node.scope(): - # Ignore this subscript if it's not in the same - # scope. This means that in the body of the for - # loop, another scope was created, where the same - # name for the iterating object was used. - continue - self.add_message('consider-using-enumerate', node=node) - return - - -class NotChecker(checkers.BaseChecker): - """checks for too many not in comparison expressions - - - "not not" should trigger a warning - - "not" followed by a comparison should trigger a warning - """ - __implements__ = (interfaces.IAstroidChecker,) - msgs = {'C0113': ('Consider changing "%s" to "%s"', - 'unneeded-not', - 'Used when a boolean expression contains an unneeded ' - 'negation.'), - } - name = 'basic' - reverse_op = {'<': '>=', '<=': '>', '>': '<=', '>=': '<', '==': '!=', - '!=': '==', 'in': 'not in', 'is': 'is not'} - # sets are not ordered, so for example "not set(LEFT_VALS) <= set(RIGHT_VALS)" is - # not equivalent to "set(LEFT_VALS) > set(RIGHT_VALS)" - skipped_nodes = (astroid.Set,) - # 'builtins' py3, '__builtin__' py2 - skipped_classnames = ['%s.%s' % (six.moves.builtins.__name__, qname) - for qname in ('set', 'frozenset')] - - @utils.check_messages('unneeded-not') - def visit_unaryop(self, node): - if node.op != 'not': - return - operand = node.operand - - if isinstance(operand, astroid.UnaryOp) and operand.op == 'not': - self.add_message('unneeded-not', node=node, - args=(node.as_string(), - operand.operand.as_string())) - elif isinstance(operand, astroid.Compare): - left = operand.left - # ignore multiple comparisons - if len(operand.ops) > 1: - return - operator, right = operand.ops[0] - if operator not in self.reverse_op: - return - # Ignore __ne__ as function of __eq__ - frame = node.frame() - if frame.name == '__ne__' and operator == '==': - return - for _type in (utils.node_type(left), utils.node_type(right)): - if not _type: - return - if isinstance(_type, self.skipped_nodes): - return - if (isinstance(_type, astroid.Instance) and - _type.qname() in self.skipped_classnames): - return - suggestion = '%s %s %s' % (left.as_string(), - self.reverse_op[operator], - right.as_string()) - self.add_message('unneeded-not', node=node, - args=(node.as_string(), suggestion)) - - -def _is_len_call(node): - """Checks if node is len(SOMETHING).""" - return (isinstance(node, astroid.Call) and isinstance(node.func, astroid.Name) and - node.func.name == 'len') - -def _is_constant_zero(node): - return isinstance(node, astroid.Const) and node.value == 0 - -def _node_is_test_condition(node): - """ Checks if node is an if, while, assert or if expression statement.""" - return isinstance(node, (astroid.If, astroid.While, astroid.Assert, astroid.IfExp)) - - -class LenChecker(checkers.BaseChecker): - """Checks for incorrect usage of len() inside conditions. - Pep8 states: - For sequences, (strings, lists, tuples), use the fact that empty sequences are false. - - Yes: if not seq: - if seq: - - No: if len(seq): - if not len(seq): - - Problems detected: - * if len(sequence): - * if not len(sequence): - * if len(sequence) == 0: - * if len(sequence) != 0: - * if len(sequence) > 0: - """ - - __implements__ = (interfaces.IAstroidChecker,) - - # configuration section name - name = 'len' - msgs = {'C1801': ('Do not use `len(SEQUENCE)` as condition value', - 'len-as-condition', - 'Used when Pylint detects incorrect use of len(sequence) inside ' - 'conditions.'), - } - - priority = -2 - options = () - - @utils.check_messages('len-as-condition') - def visit_call(self, node): - # a len(S) call is used inside a test condition - # could be if, while, assert or if expression statement - # e.g. `if len(S):` - if _is_len_call(node): - # the len() call could also be nested together with other - # boolean operations, e.g. `if z or len(x):` - parent = node.parent - while isinstance(parent, astroid.BoolOp): - parent = parent.parent - - # we're finally out of any nested boolean operations so check if - # this len() call is part of a test condition - if not _node_is_test_condition(parent): - return - if not (node is parent.test or parent.test.parent_of(node)): - return - self.add_message('len-as-condition', node=node) - - @utils.check_messages('len-as-condition') - def visit_unaryop(self, node): - """`not len(S)` must become `not S` regardless if the parent block - is a test condition or something else (boolean expression) - e.g. `if not len(S):`""" - if isinstance(node, astroid.UnaryOp) and node.op == 'not' and _is_len_call(node.operand): - self.add_message('len-as-condition', node=node) - - @utils.check_messages('len-as-condition') - def visit_compare(self, node): - # compare nodes are trickier because the len(S) expression - # may be somewhere in the middle of the node - - # note: astroid.Compare has the left most operand in node.left - # while the rest are a list of tuples in node.ops - # the format of the tuple is ('compare operator sign', node) - # here we squash everything into `ops` to make it easier for processing later - ops = [('', node.left)] - ops.extend(node.ops) - ops = list(itertools.chain(*ops)) - - for ops_idx in range(len(ops) - 2): - op_1 = ops[ops_idx] - op_2 = ops[ops_idx + 1] - op_3 = ops[ops_idx + 2] - error_detected = False - - # 0 ?? len() - if _is_constant_zero(op_1) and op_2 in ['==', '!=', '<'] and _is_len_call(op_3): - error_detected = True - # len() ?? 0 - elif _is_len_call(op_1) and op_2 in ['==', '!=', '>'] and _is_constant_zero(op_3): - error_detected = True - - if error_detected: - parent = node.parent - # traverse the AST to figure out if this comparison was part of - # a test condition - while parent and not _node_is_test_condition(parent): - parent = parent.parent - - # report only if this len() comparison is part of a test condition - # for example: return len() > 0 should not report anything - if _node_is_test_condition(parent): - self.add_message('len-as-condition', node=node) - - -def register(linter): - """Required method to auto register this checker.""" - linter.register_checker(RefactoringChecker(linter)) - linter.register_checker(NotChecker(linter)) - linter.register_checker(RecommandationChecker(linter)) - linter.register_checker(LenChecker(linter)) diff --git a/pymode/libs/pylint/checkers/similar.py b/pymode/libs/pylint/checkers/similar.py deleted file mode 100644 index aa59bfd8..00000000 --- a/pymode/libs/pylint/checkers/similar.py +++ /dev/null @@ -1,363 +0,0 @@ -# Copyright (c) 2006, 2008-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -# pylint: disable=W0622 -"""a similarities / code duplication command line tool and pylint checker -""" - -from __future__ import print_function -import sys -from collections import defaultdict - -import six -from six.moves import zip - -from pylint.interfaces import IRawChecker -from pylint.checkers import BaseChecker, table_lines_from_stats -from pylint.reporters.ureports.nodes import Table - - -class Similar(object): - """finds copy-pasted lines of code in a project""" - - def __init__(self, min_lines=4, ignore_comments=False, - ignore_docstrings=False, ignore_imports=False): - self.min_lines = min_lines - self.ignore_comments = ignore_comments - self.ignore_docstrings = ignore_docstrings - self.ignore_imports = ignore_imports - self.linesets = [] - - def append_stream(self, streamid, stream, encoding=None): - """append a file to search for similarities""" - if encoding is None: - readlines = stream.readlines - else: - readlines = lambda: [line.decode(encoding) for line in stream] - try: - self.linesets.append(LineSet(streamid, - readlines(), - self.ignore_comments, - self.ignore_docstrings, - self.ignore_imports)) - except UnicodeDecodeError: - pass - - def run(self): - """start looking for similarities and display results on stdout""" - self._display_sims(self._compute_sims()) - - def _compute_sims(self): - """compute similarities in appended files""" - no_duplicates = defaultdict(list) - for num, lineset1, idx1, lineset2, idx2 in self._iter_sims(): - duplicate = no_duplicates[num] - for couples in duplicate: - if (lineset1, idx1) in couples or (lineset2, idx2) in couples: - couples.add((lineset1, idx1)) - couples.add((lineset2, idx2)) - break - else: - duplicate.append(set([(lineset1, idx1), (lineset2, idx2)])) - sims = [] - for num, ensembles in six.iteritems(no_duplicates): - for couples in ensembles: - sims.append((num, couples)) - sims.sort() - sims.reverse() - return sims - - def _display_sims(self, sims): - """display computed similarities on stdout""" - nb_lignes_dupliquees = 0 - for num, couples in sims: - print() - print(num, "similar lines in", len(couples), "files") - couples = sorted(couples) - for lineset, idx in couples: - print("==%s:%s" % (lineset.name, idx)) - # pylint: disable=W0631 - for line in lineset._real_lines[idx:idx+num]: - print(" ", line.rstrip()) - nb_lignes_dupliquees += num * (len(couples)-1) - nb_total_lignes = sum([len(lineset) for lineset in self.linesets]) - print("TOTAL lines=%s duplicates=%s percent=%.2f" \ - % (nb_total_lignes, nb_lignes_dupliquees, - nb_lignes_dupliquees*100. / nb_total_lignes)) - - def _find_common(self, lineset1, lineset2): - """find similarities in the two given linesets""" - lines1 = lineset1.enumerate_stripped - lines2 = lineset2.enumerate_stripped - find = lineset2.find - index1 = 0 - min_lines = self.min_lines - while index1 < len(lineset1): - skip = 1 - num = 0 - for index2 in find(lineset1[index1]): - non_blank = 0 - for num, ((_, line1), (_, line2)) in enumerate( - zip(lines1(index1), lines2(index2))): - if line1 != line2: - if non_blank > min_lines: - yield num, lineset1, index1, lineset2, index2 - skip = max(skip, num) - break - if line1: - non_blank += 1 - else: - # we may have reach the end - num += 1 - if non_blank > min_lines: - yield num, lineset1, index1, lineset2, index2 - skip = max(skip, num) - index1 += skip - - def _iter_sims(self): - """iterate on similarities among all files, by making a cartesian - product - """ - for idx, lineset in enumerate(self.linesets[:-1]): - for lineset2 in self.linesets[idx+1:]: - for sim in self._find_common(lineset, lineset2): - yield sim - -def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports): - """return lines with leading/trailing whitespace and any ignored code - features removed - """ - - strippedlines = [] - docstring = None - for line in lines: - line = line.strip() - if ignore_docstrings: - if not docstring and \ - (line.startswith('"""') or line.startswith("'''")): - docstring = line[:3] - line = line[3:] - if docstring: - if line.endswith(docstring): - docstring = None - line = '' - if ignore_imports: - if line.startswith("import ") or line.startswith("from "): - line = '' - if ignore_comments: - # XXX should use regex in checkers/format to avoid cutting - # at a "#" in a string - line = line.split('#', 1)[0].strip() - strippedlines.append(line) - return strippedlines - - -class LineSet(object): - """Holds and indexes all the lines of a single source file""" - def __init__(self, name, lines, ignore_comments=False, - ignore_docstrings=False, ignore_imports=False): - self.name = name - self._real_lines = lines - self._stripped_lines = stripped_lines(lines, ignore_comments, - ignore_docstrings, - ignore_imports) - self._index = self._mk_index() - - def __str__(self): - return '' % self.name - - def __len__(self): - return len(self._real_lines) - - def __getitem__(self, index): - return self._stripped_lines[index] - - def __lt__(self, other): - return self.name < other.name - - def __hash__(self): - return id(self) - - def enumerate_stripped(self, start_at=0): - """return an iterator on stripped lines, starting from a given index - if specified, else 0 - """ - idx = start_at - if start_at: - lines = self._stripped_lines[start_at:] - else: - lines = self._stripped_lines - for line in lines: - #if line: - yield idx, line - idx += 1 - - def find(self, stripped_line): - """return positions of the given stripped line in this set""" - return self._index.get(stripped_line, ()) - - def _mk_index(self): - """create the index for this set""" - index = defaultdict(list) - for line_no, line in enumerate(self._stripped_lines): - if line: - index[line].append(line_no) - return index - - -MSGS = {'R0801': ('Similar lines in %s files\n%s', - 'duplicate-code', - 'Indicates that a set of similar lines has been detected \ - among multiple file. This usually means that the code should \ - be refactored to avoid this duplication.')} - -def report_similarities(sect, stats, old_stats): - """make a layout with some stats about duplication""" - lines = ['', 'now', 'previous', 'difference'] - lines += table_lines_from_stats(stats, old_stats, - ('nb_duplicated_lines', - 'percent_duplicated_lines')) - sect.append(Table(children=lines, cols=4, rheaders=1, cheaders=1)) - - -# wrapper to get a pylint checker from the similar class -class SimilarChecker(BaseChecker, Similar): - """checks for similarities and duplicated code. This computation may be - memory / CPU intensive, so you should disable it if you experiment some - problems. - """ - - __implements__ = (IRawChecker,) - # configuration section name - name = 'similarities' - # messages - msgs = MSGS - # configuration options - # for available dict keys/values see the optik parser 'add_option' method - options = (('min-similarity-lines', - {'default' : 4, 'type' : "int", 'metavar' : '', - 'help' : 'Minimum lines number of a similarity.'}), - ('ignore-comments', - {'default' : True, 'type' : 'yn', 'metavar' : '', - 'help': 'Ignore comments when computing similarities.'} - ), - ('ignore-docstrings', - {'default' : True, 'type' : 'yn', 'metavar' : '', - 'help': 'Ignore docstrings when computing similarities.'} - ), - ('ignore-imports', - {'default' : False, 'type' : 'yn', 'metavar' : '', - 'help': 'Ignore imports when computing similarities.'} - ), - ) - # reports - reports = (('RP0801', 'Duplication', report_similarities),) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - Similar.__init__(self, min_lines=4, - ignore_comments=True, ignore_docstrings=True) - self.stats = None - - def set_option(self, optname, value, action=None, optdict=None): - """method called to set an option (registered in the options list) - - overridden to report options setting to Similar - """ - BaseChecker.set_option(self, optname, value, action, optdict) - if optname == 'min-similarity-lines': - self.min_lines = self.config.min_similarity_lines - elif optname == 'ignore-comments': - self.ignore_comments = self.config.ignore_comments - elif optname == 'ignore-docstrings': - self.ignore_docstrings = self.config.ignore_docstrings - elif optname == 'ignore-imports': - self.ignore_imports = self.config.ignore_imports - - def open(self): - """init the checkers: reset linesets and statistics information""" - self.linesets = [] - self.stats = self.linter.add_stats(nb_duplicated_lines=0, - percent_duplicated_lines=0) - - def process_module(self, node): - """process a module - - the module's content is accessible via the stream object - - stream must implement the readlines method - """ - with node.stream() as stream: - self.append_stream(self.linter.current_name, - stream, - node.file_encoding) - - def close(self): - """compute and display similarities on closing (i.e. end of parsing)""" - total = sum(len(lineset) for lineset in self.linesets) - duplicated = 0 - stats = self.stats - for num, couples in self._compute_sims(): - msg = [] - for lineset, idx in couples: - msg.append("==%s:%s" % (lineset.name, idx)) - msg.sort() - # pylint: disable=W0631 - for line in lineset._real_lines[idx:idx+num]: - msg.append(line.rstrip()) - self.add_message('R0801', args=(len(couples), '\n'.join(msg))) - duplicated += num * (len(couples) - 1) - stats['nb_duplicated_lines'] = duplicated - stats['percent_duplicated_lines'] = total and duplicated * 100. / total - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(SimilarChecker(linter)) - -def usage(status=0): - """display command line usage information""" - print("finds copy pasted blocks in a set of files") - print() - print('Usage: symilar [-d|--duplicates min_duplicated_lines] \ -[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1...') - sys.exit(status) - -def Run(argv=None): - """standalone command line access point""" - if argv is None: - argv = sys.argv[1:] - from getopt import getopt - s_opts = 'hdi' - l_opts = ('help', 'duplicates=', 'ignore-comments', 'ignore-imports', - 'ignore-docstrings') - min_lines = 4 - ignore_comments = False - ignore_docstrings = False - ignore_imports = False - opts, args = getopt(argv, s_opts, l_opts) - for opt, val in opts: - if opt in ('-d', '--duplicates'): - min_lines = int(val) - elif opt in ('-h', '--help'): - usage() - elif opt in ('-i', '--ignore-comments'): - ignore_comments = True - elif opt in ('--ignore-docstrings',): - ignore_docstrings = True - elif opt in ('--ignore-imports',): - ignore_imports = True - if not args: - usage(1) - sim = Similar(min_lines, ignore_comments, ignore_docstrings, ignore_imports) - for filename in args: - with open(filename) as stream: - sim.append_stream(filename, stream) - sim.run() - sys.exit(0) - -if __name__ == '__main__': - Run() diff --git a/pymode/libs/pylint/checkers/spelling.py b/pymode/libs/pylint/checkers/spelling.py deleted file mode 100644 index 7b0eb8f2..00000000 --- a/pymode/libs/pylint/checkers/spelling.py +++ /dev/null @@ -1,265 +0,0 @@ -# Copyright (c) 2014 Michal Nowikowski -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015 Pavel Roskin - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Checker for spelling errors in comments and docstrings. -""" - -import os -import sys -import tokenize -import string -import re - -try: - import enchant - from enchant.tokenize import get_tokenizer, Filter, EmailFilter, URLFilter, WikiWordFilter -except ImportError: - enchant = None - class Filter: - def _skip(self, word): - raise NotImplementedError - -import six - -from pylint.interfaces import ITokenChecker, IAstroidChecker -from pylint.checkers import BaseTokenChecker -from pylint.checkers.utils import check_messages - -if sys.version_info[0] >= 3: - maketrans = str.maketrans -else: - maketrans = string.maketrans - -if enchant is not None: - br = enchant.Broker() - dicts = br.list_dicts() - dict_choices = [''] + [d[0] for d in dicts] - dicts = ["%s (%s)" % (d[0], d[1].name) for d in dicts] - dicts = ", ".join(dicts) - instr = "" -else: - dicts = "none" - dict_choices = [''] - instr = " To make it working install python-enchant package." - -table = maketrans("", "") - - -class WordsWithDigigtsFilter(Filter): - """Skips words with digits. - """ - - def _skip(self, word): - for char in word: - if char.isdigit(): - return True - return False - - -class WordsWithUnderscores(Filter): - """Skips words with underscores. - - They are probably function parameter names. - """ - def _skip(self, word): - return '_' in word - - -class SpellingChecker(BaseTokenChecker): - """Check spelling in comments and docstrings""" - __implements__ = (ITokenChecker, IAstroidChecker) - name = 'spelling' - msgs = { - 'C0401': ('Wrong spelling of a word \'%s\' in a comment:\n%s\n' - '%s\nDid you mean: \'%s\'?', - 'wrong-spelling-in-comment', - 'Used when a word in comment is not spelled correctly.'), - 'C0402': ('Wrong spelling of a word \'%s\' in a docstring:\n%s\n' - '%s\nDid you mean: \'%s\'?', - 'wrong-spelling-in-docstring', - 'Used when a word in docstring is not spelled correctly.'), - 'C0403': ('Invalid characters %r in a docstring', - 'invalid-characters-in-docstring', - 'Used when a word in docstring cannot be checked by enchant.'), - } - options = (('spelling-dict', - {'default' : '', 'type' : 'choice', 'metavar' : '', - 'choices': dict_choices, - 'help' : 'Spelling dictionary name. ' - 'Available dictionaries: %s.%s' % (dicts, instr)}), - ('spelling-ignore-words', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'List of comma separated words that ' - 'should not be checked.'}), - ('spelling-private-dict-file', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'A path to a file that contains private ' - 'dictionary; one word per line.'}), - ('spelling-store-unknown-words', - {'default' : 'n', 'type' : 'yn', 'metavar' : '', - 'help' : 'Tells whether to store unknown words to ' - 'indicated private dictionary in ' - '--spelling-private-dict-file option instead of ' - 'raising a message.'}), - ) - - def open(self): - self.initialized = False - self.private_dict_file = None - - if enchant is None: - return - dict_name = self.config.spelling_dict - if not dict_name: - return - - self.ignore_list = [w.strip() for w in self.config.spelling_ignore_words.split(",")] - # "param" appears in docstring in param description and - # "pylint" appears in comments in pylint pragmas. - self.ignore_list.extend(["param", "pylint"]) - - # Expand tilde to allow e.g. spelling-private-dict-file = ~/.pylintdict - if self.config.spelling_private_dict_file: - self.config.spelling_private_dict_file = os.path.expanduser( - self.config.spelling_private_dict_file) - - if self.config.spelling_private_dict_file: - self.spelling_dict = enchant.DictWithPWL( - dict_name, self.config.spelling_private_dict_file) - self.private_dict_file = open( - self.config.spelling_private_dict_file, "a") - else: - self.spelling_dict = enchant.Dict(dict_name) - - if self.config.spelling_store_unknown_words: - self.unknown_words = set() - - # Prepare regex for stripping punctuation signs from text. - # ' and _ are treated in a special way. - puncts = string.punctuation.replace("'", "").replace("_", "") - self.punctuation_regex = re.compile('[%s]' % re.escape(puncts)) - self.tokenizer = get_tokenizer(dict_name, filters=[EmailFilter, - URLFilter, - WikiWordFilter, - WordsWithDigigtsFilter, - WordsWithUnderscores]) - self.initialized = True - - def close(self): - if self.private_dict_file: - self.private_dict_file.close() - - def _check_spelling(self, msgid, line, line_num): - for word, _ in self.tokenizer(line.strip()): - # Skip words from ignore list. - if word in self.ignore_list: - continue - - orig_word = word - word = word.lower() - - # Strip starting u' from unicode literals and r' from raw strings. - if (word.startswith("u'") or - word.startswith('u"') or - word.startswith("r'") or - word.startswith('r"')) and len(word) > 2: - word = word[2:] - - # If it is a known word, then continue. - try: - if self.spelling_dict.check(word): - continue - except enchant.errors.Error: - # this can only happen in docstrings, not comments - self.add_message('invalid-characters-in-docstring', - line=line_num, args=(word,)) - continue - - # Store word to private dict or raise a message. - if self.config.spelling_store_unknown_words: - if word not in self.unknown_words: - self.private_dict_file.write("%s\n" % word) - self.unknown_words.add(word) - else: - # Present up to 4 suggestions. - # TODO: add support for customising this. - suggestions = self.spelling_dict.suggest(word)[:4] - - m = re.search(r"(\W|^)(%s)(\W|$)" % word, line.lower()) - if m: - # Start position of second group in regex. - col = m.regs[2][0] - else: - col = line.lower().index(word) - indicator = (" " * col) + ("^" * len(word)) - - self.add_message(msgid, line=line_num, - args=(orig_word, line, - indicator, - "'{0}'".format("' or '".join(suggestions)))) - - def process_tokens(self, tokens): - if not self.initialized: - return - - # Process tokens and look for comments. - for (tok_type, token, (start_row, _), _, _) in tokens: - if tok_type == tokenize.COMMENT: - if start_row == 1 and token.startswith('#!/'): - # Skip shebang lines - continue - if token.startswith('# pylint:'): - # Skip pylint enable/disable comments - continue - self._check_spelling('wrong-spelling-in-comment', - token, start_row) - - @check_messages('wrong-spelling-in-docstring') - def visit_module(self, node): - if not self.initialized: - return - self._check_docstring(node) - - @check_messages('wrong-spelling-in-docstring') - def visit_classdef(self, node): - if not self.initialized: - return - self._check_docstring(node) - - @check_messages('wrong-spelling-in-docstring') - def visit_functiondef(self, node): - if not self.initialized: - return - self._check_docstring(node) - - visit_asyncfunctiondef = visit_functiondef - - def _check_docstring(self, node): - """check the node has any spelling errors""" - docstring = node.doc - if not docstring: - return - - start_line = node.lineno + 1 - if six.PY2: - encoding = node.root().file_encoding - docstring = docstring.decode(encoding or sys.getdefaultencoding(), - 'replace') - - # Go through lines of docstring - for idx, line in enumerate(docstring.splitlines()): - self._check_spelling('wrong-spelling-in-docstring', - line, start_line + idx) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(SpellingChecker(linter)) diff --git a/pymode/libs/pylint/checkers/stdlib.py b/pymode/libs/pylint/checkers/stdlib.py deleted file mode 100644 index 38282c27..00000000 --- a/pymode/libs/pylint/checkers/stdlib.py +++ /dev/null @@ -1,278 +0,0 @@ -# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Vlad Temian -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015 Cezar -# Copyright (c) 2015 Chris Rebert - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Checkers for various standard library functions.""" - -import sys - -import six - -import astroid -from astroid.bases import Instance -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers import utils - - -OPEN_FILES = {'open', 'file'} -UNITTEST_CASE = 'unittest.case' -if sys.version_info >= (3, 0): - OPEN_MODULE = '_io' -else: - OPEN_MODULE = '__builtin__' - - -def _check_mode_str(mode): - # check type - if not isinstance(mode, six.string_types): - return False - # check syntax - modes = set(mode) - _mode = "rwatb+U" - creating = False - if six.PY3: - _mode += "x" - creating = "x" in modes - if modes - set(_mode) or len(mode) > len(modes): - return False - # check logic - reading = "r" in modes - writing = "w" in modes - appending = "a" in modes - text = "t" in modes - binary = "b" in modes - if "U" in modes: - if writing or appending or creating and six.PY3: - return False - reading = True - if not six.PY3: - binary = True - if text and binary: - return False - total = reading + writing + appending + (creating if six.PY3 else 0) - if total > 1: - return False - if not (reading or writing or appending or creating and six.PY3): - return False - # other 2.x constraints - if not six.PY3: - if "U" in mode: - mode = mode.replace("U", "") - if "r" not in mode: - mode = "r" + mode - return mode[0] in ("r", "w", "a", "U") - return True - - -class StdlibChecker(BaseChecker): - __implements__ = (IAstroidChecker,) - name = 'stdlib' - - msgs = { - 'W1501': ('"%s" is not a valid mode for open.', - 'bad-open-mode', - 'Python supports: r, w, a[, x] modes with b, +, ' - 'and U (only with r) options. ' - 'See http://docs.python.org/2/library/functions.html#open'), - 'W1502': ('Using datetime.time in a boolean context.', - 'boolean-datetime', - 'Using datetime.time in a boolean context can hide ' - 'subtle bugs when the time they represent matches ' - 'midnight UTC. This behaviour was fixed in Python 3.5. ' - 'See http://bugs.python.org/issue13936 for reference.', - {'maxversion': (3, 5)}), - 'W1503': ('Redundant use of %s with constant ' - 'value %r', - 'redundant-unittest-assert', - 'The first argument of assertTrue and assertFalse is ' - 'a condition. If a constant is passed as parameter, that ' - 'condition will be always true. In this case a warning ' - 'should be emitted.'), - 'W1505': ('Using deprecated method %s()', - 'deprecated-method', - 'The method is marked as deprecated and will be removed in ' - 'a future version of Python. Consider looking for an ' - 'alternative in the documentation.'), - } - - deprecated = { - 0: [ - 'cgi.parse_qs', 'cgi.parse_qsl', - 'ctypes.c_buffer', - 'distutils.command.register.register.check_metadata', - 'distutils.command.sdist.sdist.check_metadata', - 'tkinter.Misc.tk_menuBar', - 'tkinter.Menu.tk_bindForTraversal', - ], - 2: { - (2, 6, 0): [ - 'commands.getstatus', - 'os.popen2', - 'os.popen3', - 'os.popen4', - 'macostools.touched', - ], - (2, 7, 0): [ - 'unittest.case.TestCase.assertEquals', - 'unittest.case.TestCase.assertNotEquals', - 'unittest.case.TestCase.assertAlmostEquals', - 'unittest.case.TestCase.assertNotAlmostEquals', - 'unittest.case.TestCase.assert_', - 'xml.etree.ElementTree.Element.getchildren', - 'xml.etree.ElementTree.Element.getiterator', - 'xml.etree.ElementTree.XMLParser.getiterator', - 'xml.etree.ElementTree.XMLParser.doctype', - ], - }, - 3: { - (3, 0, 0): [ - 'inspect.getargspec', - 'unittest.case.TestCase._deprecate.deprecated_func', - ], - (3, 1, 0): [ - 'base64.encodestring', 'base64.decodestring', - 'ntpath.splitunc', - ], - (3, 2, 0): [ - 'cgi.escape', - 'configparser.RawConfigParser.readfp', - 'xml.etree.ElementTree.Element.getchildren', - 'xml.etree.ElementTree.Element.getiterator', - 'xml.etree.ElementTree.XMLParser.getiterator', - 'xml.etree.ElementTree.XMLParser.doctype', - ], - (3, 3, 0): [ - 'inspect.getmoduleinfo', - 'logging.warn', 'logging.Logger.warn', - 'logging.LoggerAdapter.warn', - 'nntplib._NNTPBase.xpath', - 'platform.popen', - ], - (3, 4, 0): [ - 'importlib.find_loader', - 'plistlib.readPlist', 'plistlib.writePlist', - 'plistlib.readPlistFromBytes', - 'plistlib.writePlistToBytes', - ], - (3, 4, 4): [ - 'asyncio.tasks.async', - ], - (3, 5, 0): [ - 'fractions.gcd', - 'inspect.getfullargspec', 'inspect.getargvalues', - 'inspect.formatargspec', 'inspect.formatargvalues', - 'inspect.getcallargs', - 'platform.linux_distribution', 'platform.dist', - ], - (3, 6, 0): [ - 'importlib._bootstrap_external.FileLoader.load_module', - ], - }, - } - - @utils.check_messages('bad-open-mode', 'redundant-unittest-assert', - 'deprecated-method') - def visit_call(self, node): - """Visit a CallFunc node.""" - try: - for inferred in node.func.infer(): - if inferred.root().name == OPEN_MODULE: - if getattr(node.func, 'name', None) in OPEN_FILES: - self._check_open_mode(node) - if inferred.root().name == UNITTEST_CASE: - self._check_redundant_assert(node, inferred) - self._check_deprecated_method(node, inferred) - except astroid.InferenceError: - return - - @utils.check_messages('boolean-datetime') - def visit_unaryop(self, node): - if node.op == 'not': - self._check_datetime(node.operand) - - @utils.check_messages('boolean-datetime') - def visit_if(self, node): - self._check_datetime(node.test) - - @utils.check_messages('boolean-datetime') - def visit_ifexp(self, node): - self._check_datetime(node.test) - - @utils.check_messages('boolean-datetime') - def visit_boolop(self, node): - for value in node.values: - self._check_datetime(value) - - def _check_deprecated_method(self, node, inferred): - py_vers = sys.version_info[0] - - if isinstance(node.func, astroid.Attribute): - func_name = node.func.attrname - elif isinstance(node.func, astroid.Name): - func_name = node.func.name - else: - # Not interested in other nodes. - return - - # Reject nodes which aren't of interest to us. - acceptable_nodes = (astroid.BoundMethod, - astroid.UnboundMethod, - astroid.FunctionDef) - if not isinstance(inferred, acceptable_nodes): - return - - qname = inferred.qname() - if qname in self.deprecated[0]: - self.add_message('deprecated-method', node=node, - args=(func_name, )) - else: - for since_vers, func_list in self.deprecated[py_vers].items(): - if since_vers <= sys.version_info and qname in func_list: - self.add_message('deprecated-method', node=node, - args=(func_name, )) - break - - def _check_redundant_assert(self, node, infer): - if (isinstance(infer, astroid.BoundMethod) and - node.args and isinstance(node.args[0], astroid.Const) and - infer.name in ['assertTrue', 'assertFalse']): - self.add_message('redundant-unittest-assert', - args=(infer.name, node.args[0].value, ), - node=node) - - def _check_datetime(self, node): - """ Check that a datetime was infered. - If so, emit boolean-datetime warning. - """ - try: - infered = next(node.infer()) - except astroid.InferenceError: - return - if (isinstance(infered, Instance) and - infered.qname() == 'datetime.time'): - self.add_message('boolean-datetime', node=node) - - def _check_open_mode(self, node): - """Check that the mode argument of an open or file call is valid.""" - try: - mode_arg = utils.get_argument_from_call(node, position=1, - keyword='mode') - except utils.NoSuchArgumentError: - return - if mode_arg: - mode_arg = utils.safe_infer(mode_arg) - if (isinstance(mode_arg, astroid.Const) - and not _check_mode_str(mode_arg.value)): - self.add_message('bad-open-mode', node=node, - args=mode_arg.value) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(StdlibChecker(linter)) diff --git a/pymode/libs/pylint/checkers/strings.py b/pymode/libs/pylint/checkers/strings.py deleted file mode 100644 index 8135b412..00000000 --- a/pymode/libs/pylint/checkers/strings.py +++ /dev/null @@ -1,621 +0,0 @@ -# Copyright (c) 2009-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa - - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Checker for string formatting operations. -""" - -import sys -import tokenize -import string -import numbers - -import six - -import astroid -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker -from pylint.checkers import BaseChecker, BaseTokenChecker -from pylint.checkers import utils -from pylint.checkers.utils import check_messages - - -_PY3K = sys.version_info[:2] >= (3, 0) -_PY27 = sys.version_info[:2] == (2, 7) - -MSGS = { - 'E1300': ("Unsupported format character %r (%#02x) at index %d", - "bad-format-character", - "Used when a unsupported format character is used in a format\ - string."), - 'E1301': ("Format string ends in middle of conversion specifier", - "truncated-format-string", - "Used when a format string terminates before the end of a \ - conversion specifier."), - 'E1302': ("Mixing named and unnamed conversion specifiers in format string", - "mixed-format-string", - "Used when a format string contains both named (e.g. '%(foo)d') \ - and unnamed (e.g. '%d') conversion specifiers. This is also \ - used when a named conversion specifier contains * for the \ - minimum field width and/or precision."), - 'E1303': ("Expected mapping for format string, not %s", - "format-needs-mapping", - "Used when a format string that uses named conversion specifiers \ - is used with an argument that is not a mapping."), - 'W1300': ("Format string dictionary key should be a string, not %s", - "bad-format-string-key", - "Used when a format string that uses named conversion specifiers \ - is used with a dictionary whose keys are not all strings."), - 'W1301': ("Unused key %r in format string dictionary", - "unused-format-string-key", - "Used when a format string that uses named conversion specifiers \ - is used with a dictionary that contains keys not required by the \ - format string."), - 'E1304': ("Missing key %r in format string dictionary", - "missing-format-string-key", - "Used when a format string that uses named conversion specifiers \ - is used with a dictionary that doesn't contain all the keys \ - required by the format string."), - 'E1305': ("Too many arguments for format string", - "too-many-format-args", - "Used when a format string that uses unnamed conversion \ - specifiers is given too many arguments."), - 'E1306': ("Not enough arguments for format string", - "too-few-format-args", - "Used when a format string that uses unnamed conversion \ - specifiers is given too few arguments"), - 'E1310': ("Suspicious argument in %s.%s call", - "bad-str-strip-call", - "The argument to a str.{l,r,}strip call contains a" - " duplicate character, "), - 'W1302': ("Invalid format string", - "bad-format-string", - "Used when a PEP 3101 format string is invalid.", - {'minversion': (2, 7)}), - 'W1303': ("Missing keyword argument %r for format string", - "missing-format-argument-key", - "Used when a PEP 3101 format string that uses named fields " - "doesn't receive one or more required keywords.", - {'minversion': (2, 7)}), - 'W1304': ("Unused format argument %r", - "unused-format-string-argument", - "Used when a PEP 3101 format string that uses named " - "fields is used with an argument that " - "is not required by the format string.", - {'minversion': (2, 7)}), - 'W1305': ("Format string contains both automatic field numbering " - "and manual field specification", - "format-combined-specification", - "Used when a PEP 3101 format string contains both automatic " - "field numbering (e.g. '{}') and manual field " - "specification (e.g. '{0}').", - {'minversion': (2, 7)}), - 'W1306': ("Missing format attribute %r in format specifier %r", - "missing-format-attribute", - "Used when a PEP 3101 format string uses an " - "attribute specifier ({0.length}), but the argument " - "passed for formatting doesn't have that attribute.", - {'minversion': (2, 7)}), - 'W1307': ("Using invalid lookup key %r in format specifier %r", - "invalid-format-index", - "Used when a PEP 3101 format string uses a lookup specifier " - "({a[1]}), but the argument passed for formatting " - "doesn't contain or doesn't have that key as an attribute.", - {'minversion': (2, 7)}) - } - -OTHER_NODES = (astroid.Const, astroid.List, astroid.Repr, - astroid.Lambda, astroid.FunctionDef, - astroid.ListComp, astroid.SetComp, astroid.GeneratorExp) - -if _PY3K: - import _string # pylint: disable=wrong-import-position, wrong-import-order - - def split_format_field_names(format_string): - return _string.formatter_field_name_split(format_string) -else: - def _field_iterator_convertor(iterator): - for is_attr, key in iterator: - if isinstance(key, numbers.Number): - yield is_attr, int(key) - else: - yield is_attr, key - - def split_format_field_names(format_string): - try: - keyname, fielditerator = format_string._formatter_field_name_split() - except ValueError: - raise utils.IncompleteFormatString - # it will return longs, instead of ints, which will complicate - # the output - return keyname, _field_iterator_convertor(fielditerator) - - -def collect_string_fields(format_string): - """ Given a format string, return an iterator - of all the valid format fields. It handles nested fields - as well. - """ - - formatter = string.Formatter() - try: - parseiterator = formatter.parse(format_string) - for result in parseiterator: - if all(item is None for item in result[1:]): - # not a replacement format - continue - name = result[1] - nested = result[2] - yield name - if nested: - for field in collect_string_fields(nested): - yield field - except ValueError as exc: - # Probably the format string is invalid. - if exc.args[0].startswith("cannot switch from manual"): - # On Jython, parsing a string with both manual - # and automatic positions will fail with a ValueError, - # while on CPython it will simply return the fields, - # the validation being done in the interpreter (?). - # We're just returning two mixed fields in order - # to trigger the format-combined-specification check. - yield "" - yield "1" - return - raise utils.IncompleteFormatString(format_string) - -def parse_format_method_string(format_string): - """ - Parses a PEP 3101 format string, returning a tuple of - (keys, num_args, manual_pos_arg), - where keys is the set of mapping keys in the format string, num_args - is the number of arguments required by the format string and - manual_pos_arg is the number of arguments passed with the position. - """ - keys = [] - num_args = 0 - manual_pos_arg = set() - for name in collect_string_fields(format_string): - if name and str(name).isdigit(): - manual_pos_arg.add(str(name)) - elif name: - keyname, fielditerator = split_format_field_names(name) - if isinstance(keyname, numbers.Number): - # In Python 2 it will return long which will lead - # to different output between 2 and 3 - manual_pos_arg.add(str(keyname)) - keyname = int(keyname) - try: - keys.append((keyname, list(fielditerator))) - except ValueError: - raise utils.IncompleteFormatString() - else: - num_args += 1 - return keys, num_args, len(manual_pos_arg) - -def get_args(callfunc): - """Get the arguments from the given `CallFunc` node. - - Return a tuple, where the first element is the - number of positional arguments and the second element - is the keyword arguments in a dict. - """ - if callfunc.keywords: - named = {arg.arg: utils.safe_infer(arg.value) - for arg in callfunc.keywords} - else: - named = {} - positional = len(callfunc.args) - return positional, named - -def get_access_path(key, parts): - """ Given a list of format specifiers, returns - the final access path (e.g. a.b.c[0][1]). - """ - path = [] - for is_attribute, specifier in parts: - if is_attribute: - path.append(".{}".format(specifier)) - else: - path.append("[{!r}]".format(specifier)) - return str(key) + "".join(path) - - -class StringFormatChecker(BaseChecker): - """Checks string formatting operations to ensure that the format string - is valid and the arguments match the format string. - """ - - __implements__ = (IAstroidChecker,) - name = 'string' - msgs = MSGS - - @check_messages(*(MSGS.keys())) - def visit_binop(self, node): - if node.op != '%': - return - left = node.left - args = node.right - - if not (isinstance(left, astroid.Const) - and isinstance(left.value, six.string_types)): - return - format_string = left.value - try: - required_keys, required_num_args = \ - utils.parse_format_string(format_string) - except utils.UnsupportedFormatCharacter as e: - c = format_string[e.index] - self.add_message('bad-format-character', - node=node, args=(c, ord(c), e.index)) - return - except utils.IncompleteFormatString: - self.add_message('truncated-format-string', node=node) - return - if required_keys and required_num_args: - # The format string uses both named and unnamed format - # specifiers. - self.add_message('mixed-format-string', node=node) - elif required_keys: - # The format string uses only named format specifiers. - # Check that the RHS of the % operator is a mapping object - # that contains precisely the set of keys required by the - # format string. - if isinstance(args, astroid.Dict): - keys = set() - unknown_keys = False - for k, _ in args.items: - if isinstance(k, astroid.Const): - key = k.value - if isinstance(key, six.string_types): - keys.add(key) - else: - self.add_message('bad-format-string-key', - node=node, args=key) - else: - # One of the keys was something other than a - # constant. Since we can't tell what it is, - # suppress checks for missing keys in the - # dictionary. - unknown_keys = True - if not unknown_keys: - for key in required_keys: - if key not in keys: - self.add_message('missing-format-string-key', - node=node, args=key) - for key in keys: - if key not in required_keys: - self.add_message('unused-format-string-key', - node=node, args=key) - elif isinstance(args, OTHER_NODES + (astroid.Tuple,)): - type_name = type(args).__name__ - self.add_message('format-needs-mapping', - node=node, args=type_name) - # else: - # The RHS of the format specifier is a name or - # expression. It may be a mapping object, so - # there's nothing we can check. - else: - # The format string uses only unnamed format specifiers. - # Check that the number of arguments passed to the RHS of - # the % operator matches the number required by the format - # string. - if isinstance(args, astroid.Tuple): - rhs_tuple = utils.safe_infer(args) - num_args = None - if rhs_tuple not in (None, astroid.Uninferable): - num_args = len(rhs_tuple.elts) - elif isinstance(args, OTHER_NODES + (astroid.Dict, astroid.DictComp)): - num_args = 1 - else: - # The RHS of the format specifier is a name or - # expression. It could be a tuple of unknown size, so - # there's nothing we can check. - num_args = None - if num_args is not None: - if num_args > required_num_args: - self.add_message('too-many-format-args', node=node) - elif num_args < required_num_args: - self.add_message('too-few-format-args', node=node) - - - @check_messages(*(MSGS.keys())) - def visit_call(self, node): - func = utils.safe_infer(node.func) - if (isinstance(func, astroid.BoundMethod) - and isinstance(func.bound, astroid.Instance) - and func.bound.name in ('str', 'unicode', 'bytes')): - if func.name in ('strip', 'lstrip', 'rstrip') and node.args: - arg = utils.safe_infer(node.args[0]) - if not isinstance(arg, astroid.Const): - return - if len(arg.value) != len(set(arg.value)): - self.add_message('bad-str-strip-call', node=node, - args=(func.bound.name, func.name)) - elif func.name == 'format': - if _PY27 or _PY3K: - self._check_new_format(node, func) - - def _check_new_format(self, node, func): - """ Check the new string formatting. """ - # TODO: skip (for now) format nodes which don't have - # an explicit string on the left side of the format operation. - # We do this because our inference engine can't properly handle - # redefinitions of the original string. - # For more details, see issue 287. - # - # Note that there may not be any left side at all, if the format method - # has been assigned to another variable. See issue 351. For example: - # - # fmt = 'some string {}'.format - # fmt('arg') - if (isinstance(node.func, astroid.Attribute) - and not isinstance(node.func.expr, astroid.Const)): - return - try: - strnode = next(func.bound.infer()) - except astroid.InferenceError: - return - if not isinstance(strnode, astroid.Const): - return - if not isinstance(strnode.value, six.string_types): - return - - if node.starargs or node.kwargs: - return - try: - positional, named = get_args(node) - except astroid.InferenceError: - return - try: - fields, num_args, manual_pos = parse_format_method_string(strnode.value) - except utils.IncompleteFormatString: - self.add_message('bad-format-string', node=node) - return - - named_fields = set(field[0] for field in fields - if isinstance(field[0], six.string_types)) - if num_args and manual_pos: - self.add_message('format-combined-specification', - node=node) - return - - check_args = False - # Consider "{[0]} {[1]}" as num_args. - num_args += sum(1 for field in named_fields - if field == '') - if named_fields: - for field in named_fields: - if field not in named and field: - self.add_message('missing-format-argument-key', - node=node, - args=(field, )) - for field in named: - if field not in named_fields: - self.add_message('unused-format-string-argument', - node=node, - args=(field, )) - # num_args can be 0 if manual_pos is not. - num_args = num_args or manual_pos - if positional or num_args: - empty = any(True for field in named_fields - if field == '') - if named or empty: - # Verify the required number of positional arguments - # only if the .format got at least one keyword argument. - # This means that the format strings accepts both - # positional and named fields and we should warn - # when one of the them is missing or is extra. - check_args = True - else: - check_args = True - if check_args: - # num_args can be 0 if manual_pos is not. - num_args = num_args or manual_pos - if positional > num_args: - self.add_message('too-many-format-args', node=node) - elif positional < num_args: - self.add_message('too-few-format-args', node=node) - - self._check_new_format_specifiers(node, fields, named) - - def _check_new_format_specifiers(self, node, fields, named): - """ - Check attribute and index access in the format - string ("{0.a}" and "{0[a]}"). - """ - for key, specifiers in fields: - # Obtain the argument. If it can't be obtained - # or infered, skip this check. - if key == '': - # {[0]} will have an unnamed argument, defaulting - # to 0. It will not be present in `named`, so use the value - # 0 for it. - key = 0 - if isinstance(key, numbers.Number): - try: - argname = utils.get_argument_from_call(node, key) - except utils.NoSuchArgumentError: - continue - else: - if key not in named: - continue - argname = named[key] - if argname in (astroid.YES, None): - continue - try: - argument = next(argname.infer()) - except astroid.InferenceError: - continue - if not specifiers or argument is astroid.YES: - # No need to check this key if it doesn't - # use attribute / item access - continue - if argument.parent and isinstance(argument.parent, astroid.Arguments): - # Ignore any object coming from an argument, - # because we can't infer its value properly. - continue - previous = argument - parsed = [] - for is_attribute, specifier in specifiers: - if previous is astroid.YES: - break - parsed.append((is_attribute, specifier)) - if is_attribute: - try: - previous = previous.getattr(specifier)[0] - except astroid.NotFoundError: - if (hasattr(previous, 'has_dynamic_getattr') and - previous.has_dynamic_getattr()): - # Don't warn if the object has a custom __getattr__ - break - path = get_access_path(key, parsed) - self.add_message('missing-format-attribute', - args=(specifier, path), - node=node) - break - else: - warn_error = False - if hasattr(previous, 'getitem'): - try: - previous = previous.getitem(astroid.Const(specifier)) - except (astroid.AstroidIndexError, - astroid.AstroidTypeError, - astroid.AttributeInferenceError): - warn_error = True - except astroid.InferenceError: - break - if previous is astroid.Uninferable: - break - else: - try: - # Lookup __getitem__ in the current node, - # but skip further checks, because we can't - # retrieve the looked object - previous.getattr('__getitem__') - break - except astroid.NotFoundError: - warn_error = True - if warn_error: - path = get_access_path(key, parsed) - self.add_message('invalid-format-index', - args=(specifier, path), - node=node) - break - - try: - previous = next(previous.infer()) - except astroid.InferenceError: - # can't check further if we can't infer it - break - - -class StringConstantChecker(BaseTokenChecker): - """Check string literals""" - __implements__ = (ITokenChecker, IRawChecker) - name = 'string_constant' - msgs = { - 'W1401': ('Anomalous backslash in string: \'%s\'. ' - 'String constant might be missing an r prefix.', - 'anomalous-backslash-in-string', - 'Used when a backslash is in a literal string but not as an ' - 'escape.'), - 'W1402': ('Anomalous Unicode escape in byte string: \'%s\'. ' - 'String constant might be missing an r or u prefix.', - 'anomalous-unicode-escape-in-string', - 'Used when an escape like \\u is encountered in a byte ' - 'string where it has no effect.'), - } - - # Characters that have a special meaning after a backslash in either - # Unicode or byte strings. - ESCAPE_CHARACTERS = 'abfnrtvx\n\r\t\\\'\"01234567' - - # TODO(mbp): Octal characters are quite an edge case today; people may - # prefer a separate warning where they occur. \0 should be allowed. - - # Characters that have a special meaning after a backslash but only in - # Unicode strings. - UNICODE_ESCAPE_CHARACTERS = 'uUN' - - def process_module(self, module): - self._unicode_literals = 'unicode_literals' in module.future_imports - - def process_tokens(self, tokens): - for (tok_type, token, (start_row, _), _, _) in tokens: - if tok_type == tokenize.STRING: - # 'token' is the whole un-parsed token; we can look at the start - # of it to see whether it's a raw or unicode string etc. - self.process_string_token(token, start_row) - - def process_string_token(self, token, start_row): - for i, c in enumerate(token): - if c in '\'\"': - quote_char = c - break - # pylint: disable=undefined-loop-variable - prefix = token[:i].lower() # markers like u, b, r. - after_prefix = token[i:] - if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char: - string_body = after_prefix[3:-3] - else: - string_body = after_prefix[1:-1] # Chop off quotes - # No special checks on raw strings at the moment. - if 'r' not in prefix: - self.process_non_raw_string_token(prefix, string_body, start_row) - - def process_non_raw_string_token(self, prefix, string_body, start_row): - """check for bad escapes in a non-raw string. - - prefix: lowercase string of eg 'ur' string prefix markers. - string_body: the un-parsed body of the string, not including the quote - marks. - start_row: integer line number in the source. - """ - # Walk through the string; if we see a backslash then escape the next - # character, and skip over it. If we see a non-escaped character, - # alert, and continue. - # - # Accept a backslash when it escapes a backslash, or a quote, or - # end-of-line, or one of the letters that introduce a special escape - # sequence - # - # TODO(mbp): Maybe give a separate warning about the rarely-used - # \a \b \v \f? - # - # TODO(mbp): We could give the column of the problem character, but - # add_message doesn't seem to have a way to pass it through at present. - i = 0 - while True: - i = string_body.find('\\', i) - if i == -1: - break - # There must be a next character; having a backslash at the end - # of the string would be a SyntaxError. - next_char = string_body[i+1] - match = string_body[i:i+2] - if next_char in self.UNICODE_ESCAPE_CHARACTERS: - if 'u' in prefix: - pass - elif (_PY3K or self._unicode_literals) and 'b' not in prefix: - pass # unicode by default - else: - self.add_message('anomalous-unicode-escape-in-string', - line=start_row, args=(match, )) - elif next_char not in self.ESCAPE_CHARACTERS: - self.add_message('anomalous-backslash-in-string', - line=start_row, args=(match, )) - # Whether it was a valid escape or not, backslash followed by - # another character can always be consumed whole: the second - # character can never be the start of a new backslash escape. - i += 2 - - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(StringFormatChecker(linter)) - linter.register_checker(StringConstantChecker(linter)) diff --git a/pymode/libs/pylint/checkers/typecheck.py b/pymode/libs/pylint/checkers/typecheck.py deleted file mode 100644 index 8ae8f446..00000000 --- a/pymode/libs/pylint/checkers/typecheck.py +++ /dev/null @@ -1,1289 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014, 2016 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Holger Peters -# Copyright (c) 2014 David Shea -# Copyright (c) 2015 Radu Ciorba -# Copyright (c) 2015 Rene Zhang -# Copyright (c) 2015 Dmitry Pribysh -# Copyright (c) 2016 Jakub Wilk -# Copyright (c) 2016 Jürgen Hermann - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""try to find more bugs in the code using astroid inference capabilities -""" - -import collections -import fnmatch -import heapq -import itertools -import operator -import re -import shlex -import sys - -import six - -import astroid -import astroid.context -import astroid.arguments -import astroid.nodes -from astroid import exceptions -from astroid.interpreter import dunder_lookup -from astroid import objects -from astroid import bases - -from pylint.interfaces import IAstroidChecker, INFERENCE -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - is_super, check_messages, decorated_with_property, - decorated_with, node_ignores_exception, - is_iterable, is_mapping, supports_membership_test, - is_comprehension, is_inside_abstract_class, - supports_getitem, - supports_setitem, - supports_delitem, - safe_infer, - has_known_bases, - is_builtin_object, - singledispatch) - - -BUILTINS = six.moves.builtins.__name__ -STR_FORMAT = "%s.str.format" % BUILTINS - - -def _unflatten(iterable): - for index, elem in enumerate(iterable): - if (isinstance(elem, collections.Sequence) and - not isinstance(elem, six.string_types)): - for single_elem in _unflatten(elem): - yield single_elem - elif elem and not index: - # We're interested only in the first element. - yield elem - - -def _is_owner_ignored(owner, name, ignored_classes, ignored_modules): - """Check if the given owner should be ignored - - This will verify if the owner's module is in *ignored_modules* - or the owner's module fully qualified name is in *ignored_modules* - or if the *ignored_modules* contains a pattern which catches - the fully qualified name of the module. - - Also, similar checks are done for the owner itself, if its name - matches any name from the *ignored_classes* or if its qualified - name can be found in *ignored_classes*. - """ - ignored_modules = set(ignored_modules) - module_name = owner.root().name - module_qname = owner.root().qname() - if any(module_name in ignored_modules or - module_qname in ignored_modules or - fnmatch.fnmatch(module_qname, ignore) for ignore in ignored_modules): - return True - - ignored_classes = set(ignored_classes) - if hasattr(owner, 'qname'): - qname = owner.qname() - else: - qname = '' - return any(name == ignore or qname == ignore for ignore in ignored_classes) - - -@singledispatch -def _node_names(node): - # TODO: maybe we need an ABC for checking if an object is a scoped node - # or not? - if not hasattr(node, 'locals'): - return [] - return node.locals.keys() - - -@_node_names.register(astroid.ClassDef) -@_node_names.register(astroid.Instance) -def _(node): - values = itertools.chain(node.instance_attrs.keys(), node.locals.keys()) - - try: - mro = node.mro()[1:] - except (NotImplementedError, TypeError): - mro = node.ancestors() - - other_values = [value for cls in mro for value in _node_names(cls)] - return itertools.chain(values, other_values) - - -def _string_distance(seq1, seq2): - seq2_length = len(seq2) - - row = list(range(1, seq2_length + 1)) + [0] - for seq1_index, seq1_char in enumerate(seq1): - last_row = row - row = [0] * seq2_length + [seq1_index + 1] - - for seq2_index, seq2_char in enumerate(seq2): - row[seq2_index] = min( - last_row[seq2_index] + 1, - row[seq2_index - 1] + 1, - last_row[seq2_index - 1] + (seq1_char != seq2_char) - - ) - - return row[seq2_length - 1] - - -def _similar_names(owner, attrname, distance_threshold, max_choices): - """Given an owner and a name, try to find similar names - - The similar names are searched given a distance metric and only - a given number of choices will be returned. - """ - possible_names = [] - names = _node_names(owner) - - for name in names: - if name == attrname: - continue - - distance = _string_distance(attrname, name) - if distance <= distance_threshold: - possible_names.append((name, distance)) - - # Now get back the values with a minimum, up to the given - # limit or choices. - picked = [name for (name, _) in - heapq.nsmallest(max_choices, possible_names, - key=operator.itemgetter(1))] - return sorted(picked) - - -def _missing_member_hint(owner, attrname, distance_threshold, max_choices): - names = _similar_names(owner, attrname, distance_threshold, max_choices) - if not names: - # No similar name. - return "" - - names = list(map(repr, names)) - if len(names) == 1: - names = ", ".join(names) - else: - names = "one of {} or {}".format(", ".join(names[:-1]), names[-1]) - - return "; maybe {}?".format(names) - - -MSGS = { - 'E1101': ('%s %r has no %r member%s', - 'no-member', - 'Used when a variable is accessed for an unexistent member.', - {'old_names': [('E1103', 'maybe-no-member')]}), - 'E1102': ('%s is not callable', - 'not-callable', - 'Used when an object being called has been inferred to a non \ - callable object'), - 'E1111': ('Assigning to function call which doesn\'t return', - 'assignment-from-no-return', - 'Used when an assignment is done on a function call but the \ - inferred function doesn\'t return anything.'), - 'E1120': ('No value for argument %s in %s call', - 'no-value-for-parameter', - 'Used when a function call passes too few arguments.'), - 'E1121': ('Too many positional arguments for %s call', - 'too-many-function-args', - 'Used when a function call passes too many positional \ - arguments.'), - 'E1123': ('Unexpected keyword argument %r in %s call', - 'unexpected-keyword-arg', - 'Used when a function call passes a keyword argument that \ - doesn\'t correspond to one of the function\'s parameter names.'), - 'E1124': ('Argument %r passed by position and keyword in %s call', - 'redundant-keyword-arg', - 'Used when a function call would result in assigning multiple \ - values to a function parameter, one value from a positional \ - argument and one from a keyword argument.'), - 'E1125': ('Missing mandatory keyword argument %r in %s call', - 'missing-kwoa', - ('Used when a function call does not pass a mandatory' - ' keyword-only argument.'), - {'minversion': (3, 0)}), - 'E1126': ('Sequence index is not an int, slice, or instance with __index__', - 'invalid-sequence-index', - 'Used when a sequence type is indexed with an invalid type. ' - 'Valid types are ints, slices, and objects with an __index__ ' - 'method.'), - 'E1127': ('Slice index is not an int, None, or instance with __index__', - 'invalid-slice-index', - 'Used when a slice index is not an integer, None, or an object \ - with an __index__ method.'), - 'E1128': ('Assigning to function call which only returns None', - 'assignment-from-none', - 'Used when an assignment is done on a function call but the ' - 'inferred function returns nothing but None.', - {'old_names': [('W1111', 'assignment-from-none')]}), - 'E1129': ("Context manager '%s' doesn't implement __enter__ and __exit__.", - 'not-context-manager', - 'Used when an instance in a with statement doesn\'t implement ' - 'the context manager protocol(__enter__/__exit__).'), - 'E1130': ('%s', - 'invalid-unary-operand-type', - 'Emitted when a unary operand is used on an object which does not ' - 'support this type of operation'), - 'E1131': ('%s', - 'unsupported-binary-operation', - 'Emitted when a binary arithmetic operation between two ' - 'operands is not supported.'), - 'E1132': ('Got multiple values for keyword argument %r in function call', - 'repeated-keyword', - 'Emitted when a function call got multiple values for a keyword.'), - 'E1135': ("Value '%s' doesn't support membership test", - 'unsupported-membership-test', - 'Emitted when an instance in membership test expression doesn\'t ' - 'implement membership protocol (__contains__/__iter__/__getitem__)'), - 'E1136': ("Value '%s' is unsubscriptable", - 'unsubscriptable-object', - "Emitted when a subscripted value doesn't support subscription" - "(i.e. doesn't define __getitem__ method)"), - 'E1137': ("%r does not support item assignment", - 'unsupported-assignment-operation', - "Emitted when an object does not support item assignment " - "(i.e. doesn't define __setitem__ method)"), - 'E1138': ("%r does not support item deletion", - 'unsupported-delete-operation', - "Emitted when an object does not support item deletion " - "(i.e. doesn't define __delitem__ method)"), - 'E1139': ('Invalid metaclass %r used', - 'invalid-metaclass', - 'Emitted whenever we can detect that a class is using, ' - 'as a metaclass, something which might be invalid for using as ' - 'a metaclass.'), - } - -# builtin sequence types in Python 2 and 3. -SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray', - 'xrange', 'range', 'bytes', 'memoryview']) - - -def _emit_no_member(node, owner, owner_name, ignored_mixins): - """Try to see if no-member should be emitted for the given owner. - - The following cases are ignored: - - * the owner is a function and it has decorators. - * the owner is an instance and it has __getattr__, __getattribute__ implemented - * the module is explicitly ignored from no-member checks - * the owner is a class and the name can be found in its metaclass. - * The access node is protected by an except handler, which handles - AttributeError, Exception or bare except. - """ - if node_ignores_exception(node, AttributeError): - return False - # skip None anyway - if isinstance(owner, astroid.Const) and owner.value is None: - return False - if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': - return False - if ignored_mixins and owner_name[-5:].lower() == 'mixin': - return False - if isinstance(owner, astroid.FunctionDef) and owner.decorators: - return False - if isinstance(owner, (astroid.Instance, astroid.ClassDef)): - if owner.has_dynamic_getattr() or not has_known_bases(owner): - return False - if isinstance(owner, objects.Super): - # Verify if we are dealing with an invalid Super object. - # If it is invalid, then there's no point in checking that - # it has the required attribute. Also, don't fail if the - # MRO is invalid. - try: - owner.super_mro() - except (exceptions.MroError, exceptions.SuperError): - return False - if not all(map(has_known_bases, owner.type.mro())): - return False - return True - - -def _determine_callable(callable_obj): - # Ordering is important, since BoundMethod is a subclass of UnboundMethod, - # and Function inherits Lambda. - if isinstance(callable_obj, astroid.BoundMethod): - # Bound methods have an extra implicit 'self' argument. - return callable_obj, 1, callable_obj.type - elif isinstance(callable_obj, astroid.UnboundMethod): - return callable_obj, 0, 'unbound method' - elif isinstance(callable_obj, astroid.FunctionDef): - return callable_obj, 0, callable_obj.type - elif isinstance(callable_obj, astroid.Lambda): - return callable_obj, 0, 'lambda' - elif isinstance(callable_obj, astroid.ClassDef): - # Class instantiation, lookup __new__ instead. - # If we only find object.__new__, we can safely check __init__ - # instead. If __new__ belongs to builtins, then we look - # again for __init__ in the locals, since we won't have - # argument information for the builtin __new__ function. - try: - # Use the last definition of __new__. - new = callable_obj.local_attr('__new__')[-1] - except exceptions.NotFoundError: - new = None - - from_object = new and new.parent.scope().name == 'object' - from_builtins = new and new.root().name in sys.builtin_module_names - - if not new or from_object or from_builtins: - try: - # Use the last definition of __init__. - callable_obj = callable_obj.local_attr('__init__')[-1] - except exceptions.NotFoundError: - # do nothing, covered by no-init. - raise ValueError - else: - callable_obj = new - - if not isinstance(callable_obj, astroid.FunctionDef): - raise ValueError - # both have an extra implicit 'cls'/'self' argument. - return callable_obj, 1, 'constructor' - else: - raise ValueError - - -def _has_parent_of_type(node, node_type, statement): - """Check if the given node has a parent of the given type.""" - parent = node.parent - while not isinstance(parent, node_type) and statement.parent_of(parent): - parent = parent.parent - return isinstance(parent, node_type) - - -def _is_name_used_as_variadic(name, variadics): - """Check if the given name is used as a variadic argument.""" - return any(variadic.value == name or variadic.value.parent_of(name) - for variadic in variadics) - - -def _no_context_variadic_keywords(node): - statement = node.statement() - scope = node.scope() - variadics = () - - if not isinstance(scope, astroid.FunctionDef): - return False - - if isinstance(statement, astroid.Expr) and isinstance(statement.value, astroid.Call): - call = statement.value - variadics = call.keywords or () - - return _no_context_variadic(node, scope.args.kwarg, astroid.Keyword, variadics) - - -def _no_context_variadic_positional(node): - statement = node.statement() - scope = node.scope() - variadics = () - - if not isinstance(scope, astroid.FunctionDef): - return False - - if isinstance(statement, astroid.Expr) and isinstance(statement.value, astroid.Call): - call = statement.value - variadics = call.starargs - - return _no_context_variadic(node, scope.args.vararg, astroid.Starred, variadics) - - -def _no_context_variadic(node, variadic_name, variadic_type, variadics): - """Verify if the given call node has variadic nodes without context - - This is a workaround for handling cases of nested call functions - which don't have the specific call context at hand. - Variadic arguments (variable positional arguments and variable - keyword arguments) are inferred, inherently wrong, by astroid - as a Tuple, respectively a Dict with empty elements. - This can lead pylint to believe that a function call receives - too few arguments. - """ - statement = node.statement() - for name in statement.nodes_of_class(astroid.Name): - if name.name != variadic_name: - continue - - inferred = safe_infer(name) - if isinstance(inferred, (astroid.List, astroid.Tuple)): - length = len(inferred.elts) - elif isinstance(inferred, astroid.Dict): - length = len(inferred.items) - else: - continue - - inferred_statement = inferred.statement() - if not length and isinstance(inferred_statement, astroid.FunctionDef): - is_in_starred_context = _has_parent_of_type(node, variadic_type, statement) - used_as_starred_argument = _is_name_used_as_variadic(name, variadics) - if is_in_starred_context or used_as_starred_argument: - return True - return False - - -def _is_invalid_metaclass(metaclass): - try: - mro = metaclass.mro() - except NotImplementedError: - # Cannot have a metaclass which is not a newstyle class. - return True - else: - if not any(is_builtin_object(cls) and cls.name == 'type' - for cls in mro): - return True - return False - - -def _infer_from_metaclass_constructor(cls, func): - """Try to infer what the given *func* constructor is building - - :param astroid.FunctionDef func: - A metaclass constructor. Metaclass definitions can be - functions, which should accept three arguments, the name of - the class, the bases of the class and the attributes. - The function could return anything, but usually it should - be a proper metaclass. - :param astroid.ClassDef cls: - The class for which the *func* parameter should generate - a metaclass. - :returns: - The class generated by the function or None, - if we couldn't infer it. - :rtype: astroid.ClassDef - """ - context = astroid.context.InferenceContext() - - class_bases = astroid.List() - class_bases.postinit(elts=cls.bases) - - attrs = astroid.Dict() - local_names = [(name, values[-1]) for name, values in cls.locals.items()] - attrs.postinit(local_names) - - builder_args = astroid.Tuple() - builder_args.postinit([cls.name, class_bases, attrs]) - - context.callcontext = astroid.context.CallContext(builder_args) - try: - inferred = next(func.infer_call_result(func, context), None) - except astroid.InferenceError: - return None - return inferred or None - - -class TypeChecker(BaseChecker): - """try to find bugs in the code using type inference - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'typecheck' - # messages - msgs = MSGS - priority = -1 - # configuration options - options = (('ignore-on-opaque-inference', - {'default': True, 'type': 'yn', 'metavar': '', - 'help': 'This flag controls whether pylint should warn about ' - 'no-member and similar checks whenever an opaque object ' - 'is returned when inferring. The inference can return ' - 'multiple potential results while evaluating a Python object, ' - 'but some branches might not be evaluated, which results in ' - 'partial inference. In that case, it might be useful to still emit ' - 'no-member and other checks for the rest of the inferred objects.'} - ), - ('ignore-mixin-members', - {'default' : True, 'type' : 'yn', 'metavar': '', - 'help' : 'Tells whether missing members accessed in mixin \ -class should be ignored. A mixin class is detected if its name ends with \ -"mixin" (case insensitive).'} - ), - ('ignored-modules', - {'default': (), - 'type': 'csv', - 'metavar': '', - 'help': 'List of module names for which member attributes ' - 'should not be checked (useful for modules/projects ' - 'where namespaces are manipulated during runtime and ' - 'thus existing member attributes cannot be ' - 'deduced by static analysis. It supports qualified ' - 'module names, as well as Unix pattern matching.'} - ), - # the defaults here are *stdlib* names that (almost) always - # lead to false positives, since their idiomatic use is - # 'too dynamic' for pylint to grok. - ('ignored-classes', - {'default' : ('optparse.Values', 'thread._local', '_thread._local'), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of class names for which member attributes ' - 'should not be checked (useful for classes with ' - 'dynamically set attributes). This supports ' - 'the use of qualified names.'} - ), - - ('generated-members', - {'default' : (), - 'type' : 'string', - 'metavar' : '', - 'help' : 'List of members which are set dynamically and \ -missed by pylint inference system, and so shouldn\'t trigger E1101 when \ -accessed. Python regular expressions are accepted.'} - ), - ('contextmanager-decorators', - {'default': ['contextlib.contextmanager'], - 'type': 'csv', - 'metavar': '', - 'help': 'List of decorators that produce context managers, ' - 'such as contextlib.contextmanager. Add to this list ' - 'to register other decorators that produce valid ' - 'context managers.'} - ), - ('missing-member-hint-distance', - {'default': 1, - 'type': 'int', - 'metavar': '', - 'help': 'The minimum edit distance a name should have in order ' - 'to be considered a similar match for a missing member name.' - } - ), - ('missing-member-max-choices', - {'default': 1, - 'type': "int", - 'metavar': '', - 'help': 'The total number of similar names that should be taken in ' - 'consideration when showing a hint for a missing member.' - } - ), - ('missing-member-hint', - {'default': True, - 'type': "yn", - 'metavar': '', - 'help': 'Show a hint with possible names when a member name was not ' - 'found. The aspect of finding the hint is based on edit distance.' - } - ), - ) - - def open(self): - # do this in open since config not fully initialized in __init__ - # generated_members may contain regular expressions - # (surrounded by quote `"` and followed by a comma `,`) - # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' => - # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}') - if isinstance(self.config.generated_members, six.string_types): - gen = shlex.shlex(self.config.generated_members) - gen.whitespace += ',' - gen.wordchars += r'[]-+\.*?()|' - self.config.generated_members = tuple(tok.strip('"') for tok in gen) - - @check_messages('invalid-metaclass') - def visit_classdef(self, node): - - def _metaclass_name(metaclass): - if isinstance(metaclass, (astroid.ClassDef, astroid.FunctionDef)): - return metaclass.name - return metaclass.as_string() - - metaclass = node.declared_metaclass() - if not metaclass: - return - - if isinstance(metaclass, astroid.FunctionDef): - # Try to infer the result. - metaclass = _infer_from_metaclass_constructor(node, metaclass) - if not metaclass: - # Don't do anything if we cannot infer the result. - return - - if isinstance(metaclass, astroid.ClassDef): - if _is_invalid_metaclass(metaclass): - self.add_message('invalid-metaclass', node=node, - args=(_metaclass_name(metaclass), )) - else: - self.add_message('invalid-metaclass', node=node, - args=(_metaclass_name(metaclass), )) - - def visit_assignattr(self, node): - if isinstance(node.assign_type(), astroid.AugAssign): - self.visit_attribute(node) - - def visit_delattr(self, node): - self.visit_attribute(node) - - @check_messages('no-member') - def visit_attribute(self, node): - """check that the accessed attribute exists - - to avoid too much false positives for now, we'll consider the code as - correct if a single of the inferred nodes has the accessed attribute. - - function/method, super call and metaclasses are ignored - """ - for pattern in self.config.generated_members: - # attribute is marked as generated, stop here - if re.match(pattern, node.attrname): - return - if re.match(pattern, node.as_string()): - return - - try: - inferred = list(node.expr.infer()) - except exceptions.InferenceError: - return - - # list of (node, nodename) which are missing the attribute - missingattr = set() - - non_opaque_inference_results = [ - owner for owner in inferred - if owner is not astroid.Uninferable - and not isinstance(owner, astroid.nodes.Unknown) - ] - if (len(non_opaque_inference_results) != len(inferred) - and self.config.ignore_on_opaque_inference): - # There is an ambiguity in the inference. Since we can't - # make sure that we won't emit a false positive, we just stop - # whenever the inference returns an opaque inference object. - return - - for owner in non_opaque_inference_results: - name = getattr(owner, 'name', None) - if _is_owner_ignored(owner, name, self.config.ignored_classes, - self.config.ignored_modules): - continue - - try: - if not [n for n in owner.getattr(node.attrname) - if not isinstance(n.statement(), astroid.AugAssign)]: - missingattr.add((owner, name)) - continue - except AttributeError: - # XXX method / function - continue - except exceptions.NotFoundError: - # This can't be moved before the actual .getattr call, - # because there can be more values inferred and we are - # stopping after the first one which has the attribute in question. - # The problem is that if the first one has the attribute, - # but we continue to the next values which doesn't have the - # attribute, then we'll have a false positive. - # So call this only after the call has been made. - if not _emit_no_member(node, owner, name, - self.config.ignore_mixin_members): - continue - missingattr.add((owner, name)) - continue - # stop on the first found - break - else: - # we have not found any node with the attributes, display the - # message for infered nodes - done = set() - for owner, name in missingattr: - if isinstance(owner, astroid.Instance): - actual = owner._proxied - else: - actual = owner - if actual in done: - continue - done.add(actual) - - if self.config.missing_member_hint: - hint = _missing_member_hint(owner, node.attrname, - self.config.missing_member_hint_distance, - self.config.missing_member_max_choices) - else: - hint = "" - - self.add_message('no-member', node=node, - args=(owner.display_type(), name, - node.attrname, hint), - confidence=INFERENCE) - - @check_messages('assignment-from-no-return', 'assignment-from-none') - def visit_assign(self, node): - """check that if assigning to a function call, the function is - possibly returning something valuable - """ - if not isinstance(node.value, astroid.Call): - return - function_node = safe_infer(node.value.func) - # skip class, generator and incomplete function definition - if not (isinstance(function_node, astroid.FunctionDef) and - function_node.root().fully_defined()): - return - if function_node.is_generator() \ - or function_node.is_abstract(pass_is_abstract=False): - return - returns = list(function_node.nodes_of_class(astroid.Return, - skip_klass=astroid.FunctionDef)) - if not returns: - self.add_message('assignment-from-no-return', node=node) - else: - for rnode in returns: - if not (isinstance(rnode.value, astroid.Const) - and rnode.value.value is None - or rnode.value is None): - break - else: - self.add_message('assignment-from-none', node=node) - - def _check_uninferable_callfunc(self, node): - """ - Check that the given uninferable CallFunc node does not - call an actual function. - """ - if not isinstance(node.func, astroid.Attribute): - return - - # Look for properties. First, obtain - # the lhs of the Getattr node and search the attribute - # there. If that attribute is a property or a subclass of properties, - # then most likely it's not callable. - - # TODO: since astroid doesn't understand descriptors very well - # we will not handle them here, right now. - - expr = node.func.expr - klass = safe_infer(expr) - if (klass is None or klass is astroid.YES or - not isinstance(klass, astroid.Instance)): - return - - try: - attrs = klass._proxied.getattr(node.func.attrname) - except exceptions.NotFoundError: - return - - for attr in attrs: - if attr is astroid.YES: - continue - if not isinstance(attr, astroid.FunctionDef): - continue - - # Decorated, see if it is decorated with a property. - # Also, check the returns and see if they are callable. - if decorated_with_property(attr): - if all(return_node.callable() - for return_node in attr.infer_call_result(node)): - continue - else: - self.add_message('not-callable', node=node, - args=node.func.as_string()) - break - - @check_messages(*(list(MSGS.keys()))) - def visit_call(self, node): - """check that called functions/methods are inferred to callable objects, - and that the arguments passed to the function match the parameters in - the inferred function's definition - """ - # Build the set of keyword arguments, checking for duplicate keywords, - # and count the positional arguments. - call_site = astroid.arguments.CallSite.from_call(node) - num_positional_args = len(call_site.positional_arguments) - keyword_args = list(call_site.keyword_arguments.keys()) - - # Determine if we don't have a context for our call and we use variadics. - if isinstance(node.scope(), astroid.FunctionDef): - has_no_context_positional_variadic = _no_context_variadic_positional(node) - has_no_context_keywords_variadic = _no_context_variadic_keywords(node) - else: - has_no_context_positional_variadic = has_no_context_keywords_variadic = False - - called = safe_infer(node.func) - # only function, generator and object defining __call__ are allowed - if called and not called.callable(): - if isinstance(called, astroid.Instance) and not has_known_bases(called): - # Don't emit if we can't make sure this object is callable. - pass - else: - self.add_message('not-callable', node=node, - args=node.func.as_string()) - - self._check_uninferable_callfunc(node) - - try: - called, implicit_args, callable_name = _determine_callable(called) - except ValueError: - # Any error occurred during determining the function type, most of - # those errors are handled by different warnings. - return - - num_positional_args += implicit_args - if called.args.args is None: - # Built-in functions have no argument information. - return - - if len(called.argnames()) != len(set(called.argnames())): - # Duplicate parameter name (see duplicate-argument). We can't really - # make sense of the function call in this case, so just return. - return - - # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}` - for keyword in call_site.duplicated_keywords: - self.add_message('repeated-keyword', - node=node, args=(keyword, )) - - if call_site.has_invalid_arguments() or call_site.has_invalid_keywords(): - # Can't make sense of this. - return - - # Analyze the list of formal parameters. - num_mandatory_parameters = len(called.args.args) - len(called.args.defaults) - parameters = [] - parameter_name_to_index = {} - for i, arg in enumerate(called.args.args): - if isinstance(arg, astroid.Tuple): - name = None - # Don't store any parameter names within the tuple, since those - # are not assignable from keyword arguments. - else: - assert isinstance(arg, astroid.AssignName) - # This occurs with: - # def f( (a), (b) ): pass - name = arg.name - parameter_name_to_index[name] = i - if i >= num_mandatory_parameters: - defval = called.args.defaults[i - num_mandatory_parameters] - else: - defval = None - parameters.append([(name, defval), False]) - - kwparams = {} - for i, arg in enumerate(called.args.kwonlyargs): - if isinstance(arg, astroid.Keyword): - name = arg.arg - else: - assert isinstance(arg, astroid.AssignName) - name = arg.name - kwparams[name] = [called.args.kw_defaults[i], False] - - # Match the supplied arguments against the function parameters. - - # 1. Match the positional arguments. - for i in range(num_positional_args): - if i < len(parameters): - parameters[i][1] = True - elif called.args.vararg is not None: - # The remaining positional arguments get assigned to the *args - # parameter. - break - else: - # Too many positional arguments. - self.add_message('too-many-function-args', - node=node, args=(callable_name,)) - break - - # 2. Match the keyword arguments. - for keyword in keyword_args: - if keyword in parameter_name_to_index: - i = parameter_name_to_index[keyword] - if parameters[i][1]: - # Duplicate definition of function parameter. - - # Might be too hardcoded, but this can actually - # happen when using str.format and `self` is passed - # by keyword argument, as in `.format(self=self)`. - # It's perfectly valid to so, so we're just skipping - # it if that's the case. - if not (keyword == 'self' and called.qname() == STR_FORMAT): - self.add_message('redundant-keyword-arg', - node=node, args=(keyword, callable_name)) - else: - parameters[i][1] = True - elif keyword in kwparams: - if kwparams[keyword][1]: # XXX is that even possible? - # Duplicate definition of function parameter. - self.add_message('redundant-keyword-arg', node=node, - args=(keyword, callable_name)) - else: - kwparams[keyword][1] = True - elif called.args.kwarg is not None: - # The keyword argument gets assigned to the **kwargs parameter. - pass - else: - # Unexpected keyword argument. - self.add_message('unexpected-keyword-arg', node=node, - args=(keyword, callable_name)) - - # 3. Match the **kwargs, if any. - if node.kwargs: - for i, [(name, defval), assigned] in enumerate(parameters): - # Assume that *kwargs provides values for all remaining - # unassigned named parameters. - if name is not None: - parameters[i][1] = True - else: - # **kwargs can't assign to tuples. - pass - - # Check that any parameters without a default have been assigned - # values. - for [(name, defval), assigned] in parameters: - if (defval is None) and not assigned: - if name is None: - display_name = '' - else: - display_name = repr(name) - # TODO(cpopa): this should be removed after PyCQA/astroid/issues/177 - if not has_no_context_positional_variadic: - self.add_message('no-value-for-parameter', node=node, - args=(display_name, callable_name)) - - for name in kwparams: - defval, assigned = kwparams[name] - if defval is None and not assigned and not has_no_context_keywords_variadic: - self.add_message('missing-kwoa', node=node, - args=(name, callable_name)) - - @check_messages('invalid-sequence-index') - def visit_extslice(self, node): - # Check extended slice objects as if they were used as a sequence - # index to check if the object being sliced can support them - return self.visit_index(node) - - @check_messages('invalid-sequence-index') - def visit_index(self, node): - if not node.parent or not hasattr(node.parent, "value"): - return - # Look for index operations where the parent is a sequence type. - # If the types can be determined, only allow indices to be int, - # slice or instances with __index__. - parent_type = safe_infer(node.parent.value) - if not isinstance(parent_type, (astroid.ClassDef, astroid.Instance)): - return - if not has_known_bases(parent_type): - return - - # Determine what method on the parent this index will use - # The parent of this node will be a Subscript, and the parent of that - # node determines if the Subscript is a get, set, or delete operation. - if node.parent.ctx is astroid.Store: - methodname = '__setitem__' - elif node.parent.ctx is astroid.Del: - methodname = '__delitem__' - else: - methodname = '__getitem__' - - # Check if this instance's __getitem__, __setitem__, or __delitem__, as - # appropriate to the statement, is implemented in a builtin sequence - # type. This way we catch subclasses of sequence types but skip classes - # that override __getitem__ and which may allow non-integer indices. - try: - methods = dunder_lookup.lookup(parent_type, methodname) - if methods is astroid.YES: - return - itemmethod = methods[0] - except (exceptions.NotFoundError, - exceptions.AttributeInferenceError, - IndexError): - return - if not isinstance(itemmethod, astroid.FunctionDef): - return - if itemmethod.root().name != BUILTINS: - return - if not itemmethod.parent: - return - if itemmethod.parent.name not in SEQUENCE_TYPES: - return - - # For ExtSlice objects coming from visit_extslice, no further - # inference is necessary, since if we got this far the ExtSlice - # is an error. - if isinstance(node, astroid.ExtSlice): - index_type = node - else: - index_type = safe_infer(node) - if index_type is None or index_type is astroid.YES: - return - # Constants must be of type int - if isinstance(index_type, astroid.Const): - if isinstance(index_type.value, int): - return - # Instance values must be int, slice, or have an __index__ method - elif isinstance(index_type, astroid.Instance): - if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'): - return - try: - index_type.getattr('__index__') - return - except exceptions.NotFoundError: - pass - elif isinstance(index_type, astroid.Slice): - # Delegate to visit_slice. A slice can be present - # here after inferring the index node, which could - # be a `slice(...)` call for instance. - return self.visit_slice(index_type) - - # Anything else is an error - self.add_message('invalid-sequence-index', node=node) - - @check_messages('invalid-slice-index') - def visit_slice(self, node): - # Check the type of each part of the slice - for index in (node.lower, node.upper, node.step): - if index is None: - continue - - index_type = safe_infer(index) - if index_type is None or index_type is astroid.YES: - continue - - # Constants must of type int or None - if isinstance(index_type, astroid.Const): - if isinstance(index_type.value, (int, type(None))): - continue - # Instance values must be of type int, None or an object - # with __index__ - elif isinstance(index_type, astroid.Instance): - if index_type.pytype() in (BUILTINS + '.int', - BUILTINS + '.NoneType'): - continue - - try: - index_type.getattr('__index__') - return - except exceptions.NotFoundError: - pass - - # Anything else is an error - self.add_message('invalid-slice-index', node=node) - - @check_messages('not-context-manager') - def visit_with(self, node): - for ctx_mgr, _ in node.items: - context = astroid.context.InferenceContext() - infered = safe_infer(ctx_mgr, context=context) - if infered is None or infered is astroid.YES: - continue - - if isinstance(infered, bases.Generator): - # Check if we are dealing with a function decorated - # with contextlib.contextmanager. - if decorated_with(infered.parent, - self.config.contextmanager_decorators): - continue - # If the parent of the generator is not the context manager itself, - # that means that it could have been returned from another - # function which was the real context manager. - # The following approach is more of a hack rather than a real - # solution: walk all the inferred statements for the - # given *ctx_mgr* and if you find one function scope - # which is decorated, consider it to be the real - # manager and give up, otherwise emit not-context-manager. - # See the test file for not_context_manager for a couple - # of self explaining tests. - for path in six.moves.filter(None, _unflatten(context.path)): - scope = path.scope() - if not isinstance(scope, astroid.FunctionDef): - continue - if decorated_with(scope, - self.config.contextmanager_decorators): - break - else: - self.add_message('not-context-manager', - node=node, args=(infered.name, )) - else: - try: - infered.getattr('__enter__') - infered.getattr('__exit__') - except exceptions.NotFoundError: - if isinstance(infered, astroid.Instance): - # If we do not know the bases of this class, - # just skip it. - if not has_known_bases(infered): - continue - # Just ignore mixin classes. - if self.config.ignore_mixin_members: - if infered.name[-5:].lower() == 'mixin': - continue - - self.add_message('not-context-manager', - node=node, args=(infered.name, )) - - @check_messages('invalid-unary-operand-type') - def visit_unaryop(self, node): - """Detect TypeErrors for unary operands.""" - - for error in node.type_errors(): - # Let the error customize its output. - self.add_message('invalid-unary-operand-type', - args=str(error), node=node) - - @check_messages('unsupported-binary-operation') - def _visit_binop(self, node): - """Detect TypeErrors for binary arithmetic operands.""" - self._check_binop_errors(node) - - @check_messages('unsupported-binary-operation') - def _visit_augassign(self, node): - """Detect TypeErrors for augmented binary arithmetic operands.""" - self._check_binop_errors(node) - - def _check_binop_errors(self, node): - for error in node.type_errors(): - # Let the error customize its output. - if any(isinstance(obj, astroid.ClassDef) and not has_known_bases(obj) - for obj in (error.left_type, error.right_type)): - continue - self.add_message('unsupported-binary-operation', - args=str(error), node=node) - - def _check_membership_test(self, node): - if is_inside_abstract_class(node): - return - if is_comprehension(node): - return - infered = safe_infer(node) - if infered is None or infered is astroid.YES: - return - if not supports_membership_test(infered): - self.add_message('unsupported-membership-test', - args=node.as_string(), - node=node) - - @check_messages('unsupported-membership-test') - def visit_compare(self, node): - if len(node.ops) != 1: - return - - op, right = node.ops[0] - if op in ['in', 'not in']: - self._check_membership_test(right) - - @check_messages('unsubscriptable-object', 'unsupported-assignment-operation', - 'unsupported-delete-operation') - def visit_subscript(self, node): - supported_protocol = None - if isinstance(node.value, (astroid.ListComp, astroid.DictComp)): - return - - if node.ctx == astroid.Load: - supported_protocol = supports_getitem - msg = 'unsubscriptable-object' - elif node.ctx == astroid.Store: - supported_protocol = supports_setitem - msg = 'unsupported-assignment-operation' - elif node.ctx == astroid.Del: - supported_protocol = supports_delitem - msg = 'unsupported-delete-operation' - - if isinstance(node.value, astroid.SetComp): - self.add_message(msg, args=node.value.as_string(), - node=node.value) - return - - if is_inside_abstract_class(node): - return - - inferred = safe_infer(node.value) - if inferred is None or inferred is astroid.YES: - return - - if not supported_protocol(inferred): - self.add_message(msg, args=node.value.as_string(), node=node.value) - - -class IterableChecker(BaseChecker): - """ - Checks for non-iterables used in an iterable context. - Contexts include: - - for-statement - - starargs in function call - - `yield from`-statement - - list, dict and set comprehensions - - generator expressions - Also checks for non-mappings in function call kwargs. - """ - - __implements__ = (IAstroidChecker,) - name = 'iterable_check' - - msgs = {'E1133': ('Non-iterable value %s is used in an iterating context', - 'not-an-iterable', - 'Used when a non-iterable value is used in place where ' - 'iterable is expected'), - 'E1134': ('Non-mapping value %s is used in a mapping context', - 'not-a-mapping', - 'Used when a non-mapping value is used in place where ' - 'mapping is expected'), - } - - def _check_iterable(self, node): - if is_inside_abstract_class(node): - return - if is_comprehension(node): - return - infered = safe_infer(node) - if infered is None or infered is astroid.YES: - return - if not is_iterable(infered): - self.add_message('not-an-iterable', - args=node.as_string(), - node=node) - - def _check_mapping(self, node): - if is_inside_abstract_class(node): - return - if isinstance(node, astroid.DictComp): - return - infered = safe_infer(node) - if infered is None or infered is astroid.YES: - return - if not is_mapping(infered): - self.add_message('not-a-mapping', - args=node.as_string(), - node=node) - - @check_messages('not-an-iterable') - def visit_for(self, node): - self._check_iterable(node.iter) - - @check_messages('not-an-iterable') - def visit_yieldfrom(self, node): - self._check_iterable(node.value) - - @check_messages('not-an-iterable', 'not-a-mapping') - def visit_call(self, node): - for stararg in node.starargs: - self._check_iterable(stararg.value) - for kwarg in node.kwargs: - self._check_mapping(kwarg.value) - - @check_messages('not-an-iterable') - def visit_listcomp(self, node): - for gen in node.generators: - self._check_iterable(gen.iter) - - @check_messages('not-an-iterable') - def visit_dictcomp(self, node): - for gen in node.generators: - self._check_iterable(gen.iter) - - @check_messages('not-an-iterable') - def visit_setcomp(self, node): - for gen in node.generators: - self._check_iterable(gen.iter) - - @check_messages('not-an-iterable') - def visit_generatorexp(self, node): - for gen in node.generators: - self._check_iterable(gen.iter) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(TypeChecker(linter)) - linter.register_checker(IterableChecker(linter)) diff --git a/pymode/libs/pylint/checkers/utils.py b/pymode/libs/pylint/checkers/utils.py deleted file mode 100644 index 5ed18865..00000000 --- a/pymode/libs/pylint/checkers/utils.py +++ /dev/null @@ -1,860 +0,0 @@ -# Copyright (c) 2006-2007, 2009-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2012-2014 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2015 Radu Ciorba -# Copyright (c) 2015 Dmitry Pribysh -# Copyright (c) 2016 Ashley Whetter - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -# pylint: disable=W0611 -"""some functions that may be useful for various checkers -""" -import collections -import functools -try: - from functools import singledispatch as singledispatch -except ImportError: - # pylint: disable=import-error - from singledispatch import singledispatch as singledispatch -try: - from functools import lru_cache -except ImportError: - from backports.functools_lru_cache import lru_cache -import itertools -import re -import sys -import string -import warnings - -import six -from six.moves import map, builtins # pylint: disable=redefined-builtin - -import astroid -from astroid import bases as _bases -from astroid import scoped_nodes - - -BUILTINS_NAME = builtins.__name__ -COMP_NODE_TYPES = (astroid.ListComp, astroid.SetComp, - astroid.DictComp, astroid.GeneratorExp) -PY3K = sys.version_info[0] == 3 - -if not PY3K: - EXCEPTIONS_MODULE = "exceptions" -else: - EXCEPTIONS_MODULE = "builtins" -ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod', - 'abc.abstractclassmethod', 'abc.abstractstaticmethod')) -ITER_METHOD = '__iter__' -NEXT_METHOD = 'next' if six.PY2 else '__next__' -GETITEM_METHOD = '__getitem__' -SETITEM_METHOD = '__setitem__' -DELITEM_METHOD = '__delitem__' -CONTAINS_METHOD = '__contains__' -KEYS_METHOD = 'keys' - -# Dictionary which maps the number of expected parameters a -# special method can have to a set of special methods. -# The following keys are used to denote the parameters restrictions: -# -# * None: variable number of parameters -# * number: exactly that number of parameters -# * tuple: this are the odd ones. Basically it means that the function -# can work with any number of arguments from that tuple, -# although it's best to implement it in order to accept -# all of them. -_SPECIAL_METHODS_PARAMS = { - None: ('__new__', '__init__', '__call__'), - - 0: ('__del__', '__repr__', '__str__', '__bytes__', '__hash__', '__bool__', - '__dir__', '__len__', '__length_hint__', '__iter__', '__reversed__', - '__neg__', '__pos__', '__abs__', '__invert__', '__complex__', '__int__', - '__float__', '__neg__', '__pos__', '__abs__', '__complex__', '__int__', - '__float__', '__index__', '__enter__', '__aenter__', '__getnewargs_ex__', - '__getnewargs__', '__getstate__', '__reduce__', '__copy__', - '__unicode__', '__nonzero__', '__await__', '__aiter__', '__anext__', - '__fspath__'), - - 1: ('__format__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', - '__ge__', '__getattr__', '__getattribute__', '__delattr__', - '__delete__', '__instancecheck__', '__subclasscheck__', - '__getitem__', '__missing__', '__delitem__', '__contains__', - '__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__', - '__mod__', '__divmod__', '__lshift__', '__rshift__', '__and__', - '__xor__', '__or__', '__radd__', '__rsub__', '__rmul__', '__rtruediv__', - '__rmod__', '__rdivmod__', '__rpow__', '__rlshift__', '__rrshift__', - '__rand__', '__rxor__', '__ror__', '__iadd__', '__isub__', '__imul__', - '__itruediv__', '__ifloordiv__', '__imod__', '__ilshift__', - '__irshift__', '__iand__', '__ixor__', '__ior__', '__ipow__', - '__setstate__', '__reduce_ex__', '__deepcopy__', '__cmp__', - '__matmul__', '__rmatmul__', '__div__'), - - 2: ('__setattr__', '__get__', '__set__', '__setitem__'), - - 3: ('__exit__', '__aexit__'), - - (0, 1): ('__round__', ), -} - -SPECIAL_METHODS_PARAMS = { - name: params - for params, methods in _SPECIAL_METHODS_PARAMS.items() - for name in methods -} -PYMETHODS = set(SPECIAL_METHODS_PARAMS) - - -class NoSuchArgumentError(Exception): - pass - -def is_inside_except(node): - """Returns true if node is inside the name of an except handler.""" - current = node - while current and not isinstance(current.parent, astroid.ExceptHandler): - current = current.parent - - return current and current is current.parent.name - - -def get_all_elements(node): - """Recursively returns all atoms in nested lists and tuples.""" - if isinstance(node, (astroid.Tuple, astroid.List)): - for child in node.elts: - for e in get_all_elements(child): - yield e - else: - yield node - - -def clobber_in_except(node): - """Checks if an assignment node in an except handler clobbers an existing - variable. - - Returns (True, args for W0623) if assignment clobbers an existing variable, - (False, None) otherwise. - """ - if isinstance(node, astroid.AssignAttr): - return (True, (node.attrname, 'object %r' % (node.expr.as_string(),))) - elif isinstance(node, astroid.AssignName): - name = node.name - if is_builtin(name): - return (True, (name, 'builtins')) - else: - stmts = node.lookup(name)[1] - if (stmts and not isinstance(stmts[0].assign_type(), - (astroid.Assign, astroid.AugAssign, - astroid.ExceptHandler))): - return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno)) - return (False, None) - - -def is_super(node): - """return True if the node is referencing the "super" builtin function - """ - if getattr(node, 'name', None) == 'super' and \ - node.root().name == BUILTINS_NAME: - return True - return False - -def is_error(node): - """return true if the function does nothing but raising an exception""" - for child_node in node.get_children(): - if isinstance(child_node, astroid.Raise): - return True - return False - -def is_raising(body): - """return true if the given statement node raise an exception""" - for node in body: - if isinstance(node, astroid.Raise): - return True - return False - -builtins = builtins.__dict__.copy() -SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') - -def is_builtin_object(node): - """Returns True if the given node is an object from the __builtin__ module.""" - return node and node.root().name == BUILTINS_NAME - -def is_builtin(name): - """return true if could be considered as a builtin defined by python - """ - return name in builtins or name in SPECIAL_BUILTINS - -def is_defined_before(var_node): - """return True if the variable node is defined by a parent node (list, - set, dict, or generator comprehension, lambda) or in a previous sibling - node on the same line (statement_defining ; statement_using) - """ - varname = var_node.name - _node = var_node.parent - while _node: - if isinstance(_node, COMP_NODE_TYPES): - for ass_node in _node.nodes_of_class(astroid.AssignName): - if ass_node.name == varname: - return True - elif isinstance(_node, astroid.For): - for ass_node in _node.target.nodes_of_class(astroid.AssignName): - if ass_node.name == varname: - return True - elif isinstance(_node, astroid.With): - for expr, ids in _node.items: - if expr.parent_of(var_node): - break - if (ids and - isinstance(ids, astroid.AssignName) and - ids.name == varname): - return True - elif isinstance(_node, (astroid.Lambda, astroid.FunctionDef)): - if _node.args.is_argument(varname): - # If the name is found inside a default value - # of a function, then let the search continue - # in the parent's tree. - if _node.args.parent_of(var_node): - try: - _node.args.default_value(varname) - _node = _node.parent - continue - except astroid.NoDefault: - pass - return True - if getattr(_node, 'name', None) == varname: - return True - break - elif isinstance(_node, astroid.ExceptHandler): - if isinstance(_node.name, astroid.AssignName): - ass_node = _node.name - if ass_node.name == varname: - return True - _node = _node.parent - # possibly multiple statements on the same line using semi colon separator - stmt = var_node.statement() - _node = stmt.previous_sibling() - lineno = stmt.fromlineno - while _node and _node.fromlineno == lineno: - for ass_node in _node.nodes_of_class(astroid.AssignName): - if ass_node.name == varname: - return True - for imp_node in _node.nodes_of_class((astroid.ImportFrom, astroid.Import)): - if varname in [name[1] or name[0] for name in imp_node.names]: - return True - _node = _node.previous_sibling() - return False - -def is_func_default(node): - """return true if the given Name node is used in function default argument's - value - """ - parent = node.scope() - if isinstance(parent, astroid.FunctionDef): - for default_node in parent.args.defaults: - for default_name_node in default_node.nodes_of_class(astroid.Name): - if default_name_node is node: - return True - return False - -def is_func_decorator(node): - """return true if the name is used in function decorator""" - parent = node.parent - while parent is not None: - if isinstance(parent, astroid.Decorators): - return True - if (parent.is_statement or - isinstance(parent, (astroid.Lambda, - scoped_nodes.ComprehensionScope, - scoped_nodes.ListComp))): - break - parent = parent.parent - return False - -def is_ancestor_name(frame, node): - """return True if `frame` is a astroid.Class node with `node` in the - subtree of its bases attribute - """ - try: - bases = frame.bases - except AttributeError: - return False - for base in bases: - if node in base.nodes_of_class(astroid.Name): - return True - return False - -def assign_parent(node): - """return the higher parent which is not an AssName, Tuple or List node - """ - while node and isinstance(node, (astroid.AssignName, - astroid.Tuple, - astroid.List)): - node = node.parent - return node - - -def overrides_a_method(class_node, name): - """return True if is a method overridden from an ancestor""" - for ancestor in class_node.ancestors(): - if name in ancestor and isinstance(ancestor[name], astroid.FunctionDef): - return True - return False - -def check_messages(*messages): - """decorator to store messages that are handled by a checker method""" - - def store_messages(func): - func.checks_msgs = messages - return func - return store_messages - -class IncompleteFormatString(Exception): - """A format string ended in the middle of a format specifier.""" - pass - -class UnsupportedFormatCharacter(Exception): - """A format character in a format string is not one of the supported - format characters.""" - def __init__(self, index): - Exception.__init__(self, index) - self.index = index - -def parse_format_string(format_string): - """Parses a format string, returning a tuple of (keys, num_args), where keys - is the set of mapping keys in the format string, and num_args is the number - of arguments required by the format string. Raises - IncompleteFormatString or UnsupportedFormatCharacter if a - parse error occurs.""" - keys = set() - num_args = 0 - def next_char(i): - i += 1 - if i == len(format_string): - raise IncompleteFormatString - return (i, format_string[i]) - i = 0 - while i < len(format_string): - char = format_string[i] - if char == '%': - i, char = next_char(i) - # Parse the mapping key (optional). - key = None - if char == '(': - depth = 1 - i, char = next_char(i) - key_start = i - while depth != 0: - if char == '(': - depth += 1 - elif char == ')': - depth -= 1 - i, char = next_char(i) - key_end = i - 1 - key = format_string[key_start:key_end] - - # Parse the conversion flags (optional). - while char in '#0- +': - i, char = next_char(i) - # Parse the minimum field width (optional). - if char == '*': - num_args += 1 - i, char = next_char(i) - else: - while char in string.digits: - i, char = next_char(i) - # Parse the precision (optional). - if char == '.': - i, char = next_char(i) - if char == '*': - num_args += 1 - i, char = next_char(i) - else: - while char in string.digits: - i, char = next_char(i) - # Parse the length modifier (optional). - if char in 'hlL': - i, char = next_char(i) - # Parse the conversion type (mandatory). - if PY3K: - flags = 'diouxXeEfFgGcrs%a' - else: - flags = 'diouxXeEfFgGcrs%' - if char not in flags: - raise UnsupportedFormatCharacter(i) - if key: - keys.add(key) - elif char != '%': - num_args += 1 - i += 1 - return keys, num_args - - -def is_attr_protected(attrname): - """return True if attribute name is protected (start with _ and some other - details), False otherwise. - """ - return attrname[0] == '_' and attrname != '_' and not ( - attrname.startswith('__') and attrname.endswith('__')) - -def node_frame_class(node): - """return klass node for a method node (or a staticmethod or a - classmethod), return null otherwise - """ - klass = node.frame() - - while klass is not None and not isinstance(klass, astroid.ClassDef): - if klass.parent is None: - klass = None - else: - klass = klass.parent.frame() - - return klass - - -def is_attr_private(attrname): - """Check that attribute name is private (at least two leading underscores, - at most one trailing underscore) - """ - regex = re.compile('^_{2,}.*[^_]+_?$') - return regex.match(attrname) - -def get_argument_from_call(callfunc_node, position=None, keyword=None): - """Returns the specified argument from a function call. - - :param astroid.Call callfunc_node: Node representing a function call to check. - :param int position: position of the argument. - :param str keyword: the keyword of the argument. - - :returns: The node representing the argument, None if the argument is not found. - :rtype: astroid.Name - :raises ValueError: if both position and keyword are None. - :raises NoSuchArgumentError: if no argument at the provided position or with - the provided keyword. - """ - if position is None and keyword is None: - raise ValueError('Must specify at least one of: position or keyword.') - if position is not None: - try: - return callfunc_node.args[position] - except IndexError: - pass - if keyword and callfunc_node.keywords: - for arg in callfunc_node.keywords: - if arg.arg == keyword: - return arg.value - - raise NoSuchArgumentError - -def inherit_from_std_ex(node): - """ - Return true if the given class node is subclass of - exceptions.Exception. - """ - if node.name in ('Exception', 'BaseException') \ - and node.root().name == EXCEPTIONS_MODULE: - return True - return any(inherit_from_std_ex(parent) - for parent in node.ancestors(recurs=True)) - -def error_of_type(handler, error_type): - """ - Check if the given exception handler catches - the given error_type. - - The *handler* parameter is a node, representing an ExceptHandler node. - The *error_type* can be an exception, such as AttributeError, - the name of an exception, or it can be a tuple of errors. - The function will return True if the handler catches any of the - given errors. - """ - def stringify_error(error): - if not isinstance(error, six.string_types): - return error.__name__ - return error - - if not isinstance(error_type, tuple): - error_type = (error_type, ) - expected_errors = {stringify_error(error) for error in error_type} - if not handler.type: - # bare except. While this indeed catches anything, if the desired errors - # aren't specified directly, then we just ignore it. - return False - return handler.catch(expected_errors) - - -def decorated_with_property(node): - """ Detect if the given function node is decorated with a property. """ - if not node.decorators: - return False - for decorator in node.decorators.nodes: - if not isinstance(decorator, astroid.Name): - continue - try: - if _is_property_decorator(decorator): - return True - except astroid.InferenceError: - pass - return False - - -def _is_property_decorator(decorator): - for infered in decorator.infer(): - if isinstance(infered, astroid.ClassDef): - if infered.root().name == BUILTINS_NAME and infered.name == 'property': - return True - for ancestor in infered.ancestors(): - if ancestor.name == 'property' and ancestor.root().name == BUILTINS_NAME: - return True - - -def decorated_with(func, qnames): - """Determine if the `func` node has a decorator with the qualified name `qname`.""" - decorators = func.decorators.nodes if func.decorators else [] - for decorator_node in decorators: - try: - if any(i is not None and i.qname() in qnames for i in decorator_node.infer()): - return True - except astroid.InferenceError: - continue - return False - - -@lru_cache(maxsize=1024) -def unimplemented_abstract_methods(node, is_abstract_cb=None): - """ - Get the unimplemented abstract methods for the given *node*. - - A method can be considered abstract if the callback *is_abstract_cb* - returns a ``True`` value. The check defaults to verifying that - a method is decorated with abstract methods. - The function will work only for new-style classes. For old-style - classes, it will simply return an empty dictionary. - For the rest of them, it will return a dictionary of abstract method - names and their inferred objects. - """ - if is_abstract_cb is None: - is_abstract_cb = functools.partial( - decorated_with, qnames=ABC_METHODS) - visited = {} - try: - mro = reversed(node.mro()) - except NotImplementedError: - # Old style class, it will not have a mro. - return {} - except astroid.ResolveError: - # Probably inconsistent hierarchy, don'try - # to figure this out here. - return {} - for ancestor in mro: - for obj in ancestor.values(): - infered = obj - if isinstance(obj, astroid.AssignName): - infered = safe_infer(obj) - if not infered: - # Might be an abstract function, - # but since we don't have enough information - # in order to take this decision, we're taking - # the *safe* decision instead. - if obj.name in visited: - del visited[obj.name] - continue - if not isinstance(infered, astroid.FunctionDef): - if obj.name in visited: - del visited[obj.name] - if isinstance(infered, astroid.FunctionDef): - # It's critical to use the original name, - # since after inferring, an object can be something - # else than expected, as in the case of the - # following assignment. - # - # class A: - # def keys(self): pass - # __iter__ = keys - abstract = is_abstract_cb(infered) - if abstract: - visited[obj.name] = infered - elif not abstract and obj.name in visited: - del visited[obj.name] - return visited - - -def _import_node_context(node): - current = node - ignores = (astroid.ExceptHandler, astroid.TryExcept) - while current and not isinstance(current.parent, ignores): - current = current.parent - - if current and isinstance(current.parent, ignores): - return current.parent - return None - - -def is_from_fallback_block(node): - """Check if the given node is from a fallback import block.""" - context = _import_node_context(node) - if not context: - return False - - if isinstance(context, astroid.ExceptHandler): - other_body = context.parent.body - handlers = context.parent.handlers - else: - other_body = itertools.chain.from_iterable( - handler.body for handler in context.handlers) - handlers = context.handlers - - has_fallback_imports = any(isinstance(import_node, (astroid.ImportFrom, astroid.Import)) - for import_node in other_body) - ignores_import_error = _except_handlers_ignores_exception(handlers, ImportError) - return ignores_import_error or has_fallback_imports - - -def _except_handlers_ignores_exception(handlers, exception): - func = functools.partial(error_of_type, - error_type=(exception, )) - return any(map(func, handlers)) - - -def node_ignores_exception(node, exception): - """Check if the node is in a TryExcept which handles the given exception.""" - current = node - ignores = (astroid.ExceptHandler, astroid.TryExcept) - while current and not isinstance(current.parent, ignores): - current = current.parent - - if current and isinstance(current.parent, astroid.TryExcept): - return _except_handlers_ignores_exception(current.parent.handlers, exception) - return False - - -def class_is_abstract(node): - """return true if the given class node should be considered as an abstract - class - """ - for method in node.methods(): - if method.parent.frame() is node: - if method.is_abstract(pass_is_abstract=False): - return True - return False - - -def _supports_protocol_method(value, attr): - try: - attributes = value.getattr(attr) - except astroid.NotFoundError: - return False - - first = attributes[0] - if isinstance(first, astroid.AssignName): - if isinstance(first.parent.value, astroid.Const): - return False - return True - - -def is_comprehension(node): - comprehensions = (astroid.ListComp, - astroid.SetComp, - astroid.DictComp, - astroid.GeneratorExp) - return isinstance(node, comprehensions) - - -def _supports_mapping_protocol(value): - return ( - _supports_protocol_method(value, GETITEM_METHOD) - and _supports_protocol_method(value, KEYS_METHOD) - ) - - -def _supports_membership_test_protocol(value): - return _supports_protocol_method(value, CONTAINS_METHOD) - - -def _supports_iteration_protocol(value): - return ( - _supports_protocol_method(value, ITER_METHOD) - or _supports_protocol_method(value, GETITEM_METHOD) - ) - - -def _supports_getitem_protocol(value): - return _supports_protocol_method(value, GETITEM_METHOD) - - -def _supports_setitem_protocol(value): - return _supports_protocol_method(value, SETITEM_METHOD) - - -def _supports_delitem_protocol(value): - return _supports_protocol_method(value, DELITEM_METHOD) - - -def _is_abstract_class_name(name): - lname = name.lower() - is_mixin = lname.endswith('mixin') - is_abstract = lname.startswith('abstract') - is_base = lname.startswith('base') or lname.endswith('base') - return is_mixin or is_abstract or is_base - - -def is_inside_abstract_class(node): - while node is not None: - if isinstance(node, astroid.ClassDef): - if class_is_abstract(node): - return True - name = getattr(node, 'name', None) - if name is not None and _is_abstract_class_name(name): - return True - node = node.parent - return False - - -def _supports_protocol(value, protocol_callback): - if isinstance(value, astroid.ClassDef): - if not has_known_bases(value): - return True - # classobj can only be iterable if it has an iterable metaclass - meta = value.metaclass() - if meta is not None: - if protocol_callback(meta): - return True - if isinstance(value, astroid.BaseInstance): - if not has_known_bases(value): - return True - if protocol_callback(value): - return True - - # TODO: this is not needed in astroid 2.0, where we can - # check the type using a virtual base class instead. - if (isinstance(value, _bases.Proxy) - and isinstance(value._proxied, astroid.BaseInstance) - and has_known_bases(value._proxied)): - value = value._proxied - return protocol_callback(value) - - return False - - -def is_iterable(value): - return _supports_protocol(value, _supports_iteration_protocol) - - -def is_mapping(value): - return _supports_protocol(value, _supports_mapping_protocol) - - -def supports_membership_test(value): - supported = _supports_protocol(value, _supports_membership_test_protocol) - return supported or is_iterable(value) - - -def supports_getitem(value): - return _supports_protocol(value, _supports_getitem_protocol) - - -def supports_setitem(value): - return _supports_protocol(value, _supports_setitem_protocol) - - -def supports_delitem(value): - return _supports_protocol(value, _supports_delitem_protocol) - - -# TODO(cpopa): deprecate these or leave them as aliases? -@lru_cache(maxsize=1024) -def safe_infer(node, context=None): - """Return the inferred value for the given node. - - Return None if inference failed or if there is some ambiguity (more than - one node has been inferred). - """ - try: - inferit = node.infer(context=context) - value = next(inferit) - except astroid.InferenceError: - return - try: - next(inferit) - return # None if there is ambiguity on the inferred node - except astroid.InferenceError: - return # there is some kind of ambiguity - except StopIteration: - return value - - -def has_known_bases(klass, context=None): - """Return true if all base classes of a class could be inferred.""" - try: - return klass._all_bases_known - except AttributeError: - pass - for base in klass.bases: - result = safe_infer(base, context=context) - # TODO: check for A->B->A->B pattern in class structure too? - if (not isinstance(result, astroid.ClassDef) or - result is klass or - not has_known_bases(result, context=context)): - klass._all_bases_known = False - return False - klass._all_bases_known = True - return True - - -def is_none(node): - return (node is None or - (isinstance(node, astroid.Const) and node.value is None) or - (isinstance(node, astroid.Name) and node.name == 'None') - ) - - -def node_type(node): - """Return the inferred type for `node` - - If there is more than one possible type, or if inferred type is YES or None, - return None - """ - # check there is only one possible type for the assign node. Else we - # don't handle it for now - types = set() - try: - for var_type in node.infer(): - if var_type == astroid.YES or is_none(var_type): - continue - types.add(var_type) - if len(types) > 1: - return - except astroid.InferenceError: - return - return types.pop() if types else None - - -def is_registered_in_singledispatch_function(node): - """Check if the given function node is a singledispatch function.""" - - singledispatch_qnames = ( - 'functools.singledispatch', - 'singledispatch.singledispatch' - ) - - if not isinstance(node, astroid.FunctionDef): - return False - - decorators = node.decorators.nodes if node.decorators else [] - for decorator in decorators: - # func.register are function calls - if not isinstance(decorator, astroid.Call): - continue - - func = decorator.func - if not isinstance(func, astroid.Attribute) or func.attrname != 'register': - continue - - try: - func_def = next(func.expr.infer()) - except astroid.InferenceError: - continue - - if isinstance(func_def, astroid.FunctionDef): - return decorated_with(func_def, singledispatch_qnames) - - return False diff --git a/pymode/libs/pylint/checkers/variables.py b/pymode/libs/pylint/checkers/variables.py deleted file mode 100644 index e6becff2..00000000 --- a/pymode/libs/pylint/checkers/variables.py +++ /dev/null @@ -1,1324 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2011-2014 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2014 Michal Nowikowski -# Copyright (c) 2015 Radu Ciorba -# Copyright (c) 2015 Dmitry Pribysh -# Copyright (c) 2016 Ashley Whetter - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""variables checkers for Python code -""" -import copy -import itertools -import os -import sys -import re -try: - from functools import lru_cache -except ImportError: - from backports.functools_lru_cache import lru_cache - -import six - -import astroid -from astroid import decorators -from astroid import modutils -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH -from pylint.utils import get_global_option -from pylint.checkers import BaseChecker -from pylint.checkers import utils - - -SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") -FUTURE = '__future__' -# regexp for ignored argument name -IGNORED_ARGUMENT_NAMES = re.compile('_.*|^ignored_|^unused_') -PY3K = sys.version_info >= (3, 0) - - -def _is_from_future_import(stmt, name): - """Check if the name is a future import from another module.""" - try: - module = stmt.do_import_module(stmt.modname) - except astroid.AstroidBuildingException: - return - - for local_node in module.locals.get(name, []): - if (isinstance(local_node, astroid.ImportFrom) - and local_node.modname == FUTURE): - return True - - -def in_for_else_branch(parent, stmt): - """Returns True if stmt in inside the else branch for a parent For stmt.""" - return (isinstance(parent, astroid.For) and - any(else_stmt.parent_of(stmt) or else_stmt == stmt - for else_stmt in parent.orelse)) - - -@lru_cache(maxsize=1000) -def overridden_method(klass, name): - """get overridden method if any""" - try: - parent = next(klass.local_attr_ancestors(name)) - except (StopIteration, KeyError): - return None - try: - meth_node = parent[name] - except KeyError: - # We have found an ancestor defining but it's not in the local - # dictionary. This may happen with astroid built from living objects. - return None - if isinstance(meth_node, astroid.FunctionDef): - return meth_node - return None - -def _get_unpacking_extra_info(node, infered): - """return extra information to add to the message for unpacking-non-sequence - and unbalanced-tuple-unpacking errors - """ - more = '' - infered_module = infered.root().name - if node.root().name == infered_module: - if node.lineno == infered.lineno: - more = ' %s' % infered.as_string() - elif infered.lineno: - more = ' defined at line %s' % infered.lineno - elif infered.lineno: - more = ' defined at line %s of %s' % (infered.lineno, infered_module) - return more - -def _detect_global_scope(node, frame, defframe): - """ Detect that the given frames shares a global - scope. - - Two frames shares a global scope when neither - of them are hidden under a function scope, as well - as any of parent scope of them, until the root scope. - In this case, depending from something defined later on - will not work, because it is still undefined. - - Example: - class A: - # B has the same global scope as `C`, leading to a NameError. - class B(C): ... - class C: ... - - """ - def_scope = scope = None - if frame and frame.parent: - scope = frame.parent.scope() - if defframe and defframe.parent: - def_scope = defframe.parent.scope() - if isinstance(frame, astroid.FunctionDef): - # If the parent of the current node is a - # function, then it can be under its scope - # (defined in, which doesn't concern us) or - # the `->` part of annotations. The same goes - # for annotations of function arguments, they'll have - # their parent the Arguments node. - if not isinstance(node.parent, - (astroid.FunctionDef, astroid.Arguments)): - return False - elif any(not isinstance(f, (astroid.ClassDef, astroid.Module)) - for f in (frame, defframe)): - # Not interested in other frames, since they are already - # not in a global scope. - return False - - break_scopes = [] - for s in (scope, def_scope): - # Look for parent scopes. If there is anything different - # than a module or a class scope, then they frames don't - # share a global scope. - parent_scope = s - while parent_scope: - if not isinstance(parent_scope, (astroid.ClassDef, astroid.Module)): - break_scopes.append(parent_scope) - break - if parent_scope.parent: - parent_scope = parent_scope.parent.scope() - else: - break - if break_scopes and len(set(break_scopes)) != 1: - # Store different scopes than expected. - # If the stored scopes are, in fact, the very same, then it means - # that the two frames (frame and defframe) shares the same scope, - # and we could apply our lineno analysis over them. - # For instance, this works when they are inside a function, the node - # that uses a definition and the definition itself. - return False - # At this point, we are certain that frame and defframe shares a scope - # and the definition of the first depends on the second. - return frame.lineno < defframe.lineno - -def _fix_dot_imports(not_consumed): - """ Try to fix imports with multiple dots, by returning a dictionary - with the import names expanded. The function unflattens root imports, - like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree' - and 'xml.sax' respectively. - """ - # TODO: this should be improved in issue astroid #46 - names = {} - for name, stmts in six.iteritems(not_consumed): - if any(isinstance(stmt, astroid.AssignName) - and isinstance(stmt.assign_type(), astroid.AugAssign) - for stmt in stmts): - continue - for stmt in stmts: - if not isinstance(stmt, (astroid.ImportFrom, astroid.Import)): - continue - for imports in stmt.names: - second_name = None - if imports[0] == "*": - # In case of wildcard imports, - # pick the name from inside the imported module. - second_name = name - else: - if imports[0].find(".") > -1 or name in imports: - # Most likely something like 'xml.etree', - # which will appear in the .locals as 'xml'. - # Only pick the name if it wasn't consumed. - second_name = imports[0] - if second_name and second_name not in names: - names[second_name] = stmt - return sorted(names.items(), key=lambda a: a[1].fromlineno) - -def _find_frame_imports(name, frame): - """ - Detect imports in the frame, with the required - *name*. Such imports can be considered assignments. - Returns True if an import for the given name was found. - """ - imports = frame.nodes_of_class((astroid.Import, astroid.ImportFrom)) - for import_node in imports: - for import_name, import_alias in import_node.names: - # If the import uses an alias, check only that. - # Otherwise, check only the import name. - if import_alias: - if import_alias == name: - return True - elif import_name and import_name == name: - return True - - -def _import_name_is_global(stmt, global_names): - for import_name, import_alias in stmt.names: - # If the import uses an alias, check only that. - # Otherwise, check only the import name. - if import_alias: - if import_alias in global_names: - return True - elif import_name in global_names: - return True - return False - - -def _flattened_scope_names(iterator): - values = (set(stmt.names) for stmt in iterator) - return set(itertools.chain.from_iterable(values)) - - -def _assigned_locally(name_node): - """ - Checks if name_node has corresponding assign statement in same scope - """ - assign_stmts = name_node.scope().nodes_of_class(astroid.AssignName) - return any(a.name == name_node.name for a in assign_stmts) - - -MSGS = { - 'E0601': ('Using variable %r before assignment', - 'used-before-assignment', - 'Used when a local variable is accessed before it\'s \ - assignment.'), - 'E0602': ('Undefined variable %r', - 'undefined-variable', - 'Used when an undefined variable is accessed.'), - 'E0603': ('Undefined variable name %r in __all__', - 'undefined-all-variable', - 'Used when an undefined variable name is referenced in __all__.'), - 'E0604': ('Invalid object %r in __all__, must contain only strings', - 'invalid-all-object', - 'Used when an invalid (non-string) object occurs in __all__.'), - 'E0611': ('No name %r in module %r', - 'no-name-in-module', - 'Used when a name cannot be found in a module.'), - - 'W0601': ('Global variable %r undefined at the module level', - 'global-variable-undefined', - 'Used when a variable is defined through the "global" statement \ - but the variable is not defined in the module scope.'), - 'W0602': ('Using global for %r but no assignment is done', - 'global-variable-not-assigned', - 'Used when a variable is defined through the "global" statement \ - but no assignment to this variable is done.'), - 'W0603': ('Using the global statement', # W0121 - 'global-statement', - 'Used when you use the "global" statement to update a global \ - variable. Pylint just try to discourage this \ - usage. That doesn\'t mean you cannot use it !'), - 'W0604': ('Using the global statement at the module level', # W0103 - 'global-at-module-level', - 'Used when you use the "global" statement at the module level \ - since it has no effect'), - 'W0611': ('Unused %s', - 'unused-import', - 'Used when an imported module or variable is not used.'), - 'W0612': ('Unused variable %r', - 'unused-variable', - 'Used when a variable is defined but not used.'), - 'W0613': ('Unused argument %r', - 'unused-argument', - 'Used when a function or method argument is not used.'), - 'W0614': ('Unused import %s from wildcard import', - 'unused-wildcard-import', - 'Used when an imported module or variable is not used from a \ - `\'from X import *\'` style import.'), - - 'W0621': ('Redefining name %r from outer scope (line %s)', - 'redefined-outer-name', - 'Used when a variable\'s name hide a name defined in the outer \ - scope.'), - 'W0622': ('Redefining built-in %r', - 'redefined-builtin', - 'Used when a variable or function override a built-in.'), - 'W0623': ('Redefining name %r from %s in exception handler', - 'redefine-in-handler', - 'Used when an exception handler assigns the exception \ - to an existing name'), - - 'W0631': ('Using possibly undefined loop variable %r', - 'undefined-loop-variable', - 'Used when an loop variable (i.e. defined by a for loop or \ - a list comprehension or a generator expression) is used outside \ - the loop.'), - - 'E0632': ('Possible unbalanced tuple unpacking with ' - 'sequence%s: ' - 'left side has %d label(s), right side has %d value(s)', - 'unbalanced-tuple-unpacking', - 'Used when there is an unbalanced tuple unpacking in assignment', - {'old_names': [('W0632', 'unbalanced-tuple-unpacking')]}), - - 'E0633': ('Attempting to unpack a non-sequence%s', - 'unpacking-non-sequence', - 'Used when something which is not ' - 'a sequence is used in an unpack assignment', - {'old_names': [('W0633', 'unpacking-non-sequence')]}), - - 'W0640': ('Cell variable %s defined in loop', - 'cell-var-from-loop', - 'A variable used in a closure is defined in a loop. ' - 'This will result in all closures using the same value for ' - 'the closed-over variable.'), - - } - -class VariablesChecker(BaseChecker): - """checks for - * unused variables / imports - * undefined variables - * redefinition of variable from builtins or from an outer scope - * use of variable before assignment - * __all__ consistency - """ - - __implements__ = IAstroidChecker - - name = 'variables' - msgs = MSGS - priority = -1 - options = (("init-import", - {'default': 0, 'type' : 'yn', 'metavar' : '', - 'help' : 'Tells whether we should check for unused import in ' - '__init__ files.'}), - ("dummy-variables-rgx", - {'default': '_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_', - 'type' :'regexp', 'metavar' : '', - 'help' : 'A regular expression matching the name of dummy ' - 'variables (i.e. expectedly not used).'}), - ("additional-builtins", - {'default': (), 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of additional names supposed to be defined in ' - 'builtins. Remember that you should avoid to define new builtins ' - 'when possible.' - }), - ("callbacks", - {'default' : ('cb_', '_cb'), 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of strings which can identify a callback ' - 'function by name. A callback name must start or ' - 'end with one of those strings.'} - ), - ("redefining-builtins-modules", - {'default': ('six.moves', 'future.builtins'), 'type': 'csv', - 'metavar': '', - 'help': 'List of qualified module names which can have objects ' - 'that can redefine builtins.'} - ), - ('ignored-argument-names', - {'default' : IGNORED_ARGUMENT_NAMES, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Argument names that match this expression will be ' - 'ignored. Default to name with leading underscore'} - ), - ('allow-global-unused-variables', - {'default': True, - 'type': 'yn', 'metavar': '', - 'help': 'Tells whether unused global variables should be treated as a violation.'} - ), - ) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self._to_consume = None # list of tuples: (to_consume:dict, consumed:dict, scope_type:str) - self._checking_mod_attr = None - self._loop_variables = [] - - # Relying on other checker's options, which might not have been initialized yet. - @decorators.cachedproperty - def _analyse_fallback_blocks(self): - return get_global_option(self, 'analyse-fallback-blocks', default=False) - - @decorators.cachedproperty - def _ignored_modules(self): - return get_global_option(self, 'ignored-modules', default=[]) - - @decorators.cachedproperty - def _allow_global_unused_variables(self): - return get_global_option(self, 'allow-global-unused-variables', default=True) - - @utils.check_messages('redefined-outer-name') - def visit_for(self, node): - assigned_to = [var.name for var in node.target.nodes_of_class(astroid.AssignName)] - - # Only check variables that are used - dummy_rgx = self.config.dummy_variables_rgx - assigned_to = [var for var in assigned_to if not dummy_rgx.match(var)] - - for variable in assigned_to: - for outer_for, outer_variables in self._loop_variables: - if (variable in outer_variables - and not in_for_else_branch(outer_for, node)): - self.add_message( - 'redefined-outer-name', - args=(variable, outer_for.fromlineno), - node=node - ) - break - - self._loop_variables.append((node, assigned_to)) - - @utils.check_messages('redefined-outer-name') - def leave_for(self, _): - self._loop_variables.pop() - - def visit_module(self, node): - """visit module : update consumption analysis variable - checks globals doesn't overrides builtins - """ - self._to_consume = [(copy.copy(node.locals), {}, 'module')] - for name, stmts in six.iteritems(node.locals): - if utils.is_builtin(name) and not utils.is_inside_except(stmts[0]): - if self._should_ignore_redefined_builtin(stmts[0]): - continue - self.add_message('redefined-builtin', args=name, node=stmts[0]) - - @utils.check_messages('unused-import', 'unused-wildcard-import', - 'redefined-builtin', 'undefined-all-variable', - 'invalid-all-object', 'unused-variable') - def leave_module(self, node): - """leave module: check globals - """ - assert len(self._to_consume) == 1 - not_consumed = self._to_consume.pop()[0] - # attempt to check for __all__ if defined - if '__all__' in node.locals: - self._check_all(node, not_consumed) - - # check for unused globals - self._check_globals(not_consumed) - - # don't check unused imports in __init__ files - if not self.config.init_import and node.package: - return - - self._check_imports(not_consumed) - - def _check_all(self, node, not_consumed): - assigned = next(node.igetattr('__all__')) - if assigned is astroid.YES: - return - - for elt in getattr(assigned, 'elts', ()): - try: - elt_name = next(elt.infer()) - except astroid.InferenceError: - continue - if elt_name is astroid.YES: - continue - if not elt_name.parent: - continue - - if (not isinstance(elt_name, astroid.Const) - or not isinstance(elt_name.value, six.string_types)): - self.add_message('invalid-all-object', - args=elt.as_string(), node=elt) - continue - - elt_name = elt_name.value - # If elt is in not_consumed, remove it from not_consumed - if elt_name in not_consumed: - del not_consumed[elt_name] - continue - - if elt_name not in node.locals: - if not node.package: - self.add_message('undefined-all-variable', - args=(elt_name, ), - node=elt) - else: - basename = os.path.splitext(node.file)[0] - if os.path.basename(basename) == '__init__': - name = node.name + "." + elt_name - try: - modutils.file_from_modpath(name.split(".")) - except ImportError: - self.add_message('undefined-all-variable', - args=(elt_name, ), - node=elt) - except SyntaxError: - # don't yield an syntax-error warning, - # because it will be later yielded - # when the file will be checked - pass - - def _check_globals(self, not_consumed): - if self._allow_global_unused_variables: - return - for name, nodes in six.iteritems(not_consumed): - for node in nodes: - self.add_message('unused-variable', args=(name,), node=node) - - def _check_imports(self, not_consumed): - local_names = _fix_dot_imports(not_consumed) - checked = set() - for name, stmt in local_names: - for imports in stmt.names: - real_name = imported_name = imports[0] - if imported_name == "*": - real_name = name - as_name = imports[1] - if real_name in checked: - continue - if name not in (real_name, as_name): - continue - checked.add(real_name) - - if (isinstance(stmt, astroid.Import) or - (isinstance(stmt, astroid.ImportFrom) and - not stmt.modname)): - if (isinstance(stmt, astroid.ImportFrom) and - SPECIAL_OBJ.search(imported_name)): - # Filter special objects (__doc__, __all__) etc., - # because they can be imported for exporting. - continue - if as_name == "_": - continue - if as_name is None: - msg = "import %s" % imported_name - else: - msg = "%s imported as %s" % (imported_name, as_name) - self.add_message('unused-import', args=msg, node=stmt) - elif (isinstance(stmt, astroid.ImportFrom) - and stmt.modname != FUTURE): - - if SPECIAL_OBJ.search(imported_name): - # Filter special objects (__doc__, __all__) etc., - # because they can be imported for exporting. - continue - - if _is_from_future_import(stmt, name): - # Check if the name is in fact loaded from a - # __future__ import in another module. - continue - - if imported_name == '*': - self.add_message('unused-wildcard-import', - args=name, node=stmt) - else: - if as_name is None: - msg = "%s imported from %s" % (imported_name, stmt.modname) - else: - fields = (imported_name, stmt.modname, as_name) - msg = "%s imported from %s as %s" % fields - self.add_message('unused-import', args=msg, node=stmt) - del self._to_consume - - def visit_classdef(self, node): - """visit class: update consumption analysis variable - """ - self._to_consume.append((copy.copy(node.locals), {}, 'class')) - - def leave_classdef(self, _): - """leave class: update consumption analysis variable - """ - # do not check for not used locals here (no sense) - self._to_consume.pop() - - def visit_lambda(self, node): - """visit lambda: update consumption analysis variable - """ - self._to_consume.append((copy.copy(node.locals), {}, 'lambda')) - - def leave_lambda(self, _): - """leave lambda: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_generatorexp(self, node): - """visit genexpr: update consumption analysis variable - """ - self._to_consume.append((copy.copy(node.locals), {}, 'comprehension')) - - def leave_generatorexp(self, _): - """leave genexpr: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_dictcomp(self, node): - """visit dictcomp: update consumption analysis variable - """ - self._to_consume.append((copy.copy(node.locals), {}, 'comprehension')) - - def leave_dictcomp(self, _): - """leave dictcomp: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_setcomp(self, node): - """visit setcomp: update consumption analysis variable - """ - self._to_consume.append((copy.copy(node.locals), {}, 'comprehension')) - - def leave_setcomp(self, _): - """leave setcomp: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_functiondef(self, node): - """visit function: update consumption analysis variable and check locals - """ - self._to_consume.append((copy.copy(node.locals), {}, 'function')) - if not (self.linter.is_message_enabled('redefined-outer-name') or - self.linter.is_message_enabled('redefined-builtin')): - return - globs = node.root().globals - for name, stmt in node.items(): - if utils.is_inside_except(stmt): - continue - if name in globs and not isinstance(stmt, astroid.Global): - definition = globs[name][0] - if (isinstance(definition, astroid.ImportFrom) - and definition.modname == FUTURE): - # It is a __future__ directive, not a symbol. - continue - - line = definition.fromlineno - dummy_rgx = self.config.dummy_variables_rgx - if not dummy_rgx.match(name): - self.add_message('redefined-outer-name', - args=(name, line), node=stmt) - - elif utils.is_builtin(name) and not self._should_ignore_redefined_builtin(stmt): - # do not print Redefining builtin for additional builtins - self.add_message('redefined-builtin', args=name, node=stmt) - - def _is_name_ignored(self, stmt, name): - authorized_rgx = self.config.dummy_variables_rgx - if (isinstance(stmt, astroid.AssignName) - and isinstance(stmt.parent, astroid.Arguments)): - regex = self.config.ignored_argument_names - else: - regex = authorized_rgx - return regex and regex.match(name) - - def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names): - # Ignore some special names specified by user configuration. - if self._is_name_ignored(stmt, name): - return - - # Ignore names imported by the global statement. - # FIXME: should only ignore them if it's assigned latter - if isinstance(stmt, astroid.Global): - return - if isinstance(stmt, (astroid.Import, astroid.ImportFrom)): - # Detect imports, assigned to global statements. - if global_names and _import_name_is_global(stmt, global_names): - return - - argnames = list(itertools.chain( - node.argnames(), - [arg.name for arg in node.args.kwonlyargs] - )) - is_method = node.is_method() - klass = node.parent.frame() - if is_method and isinstance(klass, astroid.ClassDef): - confidence = INFERENCE if utils.has_known_bases(klass) else INFERENCE_FAILURE - else: - confidence = HIGH - - # Care about functions with unknown argument (builtins) - if name in argnames: - if is_method: - # Don't warn for the first argument of a (non static) method - if node.type != 'staticmethod' and name == argnames[0]: - return - # Don't warn for argument of an overridden method - overridden = overridden_method(klass, node.name) - if overridden is not None and name in overridden.argnames(): - return - if node.name in utils.PYMETHODS and node.name not in ('__init__', '__new__'): - return - # Don't check callback arguments - if any(node.name.startswith(cb) or node.name.endswith(cb) - for cb in self.config.callbacks): - return - # Don't check arguments of singledispatch.register function. - if utils.is_registered_in_singledispatch_function(node): - return - self.add_message('unused-argument', args=name, node=stmt, - confidence=confidence) - else: - if stmt.parent and isinstance(stmt.parent, astroid.Assign): - if name in nonlocal_names: - return - - if isinstance(stmt, astroid.Import): - # Need the complete name, which we don't have in .locals. - qname, asname = stmt.names[0] - name = asname or qname - - self.add_message('unused-variable', args=name, node=stmt) - - def leave_functiondef(self, node): - """leave function: check function's locals are consumed""" - not_consumed = self._to_consume.pop()[0] - if not (self.linter.is_message_enabled('unused-variable') or - self.linter.is_message_enabled('unused-argument')): - return - - # Don't check arguments of function which are only raising an exception. - if utils.is_error(node): - return - - # Don't check arguments of abstract methods or within an interface. - is_method = node.is_method() - if is_method and node.is_abstract(): - return - - global_names = _flattened_scope_names(node.nodes_of_class(astroid.Global)) - nonlocal_names = _flattened_scope_names(node.nodes_of_class(astroid.Nonlocal)) - for name, stmts in six.iteritems(not_consumed): - self._check_is_unused(name, node, stmts[0], global_names, nonlocal_names) - - visit_asyncfunctiondef = visit_functiondef - leave_asyncfunctiondef = leave_functiondef - - @utils.check_messages('global-variable-undefined', 'global-variable-not-assigned', - 'global-statement', 'global-at-module-level', - 'redefined-builtin') - def visit_global(self, node): - """check names imported exists in the global scope""" - frame = node.frame() - if isinstance(frame, astroid.Module): - self.add_message('global-at-module-level', node=node) - return - - module = frame.root() - default_message = True - for name in node.names: - try: - assign_nodes = module.getattr(name) - except astroid.NotFoundError: - # unassigned global, skip - assign_nodes = [] - - if not assign_nodes: - self.add_message('global-variable-not-assigned', - args=name, node=node) - default_message = False - continue - - for anode in assign_nodes: - if (isinstance(anode, astroid.AssignName) - and anode.name in module.special_attributes): - self.add_message('redefined-builtin', args=name, node=node) - break - if anode.frame() is module: - # module level assignment - break - else: - # global undefined at the module scope - self.add_message('global-variable-undefined', args=name, node=node) - default_message = False - - if default_message: - self.add_message('global-statement', node=node) - - def _check_late_binding_closure(self, node, assignment_node): - def _is_direct_lambda_call(): - return (isinstance(node_scope.parent, astroid.Call) - and node_scope.parent.func is node_scope) - - node_scope = node.scope() - if not isinstance(node_scope, (astroid.Lambda, astroid.FunctionDef)): - return - if isinstance(node.parent, astroid.Arguments): - return - - if isinstance(assignment_node, astroid.Comprehension): - if assignment_node.parent.parent_of(node.scope()): - self.add_message('cell-var-from-loop', node=node, args=node.name) - else: - assign_scope = assignment_node.scope() - maybe_for = assignment_node - while not isinstance(maybe_for, astroid.For): - if maybe_for is assign_scope: - break - maybe_for = maybe_for.parent - else: - if (maybe_for.parent_of(node_scope) - and not _is_direct_lambda_call() - and not isinstance(node_scope.statement(), astroid.Return)): - self.add_message('cell-var-from-loop', node=node, args=node.name) - - def _loopvar_name(self, node, name): - # filter variables according to node's scope - # XXX used to filter parents but don't remember why, and removing this - # fixes a W0631 false positive reported by Paul Hachmann on 2008/12 on - # python-projects (added to func_use_for_or_listcomp_var test) - #astmts = [stmt for stmt in node.lookup(name)[1] - # if hasattr(stmt, 'ass_type')] and - # not stmt.statement().parent_of(node)] - if not self.linter.is_message_enabled('undefined-loop-variable'): - return - astmts = [stmt for stmt in node.lookup(name)[1] - if hasattr(stmt, 'ass_type')] - # filter variables according their respective scope test is_statement - # and parent to avoid #74747. This is not a total fix, which would - # introduce a mechanism similar to special attribute lookup in - # modules. Also, in order to get correct inference in this case, the - # scope lookup rules would need to be changed to return the initial - # assignment (which does not exist in code per se) as well as any later - # modifications. - if not astmts or (astmts[0].is_statement or astmts[0].parent) \ - and astmts[0].statement().parent_of(node): - _astmts = [] - else: - _astmts = astmts[:1] - for i, stmt in enumerate(astmts[1:]): - if (astmts[i].statement().parent_of(stmt) - and not in_for_else_branch(astmts[i].statement(), stmt)): - continue - _astmts.append(stmt) - astmts = _astmts - if len(astmts) == 1: - assign = astmts[0].assign_type() - if (isinstance(assign, (astroid.For, astroid.Comprehension, - astroid.GeneratorExp)) - and assign.statement() is not node.statement()): - self.add_message('undefined-loop-variable', args=name, node=node) - - def _should_ignore_redefined_builtin(self, stmt): - if not isinstance(stmt, astroid.ImportFrom): - return False - return stmt.modname in self.config.redefining_builtins_modules - - @utils.check_messages('redefine-in-handler') - def visit_excepthandler(self, node): - for name in utils.get_all_elements(node.name): - clobbering, args = utils.clobber_in_except(name) - if clobbering: - self.add_message('redefine-in-handler', args=args, node=name) - - def visit_assignname(self, node): - if isinstance(node.assign_type(), astroid.AugAssign): - self.visit_name(node) - - def visit_delname(self, node): - self.visit_name(node) - - @staticmethod - def _defined_in_function_definition(node, frame): - in_annotation_or_default = False - if (isinstance(frame, astroid.FunctionDef) and - node.statement() is frame): - in_annotation_or_default = ( - ( - PY3K and (node in frame.args.annotations - or node in frame.args.kwonlyargs_annotations - or node is frame.args.varargannotation - or node is frame.args.kwargannotation) - ) - or - frame.args.parent_of(node) - ) - return in_annotation_or_default - - @staticmethod - def _next_to_consume(node, name, to_consume): - # mark the name as consumed if it's defined in this scope - found_node = to_consume.get(name) - if (found_node - and isinstance(node.parent, astroid.Assign) - and node.parent == found_node[0].parent): - lhs = found_node[0].parent.targets[0] - if lhs.name == name: # this name is defined in this very statement - found_node = None - return found_node - - @staticmethod - def _is_variable_violation(node, name, defnode, stmt, defstmt, - frame, defframe, base_scope_type, - recursive_klass): - maybee0601 = True - annotation_return = False - use_outer_definition = False - if frame is not defframe: - maybee0601 = _detect_global_scope(node, frame, defframe) - elif defframe.parent is None: - # we are at the module level, check the name is not - # defined in builtins - if name in defframe.scope_attrs or astroid.builtin_lookup(name)[1]: - maybee0601 = False - else: - # we are in a local scope, check the name is not - # defined in global or builtin scope - # skip this lookup if name is assigned later in function scope - forbid_lookup = isinstance(frame, astroid.FunctionDef) and _assigned_locally(node) - if not forbid_lookup and defframe.root().lookup(name)[1]: - maybee0601 = False - use_outer_definition = ( - stmt == defstmt - and not isinstance(defnode, astroid.node_classes.Comprehension) - ) - else: - # check if we have a nonlocal - if name in defframe.locals: - maybee0601 = not any(isinstance(child, astroid.Nonlocal) - and name in child.names - for child in defframe.get_children()) - - if (base_scope_type == 'lambda' and - isinstance(frame, astroid.ClassDef) - and name in frame.locals): - - # This rule verifies that if the definition node of the - # checked name is an Arguments node and if the name - # is used a default value in the arguments defaults - # and the actual definition of the variable label - # is happening before the Arguments definition. - # - # bar = None - # foo = lambda bar=bar: bar - # - # In this case, maybee0601 should be False, otherwise - # it should be True. - maybee0601 = not (isinstance(defnode, astroid.Arguments) and - node in defnode.defaults and - frame.locals[name][0].fromlineno < defstmt.fromlineno) - elif (isinstance(defframe, astroid.ClassDef) and - isinstance(frame, astroid.FunctionDef)): - # Special rule for function return annotations, - # which uses the same name as the class where - # the function lives. - if (PY3K and node is frame.returns and - defframe.parent_of(frame.returns)): - maybee0601 = annotation_return = True - - if (maybee0601 and defframe.name in defframe.locals and - defframe.locals[name][0].lineno < frame.lineno): - # Detect class assignments with the same - # name as the class. In this case, no warning - # should be raised. - maybee0601 = False - if isinstance(node.parent, astroid.Arguments): - maybee0601 = stmt.fromlineno <= defstmt.fromlineno - elif recursive_klass: - maybee0601 = True - else: - maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno - if maybee0601 and stmt.fromlineno == defstmt.fromlineno: - if (isinstance(defframe, astroid.FunctionDef) - and frame is defframe - and defframe.parent_of(node) - and stmt is not defstmt): - # Single statement function, with the statement on the - # same line as the function definition - maybee0601 = False - - return maybee0601, annotation_return, use_outer_definition - - def _ignore_class_scope(self, node, name, frame): - # Detect if we are in a local class scope, as an assignment. - # For example, the following is fair game. - # - # class A: - # b = 1 - # c = lambda b=b: b * b - # - # class B: - # tp = 1 - # def func(self, arg: tp): - # ... - # class C: - # tp = 2 - # def func(self, arg=tp): - # ... - - in_annotation_or_default = self._defined_in_function_definition( - node, frame) - if in_annotation_or_default: - frame_locals = frame.parent.scope().locals - else: - frame_locals = frame.locals - return not ((isinstance(frame, astroid.ClassDef) or - in_annotation_or_default) and - name in frame_locals) - - @utils.check_messages(*(MSGS.keys())) - def visit_name(self, node): - """check that a name is defined if the current scope and doesn't - redefine a built-in - """ - stmt = node.statement() - if stmt.fromlineno is None: - # name node from a astroid built from live code, skip - assert not stmt.root().file.endswith('.py') - return - name = node.name - frame = stmt.scope() - # if the name node is used as a function default argument's value or as - # a decorator, then start from the parent frame of the function instead - # of the function frame - and thus open an inner class scope - if (utils.is_func_default(node) or utils.is_func_decorator(node) - or utils.is_ancestor_name(frame, node)): - start_index = len(self._to_consume) - 2 - else: - start_index = len(self._to_consume) - 1 - # iterates through parent scopes, from the inner to the outer - base_scope_type = self._to_consume[start_index][-1] - # pylint: disable=too-many-nested-blocks; refactoring this block is a pain. - for i in range(start_index, -1, -1): - to_consume, consumed, scope_type = self._to_consume[i] - # if the current scope is a class scope but it's not the inner - # scope, ignore it. This prevents to access this scope instead of - # the globals one in function members when there are some common - # names. The only exception is when the starting scope is a - # comprehension and its direct outer scope is a class - if scope_type == 'class' and i != start_index and not ( - base_scope_type == 'comprehension' and i == start_index-1): - if self._ignore_class_scope(node, name, frame): - continue - - # the name has already been consumed, only check it's not a loop - # variable used outside the loop - if name in consumed: - defnode = utils.assign_parent(consumed[name][0]) - self._check_late_binding_closure(node, defnode) - self._loopvar_name(node, name) - break - found_node = self._next_to_consume(node, name, to_consume) - if found_node is None: - continue - # checks for use before assignment - defnode = utils.assign_parent(to_consume[name][0]) - if defnode is not None: - self._check_late_binding_closure(node, defnode) - defstmt = defnode.statement() - defframe = defstmt.frame() - # The class reuses itself in the class scope. - recursive_klass = (frame is defframe and - defframe.parent_of(node) and - isinstance(defframe, astroid.ClassDef) and - node.name == defframe.name) - - maybee0601, annotation_return, use_outer_definition = self._is_variable_violation( - node, name, defnode, stmt, defstmt, - frame, defframe, - base_scope_type, recursive_klass) - - if use_outer_definition: - continue - - if (maybee0601 - and not utils.is_defined_before(node) - and not astroid.are_exclusive(stmt, defstmt, ('NameError',))): - - # Used and defined in the same place, e.g `x += 1` and `del x` - defined_by_stmt = ( - defstmt is stmt - and isinstance(node, (astroid.DelName, astroid.AssignName)) - ) - if (recursive_klass - or defined_by_stmt - or annotation_return - or isinstance(defstmt, astroid.Delete)): - if not utils.node_ignores_exception(node, NameError): - self.add_message('undefined-variable', args=name, - node=node) - elif base_scope_type != 'lambda': - # E0601 may *not* occurs in lambda scope. - self.add_message('used-before-assignment', args=name, node=node) - elif base_scope_type == 'lambda': - # E0601 can occur in class-level scope in lambdas, as in - # the following example: - # class A: - # x = lambda attr: f + attr - # f = 42 - if isinstance(frame, astroid.ClassDef) and name in frame.locals: - if isinstance(node.parent, astroid.Arguments): - if stmt.fromlineno <= defstmt.fromlineno: - # Doing the following is fine: - # class A: - # x = 42 - # y = lambda attr=x: attr - self.add_message('used-before-assignment', - args=name, node=node) - else: - self.add_message('undefined-variable', - args=name, node=node) - elif scope_type == 'lambda': - self.add_message('undefined-variable', - node=node, args=name) - - consumed[name] = found_node - del to_consume[name] - # check it's not a loop variable used outside the loop - self._loopvar_name(node, name) - break - else: - # we have not found the name, if it isn't a builtin, that's an - # undefined name ! - if not (name in astroid.Module.scope_attrs or utils.is_builtin(name) - or name in self.config.additional_builtins): - if not utils.node_ignores_exception(node, NameError): - self.add_message('undefined-variable', args=name, node=node) - - @utils.check_messages('no-name-in-module') - def visit_import(self, node): - """check modules attribute accesses""" - if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node): - # No need to verify this, since ImportError is already - # handled by the client code. - return - - for name, _ in node.names: - parts = name.split('.') - try: - module = next(node.infer_name_module(parts[0])) - except astroid.ResolveError: - continue - self._check_module_attrs(node, module, parts[1:]) - - @utils.check_messages('no-name-in-module') - def visit_importfrom(self, node): - """check modules attribute accesses""" - if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node): - # No need to verify this, since ImportError is already - # handled by the client code. - return - - name_parts = node.modname.split('.') - try: - module = node.do_import_module(name_parts[0]) - except astroid.AstroidBuildingException: - return - module = self._check_module_attrs(node, module, name_parts[1:]) - if not module: - return - for name, _ in node.names: - if name == '*': - continue - self._check_module_attrs(node, module, name.split('.')) - - @utils.check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence') - def visit_assign(self, node): - """Check unbalanced tuple unpacking for assignments - and unpacking non-sequences. - """ - if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)): - return - - targets = node.targets[0].itered() - try: - infered = utils.safe_infer(node.value) - if infered is not None: - self._check_unpacking(infered, node, targets) - except astroid.InferenceError: - return - - def _check_unpacking(self, infered, node, targets): - """ Check for unbalanced tuple unpacking - and unpacking non sequences. - """ - if utils.is_inside_abstract_class(node): - return - if utils.is_comprehension(node): - return - if infered is astroid.YES: - return - if (isinstance(infered.parent, astroid.Arguments) and - isinstance(node.value, astroid.Name) and - node.value.name == infered.parent.vararg): - # Variable-length argument, we can't determine the length. - return - if isinstance(infered, (astroid.Tuple, astroid.List)): - # attempt to check unpacking is properly balanced - values = infered.itered() - if len(targets) != len(values): - # Check if we have starred nodes. - if any(isinstance(target, astroid.Starred) - for target in targets): - return - self.add_message('unbalanced-tuple-unpacking', node=node, - args=(_get_unpacking_extra_info(node, infered), - len(targets), - len(values))) - # attempt to check unpacking may be possible (ie RHS is iterable) - else: - if not utils.is_iterable(infered): - self.add_message('unpacking-non-sequence', node=node, - args=(_get_unpacking_extra_info(node, infered),)) - - - def _check_module_attrs(self, node, module, module_names): - """check that module_names (list of string) are accessible through the - given module - if the latest access name corresponds to a module, return it - """ - assert isinstance(module, astroid.Module), module - while module_names: - name = module_names.pop(0) - if name == '__dict__': - module = None - break - try: - module = next(module.getattr(name)[0].infer()) - if module is astroid.YES: - return None - except astroid.NotFoundError: - if module.name in self._ignored_modules: - return None - self.add_message('no-name-in-module', - args=(name, module.name), node=node) - return None - except astroid.InferenceError: - return None - if module_names: - # FIXME: other message if name is not the latest part of - # module_names ? - modname = module.name if module else '__dict__' - self.add_message('no-name-in-module', node=node, - args=('.'.join(module_names), modname)) - return None - if isinstance(module, astroid.Module): - return module - return None - - -class VariablesChecker3k(VariablesChecker): - '''Modified variables checker for 3k''' - # listcomp have now also their scope - - def visit_listcomp(self, node): - """visit dictcomp: update consumption analysis variable - """ - self._to_consume.append((copy.copy(node.locals), {}, 'comprehension')) - - def leave_listcomp(self, _): - """leave dictcomp: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def leave_functiondef(self, node): - self._check_metaclasses(node) - super(VariablesChecker3k, self).leave_functiondef(node) - - def leave_module(self, node): - self._check_metaclasses(node) - super(VariablesChecker3k, self).leave_module(node) - - def _check_metaclasses(self, node): - """ Update consumption analysis for metaclasses. """ - consumed = [] # [(scope_locals, consumed_key)] - - for child_node in node.get_children(): - if isinstance(child_node, astroid.ClassDef): - consumed.extend(self._check_classdef_metaclasses(child_node, node)) - - # Pop the consumed items, in order to avoid having - # unused-import and unused-variable false positives - for scope_locals, name in consumed: - scope_locals.pop(name, None) - - def _check_classdef_metaclasses(self, klass, parent_node): - if not klass._metaclass: - # Skip if this class doesn't use explicitly a metaclass, but inherits it from ancestors - return [] - - consumed = [] # [(scope_locals, consumed_key)] - metaclass = klass.metaclass() - - name = None - if isinstance(klass._metaclass, astroid.Name): - name = klass._metaclass.name - elif metaclass: - name = metaclass.root().name - - found = None - if name: - # check enclosing scopes starting from most local - for scope_locals, _, _ in self._to_consume[::-1]: - found = scope_locals.get(name) - if found: - consumed.append((scope_locals, name)) - break - - if found is None and not metaclass: - name = None - if isinstance(klass._metaclass, astroid.Name): - name = klass._metaclass.name - elif isinstance(klass._metaclass, astroid.Attribute): - name = klass._metaclass.as_string() - - if name is not None: - if not (name in astroid.Module.scope_attrs or - utils.is_builtin(name) or - name in self.config.additional_builtins or - name in parent_node.locals): - self.add_message('undefined-variable', - node=klass, - args=(name,)) - - return consumed - - -if sys.version_info >= (3, 0): - VariablesChecker = VariablesChecker3k - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(VariablesChecker(linter)) diff --git a/pymode/libs/pylint/config.py b/pymode/libs/pylint/config.py deleted file mode 100644 index 6922949f..00000000 --- a/pymode/libs/pylint/config.py +++ /dev/null @@ -1,831 +0,0 @@ -# Copyright (c) 2006-2010, 2012-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015 Aru Sahni - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""utilities for Pylint configuration : - -* pylintrc -* pylint.d (PYLINTHOME) -""" -from __future__ import print_function - -# TODO(cpopa): this module contains the logic for the -# configuration parser and for the command line parser, -# but it's really coupled to optparse's internals. -# The code was copied almost verbatim from logilab.common, -# in order to not depend on it anymore and it will definitely -# need a cleanup. It could be completely reengineered as well. - -import contextlib -import collections -import copy -import io -import optparse -import os -import pickle -import re -import sys -import time - -import configparser -from six.moves import range - -from pylint import utils - - -USER_HOME = os.path.expanduser('~') -if 'PYLINTHOME' in os.environ: - PYLINT_HOME = os.environ['PYLINTHOME'] - if USER_HOME == '~': - USER_HOME = os.path.dirname(PYLINT_HOME) -elif USER_HOME == '~': - PYLINT_HOME = ".pylint.d" -else: - PYLINT_HOME = os.path.join(USER_HOME, '.pylint.d') - - -def _get_pdata_path(base_name, recurs): - base_name = base_name.replace(os.sep, '_') - return os.path.join(PYLINT_HOME, "%s%s%s"%(base_name, recurs, '.stats')) - - -def load_results(base): - data_file = _get_pdata_path(base, 1) - try: - with open(data_file, _PICK_LOAD) as stream: - return pickle.load(stream) - except Exception: # pylint: disable=broad-except - return {} - -if sys.version_info < (3, 0): - _PICK_DUMP, _PICK_LOAD = 'w', 'r' -else: - _PICK_DUMP, _PICK_LOAD = 'wb', 'rb' - -def save_results(results, base): - if not os.path.exists(PYLINT_HOME): - try: - os.mkdir(PYLINT_HOME) - except OSError: - print('Unable to create directory %s' % PYLINT_HOME, file=sys.stderr) - data_file = _get_pdata_path(base, 1) - try: - with open(data_file, _PICK_DUMP) as stream: - pickle.dump(results, stream) - except (IOError, OSError) as ex: - print('Unable to create file %s: %s' % (data_file, ex), file=sys.stderr) - - -def find_pylintrc(): - """search the pylint rc file and return its path if it find it, else None - """ - # is there a pylint rc file in the current directory ? - if os.path.exists('pylintrc'): - return os.path.abspath('pylintrc') - if os.path.exists('.pylintrc'): - return os.path.abspath('.pylintrc') - if os.path.isfile('__init__.py'): - curdir = os.path.abspath(os.getcwd()) - while os.path.isfile(os.path.join(curdir, '__init__.py')): - curdir = os.path.abspath(os.path.join(curdir, '..')) - if os.path.isfile(os.path.join(curdir, 'pylintrc')): - return os.path.join(curdir, 'pylintrc') - if os.path.isfile(os.path.join(curdir, '.pylintrc')): - return os.path.join(curdir, '.pylintrc') - if 'PYLINTRC' in os.environ and os.path.exists(os.environ['PYLINTRC']): - pylintrc = os.environ['PYLINTRC'] - else: - user_home = os.path.expanduser('~') - if user_home == '~' or user_home == '/root': - pylintrc = ".pylintrc" - else: - pylintrc = os.path.join(user_home, '.pylintrc') - if not os.path.isfile(pylintrc): - pylintrc = os.path.join(user_home, '.config', 'pylintrc') - if not os.path.isfile(pylintrc): - if os.path.isfile('/etc/pylintrc'): - pylintrc = '/etc/pylintrc' - else: - pylintrc = None - return pylintrc - -PYLINTRC = find_pylintrc() - -ENV_HELP = ''' -The following environment variables are used: - * PYLINTHOME - Path to the directory where the persistent for the run will be stored. If -not found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working -directory). - * PYLINTRC - Path to the configuration file. See the documentation for the method used -to search for configuration file. -''' % globals() - - -class UnsupportedAction(Exception): - """raised by set_option when it doesn't know what to do for an action""" - - -def _multiple_choice_validator(choices, name, value): - values = utils._check_csv(value) - for csv_value in values: - if csv_value not in choices: - msg = "option %s: invalid value: %r, should be in %s" - raise optparse.OptionValueError(msg % (name, csv_value, choices)) - return values - - -def _choice_validator(choices, name, value): - if value not in choices: - msg = "option %s: invalid value: %r, should be in %s" - raise optparse.OptionValueError(msg % (name, value, choices)) - return value - -# pylint: disable=unused-argument -def _csv_validator(_, name, value): - return utils._check_csv(value) - - -# pylint: disable=unused-argument -def _regexp_validator(_, name, value): - if hasattr(value, 'pattern'): - return value - return re.compile(value) - -# pylint: disable=unused-argument -def _regexp_csv_validator(_, name, value): - return [_regexp_validator(_, name, val) for val in _csv_validator(_, name, value)] - -def _yn_validator(opt, _, value): - if isinstance(value, int): - return bool(value) - if value in ('y', 'yes'): - return True - if value in ('n', 'no'): - return False - msg = "option %s: invalid yn value %r, should be in (y, yes, n, no)" - raise optparse.OptionValueError(msg % (opt, value)) - - -def _non_empty_string_validator(opt, _, value): - if not value: - msg = "indent string can't be empty." - raise optparse.OptionValueError(msg) - return utils._unquote(value) - - -VALIDATORS = { - 'string': utils._unquote, - 'int': int, - 'regexp': re.compile, - 'regexp_csv': _regexp_csv_validator, - 'csv': _csv_validator, - 'yn': _yn_validator, - 'choice': lambda opt, name, value: _choice_validator(opt['choices'], name, value), - 'multiple_choice': lambda opt, name, value: _multiple_choice_validator(opt['choices'], - name, value), - 'non_empty_string': _non_empty_string_validator, -} - -def _call_validator(opttype, optdict, option, value): - if opttype not in VALIDATORS: - raise Exception('Unsupported type "%s"' % opttype) - try: - return VALIDATORS[opttype](optdict, option, value) - except TypeError: - try: - return VALIDATORS[opttype](value) - except Exception: - raise optparse.OptionValueError('%s value (%r) should be of type %s' % - (option, value, opttype)) - - -def _validate(value, optdict, name=''): - """return a validated value for an option according to its type - - optional argument name is only used for error message formatting - """ - try: - _type = optdict['type'] - except KeyError: - # FIXME - return value - return _call_validator(_type, optdict, name, value) - - -def _level_options(group, outputlevel): - return [option for option in group.option_list - if (getattr(option, 'level', 0) or 0) <= outputlevel - and option.help is not optparse.SUPPRESS_HELP] - - -def _expand_default(self, option): - """Patch OptionParser.expand_default with custom behaviour - - This will handle defaults to avoid overriding values in the - configuration file. - """ - if self.parser is None or not self.default_tag: - return option.help - optname = option._long_opts[0][2:] - try: - provider = self.parser.options_manager._all_options[optname] - except KeyError: - value = None - else: - optdict = provider.get_option_def(optname) - optname = provider.option_attrname(optname, optdict) - value = getattr(provider.config, optname, optdict) - value = utils._format_option_value(optdict, value) - if value is optparse.NO_DEFAULT or not value: - value = self.NO_DEFAULT_VALUE - return option.help.replace(self.default_tag, str(value)) - - -@contextlib.contextmanager -def _patch_optparse(): - orig_default = optparse.HelpFormatter - try: - optparse.HelpFormatter.expand_default = _expand_default - yield - finally: - optparse.HelpFormatter.expand_default = orig_default - - -def _multiple_choices_validating_option(opt, name, value): - return _multiple_choice_validator(opt.choices, name, value) - - -class Option(optparse.Option): - TYPES = optparse.Option.TYPES + ('regexp', 'regexp_csv', 'csv', 'yn', - 'multiple_choice', - 'non_empty_string') - ATTRS = optparse.Option.ATTRS + ['hide', 'level'] - TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER) - TYPE_CHECKER['regexp'] = _regexp_validator - TYPE_CHECKER['regexp_csv'] = _regexp_csv_validator - TYPE_CHECKER['csv'] = _csv_validator - TYPE_CHECKER['yn'] = _yn_validator - TYPE_CHECKER['multiple_choice'] = _multiple_choices_validating_option - TYPE_CHECKER['non_empty_string'] = _non_empty_string_validator - - def __init__(self, *opts, **attrs): - optparse.Option.__init__(self, *opts, **attrs) - if hasattr(self, "hide") and self.hide: - self.help = optparse.SUPPRESS_HELP - - def _check_choice(self): - if self.type in ("choice", "multiple_choice"): - if self.choices is None: - raise optparse.OptionError( - "must supply a list of choices for type 'choice'", self) - elif not isinstance(self.choices, (tuple, list)): - raise optparse.OptionError( - "choices must be a list of strings ('%s' supplied)" - % str(type(self.choices)).split("'")[1], self) - elif self.choices is not None: - raise optparse.OptionError( - "must not supply choices for type %r" % self.type, self) - optparse.Option.CHECK_METHODS[2] = _check_choice - - def process(self, opt, value, values, parser): - # First, convert the value(s) to the right type. Howl if any - # value(s) are bogus. - value = self.convert_value(opt, value) - if self.type == 'named': - existent = getattr(values, self.dest) - if existent: - existent.update(value) - value = existent - # And then take whatever action is expected of us. - # This is a separate method to make life easier for - # subclasses to add new actions. - return self.take_action( - self.action, self.dest, opt, value, values, parser) - - -class OptionParser(optparse.OptionParser): - - def __init__(self, option_class=Option, *args, **kwargs): - optparse.OptionParser.__init__(self, option_class=Option, *args, **kwargs) - - def format_option_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - outputlevel = getattr(formatter, 'output_level', 0) - formatter.store_option_strings(self) - result = [] - result.append(formatter.format_heading("Options")) - formatter.indent() - if self.option_list: - result.append(optparse.OptionContainer.format_option_help(self, formatter)) - result.append("\n") - for group in self.option_groups: - if group.level <= outputlevel and ( - group.description or _level_options(group, outputlevel)): - result.append(group.format_help(formatter)) - result.append("\n") - formatter.dedent() - # Drop the last "\n", or the header if no options or option groups: - return "".join(result[:-1]) - - def _match_long_opt(self, opt): - """Disable abbreviations.""" - if opt not in self._long_opt: - raise optparse.BadOptionError(opt) - return opt - - -# pylint: disable=abstract-method; by design? -class _ManHelpFormatter(optparse.HelpFormatter): - - def __init__(self, indent_increment=0, max_help_position=24, - width=79, short_first=0): - optparse.HelpFormatter.__init__( - self, indent_increment, max_help_position, width, short_first) - - def format_heading(self, heading): - return '.SH %s\n' % heading.upper() - - def format_description(self, description): - return description - - def format_option(self, option): - try: - optstring = option.option_strings - except AttributeError: - optstring = self.format_option_strings(option) - if option.help: - help_text = self.expand_default(option) - help = ' '.join([l.strip() for l in help_text.splitlines()]) - else: - help = '' - return '''.IP "%s" -%s -''' % (optstring, help) - - def format_head(self, optparser, pkginfo, section=1): - long_desc = "" - try: - pgm = optparser._get_prog_name() - except AttributeError: - # py >= 2.4.X (dunno which X exactly, at least 2) - pgm = optparser.get_prog_name() - short_desc = self.format_short_description(pgm, pkginfo.description) - if hasattr(pkginfo, "long_desc"): - long_desc = self.format_long_description(pgm, pkginfo.long_desc) - return '%s\n%s\n%s\n%s' % (self.format_title(pgm, section), - short_desc, self.format_synopsis(pgm), - long_desc) - - @staticmethod - def format_title(pgm, section): - date = '-'.join(str(num) for num in time.localtime()[:3]) - return '.TH %s %s "%s" %s' % (pgm, section, date, pgm) - - @staticmethod - def format_short_description(pgm, short_desc): - return '''.SH NAME -.B %s -\\- %s -''' % (pgm, short_desc.strip()) - - @staticmethod - def format_synopsis(pgm): - return '''.SH SYNOPSIS -.B %s -[ -.I OPTIONS -] [ -.I -] -''' % pgm - - @staticmethod - def format_long_description(pgm, long_desc): - long_desc = '\n'.join(line.lstrip() - for line in long_desc.splitlines()) - long_desc = long_desc.replace('\n.\n', '\n\n') - if long_desc.lower().startswith(pgm): - long_desc = long_desc[len(pgm):] - return '''.SH DESCRIPTION -.B %s -%s -''' % (pgm, long_desc.strip()) - - @staticmethod - def format_tail(pkginfo): - tail = '''.SH SEE ALSO -/usr/share/doc/pythonX.Y-%s/ - -.SH BUGS -Please report bugs on the project\'s mailing list: -%s - -.SH AUTHOR -%s <%s> -''' % (getattr(pkginfo, 'debian_name', pkginfo.modname), - pkginfo.mailinglist, pkginfo.author, pkginfo.author_email) - - if hasattr(pkginfo, "copyright"): - tail += ''' -.SH COPYRIGHT -%s -''' % pkginfo.copyright - - return tail - - -class OptionsManagerMixIn(object): - """Handle configuration from both a configuration file and command line options""" - - def __init__(self, usage, config_file=None, version=None, quiet=0): - self.config_file = config_file - self.reset_parsers(usage, version=version) - # list of registered options providers - self.options_providers = [] - # dictionary associating option name to checker - self._all_options = collections.OrderedDict() - self._short_options = {} - self._nocallback_options = {} - self._mygroups = {} - # verbosity - self.quiet = quiet - self._maxlevel = 0 - - def reset_parsers(self, usage='', version=None): - # configuration file parser - self.cfgfile_parser = configparser.ConfigParser(inline_comment_prefixes=('#', ';')) - # command line parser - self.cmdline_parser = OptionParser(usage=usage, version=version) - self.cmdline_parser.options_manager = self - self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS) - - def register_options_provider(self, provider, own_group=True): - """register an options provider""" - assert provider.priority <= 0, "provider's priority can't be >= 0" - for i in range(len(self.options_providers)): - if provider.priority > self.options_providers[i].priority: - self.options_providers.insert(i, provider) - break - else: - self.options_providers.append(provider) - non_group_spec_options = [option for option in provider.options - if 'group' not in option[1]] - groups = getattr(provider, 'option_groups', ()) - if own_group and non_group_spec_options: - self.add_option_group(provider.name.upper(), provider.__doc__, - non_group_spec_options, provider) - else: - for opt, optdict in non_group_spec_options: - self.add_optik_option(provider, self.cmdline_parser, opt, optdict) - for gname, gdoc in groups: - gname = gname.upper() - goptions = [option for option in provider.options - if option[1].get('group', '').upper() == gname] - self.add_option_group(gname, gdoc, goptions, provider) - - def add_option_group(self, group_name, _, options, provider): - # add option group to the command line parser - if group_name in self._mygroups: - group = self._mygroups[group_name] - else: - group = optparse.OptionGroup(self.cmdline_parser, - title=group_name.capitalize()) - self.cmdline_parser.add_option_group(group) - group.level = provider.level - self._mygroups[group_name] = group - # add section to the config file - if group_name != "DEFAULT" and \ - group_name not in self.cfgfile_parser._sections: - self.cfgfile_parser.add_section(group_name) - # add provider's specific options - for opt, optdict in options: - self.add_optik_option(provider, group, opt, optdict) - - def add_optik_option(self, provider, optikcontainer, opt, optdict): - args, optdict = self.optik_option(provider, opt, optdict) - option = optikcontainer.add_option(*args, **optdict) - self._all_options[opt] = provider - self._maxlevel = max(self._maxlevel, option.level or 0) - - def optik_option(self, provider, opt, optdict): - """get our personal option definition and return a suitable form for - use with optik/optparse - """ - optdict = copy.copy(optdict) - if 'action' in optdict: - self._nocallback_options[provider] = opt - else: - optdict['action'] = 'callback' - optdict['callback'] = self.cb_set_provider_option - # default is handled here and *must not* be given to optik if you - # want the whole machinery to work - if 'default' in optdict: - if ('help' in optdict - and optdict.get('default') is not None - and optdict['action'] not in ('store_true', 'store_false')): - optdict['help'] += ' [current: %default]' - del optdict['default'] - args = ['--' + str(opt)] - if 'short' in optdict: - self._short_options[optdict['short']] = opt - args.append('-' + optdict['short']) - del optdict['short'] - # cleanup option definition dict before giving it to optik - for key in list(optdict.keys()): - if key not in self._optik_option_attrs: - optdict.pop(key) - return args, optdict - - def cb_set_provider_option(self, option, opt, value, parser): - """optik callback for option setting""" - if opt.startswith('--'): - # remove -- on long option - opt = opt[2:] - else: - # short option, get its long equivalent - opt = self._short_options[opt[1:]] - # trick since we can't set action='store_true' on options - if value is None: - value = 1 - self.global_set_option(opt, value) - - def global_set_option(self, opt, value): - """set option on the correct option provider""" - self._all_options[opt].set_option(opt, value) - - def generate_config(self, stream=None, skipsections=(), encoding=None): - """write a configuration file according to the current configuration - into the given stream or stdout - """ - options_by_section = {} - sections = [] - for provider in self.options_providers: - for section, options in provider.options_by_section(): - if section is None: - section = provider.name - if section in skipsections: - continue - options = [(n, d, v) for (n, d, v) in options - if d.get('type') is not None - and not d.get('deprecated')] - if not options: - continue - if section not in sections: - sections.append(section) - alloptions = options_by_section.setdefault(section, []) - alloptions += options - stream = stream or sys.stdout - encoding = utils._get_encoding(encoding, stream) - printed = False - for section in sections: - if printed: - print('\n', file=stream) - utils.format_section(stream, section.upper(), - sorted(options_by_section[section]), - encoding) - printed = True - - def generate_manpage(self, pkginfo, section=1, stream=None): - with _patch_optparse(): - _generate_manpage(self.cmdline_parser, pkginfo, - section, stream=stream or sys.stdout, - level=self._maxlevel) - - def load_provider_defaults(self): - """initialize configuration using default values""" - for provider in self.options_providers: - provider.load_defaults() - - def read_config_file(self, config_file=None): - """read the configuration file but do not load it (i.e. dispatching - values to each options provider) - """ - helplevel = 1 - while helplevel <= self._maxlevel: - opt = '-'.join(['long'] * helplevel) + '-help' - if opt in self._all_options: - break # already processed - # pylint: disable=unused-argument - def helpfunc(option, opt, val, p, level=helplevel): - print(self.help(level)) - sys.exit(0) - helpmsg = '%s verbose help.' % ' '.join(['more'] * helplevel) - optdict = {'action': 'callback', 'callback': helpfunc, - 'help': helpmsg} - provider = self.options_providers[0] - self.add_optik_option(provider, self.cmdline_parser, opt, optdict) - provider.options += ((opt, optdict),) - helplevel += 1 - if config_file is None: - config_file = self.config_file - if config_file is not None: - config_file = os.path.expanduser(config_file) - if config_file and os.path.exists(config_file): - parser = self.cfgfile_parser - - # Use this encoding in order to strip the BOM marker, if any. - with io.open(config_file, 'r', encoding='utf_8_sig') as fp: - # pylint: disable=deprecated-method - parser.readfp(fp) - - # normalize sections'title - for sect, values in list(parser._sections.items()): - if not sect.isupper() and values: - parser._sections[sect.upper()] = values - elif not self.quiet: - msg = 'No config file found, using default configuration' - print(msg, file=sys.stderr) - return - - def load_config_file(self): - """dispatch values previously read from a configuration file to each - options provider) - """ - parser = self.cfgfile_parser - for section in parser.sections(): - for option, value in parser.items(section): - try: - self.global_set_option(option, value) - except (KeyError, optparse.OptionError): - # TODO handle here undeclared options appearing in the config file - continue - - def load_configuration(self, **kwargs): - """override configuration according to given parameters""" - return self.load_configuration_from_config(kwargs) - - def load_configuration_from_config(self, config): - for opt, opt_value in config.items(): - opt = opt.replace('_', '-') - provider = self._all_options[opt] - provider.set_option(opt, opt_value) - - def load_command_line_configuration(self, args=None): - """Override configuration according to command line parameters - - return additional arguments - """ - with _patch_optparse(): - if args is None: - args = sys.argv[1:] - else: - args = list(args) - (options, args) = self.cmdline_parser.parse_args(args=args) - for provider in self._nocallback_options: - config = provider.config - for attr in config.__dict__.keys(): - value = getattr(options, attr, None) - if value is None: - continue - setattr(config, attr, value) - return args - - def add_help_section(self, title, description, level=0): - """add a dummy option section for help purpose """ - group = optparse.OptionGroup(self.cmdline_parser, - title=title.capitalize(), - description=description) - group.level = level - self._maxlevel = max(self._maxlevel, level) - self.cmdline_parser.add_option_group(group) - - def help(self, level=0): - """return the usage string for available options """ - self.cmdline_parser.formatter.output_level = level - with _patch_optparse(): - return self.cmdline_parser.format_help() - - -class OptionsProviderMixIn(object): - """Mixin to provide options to an OptionsManager""" - - # those attributes should be overridden - priority = -1 - name = 'default' - options = () - level = 0 - - def __init__(self): - self.config = optparse.Values() - self.load_defaults() - - def load_defaults(self): - """initialize the provider using default values""" - for opt, optdict in self.options: - action = optdict.get('action') - if action != 'callback': - # callback action have no default - if optdict is None: - optdict = self.get_option_def(opt) - default = optdict.get('default') - self.set_option(opt, default, action, optdict) - - def option_attrname(self, opt, optdict=None): - """get the config attribute corresponding to opt""" - if optdict is None: - optdict = self.get_option_def(opt) - return optdict.get('dest', opt.replace('-', '_')) - - def option_value(self, opt): - """get the current value for the given option""" - return getattr(self.config, self.option_attrname(opt), None) - - def set_option(self, optname, value, action=None, optdict=None): - """method called to set an option (registered in the options list)""" - if optdict is None: - optdict = self.get_option_def(optname) - if value is not None: - value = _validate(value, optdict, optname) - if action is None: - action = optdict.get('action', 'store') - if action == 'store': - setattr(self.config, self.option_attrname(optname, optdict), value) - elif action in ('store_true', 'count'): - setattr(self.config, self.option_attrname(optname, optdict), 0) - elif action == 'store_false': - setattr(self.config, self.option_attrname(optname, optdict), 1) - elif action == 'append': - optname = self.option_attrname(optname, optdict) - _list = getattr(self.config, optname, None) - if _list is None: - if isinstance(value, (list, tuple)): - _list = value - elif value is not None: - _list = [] - _list.append(value) - setattr(self.config, optname, _list) - elif isinstance(_list, tuple): - setattr(self.config, optname, _list + (value,)) - else: - _list.append(value) - elif action == 'callback': - optdict['callback'](None, optname, value, None) - else: - raise UnsupportedAction(action) - - def get_option_def(self, opt): - """return the dictionary defining an option given its name""" - assert self.options - for option in self.options: - if option[0] == opt: - return option[1] - raise optparse.OptionError('no such option %s in section %r' - % (opt, self.name), opt) - - def options_by_section(self): - """return an iterator on options grouped by section - - (section, [list of (optname, optdict, optvalue)]) - """ - sections = {} - for optname, optdict in self.options: - sections.setdefault(optdict.get('group'), []).append( - (optname, optdict, self.option_value(optname))) - if None in sections: - yield None, sections.pop(None) - for section, options in sorted(sections.items()): - yield section.upper(), options - - def options_and_values(self, options=None): - if options is None: - options = self.options - for optname, optdict in options: - yield (optname, optdict, self.option_value(optname)) - - -class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn): - """basic mixin for simple configurations which don't need the - manager / providers model - """ - def __init__(self, *args, **kwargs): - if not args: - kwargs.setdefault('usage', '') - kwargs.setdefault('quiet', 1) - OptionsManagerMixIn.__init__(self, *args, **kwargs) - OptionsProviderMixIn.__init__(self) - if not getattr(self, 'option_groups', None): - self.option_groups = [] - for _, optdict in self.options: - try: - gdef = (optdict['group'].upper(), '') - except KeyError: - continue - if gdef not in self.option_groups: - self.option_groups.append(gdef) - self.register_options_provider(self, own_group=False) - - -def _generate_manpage(optparser, pkginfo, section=1, - stream=sys.stdout, level=0): - formatter = _ManHelpFormatter() - formatter.output_level = level - formatter.parser = optparser - print(formatter.format_head(optparser, pkginfo, section), file=stream) - print(optparser.format_option_help(formatter), file=stream) - print(formatter.format_tail(pkginfo), file=stream) diff --git a/pymode/libs/pylint/epylint.py b/pymode/libs/pylint/epylint.py deleted file mode 100644 index 0b714fb4..00000000 --- a/pymode/libs/pylint/epylint.py +++ /dev/null @@ -1,175 +0,0 @@ -# -*- coding: utf-8; -# mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -# -*- vim:fenc=utf-8:ft=python:et:sw=4:ts=4:sts=4 - -# Copyright (c) 2008-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Manuel Vázquez Acosta -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Emacs and Flymake compatible Pylint. - -This script is for integration with emacs and is compatible with flymake mode. - -epylint walks out of python packages before invoking pylint. This avoids -reporting import errors that occur when a module within a package uses the -absolute import path to get another module within this package. - -For example: - - Suppose a package is structured as - - a/__init__.py - a/b/x.py - a/c/y.py - - - Then if y.py imports x as "from a.b import x" the following produces pylint - errors - - cd a/c; pylint y.py - - - The following obviously doesn't - - pylint a/c/y.py - - - As this script will be invoked by emacs within the directory of the file - we are checking we need to go out of it to avoid these false positives. - - -You may also use py_run to run pylint with desired options and get back (or not) -its output. -""" -from __future__ import print_function - -import os -import os.path as osp -import sys -import shlex -from subprocess import Popen, PIPE - -import six - - -def _get_env(): - '''Extracts the environment PYTHONPATH and appends the current sys.path to - those.''' - env = dict(os.environ) - env['PYTHONPATH'] = os.pathsep.join(sys.path) - return env - -def lint(filename, options=None): - """Pylint the given file. - - When run from emacs we will be in the directory of a file, and passed its - filename. If this file is part of a package and is trying to import other - modules from within its own package or another package rooted in a directory - below it, pylint will classify it as a failed import. - - To get around this, we traverse down the directory tree to find the root of - the package this module is in. We then invoke pylint from this directory. - - Finally, we must correct the filenames in the output generated by pylint so - Emacs doesn't become confused (it will expect just the original filename, - while pylint may extend it with extra directories if we've traversed down - the tree) - """ - # traverse downwards until we are out of a python package - full_path = osp.abspath(filename) - parent_path = osp.dirname(full_path) - child_path = osp.basename(full_path) - - while parent_path != "/" and osp.exists(osp.join(parent_path, '__init__.py')): - child_path = osp.join(osp.basename(parent_path), child_path) - parent_path = osp.dirname(parent_path) - - # Start pylint - # Ensure we use the python and pylint associated with the running epylint - run_cmd = "import sys; from pylint.lint import Run; Run(sys.argv[1:])" - options = options or ['--disable=C,R,I'] - cmd = [sys.executable, "-c", run_cmd] + [ - '--msg-template', '{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}', - '-r', 'n', child_path] + options - process = Popen(cmd, stdout=PIPE, cwd=parent_path, env=_get_env(), - universal_newlines=True) - - for line in process.stdout: - # remove pylintrc warning - if line.startswith("No config file found"): - continue - - # modify the file name thats output to reverse the path traversal we made - parts = line.split(":") - if parts and parts[0] == child_path: - line = ":".join([filename] + parts[1:]) - print(line, end=' ') - - process.wait() - return process.returncode - - -def py_run(command_options='', return_std=False, stdout=None, stderr=None): - """Run pylint from python - - ``command_options`` is a string containing ``pylint`` command line options; - ``return_std`` (boolean) indicates return of created standard output - and error (see below); - ``stdout`` and ``stderr`` are 'file-like' objects in which standard output - could be written. - - Calling agent is responsible for stdout/err management (creation, close). - Default standard output and error are those from sys, - or standalone ones (``subprocess.PIPE``) are used - if they are not set and ``return_std``. - - If ``return_std`` is set to ``True``, this function returns a 2-uple - containing standard output and error related to created process, - as follows: ``(stdout, stderr)``. - - A trivial usage could be as follows: - >>> py_run( '--version') - No config file found, using default configuration - pylint 0.18.1, - ... - - To silently run Pylint on a module, and get its standard output and error: - >>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True) - """ - # Create command line to call pylint - epylint_part = [sys.executable, "-c", "from pylint import epylint;epylint.Run()"] - options = shlex.split(command_options) - cli = epylint_part + options - - # Providing standard output and/or error if not set - if stdout is None: - if return_std: - stdout = PIPE - else: - stdout = sys.stdout - if stderr is None: - if return_std: - stderr = PIPE - else: - stderr = sys.stderr - # Call pylint in a subprocess - process = Popen(cli, shell=False, stdout=stdout, stderr=stderr, - env=_get_env(), universal_newlines=True) - proc_stdout, proc_stderr = process.communicate() - # Return standard output and error - if return_std: - return six.moves.StringIO(proc_stdout), six.moves.StringIO(proc_stderr) - - -def Run(): - if len(sys.argv) == 1: - print("Usage: %s [options]" % sys.argv[0]) - sys.exit(1) - elif not osp.exists(sys.argv[1]): - print("%s does not exist" % sys.argv[1]) - sys.exit(1) - else: - sys.exit(lint(sys.argv[1], sys.argv[2:])) - - -if __name__ == '__main__': - Run() diff --git a/pymode/libs/pylint/exceptions.py b/pymode/libs/pylint/exceptions.py deleted file mode 100644 index 429379d9..00000000 --- a/pymode/libs/pylint/exceptions.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2016 Glenn Matthews -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Exception classes raised by various operations within pylint.""" - - -class InvalidMessageError(Exception): - """raised when a message creation, registration or addition is rejected""" - -class UnknownMessageError(Exception): - """raised when a unregistered message id is encountered""" - -class EmptyReportError(Exception): - """raised when a report is empty and so should not be displayed""" diff --git a/pymode/libs/pylint/extensions/_check_docs_utils.py b/pymode/libs/pylint/extensions/_check_docs_utils.py deleted file mode 100644 index 920e02f3..00000000 --- a/pymode/libs/pylint/extensions/_check_docs_utils.py +++ /dev/null @@ -1,580 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2016 Ashley Whetter -# Copyright (c) 2016 Moisés López -# Copyright (c) 2016 Glenn Matthews -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Utility methods for docstring checking.""" - -from __future__ import absolute_import, print_function - -import re - -import astroid - -from pylint.checkers.utils import ( - inherit_from_std_ex, - node_ignores_exception, - safe_infer, -) - - -def space_indentation(s): - """The number of leading spaces in a string - - :param str s: input string - - :rtype: int - :return: number of leading spaces - """ - return len(s) - len(s.lstrip(' ')) - - -def returns_something(return_node): - """Check if a return node returns a value other than None. - - :param return_node: The return node to check. - :type return_node: astroid.Return - - :rtype: bool - :return: True if the return node returns a value other than None, - False otherwise. - """ - returns = return_node.value - - if returns is None: - return False - - return not (isinstance(returns, astroid.Const) and returns.value is None) - - -def possible_exc_types(node): - """ - Gets all of the possible raised exception types for the given raise node. - - .. note:: - - Caught exception types are ignored. - - - :param node: The raise node to find exception types for. - :type node: astroid.node_classes.NodeNG - - :returns: A list of exception types possibly raised by :param:`node`. - :rtype: list(str) - """ - excs = [] - if isinstance(node.exc, astroid.Name): - inferred = safe_infer(node.exc) - if inferred: - excs = [inferred.name] - elif (isinstance(node.exc, astroid.Call) and - isinstance(node.exc.func, astroid.Name)): - target = safe_infer(node.exc.func) - if isinstance(target, astroid.ClassDef): - excs = [target.name] - elif isinstance(target, astroid.FunctionDef): - for ret in target.nodes_of_class(astroid.Return): - if ret.frame() != target: - # return from inner function - ignore it - continue - - val = safe_infer(ret.value) - if (val and isinstance(val, (astroid.Instance, astroid.ClassDef)) - and inherit_from_std_ex(val)): - excs.append(val.name) - elif node.exc is None: - handler = node.parent - while handler and not isinstance(handler, astroid.ExceptHandler): - handler = handler.parent - - if handler and handler.type: - inferred_excs = astroid.unpack_infer(handler.type) - excs = (exc.name for exc in inferred_excs - if exc is not astroid.Uninferable) - - - try: - return set(exc for exc in excs if not node_ignores_exception(node, exc)) - except astroid.InferenceError: - return () - - -def docstringify(docstring): - for docstring_type in [SphinxDocstring, EpytextDocstring, - GoogleDocstring, NumpyDocstring]: - instance = docstring_type(docstring) - if instance.is_valid(): - return instance - - return Docstring(docstring) - - -class Docstring(object): - re_for_parameters_see = re.compile(r""" - For\s+the\s+(other)?\s*parameters\s*,\s+see - """, re.X | re.S) - - supports_yields = None - """True if the docstring supports a "yield" section. - - False if the docstring uses the returns section to document generators. - """ - - # These methods are designed to be overridden - # pylint: disable=no-self-use - def __init__(self, doc): - doc = doc or "" - self.doc = doc.expandtabs() - - def is_valid(self): - return False - - def exceptions(self): - return set() - - def has_params(self): - return False - - def has_returns(self): - return False - - def has_rtype(self): - return False - - def has_yields(self): - return False - - def has_yields_type(self): - return False - - def match_param_docs(self): - return set(), set() - - def params_documented_elsewhere(self): - return self.re_for_parameters_see.search(self.doc) is not None - - -class SphinxDocstring(Docstring): - re_type = r"[\w\.]+" - - re_xref = r""" - (?::\w+:)? # optional tag - `{0}` # what to reference - """.format(re_type) - - re_param_raw = r""" - : # initial colon - (?: # Sphinx keywords - param|parameter| - arg|argument| - key|keyword - ) - \s+ # whitespace - - (?: # optional type declaration - ({type}) - \s+ - )? - - (\w+) # Parameter name - \s* # whitespace - : # final colon - """.format(type=re_type) - re_param_in_docstring = re.compile(re_param_raw, re.X | re.S) - - re_type_raw = r""" - :type # Sphinx keyword - \s+ # whitespace - ({type}) # Parameter name - \s* # whitespace - : # final colon - """.format(type=re_type) - re_type_in_docstring = re.compile(re_type_raw, re.X | re.S) - - re_raise_raw = r""" - : # initial colon - (?: # Sphinx keyword - raises?| - except|exception - ) - \s+ # whitespace - - (?: # type declaration - ({type}) - \s+ - )? - - (\w+) # Parameter name - \s* # whitespace - : # final colon - """.format(type=re_type) - re_raise_in_docstring = re.compile(re_raise_raw, re.X | re.S) - - re_rtype_in_docstring = re.compile(r":rtype:") - - re_returns_in_docstring = re.compile(r":returns?:") - - supports_yields = False - - def is_valid(self): - return bool(self.re_param_in_docstring.search(self.doc) or - self.re_raise_in_docstring.search(self.doc) or - self.re_rtype_in_docstring.search(self.doc) or - self.re_returns_in_docstring.search(self.doc)) - - def exceptions(self): - types = set() - - for match in re.finditer(self.re_raise_in_docstring, self.doc): - raise_type = match.group(2) - types.add(raise_type) - - return types - - def has_params(self): - if not self.doc: - return False - - return self.re_param_in_docstring.search(self.doc) is not None - - def has_returns(self): - if not self.doc: - return False - - return bool(self.re_returns_in_docstring.search(self.doc)) - - def has_rtype(self): - if not self.doc: - return False - - return bool(self.re_rtype_in_docstring.search(self.doc)) - - def match_param_docs(self): - params_with_doc = set() - params_with_type = set() - - for match in re.finditer(self.re_param_in_docstring, self.doc): - name = match.group(2) - params_with_doc.add(name) - param_type = match.group(1) - if param_type is not None: - params_with_type.add(name) - - params_with_type.update(re.findall(self.re_type_in_docstring, self.doc)) - return params_with_doc, params_with_type - - -class EpytextDocstring(SphinxDocstring): - """ - Epytext is similar to Sphinx. See the docs: - http://epydoc.sourceforge.net/epytext.html - http://epydoc.sourceforge.net/fields.html#fields - - It's used in PyCharm: - https://www.jetbrains.com/help/pycharm/2016.1/creating-documentation-comments.html#d848203e314 - https://www.jetbrains.com/help/pycharm/2016.1/using-docstrings-to-specify-types.html - """ - re_param_in_docstring = re.compile( - SphinxDocstring.re_param_raw.replace(':', '@', 1), - re.X | re.S) - - re_type_in_docstring = re.compile( - SphinxDocstring.re_type_raw.replace(':', '@', 1), - re.X | re.S) - - re_raise_in_docstring = re.compile( - SphinxDocstring.re_raise_raw.replace(':', '@', 1), - re.X | re.S) - - re_rtype_in_docstring = re.compile(r""" - @ # initial "at" symbol - (?: # Epytext keyword - rtype|returntype - ) - : # final colon - """, re.X | re.S) - - re_returns_in_docstring = re.compile(r"@returns?:") - - -class GoogleDocstring(Docstring): - re_type = SphinxDocstring.re_type - - re_xref = SphinxDocstring.re_xref - - re_container_type = r""" - (?:{type}|{xref}) # a container type - [\(\[] [^\n]+ [\)\]] # with the contents of the container - """.format(type=re_type, xref=re_xref) - - re_multiple_type = r""" - (?:{container_type}|{type}) - (?:\s+or\s+(?:{container_type}|{type}))* - """.format(type=re_type, container_type=re_container_type) - - _re_section_template = r""" - ^([ ]*) {0} \s*: \s*$ # Google parameter header - ( .* ) # section - """ - - re_param_section = re.compile( - _re_section_template.format(r"(?:Args|Arguments|Parameters)"), - re.X | re.S | re.M - ) - - re_param_line = re.compile(r""" - \s* \*{{0,2}}(\w+) # identifier potentially with asterisks - \s* ( [(] - {type} - [)] )? \s* : # optional type declaration - \s* (.*) # beginning of optional description - """.format( - type=re_multiple_type, - ), re.X | re.S | re.M) - - re_raise_section = re.compile( - _re_section_template.format(r"Raises"), - re.X | re.S | re.M - ) - - re_raise_line = re.compile(r""" - \s* ({type}) \s* : # identifier - \s* (.*) # beginning of optional description - """.format(type=re_type), re.X | re.S | re.M) - - re_returns_section = re.compile( - _re_section_template.format(r"Returns?"), - re.X | re.S | re.M - ) - - re_returns_line = re.compile(r""" - \s* ({type}:)? # identifier - \s* (.*) # beginning of description - """.format( - type=re_multiple_type, - ), re.X | re.S | re.M) - - re_yields_section = re.compile( - _re_section_template.format(r"Yields?"), - re.X | re.S | re.M - ) - - re_yields_line = re_returns_line - - supports_yields = True - - def is_valid(self): - return bool(self.re_param_section.search(self.doc) or - self.re_raise_section.search(self.doc) or - self.re_returns_section.search(self.doc) or - self.re_yields_section.search(self.doc)) - - def has_params(self): - if not self.doc: - return False - - return self.re_param_section.search(self.doc) is not None - - def has_returns(self): - if not self.doc: - return False - - entries = self._parse_section(self.re_returns_section) - for entry in entries: - match = self.re_returns_line.match(entry) - if not match: - continue - - return_desc = match.group(2) - if return_desc: - return True - - return False - - def has_rtype(self): - if not self.doc: - return False - - entries = self._parse_section(self.re_returns_section) - for entry in entries: - match = self.re_returns_line.match(entry) - if not match: - continue - - return_type = match.group(1) - if return_type: - return True - - return False - - def has_yields(self): - if not self.doc: - return False - - entries = self._parse_section(self.re_yields_section) - for entry in entries: - match = self.re_yields_line.match(entry) - if not match: - continue - - yield_desc = match.group(2) - if yield_desc: - return True - - return False - - def has_yields_type(self): - if not self.doc: - return False - - entries = self._parse_section(self.re_yields_section) - for entry in entries: - match = self.re_yields_line.match(entry) - if not match: - continue - - yield_type = match.group(1) - if yield_type: - return True - - return False - - def exceptions(self): - types = set() - - entries = self._parse_section(self.re_raise_section) - for entry in entries: - match = self.re_raise_line.match(entry) - if not match: - continue - - exc_type = match.group(1) - exc_desc = match.group(2) - if exc_desc: - types.add(exc_type) - - return types - - def match_param_docs(self): - params_with_doc = set() - params_with_type = set() - - entries = self._parse_section(self.re_param_section) - for entry in entries: - match = self.re_param_line.match(entry) - if not match: - continue - - param_name = match.group(1) - param_type = match.group(2) - param_desc = match.group(3) - if param_type: - params_with_type.add(param_name) - - if param_desc: - params_with_doc.add(param_name) - - return params_with_doc, params_with_type - - @staticmethod - def min_section_indent(section_match): - return len(section_match.group(1)) + 1 - - def _parse_section(self, section_re): - section_match = section_re.search(self.doc) - if section_match is None: - return [] - - min_indentation = self.min_section_indent(section_match) - - entries = [] - entry = [] - is_first = True - for line in section_match.group(2).splitlines(): - if not line.strip(): - continue - indentation = space_indentation(line) - if indentation < min_indentation: - break - - # The first line after the header defines the minimum - # indentation. - if is_first: - min_indentation = indentation - is_first = False - - if indentation == min_indentation: - # Lines with minimum indentation must contain the beginning - # of a new parameter documentation. - if entry: - entries.append("\n".join(entry)) - entry = [] - - entry.append(line) - - if entry: - entries.append("\n".join(entry)) - - return entries - - -class NumpyDocstring(GoogleDocstring): - _re_section_template = r""" - ^([ ]*) {0} \s*?$ # Numpy parameters header - \s* [-=]+ \s*?$ # underline - ( .* ) # section - """ - - re_param_section = re.compile( - _re_section_template.format(r"(?:Args|Arguments|Parameters)"), - re.X | re.S | re.M - ) - - re_param_line = re.compile(r""" - \s* (\w+) # identifier - \s* : - \s* (?:({type})(?:,\s+optional)?)? # optional type declaration - \n # description starts on a new line - \s* (.*) # description - """.format( - type=GoogleDocstring.re_multiple_type, - ), re.X | re.S) - - re_raise_section = re.compile( - _re_section_template.format(r"Raises"), - re.X | re.S | re.M - ) - - re_raise_line = re.compile(r""" - \s* ({type})$ # type declaration - \s* (.*) # optional description - """.format(type=GoogleDocstring.re_type), re.X | re.S | re.M) - - re_returns_section = re.compile( - _re_section_template.format(r"Returns?"), - re.X | re.S | re.M - ) - - re_returns_line = re.compile(r""" - \s* ({type})$ # type declaration - \s* (.*) # optional description - """.format( - type=GoogleDocstring.re_multiple_type, - ), re.X | re.S | re.M) - - re_yields_section = re.compile( - _re_section_template.format(r"Yields?"), - re.X | re.S | re.M - ) - - re_yields_line = re_returns_line - - supports_yields = True - - @staticmethod - def min_section_indent(section_match): - return len(section_match.group(1)) diff --git a/pymode/libs/pylint/extensions/bad_builtin.py b/pymode/libs/pylint/extensions/bad_builtin.py deleted file mode 100644 index 9876922e..00000000 --- a/pymode/libs/pylint/extensions/bad_builtin.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Checker for deprecated builtins.""" -import sys - -import astroid -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages -from pylint.interfaces import IAstroidChecker - - -BAD_FUNCTIONS = ['map', 'filter'] -if sys.version_info < (3, 0): - BAD_FUNCTIONS.append('input') -# Some hints regarding the use of bad builtins. -BUILTIN_HINTS = { - 'map': 'Using a list comprehension can be clearer.', -} -BUILTIN_HINTS['filter'] = BUILTIN_HINTS['map'] - - -class BadBuiltinChecker(BaseChecker): - - __implements__ = (IAstroidChecker, ) - name = 'deprecated_builtins' - msgs = {'W0141': ('Used builtin function %s', - 'bad-builtin', - 'Used when a black listed builtin function is used (see the ' - 'bad-function option). Usual black listed functions are the ones ' - 'like map, or filter , where Python offers now some cleaner ' - 'alternative like list comprehension.'), - } - - options = (('bad-functions', - {'default' : BAD_FUNCTIONS, - 'type' :'csv', 'metavar' : '', - 'help' : 'List of builtins function names that should not be ' - 'used, separated by a comma'} - ), - ) - - @check_messages('bad-builtin') - def visit_call(self, node): - if isinstance(node.func, astroid.Name): - name = node.func.name - # ignore the name if it's not a builtin (i.e. not defined in the - # locals nor globals scope) - if not (name in node.frame() or name in node.root()): - if name in self.config.bad_functions: - hint = BUILTIN_HINTS.get(name) - if hint: - args = "%r. %s" % (name, hint) - else: - args = repr(name) - self.add_message('bad-builtin', node=node, args=args) - - -def register(linter): - """Required method to auto register this checker. - - :param linter: Main interface object for Pylint plugins - :type linter: Pylint object - """ - linter.register_checker(BadBuiltinChecker(linter)) diff --git a/pymode/libs/pylint/extensions/check_docs.py b/pymode/libs/pylint/extensions/check_docs.py deleted file mode 100644 index a01d6fa4..00000000 --- a/pymode/libs/pylint/extensions/check_docs.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2014-2015 Bruno Daniel -# Copyright (c) 2015-2016 Claudiu Popa -# Copyright (c) 2016 Ashley Whetter - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -import warnings - -from pylint.extensions import docparams - - -def register(linter): - """Required method to auto register this checker. - - :param linter: Main interface object for Pylint plugins - :type linter: Pylint object - """ - warnings.warn("This plugin is deprecated, use pylint.extensions.docparams instead.", - DeprecationWarning) - linter.register_checker(docparams.DocstringParameterChecker(linter)) diff --git a/pymode/libs/pylint/extensions/check_elif.py b/pymode/libs/pylint/extensions/check_elif.py deleted file mode 100644 index 3fbe1c34..00000000 --- a/pymode/libs/pylint/extensions/check_elif.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2015 LOGILAB S.A. (Paris, FRANCE) - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -import astroid -from pylint.checkers import BaseTokenChecker -from pylint.checkers.utils import check_messages -from pylint.interfaces import ITokenChecker, IAstroidChecker - - -class ElseifUsedChecker(BaseTokenChecker): - """Checks for use of "else if" when a "elif" could be used - """ - - __implements__ = (ITokenChecker, IAstroidChecker) - name = 'else_if_used' - msgs = {'R5501': ('Consider using "elif" instead of "else if"', - 'else-if-used', - 'Used when an else statement is immediately followed by ' - 'an if statement and does not contain statements that ' - 'would be unrelated to it.'), - } - - def __init__(self, linter=None): - BaseTokenChecker.__init__(self, linter) - self._init() - - def _init(self): - self._elifs = [] - self._if_counter = 0 - - def process_tokens(self, tokens): - # Process tokens and look for 'if' or 'elif' - for _, token, _, _, _ in tokens: - if token == 'elif': - self._elifs.append(True) - elif token == 'if': - self._elifs.append(False) - - def leave_module(self, _): - self._init() - - def visit_ifexp(self, _): - self._if_counter += 1 - - def visit_comprehension(self, node): - self._if_counter += len(node.ifs) - - @check_messages('else-if-used') - def visit_if(self, node): - if isinstance(node.parent, astroid.If): - orelse = node.parent.orelse - # current if node must directly follow a "else" - if orelse and orelse == [node]: - if not self._elifs[self._if_counter]: - self.add_message('else-if-used', node=node) - self._if_counter += 1 - - -def register(linter): - """Required method to auto register this checker. - - :param linter: Main interface object for Pylint plugins - :type linter: Pylint object - """ - linter.register_checker(ElseifUsedChecker(linter)) diff --git a/pymode/libs/pylint/extensions/comparetozero.py b/pymode/libs/pylint/extensions/comparetozero.py deleted file mode 100644 index 00e3eae5..00000000 --- a/pymode/libs/pylint/extensions/comparetozero.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2016 Alexander Todorov - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Looks for comparisons to empty string.""" - -import itertools - -import astroid - -from pylint import interfaces -from pylint import checkers -from pylint.checkers import utils - - -def _is_constant_zero(node): - return isinstance(node, astroid.Const) and node.value == 0 - - -class CompareToZeroChecker(checkers.BaseChecker): - """Checks for comparisons to zero. - Most of the times you should use the fact that integers with a value of 0 are false. - An exception to this rule is when 0 is allowed in the program and has a - different meaning than None! - """ - - __implements__ = (interfaces.IAstroidChecker,) - - # configuration section name - name = 'compare-to-zero' - msgs = {'C2001': ('Avoid comparisons to zero', - 'compare-to-zero', - 'Used when Pylint detects comparison to a 0 constant.'), - } - - priority = -2 - options = () - - @utils.check_messages('compare-to-zero') - def visit_compare(self, node): - _operators = ['!=', '==', 'is not', 'is'] - # note: astroid.Compare has the left most operand in node.left - # while the rest are a list of tuples in node.ops - # the format of the tuple is ('compare operator sign', node) - # here we squash everything into `ops` to make it easier for processing later - ops = [('', node.left)] - ops.extend(node.ops) - ops = list(itertools.chain(*ops)) - - for ops_idx in range(len(ops) - 2): - op_1 = ops[ops_idx] - op_2 = ops[ops_idx + 1] - op_3 = ops[ops_idx + 2] - error_detected = False - - # 0 ?? X - if _is_constant_zero(op_1) and op_2 in _operators + ['<']: - error_detected = True - # X ?? 0 - elif op_2 in _operators + ['>'] and _is_constant_zero(op_3): - error_detected = True - - if error_detected: - self.add_message('compare-to-zero', node=node) - - -def register(linter): - """Required method to auto register this checker.""" - linter.register_checker(CompareToZeroChecker(linter)) diff --git a/pymode/libs/pylint/extensions/docparams.py b/pymode/libs/pylint/extensions/docparams.py deleted file mode 100644 index 43e46351..00000000 --- a/pymode/libs/pylint/extensions/docparams.py +++ /dev/null @@ -1,419 +0,0 @@ -# Copyright (c) 2014-2015 Bruno Daniel -# Copyright (c) 2015-2016 Claudiu Popa -# Copyright (c) 2016 Ashley Whetter -# Copyright (c) 2016 Glenn Matthews - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Pylint plugin for checking in Sphinx, Google, or Numpy style docstrings -""" -from __future__ import print_function, division, absolute_import - -import astroid - -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers.utils import node_frame_class -import pylint.extensions._check_docs_utils as utils - - -class DocstringParameterChecker(BaseChecker): - """Checker for Sphinx, Google, or Numpy style docstrings - - * Check that all function, method and constructor parameters are mentioned - in the params and types part of the docstring. Constructor parameters - can be documented in either the class docstring or ``__init__`` docstring, - but not both. - * Check that there are no naming inconsistencies between the signature and - the documentation, i.e. also report documented parameters that are missing - in the signature. This is important to find cases where parameters are - renamed only in the code, not in the documentation. - * Check that all explicitly raised exceptions in a function are documented - in the function docstring. Caught exceptions are ignored. - - Activate this checker by adding the line:: - - load-plugins=pylint.extensions.docparams - - to the ``MASTER`` section of your ``.pylintrc``. - - :param linter: linter object - :type linter: :class:`pylint.lint.PyLinter` - """ - __implements__ = IAstroidChecker - - name = 'parameter_documentation' - msgs = { - 'W9005': ('"%s" has constructor parameters documented in class and __init__', - 'multiple-constructor-doc', - 'Please remove parameter declarations in the class or constructor.'), - 'W9006': ('"%s" not documented as being raised', - 'missing-raises-doc', - 'Please document exceptions for all raised exception types.'), - 'W9008': ('Redundant returns documentation', - 'redundant-returns-doc', - 'Please remove the return/rtype documentation from this method.'), - 'W9010': ('Redundant yields documentation', - 'redundant-yields-doc', - 'Please remove the yields documentation from this method.'), - 'W9011': ('Missing return documentation', - 'missing-return-doc', - 'Please add documentation about what this method returns.', - {'old_names': [('W9007', 'missing-returns-doc')]}), - 'W9012': ('Missing return type documentation', - 'missing-return-type-doc', - 'Please document the type returned by this method.', - # we can't use the same old_name for two different warnings - # {'old_names': [('W9007', 'missing-returns-doc')]}, - ), - 'W9013': ('Missing yield documentation', - 'missing-yield-doc', - 'Please add documentation about what this generator yields.', - {'old_names': [('W9009', 'missing-yields-doc')]}), - 'W9014': ('Missing yield type documentation', - 'missing-yield-type-doc', - 'Please document the type yielded by this method.', - # we can't use the same old_name for two different warnings - # {'old_names': [('W9009', 'missing-yields-doc')]}, - ), - 'W9015': ('"%s" missing in parameter documentation', - 'missing-param-doc', - 'Please add parameter declarations for all parameters.', - {'old_names': [('W9003', 'missing-param-doc')]}), - 'W9016': ('"%s" missing in parameter type documentation', - 'missing-type-doc', - 'Please add parameter type declarations for all parameters.', - {'old_names': [('W9004', 'missing-type-doc')]}), - 'W9017': ('"%s" differing in parameter documentation', - 'differing-param-doc', - 'Please check parameter names in declarations.', - ), - 'W9018': ('"%s" differing in parameter type documentation', - 'differing-type-doc', - 'Please check parameter names in type declarations.', - ), - } - - options = (('accept-no-param-doc', - {'default': True, 'type' : 'yn', 'metavar' : '', - 'help': 'Whether to accept totally missing parameter ' - 'documentation in the docstring of a function that has ' - 'parameters.' - }), - ('accept-no-raise-doc', - {'default': True, 'type' : 'yn', 'metavar' : '', - 'help': 'Whether to accept totally missing raises ' - 'documentation in the docstring of a function that ' - 'raises an exception.' - }), - ('accept-no-return-doc', - {'default': True, 'type' : 'yn', 'metavar' : '', - 'help': 'Whether to accept totally missing return ' - 'documentation in the docstring of a function that ' - 'returns a statement.' - }), - ('accept-no-yields-doc', - {'default': True, 'type' : 'yn', 'metavar': '', - 'help': 'Whether to accept totally missing yields ' - 'documentation in the docstring of a generator.' - }), - ) - - priority = -2 - - constructor_names = {'__init__', '__new__'} - not_needed_param_in_docstring = {'self', 'cls'} - - def visit_functiondef(self, node): - """Called for function and method definitions (def). - - :param node: Node for a function or method definition in the AST - :type node: :class:`astroid.scoped_nodes.Function` - """ - node_doc = utils.docstringify(node.doc) - self.check_functiondef_params(node, node_doc) - self.check_functiondef_returns(node, node_doc) - self.check_functiondef_yields(node, node_doc) - - def check_functiondef_params(self, node, node_doc): - node_allow_no_param = None - if node.name in self.constructor_names: - class_node = node_frame_class(node) - if class_node is not None: - class_doc = utils.docstringify(class_node.doc) - self.check_single_constructor_params(class_doc, node_doc, class_node) - - # __init__ or class docstrings can have no parameters documented - # as long as the other documents them. - node_allow_no_param = ( - class_doc.has_params() or - class_doc.params_documented_elsewhere() or - None - ) - class_allow_no_param = ( - node_doc.has_params() or - node_doc.params_documented_elsewhere() or - None - ) - - self.check_arguments_in_docstring( - class_doc, node.args, class_node, class_allow_no_param) - - self.check_arguments_in_docstring( - node_doc, node.args, node, node_allow_no_param) - - def check_functiondef_returns(self, node, node_doc): - if not node_doc.supports_yields and node.is_generator(): - return - - return_nodes = node.nodes_of_class(astroid.Return) - if ((node_doc.has_returns() or node_doc.has_rtype()) and - not any(utils.returns_something(ret_node) for ret_node in return_nodes)): - self.add_message( - 'redundant-returns-doc', - node=node) - - def check_functiondef_yields(self, node, node_doc): - if not node_doc.supports_yields: - return - - if ((node_doc.has_yields() or node_doc.has_yields_type()) and - not node.is_generator()): - self.add_message( - 'redundant-yields-doc', - node=node) - - def visit_raise(self, node): - func_node = node.frame() - if not isinstance(func_node, astroid.FunctionDef): - return - - expected_excs = utils.possible_exc_types(node) - if not expected_excs: - return - - doc = utils.docstringify(func_node.doc) - if not doc.is_valid(): - if doc.doc: - self._handle_no_raise_doc(expected_excs, func_node) - return - - found_excs = doc.exceptions() - missing_excs = expected_excs - found_excs - self._add_raise_message(missing_excs, func_node) - - def visit_return(self, node): - if not utils.returns_something(node): - return - - func_node = node.frame() - if not isinstance(func_node, astroid.FunctionDef): - return - - doc = utils.docstringify(func_node.doc) - if not doc.is_valid() and self.config.accept_no_return_doc: - return - - if not doc.has_returns(): - self.add_message( - 'missing-return-doc', - node=func_node - ) - - if not doc.has_rtype(): - self.add_message( - 'missing-return-type-doc', - node=func_node - ) - - def visit_yield(self, node): - func_node = node.frame() - if not isinstance(func_node, astroid.FunctionDef): - return - - doc = utils.docstringify(func_node.doc) - if not doc.is_valid() and self.config.accept_no_yields_doc: - return - - if doc.supports_yields: - doc_has_yields = doc.has_yields() - doc_has_yields_type = doc.has_yields_type() - else: - doc_has_yields = doc.has_returns() - doc_has_yields_type = doc.has_rtype() - - if not doc_has_yields: - self.add_message( - 'missing-yield-doc', - node=func_node - ) - - if not doc_has_yields_type: - self.add_message( - 'missing-yield-type-doc', - node=func_node - ) - - def visit_yieldfrom(self, node): - self.visit_yield(node) - - def check_arguments_in_docstring(self, doc, arguments_node, warning_node, - accept_no_param_doc=None): - """Check that all parameters in a function, method or class constructor - on the one hand and the parameters mentioned in the parameter - documentation (e.g. the Sphinx tags 'param' and 'type') on the other - hand are consistent with each other. - - * Undocumented parameters except 'self' are noticed. - * Undocumented parameter types except for 'self' and the ``*`` - and ``**`` parameters are noticed. - * Parameters mentioned in the parameter documentation that don't or no - longer exist in the function parameter list are noticed. - * If the text "For the parameters, see" or "For the other parameters, - see" (ignoring additional whitespace) is mentioned in the docstring, - missing parameter documentation is tolerated. - * If there's no Sphinx style, Google style or NumPy style parameter - documentation at all, i.e. ``:param`` is never mentioned etc., the - checker assumes that the parameters are documented in another format - and the absence is tolerated. - - :param doc: Docstring for the function, method or class. - :type doc: str - - :param arguments_node: Arguments node for the function, method or - class constructor. - :type arguments_node: :class:`astroid.scoped_nodes.Arguments` - - :param warning_node: The node to assign the warnings to - :type warning_node: :class:`astroid.scoped_nodes.Node` - - :param accept_no_param_doc: Whether or not to allow no parameters - to be documented. - If None then this value is read from the configuration. - :type accept_no_param_doc: bool or None - """ - # Tolerate missing param or type declarations if there is a link to - # another method carrying the same name. - if not doc.doc: - return - - if accept_no_param_doc is None: - accept_no_param_doc = self.config.accept_no_param_doc - tolerate_missing_params = doc.params_documented_elsewhere() - - # Collect the function arguments. - expected_argument_names = set(arg.name for arg in arguments_node.args) - expected_argument_names.update(arg.name for arg in arguments_node.kwonlyargs) - not_needed_type_in_docstring = ( - self.not_needed_param_in_docstring.copy()) - - if arguments_node.vararg is not None: - expected_argument_names.add(arguments_node.vararg) - not_needed_type_in_docstring.add(arguments_node.vararg) - if arguments_node.kwarg is not None: - expected_argument_names.add(arguments_node.kwarg) - not_needed_type_in_docstring.add(arguments_node.kwarg) - params_with_doc, params_with_type = doc.match_param_docs() - - # Tolerate no parameter documentation at all. - if (not params_with_doc and not params_with_type - and accept_no_param_doc): - tolerate_missing_params = True - - def _compare_missing_args(found_argument_names, message_id, - not_needed_names): - """Compare the found argument names with the expected ones and - generate a message if there are arguments missing. - - :param set found_argument_names: argument names found in the - docstring - - :param str message_id: pylint message id - - :param not_needed_names: names that may be omitted - :type not_needed_names: set of str - """ - if not tolerate_missing_params: - missing_argument_names = ( - (expected_argument_names - found_argument_names) - - not_needed_names) - if missing_argument_names: - self.add_message( - message_id, - args=(', '.join( - sorted(missing_argument_names)),), - node=warning_node) - - def _compare_different_args(found_argument_names, message_id, - not_needed_names): - """Compare the found argument names with the expected ones and - generate a message if there are extra arguments found. - - :param set found_argument_names: argument names found in the - docstring - - :param str message_id: pylint message id - - :param not_needed_names: names that may be omitted - :type not_needed_names: set of str - """ - differing_argument_names = ( - (expected_argument_names ^ found_argument_names) - - not_needed_names - expected_argument_names) - - if differing_argument_names: - self.add_message( - message_id, - args=(', '.join( - sorted(differing_argument_names)),), - node=warning_node) - - _compare_missing_args(params_with_doc, 'missing-param-doc', - self.not_needed_param_in_docstring) - _compare_missing_args(params_with_type, 'missing-type-doc', - not_needed_type_in_docstring) - - _compare_different_args(params_with_doc, 'differing-param-doc', - self.not_needed_param_in_docstring) - _compare_different_args(params_with_type, 'differing-type-doc', - not_needed_type_in_docstring) - - def check_single_constructor_params(self, class_doc, init_doc, class_node): - if class_doc.has_params() and init_doc.has_params(): - self.add_message( - 'multiple-constructor-doc', - args=(class_node.name,), - node=class_node) - - def _handle_no_raise_doc(self, excs, node): - if self.config.accept_no_raise_doc: - return - - self._add_raise_message(excs, node) - - def _add_raise_message(self, missing_excs, node): - """ - Adds a message on :param:`node` for the missing exception type. - - :param missing_excs: A list of missing exception types. - :type missing_excs: list - - :param node: The node show the message on. - :type node: astroid.node_classes.NodeNG - """ - if not missing_excs: - return - - self.add_message( - 'missing-raises-doc', - args=(', '.join(sorted(missing_excs)),), - node=node) - -def register(linter): - """Required method to auto register this checker. - - :param linter: Main interface object for Pylint plugins - :type linter: Pylint object - """ - linter.register_checker(DocstringParameterChecker(linter)) diff --git a/pymode/libs/pylint/extensions/docstyle.py b/pymode/libs/pylint/extensions/docstyle.py deleted file mode 100644 index 12a58d09..00000000 --- a/pymode/libs/pylint/extensions/docstyle.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2016 Luis Escobar -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -import linecache - -from pylint import checkers -from pylint.interfaces import IAstroidChecker, HIGH -from pylint.checkers.utils import check_messages - - -class DocStringStyleChecker(checkers.BaseChecker): - """Checks format of docstrings based on PEP 0257""" - - __implements__ = IAstroidChecker - name = 'docstyle' - - msgs = { - 'C0198': ('Bad docstring quotes in %s, expected """, given %s', - 'bad-docstring-quotes', - 'Used when a docstring does not have triple double quotes.'), - 'C0199': ('First line empty in %s docstring', - 'docstring-first-line-empty', - 'Used when a blank line is found at the beginning of a docstring.'), - } - - @check_messages('docstring-first-line-empty', 'bad-docstring-quotes') - def visit_module(self, node): - self._check_docstring('module', node) - - def visit_classdef(self, node): - self._check_docstring('class', node) - - def visit_functiondef(self, node): - ftype = 'method' if node.is_method() else 'function' - self._check_docstring(ftype, node) - - visit_asyncfunctiondef = visit_functiondef - - def _check_docstring(self, node_type, node): - docstring = node.doc - if docstring and docstring[0] == '\n': - self.add_message('docstring-first-line-empty', node=node, - args=(node_type,), confidence=HIGH) - - # Use "linecache", instead of node.as_string(), because the latter - # looses the original form of the docstrings. - - if docstring: - lineno = node.fromlineno + 1 - line = linecache.getline(node.root().file, lineno).lstrip() - if line and line.find('"""') == 0: - return - if line and '\'\'\'' in line: - quotes = '\'\'\'' - elif line and line[0] == '"': - quotes = '"' - elif line and line[0] == '\'': - quotes = '\'' - else: - quotes = False - if quotes: - self.add_message('bad-docstring-quotes', node=node, - args=(node_type, quotes), confidence=HIGH) - - -def register(linter): - """Required method to auto register this checker. - - :param linter: Main interface object for Pylint plugins - :type linter: Pylint object - """ - linter.register_checker(DocStringStyleChecker(linter)) diff --git a/pymode/libs/pylint/extensions/emptystring.py b/pymode/libs/pylint/extensions/emptystring.py deleted file mode 100644 index d3e05e9d..00000000 --- a/pymode/libs/pylint/extensions/emptystring.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2016 Alexander Todorov - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Looks for comparisons to empty string.""" - -import itertools - -import astroid - -from pylint import interfaces -from pylint import checkers -from pylint.checkers import utils - - -def _is_constant_empty_str(node): - return isinstance(node, astroid.Const) and node.value == '' - - -class CompareToEmptyStringChecker(checkers.BaseChecker): - """Checks for comparisons to empty string. - Most of the times you should use the fact that empty strings are false. - An exception to this rule is when an empty string value is allowed in the program - and has a different meaning than None! - """ - - __implements__ = (interfaces.IAstroidChecker,) - - # configuration section name - name = 'compare-to-empty-string' - msgs = {'C1901': ('Avoid comparisons to empty string', - 'compare-to-empty-string', - 'Used when Pylint detects comparison to an empty string constant.'), - } - - priority = -2 - options = () - - @utils.check_messages('compare-to-empty-string') - def visit_compare(self, node): - _operators = ['!=', '==', 'is not', 'is'] - # note: astroid.Compare has the left most operand in node.left - # while the rest are a list of tuples in node.ops - # the format of the tuple is ('compare operator sign', node) - # here we squash everything into `ops` to make it easier for processing later - ops = [('', node.left)] - ops.extend(node.ops) - ops = list(itertools.chain(*ops)) - - for ops_idx in range(len(ops) - 2): - op_1 = ops[ops_idx] - op_2 = ops[ops_idx + 1] - op_3 = ops[ops_idx + 2] - error_detected = False - - # x ?? "" - if _is_constant_empty_str(op_1) and op_2 in _operators: - error_detected = True - # '' ?? X - elif op_2 in _operators and _is_constant_empty_str(op_3): - error_detected = True - - if error_detected: - self.add_message('compare-to-empty-string', node=node) - - -def register(linter): - """Required method to auto register this checker.""" - linter.register_checker(CompareToEmptyStringChecker(linter)) diff --git a/pymode/libs/pylint/extensions/mccabe.py b/pymode/libs/pylint/extensions/mccabe.py deleted file mode 100644 index 8e231ccd..00000000 --- a/pymode/libs/pylint/extensions/mccabe.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Module to add McCabe checker class for pylint. """ - -from __future__ import absolute_import - -from mccabe import PathGraph as Mccabe_PathGraph, \ - PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor -from pylint import checkers -from pylint.checkers.utils import check_messages -from pylint.interfaces import HIGH, IAstroidChecker - - -class PathGraph(Mccabe_PathGraph): - def __init__(self, node): - super(PathGraph, self).__init__(name='', entity='', lineno=1) - self.root = node - - -class PathGraphingAstVisitor(Mccabe_PathGraphingAstVisitor): - def __init__(self): - super(PathGraphingAstVisitor, self).__init__() - self._bottom_counter = 0 - - def default(self, node, *args): - for child in node.get_children(): - self.dispatch(child, *args) - - def dispatch(self, node, *args): - self.node = node - klass = node.__class__ - meth = self._cache.get(klass) - if meth is None: - className = klass.__name__ - meth = getattr(self.visitor, 'visit' + className, self.default) - self._cache[klass] = meth - return meth(node, *args) - - def visitFunctionDef(self, node): - if self.graph is not None: - # closure - pathnode = self._append_node(node) - self.tail = pathnode - self.dispatch_list(node.body) - bottom = "%s" % self._bottom_counter - self._bottom_counter += 1 - self.graph.connect(self.tail, bottom) - self.graph.connect(node, bottom) - self.tail = bottom - else: - self.graph = PathGraph(node) - self.tail = node - self.dispatch_list(node.body) - self.graphs["%s%s" % (self.classname, node.name)] = self.graph - self.reset() - - visitAsyncFunctionDef = visitFunctionDef - - def visitSimpleStatement(self, node): - self._append_node(node) - - visitAssert = visitAssign = visitAugAssign = visitDelete = visitPrint = \ - visitRaise = visitYield = visitImport = visitCall = visitSubscript = \ - visitPass = visitContinue = visitBreak = visitGlobal = visitReturn = \ - visitExpr = visitAwait = visitSimpleStatement - - def visitWith(self, node): - self._append_node(node) - self.dispatch_list(node.body) - - visitAsyncWith = visitWith - - def _append_node(self, node): - if not self.tail: - return - self.graph.connect(self.tail, node) - self.tail = node - return node - - def _subgraph(self, node, name, extra_blocks=()): - """create the subgraphs representing any `if` and `for` statements""" - if self.graph is None: - # global loop - self.graph = PathGraph(node) - self._subgraph_parse(node, node, extra_blocks) - self.graphs["%s%s" % (self.classname, name)] = self.graph - self.reset() - else: - self._append_node(node) - self._subgraph_parse(node, node, extra_blocks) - - def _subgraph_parse(self, node, pathnode, extra_blocks): # pylint: disable=unused-argument - """parse the body and any `else` block of `if` and `for` statements""" - loose_ends = [] - self.tail = node - self.dispatch_list(node.body) - loose_ends.append(self.tail) - for extra in extra_blocks: - self.tail = node - self.dispatch_list(extra.body) - loose_ends.append(self.tail) - if node.orelse: - self.tail = node - self.dispatch_list(node.orelse) - loose_ends.append(self.tail) - else: - loose_ends.append(node) - if node: - bottom = "%s" % self._bottom_counter - self._bottom_counter += 1 - for le in loose_ends: - self.graph.connect(le, bottom) - self.tail = bottom - - -class McCabeMethodChecker(checkers.BaseChecker): - """Checks McCabe complexity cyclomatic threshold in methods and functions - to validate a too complex code. - """ - - __implements__ = IAstroidChecker - name = 'design' - - msgs = { - 'R1260': ( - "%s is too complex. The McCabe rating is %d", - 'too-complex', - 'Used when a method or function is too complex based on ' - 'McCabe Complexity Cyclomatic'), - } - options = ( - ('max-complexity', { - 'default': 10, - 'type': 'int', - 'metavar': '', - 'help': 'McCabe complexity cyclomatic threshold', - }), - ) - - @check_messages('too-complex') - def visit_module(self, node): - """visit an astroid.Module node to check too complex rating and - add message if is greather than max_complexity stored from options""" - visitor = PathGraphingAstVisitor() - for child in node.body: - visitor.preorder(child, visitor) - for graph in visitor.graphs.values(): - complexity = graph.complexity() - node = graph.root - if hasattr(node, 'name'): - node_name = "'%s'" % node.name - else: - node_name = "This '%s'" % node.__class__.__name__.lower() - if complexity <= self.config.max_complexity: - continue - self.add_message( - 'too-complex', node=node, confidence=HIGH, - args=(node_name, complexity)) - - -def register(linter): - """Required method to auto register this checker. - - :param linter: Main interface object for Pylint plugins - :type linter: Pylint object - """ - linter.register_checker(McCabeMethodChecker(linter)) diff --git a/pymode/libs/pylint/extensions/overlapping_exceptions.py b/pymode/libs/pylint/extensions/overlapping_exceptions.py deleted file mode 100644 index c231ba35..00000000 --- a/pymode/libs/pylint/extensions/overlapping_exceptions.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Looks for overlapping exceptions.""" - -import astroid - -from pylint import interfaces -from pylint import checkers -from pylint.checkers import utils - -from pylint.checkers.exceptions import _annotated_unpack_infer - - -class OverlappingExceptionsChecker(checkers.BaseChecker): - """Checks for two or more exceptions in the same exception handler - clause that are identical or parts of the same inheritance hierarchy - (i.e. overlapping).""" - - __implements__ = interfaces.IAstroidChecker - - name = 'overlap-except' - msgs = {'W0714': ('Overlapping exceptions (%s)', - 'overlapping-except', - 'Used when exceptions in handler overlap or are identical')} - priority = -2 - options = () - - @utils.check_messages('overlapping-except') - def visit_tryexcept(self, node): - """check for empty except""" - for handler in node.handlers: - if handler.type is None: - continue - if isinstance(handler.type, astroid.BoolOp): - continue - try: - excs = list(_annotated_unpack_infer(handler.type)) - except astroid.InferenceError: - continue - - handled_in_clause = [] - for part, exc in excs: - if exc is astroid.YES: - continue - if (isinstance(exc, astroid.Instance) and - utils.inherit_from_std_ex(exc)): - # pylint: disable=protected-access - exc = exc._proxied - - if not isinstance(exc, astroid.ClassDef): - continue - - exc_ancestors = [anc for anc in exc.ancestors() - if isinstance(anc, astroid.ClassDef)] - - for prev_part, prev_exc in handled_in_clause: - prev_exc_ancestors = [anc for anc in prev_exc.ancestors() - if isinstance(anc, astroid.ClassDef)] - if exc == prev_exc: - self.add_message('overlapping-except', - node=handler.type, - args='%s and %s are the same' % - (prev_part.as_string(), - part.as_string())) - elif (prev_exc in exc_ancestors or - exc in prev_exc_ancestors): - ancestor = part if exc in prev_exc_ancestors else prev_part - descendant = part if prev_exc in exc_ancestors else prev_part - self.add_message('overlapping-except', - node=handler.type, - args='%s is an ancestor class of %s' % - (ancestor.as_string(), descendant.as_string())) - handled_in_clause += [(part, exc)] - - -def register(linter): - """Required method to auto register this checker.""" - linter.register_checker(OverlappingExceptionsChecker(linter)) diff --git a/pymode/libs/pylint/extensions/redefined_variable_type.py b/pymode/libs/pylint/extensions/redefined_variable_type.py deleted file mode 100644 index 6b6abb83..00000000 --- a/pymode/libs/pylint/extensions/redefined_variable_type.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -import six - -import astroid -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages, is_none, node_type -from pylint.interfaces import IAstroidChecker - - -BUILTINS = six.moves.builtins.__name__ - - -class MultipleTypesChecker(BaseChecker): - """Checks for variable type redefinitions (NoneType excepted) - - At a function, method, class or module scope - - This rule could be improved: - - - Currently, if an attribute is set to different types in 2 methods of a - same class, it won't be detected (see functional test) - - One could improve the support for inference on assignment with tuples, - ifexpr, etc. Also it would be great to have support for inference on - str.split() - """ - __implements__ = IAstroidChecker - - name = 'multiple_types' - msgs = {'R0204': ('Redefinition of %s type from %s to %s', - 'redefined-variable-type', - 'Used when the type of a variable changes inside a ' - 'method or a function.' - ), - } - - def visit_classdef(self, _): - self._assigns.append({}) - - @check_messages('redefined-variable-type') - def leave_classdef(self, _): - self._check_and_add_messages() - - visit_functiondef = visit_classdef - leave_functiondef = leave_module = leave_classdef - - def visit_module(self, _): - self._assigns = [{}] - - def _check_and_add_messages(self): - assigns = self._assigns.pop() - for name, args in assigns.items(): - if len(args) <= 1: - continue - orig_node, orig_type = args[0] - # Check if there is a type in the following nodes that would be - # different from orig_type. - for redef_node, redef_type in args[1:]: - if redef_type == orig_type: - continue - # if a variable is defined to several types in a if node, - # this is not actually redefining. - orig_parent = orig_node.parent - redef_parent = redef_node.parent - if isinstance(orig_parent, astroid.If): - if orig_parent == redef_parent: - if (redef_node in orig_parent.orelse and - orig_node not in orig_parent.orelse): - orig_node, orig_type = redef_node, redef_type - continue - elif (isinstance(redef_parent, astroid.If) and - redef_parent in orig_parent.nodes_of_class(astroid.If)): - orig_node, orig_type = redef_node, redef_type - continue - orig_type = orig_type.replace(BUILTINS + ".", '') - redef_type = redef_type.replace(BUILTINS + ".", '') - self.add_message('redefined-variable-type', node=redef_node, - args=(name, orig_type, redef_type)) - break - - def visit_assign(self, node): - # we don't handle multiple assignment nor slice assignment - target = node.targets[0] - if isinstance(target, (astroid.Tuple, astroid.Subscript)): - return - # ignore NoneType - if is_none(node): - return - _type = node_type(node.value) - if _type: - self._assigns[-1].setdefault(target.as_string(), []).append( - (node, _type.pytype())) - - -def register(linter): - """Required method to auto register this checker. - - :param linter: Main interface object for Pylint plugins - :type linter: Pylint object - """ - linter.register_checker(MultipleTypesChecker(linter)) diff --git a/pymode/libs/pylint/graph.py b/pymode/libs/pylint/graph.py deleted file mode 100644 index f2c2c5bb..00000000 --- a/pymode/libs/pylint/graph.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (c) 2015 Florian Bruhin -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Graph manipulation utilities. - -(dot generation adapted from pypy/translator/tool/make_dot.py) -""" - -import os.path as osp -import os -import sys -import tempfile -import codecs - -def target_info_from_filename(filename): - """Transforms /some/path/foo.png into ('/some/path', 'foo.png', 'png').""" - basename = osp.basename(filename) - storedir = osp.dirname(osp.abspath(filename)) - target = filename.split('.')[-1] - return storedir, basename, target - - -class DotBackend(object): - """Dot File backend.""" - def __init__(self, graphname, rankdir=None, size=None, ratio=None, - charset='utf-8', renderer='dot', additional_param=None): - if additional_param is None: - additional_param = {} - self.graphname = graphname - self.renderer = renderer - self.lines = [] - self._source = None - self.emit("digraph %s {" % normalize_node_id(graphname)) - if rankdir: - self.emit('rankdir=%s' % rankdir) - if ratio: - self.emit('ratio=%s' % ratio) - if size: - self.emit('size="%s"' % size) - if charset: - assert charset.lower() in ('utf-8', 'iso-8859-1', 'latin1'), \ - 'unsupported charset %s' % charset - self.emit('charset="%s"' % charset) - for param in additional_param.items(): - self.emit('='.join(param)) - - def get_source(self): - """returns self._source""" - if self._source is None: - self.emit("}\n") - self._source = '\n'.join(self.lines) - del self.lines - return self._source - - source = property(get_source) - - def generate(self, outputfile=None, dotfile=None, mapfile=None): - """Generates a graph file. - - :param str outputfile: filename and path [defaults to graphname.png] - :param str dotfile: filename and path [defaults to graphname.dot] - :param str mapfile: filename and path - - :rtype: str - :return: a path to the generated file - """ - import subprocess # introduced in py 2.4 - name = self.graphname - if not dotfile: - # if 'outputfile' is a dot file use it as 'dotfile' - if outputfile and outputfile.endswith(".dot"): - dotfile = outputfile - else: - dotfile = '%s.dot' % name - if outputfile is not None: - storedir, _, target = target_info_from_filename(outputfile) - if target != "dot": - pdot, dot_sourcepath = tempfile.mkstemp(".dot", name) - os.close(pdot) - else: - dot_sourcepath = osp.join(storedir, dotfile) - else: - target = 'png' - pdot, dot_sourcepath = tempfile.mkstemp(".dot", name) - ppng, outputfile = tempfile.mkstemp(".png", name) - os.close(pdot) - os.close(ppng) - pdot = codecs.open(dot_sourcepath, 'w', encoding='utf8') - pdot.write(self.source) - pdot.close() - if target != 'dot': - use_shell = sys.platform == 'win32' - if mapfile: - subprocess.call([self.renderer, '-Tcmapx', '-o', - mapfile, '-T', target, dot_sourcepath, - '-o', outputfile], - shell=use_shell) - else: - subprocess.call([self.renderer, '-T', target, - dot_sourcepath, '-o', outputfile], - shell=use_shell) - os.unlink(dot_sourcepath) - return outputfile - - def emit(self, line): - """Adds to final output.""" - self.lines.append(line) - - def emit_edge(self, name1, name2, **props): - """emit an edge from to . - edge properties: see http://www.graphviz.org/doc/info/attrs.html - """ - attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] - n_from, n_to = normalize_node_id(name1), normalize_node_id(name2) - self.emit('%s -> %s [%s];' % (n_from, n_to, ', '.join(sorted(attrs)))) - - def emit_node(self, name, **props): - """emit a node with given properties. - node properties: see http://www.graphviz.org/doc/info/attrs.html - """ - attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] - self.emit('%s [%s];' % (normalize_node_id(name), ', '.join(sorted(attrs)))) - -def normalize_node_id(nid): - """Returns a suitable DOT node id for `nid`.""" - return '"%s"' % nid - -def get_cycles(graph_dict, vertices=None): - '''given a dictionary representing an ordered graph (i.e. key are vertices - and values is a list of destination vertices representing edges), return a - list of detected cycles - ''' - if not graph_dict: - return () - result = [] - if vertices is None: - vertices = graph_dict.keys() - for vertice in vertices: - _get_cycles(graph_dict, [], set(), result, vertice) - return result - -def _get_cycles(graph_dict, path, visited, result, vertice): - """recursive function doing the real work for get_cycles""" - if vertice in path: - cycle = [vertice] - for node in path[::-1]: - if node == vertice: - break - cycle.insert(0, node) - # make a canonical representation - start_from = min(cycle) - index = cycle.index(start_from) - cycle = cycle[index:] + cycle[0:index] - # append it to result if not already in - if cycle not in result: - result.append(cycle) - return - path.append(vertice) - try: - for node in graph_dict[vertice]: - # don't check already visited nodes again - if node not in visited: - _get_cycles(graph_dict, path, visited, result, node) - visited.add(node) - except KeyError: - pass - path.pop() diff --git a/pymode/libs/pylint/interfaces.py b/pymode/libs/pylint/interfaces.py deleted file mode 100644 index 5403818a..00000000 --- a/pymode/libs/pylint/interfaces.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2009-2010, 2012-2013 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2015 Florian Bruhin -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -"""Interfaces for Pylint objects""" -from collections import namedtuple - -Confidence = namedtuple('Confidence', ['name', 'description']) -# Warning Certainties -HIGH = Confidence('HIGH', 'No false positive possible.') -INFERENCE = Confidence('INFERENCE', 'Warning based on inference result.') -INFERENCE_FAILURE = Confidence('INFERENCE_FAILURE', - 'Warning based on inference with failures.') -UNDEFINED = Confidence('UNDEFINED', - 'Warning without any associated confidence level.') - -CONFIDENCE_LEVELS = [HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED] - - -class Interface(object): - """Base class for interfaces.""" - @classmethod - def is_implemented_by(cls, instance): - return implements(instance, cls) - - -def implements(obj, interface): - """Return true if the give object (maybe an instance or class) implements - the interface. - """ - kimplements = getattr(obj, '__implements__', ()) - if not isinstance(kimplements, (list, tuple)): - kimplements = (kimplements,) - for implementedinterface in kimplements: - if issubclass(implementedinterface, interface): - return True - return False - - -class IChecker(Interface): - """This is an base interface, not designed to be used elsewhere than for - sub interfaces definition. - """ - - def open(self): - """called before visiting project (i.e set of modules)""" - - def close(self): - """called after visiting project (i.e set of modules)""" - - -class IRawChecker(IChecker): - """interface for checker which need to parse the raw file - """ - - def process_module(self, astroid): - """ process a module - - the module's content is accessible via astroid.stream - """ - - -class ITokenChecker(IChecker): - """Interface for checkers that need access to the token list.""" - def process_tokens(self, tokens): - """Process a module. - - tokens is a list of all source code tokens in the file. - """ - - -class IAstroidChecker(IChecker): - """ interface for checker which prefers receive events according to - statement type - """ - - -class IReporter(Interface): - """ reporter collect messages and display results encapsulated in a layout - """ - - def handle_message(self, msg): - """Handle the given message object.""" - - def display_reports(self, layout): - """display results encapsulated in the layout tree - """ - - -__all__ = ('IRawChecker', 'IAstroidChecker', 'ITokenChecker', 'IReporter') diff --git a/pymode/libs/pylint/lint.py b/pymode/libs/pylint/lint.py deleted file mode 100644 index 537d6f25..00000000 --- a/pymode/libs/pylint/lint.py +++ /dev/null @@ -1,1365 +0,0 @@ -# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2011-2014 Google, Inc. -# Copyright (c) 2012 FELD Boris -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014-2015 Michal Nowikowski -# Copyright (c) 2015 Mihai Balint -# Copyright (c) 2015 Simu Toni -# Copyright (c) 2015 Aru Sahni -# Copyright (c) 2015-2016 Florian Bruhin -# Copyright (c) 2016 Glenn Matthews - -# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint/blob/master/COPYING - -""" %prog [options] module_or_package - - Check that a module satisfies a coding standard (and more !). - - %prog --help - - Display this help message and exit. - - %prog --help-msg [,] - - Display help messages about given message identifiers and exit. -""" -from __future__ import print_function - -import collections -import contextlib -import operator -import os -try: - import multiprocessing -except ImportError: - multiprocessing = None -import sys -import tokenize -import warnings - -import six - -import astroid -from astroid.__pkginfo__ import version as astroid_version -from astroid import modutils -from pylint import checkers -from pylint import interfaces -from pylint import reporters -from pylint import exceptions -from pylint import utils -from pylint import config -from pylint.__pkginfo__ import version -from pylint.reporters.ureports import nodes as report_nodes - - -MANAGER = astroid.MANAGER - - -def _get_new_args(message): - location = ( - message.abspath, - message.path, - message.module, - message.obj, - message.line, - message.column, - ) - return ( - message.msg_id, - message.symbol, - location, - message.msg, - message.confidence, - ) - -def _get_python_path(filepath): - dirname = os.path.realpath(os.path.expanduser(filepath)) - if not os.path.isdir(dirname): - dirname = os.path.dirname(dirname) - while True: - if not os.path.exists(os.path.join(dirname, "__init__.py")): - return dirname - old_dirname = dirname - dirname = os.path.dirname(dirname) - if old_dirname == dirname: - return os.getcwd() - - -def _merge_stats(stats): - merged = {} - by_msg = collections.Counter() - for stat in stats: - message_stats = stat.pop('by_msg', {}) - by_msg.update(message_stats) - - for key, item in six.iteritems(stat): - if key not in merged: - merged[key] = item - else: - if isinstance(item, dict): - merged[key].update(item) - else: - merged[key] = merged[key] + item - - merged['by_msg'] = by_msg - return merged - - -@contextlib.contextmanager -def _patch_sysmodules(): - # Context manager that permits running pylint, on Windows, with -m switch - # and with --jobs, as in 'python -2 -m pylint .. --jobs'. - # For more details why this is needed, - # see Python issue http://bugs.python.org/issue10845. - - mock_main = __name__ != '__main__' # -m switch - if mock_main: - sys.modules['__main__'] = sys.modules[__name__] - - try: - yield - finally: - if mock_main: - sys.modules.pop('__main__') - - -# Python Linter class ######################################################### - -MSGS = { - 'F0001': ('%s', - 'fatal', - 'Used when an error occurred preventing the analysis of a \ - module (unable to find it for instance).'), - 'F0002': ('%s: %s', - 'astroid-error', - 'Used when an unexpected error occurred while building the ' - 'Astroid representation. This is usually accompanied by a ' - 'traceback. Please report such errors !'), - 'F0010': ('error while code parsing: %s', - 'parse-error', - 'Used when an exception occurred while building the Astroid ' - 'representation which could be handled by astroid.'), - - 'I0001': ('Unable to run raw checkers on built-in module %s', - 'raw-checker-failed', - 'Used to inform that a built-in module has not been checked ' - 'using the raw checkers.'), - - 'I0010': ('Unable to consider inline option %r', - 'bad-inline-option', - 'Used when an inline option is either badly formatted or can\'t ' - 'be used inside modules.'), - - 'I0011': ('Locally disabling %s (%s)', - 'locally-disabled', - 'Used when an inline option disables a message or a messages ' - 'category.'), - 'I0012': ('Locally enabling %s (%s)', - 'locally-enabled', - 'Used when an inline option enables a message or a messages ' - 'category.'), - 'I0013': ('Ignoring entire file', - 'file-ignored', - 'Used to inform that the file will not be checked'), - 'I0020': ('Suppressed %s (from line %d)', - 'suppressed-message', - 'A message was triggered on a line, but suppressed explicitly ' - 'by a disable= comment in the file. This message is not ' - 'generated for messages that are ignored due to configuration ' - 'settings.'), - 'I0021': ('Useless suppression of %s', - 'useless-suppression', - 'Reported when a message is explicitly disabled for a line or ' - 'a block of code, but never triggered.'), - 'I0022': ('Pragma "%s" is deprecated, use "%s" instead', - 'deprecated-pragma', - 'Some inline pylint options have been renamed or reworked, ' - 'only the most recent form should be used. ' - 'NOTE:skip-all is only available with pylint >= 0.26', - {'old_names': [('I0014', 'deprecated-disable-all')]}), - - 'E0001': ('%s', - 'syntax-error', - 'Used when a syntax error is raised for a module.'), - - 'E0011': ('Unrecognized file option %r', - 'unrecognized-inline-option', - 'Used when an unknown inline option is encountered.'), - 'E0012': ('Bad option value %r', - 'bad-option-value', - 'Used when a bad value for an inline option is encountered.'), - } - - -if multiprocessing is not None: - class ChildLinter(multiprocessing.Process): - def run(self): - # pylint: disable=no-member, unbalanced-tuple-unpacking - tasks_queue, results_queue, self._config = self._args - - self._config["jobs"] = 1 # Child does not parallelize any further. - self._python3_porting_mode = self._config.pop( - 'python3_porting_mode', None) - self._plugins = self._config.pop('plugins', None) - - # Run linter for received files/modules. - for file_or_module in iter(tasks_queue.get, 'STOP'): - result = self._run_linter(file_or_module[0]) - try: - results_queue.put(result) - except Exception as ex: - print("internal error with sending report for module %s" % - file_or_module, file=sys.stderr) - print(ex, file=sys.stderr) - results_queue.put({}) - - def _run_linter(self, file_or_module): - linter = PyLinter() - - # Register standard checkers. - linter.load_default_plugins() - # Load command line plugins. - if self._plugins: - linter.load_plugin_modules(self._plugins) - - linter.load_configuration_from_config(self._config) - linter.set_reporter(reporters.CollectingReporter()) - - # Enable the Python 3 checker mode. This option is - # passed down from the parent linter up to here, since - # the Python 3 porting flag belongs to the Run class, - # instead of the Linter class. - if self._python3_porting_mode: - linter.python3_porting_mode() - - # Run the checks. - linter.check(file_or_module) - - msgs = [_get_new_args(m) for m in linter.reporter.messages] - return (file_or_module, linter.file_state.base_name, linter.current_name, - msgs, linter.stats, linter.msg_status) - - -class PyLinter(config.OptionsManagerMixIn, - utils.MessagesHandlerMixIn, - utils.ReportsHandlerMixIn, - checkers.BaseTokenChecker): - """lint Python modules using external checkers. - - This is the main checker controlling the other ones and the reports - generation. It is itself both a raw checker and an astroid checker in order - to: - * handle message activation / deactivation at the module level - * handle some basic but necessary stats'data (number of classes, methods...) - - IDE plugin developers: you may have to call - `astroid.builder.MANAGER.astroid_cache.clear()` across runs if you want - to ensure the latest code version is actually checked. - """ - - __implements__ = (interfaces.ITokenChecker, ) - - name = 'master' - priority = 0 - level = 0 - msgs = MSGS - - @staticmethod - def make_options(): - return (('ignore', - {'type' : 'csv', 'metavar' : '[,...]', - 'dest' : 'black_list', 'default' : ('CVS',), - 'help' : 'Add files or directories to the blacklist. ' - 'They should be base names, not paths.'}), - - ('ignore-patterns', - {'type' : 'regexp_csv', 'metavar' : '[,...]', - 'dest' : 'black_list_re', 'default' : (), - 'help' : 'Add files or directories matching the regex patterns to the' - ' blacklist. The regex matches against base names, not paths.'}), - - ('persistent', - {'default': True, 'type' : 'yn', 'metavar' : '', - 'level': 1, - 'help' : 'Pickle collected data for later comparisons.'}), - - ('load-plugins', - {'type' : 'csv', 'metavar' : '', 'default' : (), - 'level': 1, - 'help' : 'List of plugins (as comma separated values of ' - 'python modules names) to load, usually to register ' - 'additional checkers.'}), - - ('output-format', - {'default': 'text', 'type': 'string', 'metavar' : '', - 'short': 'f', - 'group': 'Reports', - 'help' : 'Set the output format. Available formats are text,' - ' parseable, colorized, json and msvs (visual studio).' - 'You can also give a reporter class, eg mypackage.mymodule.' - 'MyReporterClass.'}), - - ('reports', - {'default': False, 'type' : 'yn', 'metavar' : '', - 'short': 'r', - 'group': 'Reports', - 'help' : 'Tells whether to display a full report or only the ' - 'messages'}), - - ('evaluation', - {'type' : 'string', 'metavar' : '', - 'group': 'Reports', 'level': 1, - 'default': '10.0 - ((float(5 * error + warning + refactor + ' - 'convention) / statement) * 10)', - 'help' : 'Python expression which should return a note less ' - 'than 10 (10 is the highest note). You have access ' - 'to the variables errors warning, statement which ' - 'respectively contain the number of errors / ' - 'warnings messages and the total number of ' - 'statements analyzed. This is used by the global ' - 'evaluation report (RP0004).'}), - ('score', - {'default': True, 'type': 'yn', 'metavar': '', - 'short': 's', - 'group': 'Reports', - 'help': 'Activate the evaluation score.'}), - - ('confidence', - {'type' : 'multiple_choice', 'metavar': '', - 'default': '', - 'choices': [c.name for c in interfaces.CONFIDENCE_LEVELS], - 'group': 'Messages control', - 'help' : 'Only show warnings with the listed confidence levels.' - ' Leave empty to show all. Valid levels: %s' % ( - ', '.join(c.name for c in interfaces.CONFIDENCE_LEVELS),)}), - - ('enable', - {'type' : 'csv', 'metavar': '', - 'short': 'e', - 'group': 'Messages control', - 'help' : 'Enable the message, report, category or checker with the ' - 'given id(s). You can either give multiple identifier ' - 'separated by comma (,) or put this option multiple time ' - '(only on the command line, not in the configuration file ' - 'where it should appear only once). ' - 'See also the "--disable" option for examples. '}), - - ('disable', - {'type' : 'csv', 'metavar': '', - 'short': 'd', - 'group': 'Messages control', - 'help' : 'Disable the message, report, category or checker ' - 'with the given id(s). You can either give multiple identifiers' - ' separated by comma (,) or put this option multiple times ' - '(only on the command line, not in the configuration file ' - 'where it should appear only once).' - 'You can also use "--disable=all" to disable everything first ' - 'and then reenable specific checks. For example, if you want ' - 'to run only the similarities checker, you can use ' - '"--disable=all --enable=similarities". ' - 'If you want to run only the classes checker, but have no ' - 'Warning level messages displayed, use' - '"--disable=all --enable=classes --disable=W"'}), - - ('msg-template', - {'type' : 'string', 'metavar': '