diff --git a/.bumpversion.cfg b/.bumpversion.cfg index e29f1ee9..84607ec8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,6 +1,6 @@ [bumpversion] commit = True -current_version = 0.9.5 +current_version = 0.14.0 files = plugin/pymode.vim tag = True tag_name = {new_version} @@ -9,3 +9,6 @@ tag_name = {new_version} search = Version: {current_version} replace = Version: {new_version} +[bumpversion:file:CHANGELOG.md] +search = Version: {current_version} +replace = Version: {new_version} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..8847bdf2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +liberapay: diraol diff --git a/.github/workflows/test_pymode.yml b/.github/workflows/test_pymode.yml new file mode 100644 index 00000000..332dcdad --- /dev/null +++ b/.github/workflows/test_pymode.yml @@ -0,0 +1,71 @@ +name: Testing python-mode + +on: [push] + +jobs: + test-python-3_8: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install dependencies + run: | + sudo apt update + export PYTHON_CONFIGURE_OPTS="--enable-shared" + sudo apt install -yqq libncurses5-dev libgtk2.0-dev libatk1.0-dev libcairo2-dev libx11-dev libxpm-dev libxt-dev python3-dev lua5.2 liblua5.2-dev libperl-dev git + sudo apt remove --purge -yqq vim vim-runtime gvim + - name: build and install vim from source + working-directory: /tmp + run: | + export PYTHON_CONFIGURE_OPTS="--enable-shared" + git clone https://github.com/vim/vim.git + cd vim + ./configure --with-features=huge --enable-multibyte --enable-python3interp=yes --with-python3-config-dir=/usr/lib/python3.8/config-3.8m-x86_64-linux-gnu --enable-perlinterp=yes --enable-luainterp=yes --enable-cscope --prefix=/usr/local + sudo make && sudo make install + - name: Install python-mode + run: | + export PYMODE_DIR="${HOME}/work/python-mode/python-mode" + mkdir -p ${HOME}/.vim/pack/foo/start/ + ln -s ${PYMODE_DIR} ${HOME}/.vim/pack/foo/start/python-mode + cp ${PYMODE_DIR}/tests/utils/pymoderc ${HOME}/.pymoderc + cp ${PYMODE_DIR}/tests/utils/vimrc ${HOME}/.vimrc + touch ${HOME}/.vimrc.before ${HOME}/.vimrc.after + - name: Run python-mode test script + run: | + alias python=python3 + cd ${HOME}/work/python-mode/python-mode + git submodule update --init --recursive + git submodule sync + bash tests/test.sh + test-python-3_9: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install dependencies + run: | + sudo apt update + export PYTHON_CONFIGURE_OPTS="--enable-shared" + sudo apt install -yqq libncurses5-dev libgtk2.0-dev libatk1.0-dev libcairo2-dev libx11-dev libxpm-dev libxt-dev python3-dev lua5.2 liblua5.2-dev libperl-dev git + sudo apt remove --purge -yqq vim vim-runtime gvim + - name: build and install vim from source + working-directory: /tmp + run: | + export PYTHON_CONFIGURE_OPTS="--enable-shared" + git clone https://github.com/vim/vim.git + cd vim + ./configure --with-features=huge --enable-multibyte --enable-python3interp=yes --with-python3-config-dir=/usr/lib/python3.9/config-3.9m-x86_64-linux-gnu --enable-perlinterp=yes --enable-luainterp=yes --enable-cscope --prefix=/usr/local + sudo make && sudo make install + - name: Install python-mode + run: | + export PYMODE_DIR="${HOME}/work/python-mode/python-mode" + mkdir -p ${HOME}/.vim/pack/foo/start/ + ln -s ${PYMODE_DIR} ${HOME}/.vim/pack/foo/start/python-mode + cp ${PYMODE_DIR}/tests/utils/pymoderc ${HOME}/.pymoderc + cp ${PYMODE_DIR}/tests/utils/vimrc ${HOME}/.vimrc + touch ${HOME}/.vimrc.before ${HOME}/.vimrc.after + - name: Run python-mode test script + run: | + alias python=python3 + cd ${HOME}/work/python-mode/python-mode + git submodule update --init --recursive + git submodule sync + bash tests/test.sh diff --git a/.gitmodules b/.gitmodules index 5a82bbf1..59d00541 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,27 +2,58 @@ path = submodules/autopep8 url = https://github.com/hhatto/autopep8 ignore = dirty + shallow = true [submodule "submodules/pycodestyle"] path = submodules/pycodestyle url = https://github.com/PyCQA/pycodestyle ignore = dirty + shallow = true [submodule "submodules/pydocstyle"] path = submodules/pydocstyle url = https://github.com/PyCQA/pydocstyle ignore = dirty + shallow = true [submodule "submodules/mccabe"] path = submodules/mccabe url = https://github.com/PyCQA/mccabe ignore = dirty + shallow = true [submodule "submodules/pyflakes"] path = submodules/pyflakes url = https://github.com/PyCQA/pyflakes ignore = dirty + shallow = true [submodule "submodules/snowball_py"] path = submodules/snowball_py url = https://github.com/diraol/snowball_py ignore = dirty + branch = develop + shallow = true +[submodule "submodules/pylint"] + path = submodules/pylint + url = https://github.com/PyCQA/pylint + shallow = true +[submodule "submodules/rope"] + path = submodules/rope + url = https://github.com/python-rope/rope + shallow = true +[submodule "submodules/astroid"] + path = submodules/astroid + url = https://github.com/PyCQA/astroid + shallow = true [submodule "submodules/pylama"] path = submodules/pylama - url = https://github.com/fmv1992/pylama - ignore = dirty + url = https://github.com/klen/pylama + shallow = true +[submodule "submodules/toml"] + path = submodules/toml + url = https://github.com/uiri/toml.git +[submodule "submodules/pytoolconfig"] + path = submodules/pytoolconfig + url = https://github.com/bagel897/pytoolconfig.git +[submodule "submodules/tomli"] + path = submodules/tomli + url = https://github.com/hukkin/tomli.git +[submodule "submodules/appdirs"] + path = submodules/appdirs + url = https://github.com/ActiveState/appdirs.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9be7bb46..00000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Build vim from source with python3 support and execute tests. -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 - - cd /tmp - - git clone https://github.com/vim/vim.git - - cd vim - - ./configure --with-features=huge --enable-multibyte --enable-python3interp=yes --with-python3-config-dir=/usr/lib/python3.5/config --enable-perlinterp=yes --enable-luainterp=yes --enable-gui=gtk2 --enable-cscope --prefix=/usr/local - - sudo make && sudo make install - - cd $ORIGINAL_FOLDER -install: git clone --recursive https://github.com/python-mode/python-mode -script: vim --version && cd ./tests && bash -x ./test.sh diff --git a/AUTHORS b/AUTHORS index f2887837..a4bcbf28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,37 +4,45 @@ Author: Maintainers: -* Felipe M. Vieira (https://github.com/fmv1992) +* Diego Rabatone Oliveira (https://github.com/diraol); Contributors: * Alvin Francis (http://github.com/alvinfrancis); -* Andriy Kohut (https://github.com/andriykohut) +* Amir Eldor (https://github.com/amireldor); +* Andriy Kohut (https://github.com/andriykohut); * Anler Hp (http://github.com/ikame); * Anton Parkhomenko (http://github.com/chuwy); * Ashley Hewson (http://github.com/ashleyh); +* Ben Davis (https://github.com/bendavis78); * Benjamin Ruston (http://github.com/bruston); +* Boatx (https://github.com/boatx); * Boris Filippov (http://github.com/frenzykryger); -* Brad Mease (http://github.com/bmease) -* Brendan Maguire (https://github.com/brendanmaguire) -* Bryce Guinta (https://github.com/brycepg) -* Daniel Hahler (http://github.com/blueyed) +* Brad Belyeu (https://github.com/bbelyeu); +* Brad Mease (http://github.com/bmease); +* Brendan Maguire (https://github.com/brendanmaguire); +* Bryce Guinta (https://github.com/brycepg); +* Daniel Hahler (http://github.com/blueyed); * David Vogt (http://github.com/winged); * Denis Kasak (http://github.com/dkasak); -* Diego Rabatone Oliveira (https://github.com/diraol) * Dimitrios Semitsoglou-Tsiapos (https://github.com/dset0x); * Dirk Wallenstein (http://github.com/dirkwallenstein); +* Felipe M. Vieira (https://github.com/fmv1992) +* Filip Poboril (https://github.com/fpob); * Florent Xicluna (http://github.com/florentx); * Fredrik Henrysson (http://github.com/fhenrysson); +* fwuzju (https://github.com/fwuzju); * Grzegorz Janik (http://github.com/glujan); * Igor Guerrero (http://github.com/igorgue); -* Jacob Niehus (https://github.com/wilywampa) -* Jason Harvey (http://github.com/alienth) -* Jay Rainey (https://github.com/jawrainey) +* Jacob Niehus (https://github.com/wilywampa); +* Jason Harvey (http://github.com/alienth); +* Jay Rainey (https://github.com/jawrainey); * Jonathan McCall (http://github.com/Jonnymcc); * Kevin Deldycke (http://github.com/kdeldycke); * Kurtis Rader (https://github.com/krader1961); * Lawrence Akka (https://github.com/lawrenceakka); +* lee (https://github.com/loyalpartner); +* Lie Ryan (https://github.com/lieryan/); * Lowe Thiderman (http://github.com/thiderman); * Martin Brochhaus (http://github.com/mbrochh); * Matt Dodge (https://github.com/mattdodge); @@ -44,6 +52,7 @@ Contributors: * Mohammed (http://github.com/mbadran); * Naoya Inada (http://github.com/naoina); * Nate Zhang (https://github.com/natezhang93); +* nixon (https://github.com/nixon); * Paweł Korzeniewski (https://github.com/korzeniewskipl); * Pedro Algarvio (http://github.com/s0undt3ch); * Phillip Cloud (http://github.com/cpcloud); @@ -53,17 +62,17 @@ Contributors: * Ronald Andreu Kaiser (http://github.com/cathoderay);; * Samir Benmendil (https://github.com/Ram-Z); * Sorin Ionescu (sorin-ionescu); +* sphaugh (https://github.com/sphaugh); * Steve Losh (http://github.com/sjl); * Tommy Allen (https://github.com/tweekmonster); * Tony Narlock (https://github.com/tony); +* tramchamploo (https://github.com/tramchamploo); * Tyler Fenby (https://github.com/TFenby); * Vincent Driessen (https://github.com/nvie); * Wang Feng (https://github.com/mapler); * Wayne Ye (https://github.com/WayneYe); * Wes Turner (https://github.com/westurner); -* bendavis78 (https://github.com/bendavis78); -* fwuzju (https://github.com/fwuzju); -* lee (https://github.com/loyalpartner); -* nixon (https://github.com/nixon); -* sphaugh (https://github.com/sphaugh); -* tramchamploo (https://github.com/tramchamploo); +* Yury A. Kartynnik (https://github.com/kartynnik); +* Xiangyu Xu (https://github.com/bkbncn); +* Zach Himsel (https://github.com/zhimsel); +* Nathan Pemberton (https://github.com/NathanTP); diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..4e7668dd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,445 @@ +# Changelog + +## TODO + +## 2023-07-02 0.14.0 + +- Update submodules + - Fix Errors related to these updates +- Improve tests outputs +- Fix Global and Module MoveRefactoring (#1141) Thanks to @lieryan +- Text object/operator/motion mapping to select logical line (#1145). Thanks to + @lieryan +- Remove dead keywords and builtins; add match, case (#1149). Thanks to + @NeilGirdhar +- Add syntax highlight for walrus (#1147) Thanks to @fpob +- Add configurable prefix for rope commands (#1137) TThanks to @NathanTP +- Add option g:pymode_indent_hanging_width for different hanging indentation + width (#1138). Thanks to @wookayin + +## 2020-10-08 0.13.0 + +- Add toml submodule + +## 2020-10-08 0.12.0 + +- Improve breakpoint feature +- Improve debugging script +- Update submodules +- Improve tests + +## 2020-05-28 0.11.0 + +- Move changelog rst syntax to markdown +- `pymode_rope`: check disables +- BREAKING CHANGE: Remove supoort for python 2. From 0.11.0 on we will focus on + supporting python 3+ (probably 3.5+). +- Inspect why files starting with the following code do not get loaded: + + ```python + def main(): + pass + + if __name__ == '__main__': + main() + ``` + +- added github actions test suit and remove travis +- improved submodules cloning (shallow) +- Removes `six` submodule +- Fix motion mapping +- Fix breakpoint feature + +## 2019-05-11 0.10.0 + +After many changes, including moving most of our dependencies from copied +source code to submodules, and lot's of problems maintaining Python 2 and +Python 3 support, this (0.10.x) is the last version of python-mode that will +support Python 2. Some patches may be done in order to fix issues related to +Python 2 or some backward compatible changes that can be applied. + +## 2017-07-xxx 0.9.5 + +- pylama: migrated to submodule + +## 2017-07-11 0.9.4 + +- pylama: fixed erratic behavior of skip option causing unintended + skipping of lint checkers + +- PEP257 requires `snowbalstemmer`: added as submodule + +- Fixed handling of `g:pymode_lint_ignore` and `g:pymode_lint_select`: from + strings to list + +- Migrated modules from pymode/libs to + [submodules/](https://github.com/fmv1992/python-mode/tree/develop/submodules) + + - Rationale: no need to single handedly update each module; + removes burden from developers + +- Improved folding accuracy + + - Improved nested definitions folding + - Improved block delimiting + +## (changelog poorly maintained) 0.8.2 + +- Pylama updated to version 5.0.5 +- Rope libs updated +- Add wdb to debugger list in breakpoint cmd +- Add `pymode_options_max_line_length` option +- Add ability to set related checker options `:help pymode-lint-options` + Options added: `pymode_lint_options_pep8`, `pymode_lint_options_pep257`, + `pymode_lint_options_mccabe`, `pymode_lint_options_pyflakes`, + `pymode_lint_options_pylint` +- Highlight comments inside class/function arg lists +- Don't fold single line def +- Don't skip a line when the first docstring contains text +- Add Python documentation vertical display option +- Rope: correct refactoring function calls + +## 2014-06-11 0.8.1 + +- Pylama updated to version 3.3.2 + +- Get fold's expression symbol from &fillchars; + +- Fixed error when setting `g:pymode_breakpoint_cmd` (expobrain); + +- Fixed code running; + +- Ability to override rope project root and .ropeproject folder + +- Added path argument to PymodeRopeNewProject which skips prompt + +- Disable `pymode_rope_lookup_project` by default + +- Options added: + - `pymode_rope_project_root`, `pymode_rope_ropefolder` + +## 2013-12-04 0.7.8b + +- Update indentation support; + +- Python3 support; + +- Removed pymode modeline support; + +- Disabled async code checking support; + +- Options changes: + - `pymode_doc_key` -> `pymode_doc_bind` + - `pymode_run_key` -> `pymode_run_bind` + - `pymode_breakpoint_key` -> `pymode_breakpoint_bind` + - `pymode_breakpoint_template` -> `pymode_breakpoint_cmd` + - `pymode_lint_write` -> `pymode_lint_on_write` + - `pymode_lint_onfly` -> `pymode_lint_on_fly` + - `pymode_lint_checker` -> `pymode_lint_checkers` + - `pymode_lint_minheight` -> `pymode_quickfix_minheight` + - `pymode_lint_maxheight` -> `pymode_quickfix_maxheight` + - `pymode_rope_autocomplete_map` -> `pymode_rope_completion_bind` + - `pymode_rope_enable_autoimport` -> `pymode_rope_autoimport` + +- Options removed: + + - `pymode_lint_hold`, `pymode_lint_config`, `pymode_lint_jump`, + `pymode_lint_signs_always_visible`, `pymode_rope_extended_complete`, + `pymode_rope_auto_project`, `pymode_rope_autoimport_generate`, + `pymode_rope_autoimport_underlines`, `pymode_rope_codeassist_maxfixes`, + `pymode_rope_sorted_completions`, `pymode_rope_extended_complete`, + `pymode_rope_confirm_saving`, `pymode_rope_global_prefix`, + `pymode_rope_local_prefix`, `pymode_rope_vim_completion`, + `pymode_rope_guess_project`, `pymode_rope_goto_def_newwin`, + `pymode_rope_always_show_complete_menu` + +- Options added: + + - `pymode_rope_regenerate_on_write`, `pymode_rope_completion`, + `pymode_rope_complete_on_dot`, `pymode_lint_sort`, + `pymode_rope_lookup_project`, `pymode_lint_unmodified` + +- Commands added: + + - `PymodeVirtualenv` + +- Commands changed: + + - `PyDoc` -> `PymodeDoc` + - `Pyrun` -> `PymodeRun` + - `PyLintToggle` -> `PymodeLintToggle` + - `PyLint` -> `PymodeLint` + - `PyLintAuto` -> `PymodeLintAuto` + - `RopeOpenProject` -> `PymodeRopeNewProject` + - `RopeUndo` -> `PymodeRopeUndo` + - `RopeRedo` -> `PymodeRopeRedo` + - `RopeRenameCurrentModule` -> `PymodeRopeRenameModule` + - `RopeModuleToPackage` -> `PymodeRopeModuleToPackage` + - `RopeGenerateAutoimportCache` -> `PymodeRopeRegenerate` + - `RopeOrgamizeImports` -> `PymodeRopeAutoImport` + +- Commands removed: + - `PyLintCheckerToggle`, `RopeCloseProject`, `RopeProjectConfig`, + `RopeRename`, `RopeCreate<...>`, `RopeWriteProject`, `RopeRename`, + `RopeExtractVariable`, `RopeExtractMethod`, `RopeInline`, `RopeMove`, + `RopeRestructure`, `RopeUseFunction`, `RopeIntroduceFactory`, + `RopeChangeSignature`, `RopeMoveCurrentModule`, `RopeGenerate<...>`, + `RopeAnalizeModule`, `RopeAutoImport` + +## 2013-10-29 0.6.19 + +- Added `g:pymode_rope_autocomplete_map` option; +- Removed `g:pymode_rope_map_space` option; +- Added PEP257 checker; +- Support 'pudb' in breakpoints; +- Pyrun can now operate on a range of lines, and does not need to save + (c) lawrenceakka +- Update pylama to version 1.5.0 +- Add a set of `g:pymode_lint_*_symbol` options (c) kdeldycke; +- Support virtualenv for python3 (c) mlmoses; + +## 2013-05-15 0.6.18 + +- Fixed autopep8 (PyLintAuto) command; +- Fix error on non-ascii characters in docstrings; +- Update python syntax; + +## 2013-05-03 0.6.17 + +- Update Pylint to version 0.28.0; +- Update pyflakes to version 0.7.3; +- Fixed lint\_ignore options bug; +- Fixed encoding problems when code running; + +## 2013-04-26 0.6.16 + +- Improvement folding (thanks @alvinfrancis); + +## 2013-04-01 0.6.15 + +- Bugfix release + +## 2013-03-16 0.6.14 + +- Update PEP8 to version 1.4.5; +- Update Pylint to version 0.27.0; +- Update pyflakes to version 0.6.1; +- Update autopep8 to version 0.8.7; +- Fix breakpoint definition; +- Update python syntax; +- Fixed run-time error when output non-ascii in multibyte locale; +- Move initialization into ftplugin as it is python specific; +- Pyrex (Cython) files support; +- Support raw\_input in run python code; + +## 2012-09-07 0.6.10 + +- Dont raise an exception when Logger has no message handler (c) nixon +- Improve performance of white space removal (c) Dave Smith +- Improve ropemode support (c) s0undt3ch +- Add `g:pymode_updatetime` option +- Update autopep8 to version 0.8.1 + +## 2012-09-07 0.6.9 + +- Update autopep8 +- Improve `pymode#troubleshooting#Test()` + +## 2012-09-06 0.6.8 + +- Add PEP8 indentation `:help pymode_indent` + +## 2012-08-15 0.6.7 + +- Fix documentation. Thanks (c) bgrant; +- Fix pymode "async queue" support. + +## 2012-08-02 0.6.6 + +- Updated Pep8 to version 1.3.3 +- Updated Pylint to version 0.25.2 +- Fixed virtualenv support for windows users +- Added pymode modeline `:help PythonModeModeline` +- Added diagnostic tool `:call pymode#troubleshooting#Test()` +- Added PyLintAuto command `:help PyLintAuto` +- Code checking is async operation now +- More, more fast the pymode folding +- Repaired execution of python code + +## 2012-05-24 0.6.4 + +- Add `pymode_paths` option +- Rope updated to version 0.9.4 + +## 2012-04-18 0.6.3 + +- Fix pydocs integration + +## 2012-04-10 0.6.2 + +- Fix `pymode_run` for "unnamed" clipboard +- Add `pymode_lint_mccabe_complexity` option +- Update Pep8 to version 1.0.1 +- Warning! Change `pymode_rope_goto_def_newwin` option for open "goto + definition" in new window, set it to 'new' or 'vnew' for horizontally or + vertically split If you use default behaviour (in the same buffer), not + changes needed. + +## 2012-03-13 0.6.0 + +- Add `pymode_lint_hold` option +- Improve pymode loading speed +- Add pep8, mccabe lint checkers +- Now `g:pymode_lint_checker` can have many values Ex. "pep8,pyflakes,mccabe" +- Add `pymode_lint_ignore` and `pymode_lint_select` options +- Fix rope keys +- Fix python motion in visual mode +- Add folding `pymode_folding` +- Warning: `pymode_lint_checker` now set to 'pyflakes,pep8,mccabe' by default + +## 2012-02-12 0.5.8 + +- Fix pylint for Windows users +- Python documentation search running from Vim (delete `g:pydoc` option) +- Python code execution running from Vim (delete `g:python` option) + +## 2012-02-11 0.5.7 + +- Fix `g:pymode_lint_message` mode error +- Fix breakpoints +- Fix python paths and virtualenv detection + +## 2012-02-06 0.5.6 + +- Fix `g:pymode_syntax` option +- Show error message in bottom part of screen see `g:pymode_lint_message` +- Fix pylint for windows users +- Fix breakpoint command (Use pdb when idpb not installed) + +## 2012-01-17 0.5.5 + +- Add a sign for info messages from pylint. (c) Fredrik Henrysson +- Change motion keys: vic - viC, dam - daM and etc +- Add `g:pymode_lint_onfly` option + +## 2012-01-09 0.5.3 + +- Prevent the configuration from breaking python-mode (c) Dirk Wallenstein + +## 2012-01-08 0.5.2 + +- Fix ropeomnicompletion +- Add preview documentation + +## 2012-01-06 0.5.1 + +- Happy new year! +- Objects and motion fixes + +## 2011-11-30 0.5.0 + +- Add python objects and motions (beta) `:h pymode_motion` + +## 2011-11-27 0.4.8 + +- Add PyLintWindowToggle command +- Fix some bugs + +## 2011-11-23 0.4.6 + +- Enable all syntax highlighting For old settings set in your vimrc: + + ``` + let g:pymode_syntax_builtin_objs = 0 + let g:pymode_syntax_builtin_funcs = 0 + ``` + +- Change namespace of syntax variables See README + +## 2011-11-18 0.4.5 + +- Add `g:pymode_syntax` option +- Highlight 'self' keyword + +## 2011-11-16 0.4.4 + +- Minor fixes + +## 2011-11-11 0.4.3 + +- Fix pyflakes + +## 2011-11-09 0.4.2 + +- Add FAQ +- Some refactoring and fixes + +## 2011-11-08 0.4.0 + +- Add alternative code checker "pyflakes" See `:h pymode_lint_checker` +- Update install docs + +## 2011-10-30 0.3.3 + +- Fix RopeShowDoc + +## 2011-10-28 0.3.2 + +- Add `g:pymode_options_*` stuff, for ability to disable default pymode options + for python buffers + +## 2011-10-27 0.3.1 + +- Add `g:pymode_rope_always_show_complete_menu` option +- Some pylint fixes + +## 2011-10-25 0.3.0 + +- Add `g:pymode_lint_minheight` and `g:pymode_lint_maxheight` options +- Fix PyLintToggle +- Fix Rope and PyLint libs loading + +## 2011-10-21 0.2.12 + +- Auto open cwindow with results on rope find operations + +## 2011-10-20 0.2.11 + +- Add `pymode_lint_jump` option + +## 2011-10-19 0.2.10 + +- Minor fixes (virtualenv loading, buffer commands) + +## 2011-10-18 0.2.6 + +- Add `` shortcut for macvim users. +- Add VIRTUALENV support + +## 2011-10-17 0.2.4 + +- Add current work path to sys.path +- Add `g:pymode` option (disable/enable pylint and rope) +- Fix pylint copyright +- Hotfix rope autocomplete + +## 2011-10-15 0.2.1 + +- Change rope variables (`ropevim_` -> `pymode_rope_`) +- Add `pymode_rope_auto_project` option (default: 1) +- Update and fix docs +- `pymode_rope_extended_complete` set by default +- Auto generate rope project and cache +- `r a` for RopeAutoImport + +## 2011-10-12 0.1.4 + +- Add default pylint configuration + +## 2011-10-12 0.1.3 + +- Fix pylint and update docs + +## 2011-10-11 0.1.2 + +- First public release diff --git a/Changelog.rst b/Changelog.rst deleted file mode 100644 index 158c4db5..00000000 --- a/Changelog.rst +++ /dev/null @@ -1,401 +0,0 @@ -Changelog -========= - - -## TODO -------- -* Move changelog rst syntax to markdown -* pymode_rope: check disables -* When loading a file without a history, substituting a word (eg 'cw') moves - the cursor to position 0 (equivalent to 'cw' then '0l') - * Fixed on `917e484` -* Inspect why files starting with: -~~~~~~ -def main(): - pass - - -if __name__ == '__main__': - main() -~~~~~~ -do not get loaded. - - -## 2017-07-xxx 0.9.5 --------------------- -* pylama: migrated to submodule - - -## 2017-07-11 0.9.4 --------------------- -* pylama: fixed erratic behavior of `skip` option causing unintended skipping - of lint checkers -* PEP257 requires `snowbalstemmer`: added as submodule -* Fixed handling of `g:pymode_lint_ignore` and `g:pymode_lint_select`: from - strings to list -* Migrated modules from `pymode/libs` to `submodules/ `__ - * Rationale: no need to single handedly update each module; removes burden - from developers -* Improved folding accuracy - * Improved nested definitions folding - * Improved block delimiting - - -## (changelog poorly maintained) 0.8.2 --------------------------------------- -* Pylama updated to version 5.0.5 -* Rope libs updated -* Add wdb to debugger list in breakpoint cmd -* Add 'pymode_options_max_line_length' option -* Add ability to set related checker options `:help pymode-lint-options` - Options added: 'pymode_lint_options_pep8', 'pymode_lint_options_pep257', - 'pymode_lint_options_mccabe', 'pymode_lint_options_pyflakes', - 'pymode_lint_options_pylint' -* Highlight comments inside class/function arg lists -* Don't fold single line def -* Don't skip a line when the first docstring contains text -* Add Python documentation vertical display option -* Rope: correct refactoring function calls - - -## 2014-06-11 0.8.1 -------------------- -* Pylama updated to version 3.3.2 -* Get fold's expression symbol from &fillchars; -* Fixed error when setting g:pymode_breakpoint_cmd (expobrain); -* Fixed code running; -* Ability to override rope project root and .ropeproject folder -* Added path argument to `PymodeRopeNewProject` which skips prompt -* Disable `pymode_rope_lookup_project` by default -* Options added: - 'pymode_rope_project_root', 'pymode_rope_ropefolder' - - -## 2013-12-04 0.7.8b --------------------- - * Update indentation support; - * Python3 support; - * Removed pymode modeline support; - * Disabled async code checking support; - * Options changes: - 'pymode_doc_key' -> 'pymode_doc_bind' - 'pymode_run_key' -> 'pymode_run_bind' - 'pymode_breakpoint_key' -> 'pymode_breakpoint_bind' - 'pymode_breakpoint_template' -> 'pymode_breakpoint_cmd' - 'pymode_lint_write' -> 'pymode_lint_on_write' - 'pymode_lint_onfly' -> 'pymode_lint_on_fly' - 'pymode_lint_checker' -> 'pymode_lint_checkers' - 'pymode_lint_minheight' -> 'pymode_quickfix_minheight' - 'pymode_lint_maxheight' -> 'pymode_quickfix_maxheight' - 'pymode_rope_autocomplete_map' -> 'pymode_rope_completion_bind' - 'pymode_rope_enable_autoimport' -> 'pymode_rope_autoimport' - - * Options removed: - - 'pymode_lint_hold', 'pymode_lint_config', 'pymode_lint_jump', - 'pymode_lint_signs_always_visible', 'pymode_rope_extended_complete', - 'pymode_rope_auto_project', 'pymode_rope_autoimport_generate', - 'pymode_rope_autoimport_underlines', 'pymode_rope_codeassist_maxfixes', - 'pymode_rope_sorted_completions', 'pymode_rope_extended_complete', - 'pymode_rope_confirm_saving', 'pymode_rope_global_prefix', - 'pymode_rope_local_prefix', 'pymode_rope_vim_completion', - 'pymode_rope_guess_project', 'pymode_rope_goto_def_newwin', - 'pymode_rope_always_show_complete_menu' - - * Options added: - 'pymode_rope_regenerate_on_write', 'pymode_rope_completion', - 'pymode_rope_complete_on_dot', 'pymode_lint_sort', - 'pymode_rope_lookup_project', 'pymode_lint_unmodified' - - * Commands added: - 'PymodeVirtualenv' - - * Commands changed: - 'PyDoc' -> 'PymodeDoc' - 'Pyrun' -> 'PymodeRun' - 'PyLintToggle' -> 'PymodeLintToggle' - 'PyLint' -> 'PymodeLint' - 'PyLintAuto' -> 'PymodeLintAuto' - 'RopeOpenProject' -> 'PymodeRopeNewProject' - 'RopeUndo' -> 'PymodeRopeUndo' - 'RopeRedo' -> 'PymodeRopeRedo' - 'RopeRenameCurrentModule' -> 'PymodeRopeRenameModule' - 'RopeModuleToPackage' -> 'PymodeRopeModuleToPackage' - 'RopeGenerateAutoimportCache' -> 'PymodeRopeRegenerate' - 'RopeOrgamizeImports' -> 'PymodeRopeAutoImport' - - * Commands removed: - 'PyLintCheckerToggle', 'RopeCloseProject', 'RopeProjectConfig', - 'RopeRename', 'RopeCreate<...>', 'RopeWriteProject', 'RopeRename', - 'RopeExtractVariable', 'RopeExtractMethod', 'RopeInline', 'RopeMove', - 'RopeRestructure', 'RopeUseFunction', 'RopeIntroduceFactory', - 'RopeChangeSignature', 'RopeMoveCurrentModule', - 'RopeGenerate<...>', 'RopeAnalizeModule', 'RopeAutoImport', - - -## 2013-10-29 0.6.19 --------------------- -* Added `g:pymode_rope_autocomplete_map` option; -* Removed `g:pymode_rope_map_space` option; -* Added PEP257 checker; -* Support 'pudb' in breakpoints; -* Pyrun can now operate on a range of lines, and does not need to save (c) lawrenceakka -* Update pylama to version 1.5.0 -* Add a set of `g:pymode_lint_*_symbol` options (c) kdeldycke; -* Support virtualenv for python3 (c) mlmoses; - -## 2013-05-15 0.6.18 --------------------- -* Fixed autopep8 (`PyLintAuto`) command; -* Fix error on non-ascii characters in docstrings; -* Update python syntax; - -## 2013-05-03 0.6.17 --------------------- -* Update `Pylint` to version 0.28.0; -* Update `pyflakes` to version 0.7.3; -* Fixed `lint_ignore` options bug; -* Fixed encoding problems when code running; - -## 2013-04-26 0.6.16 --------------------- -* Improvement folding (thanks @alvinfrancis); - -## 2013-04-01 0.6.15 --------------------- -* Bugfix release - -## 2013-03-16 0.6.14 --------------------- -* Update `PEP8` to version 1.4.5; -* Update `Pylint` to version 0.27.0; -* Update `pyflakes` to version 0.6.1; -* Update `autopep8` to version 0.8.7; -* Fix breakpoint definition; -* Update python syntax; -* Fixed run-time error when output non-ascii in multibyte locale; -* Move initialization into ftplugin as it is python specific; -* Pyrex (Cython) files support; -* Support `raw_input` in run python code; - -## 2012-09-07 0.6.10 --------------------- -* Dont raise an exception when Logger has no message handler (c) nixon -* Improve performance of white space removal (c) Dave Smith -* Improve ropemode support (c) s0undt3ch -* Add `g:pymode_updatetime` option -* Update autopep8 to version 0.8.1 - -## 2012-09-07 0.6.9 -------------------- -* Update autopep8 -* Improve pymode#troubleshooting#Test() - -## 2012-09-06 0.6.8 -------------------- -* Add PEP8 indentation ":help 'pymode_indent'" - -## 2012-08-15 0.6.7 -------------------- -* Fix documentation. Thanks (c) bgrant; -* Fix pymode "async queue" support. - -## 2012-08-02 0.6.6 -------------------- -* Updated Pep8 to version 1.3.3 -* Updated Pylint to version 0.25.2 -* Fixed virtualenv support for windows users -* Added pymode modeline ':help PythonModeModeline' -* Added diagnostic tool ':call pymode#troubleshooting#Test()' -* Added `PyLintAuto` command ':help PyLintAuto' -* Code checking is async operation now -* More, more fast the pymode folding -* Repaired execution of python code - -## 2012-05-24 0.6.4 -------------------- -* Add 'pymode_paths' option -* Rope updated to version 0.9.4 - -## 2012-04-18 0.6.3 -------------------- -* Fix pydocs integration - -## 2012-04-10 0.6.2 -------------------- -* Fix pymode_run for "unnamed" clipboard -* Add 'pymode_lint_mccabe_complexity' option -* Update Pep8 to version 1.0.1 -* Warning! Change 'pymode_rope_goto_def_newwin' option - for open "goto definition" in new window, set it to 'new' or 'vnew' - for horizontally or vertically split - If you use default behaviour (in the same buffer), not changes needed. - -## 2012-03-13 0.6.0 -------------------- -* Add 'pymode_lint_hold' option -* Improve pymode loading speed -* Add pep8, mccabe lint checkers -* Now g:pymode_lint_checker can have many values - Ex. "pep8,pyflakes,mccabe" -* Add 'pymode_lint_ignore' and 'pymode_lint_select' options -* Fix rope keys -* Fix python motion in visual mode -* Add folding 'pymode_folding' -* Warning: 'pymode_lint_checker' now set to 'pyflakes,pep8,mccabe' by default - -## 2012-02-12 0.5.8 -------------------- -* Fix pylint for Windows users -* Python documentation search running from Vim (delete g:pydoc option) -* Python code execution running from Vim (delete g:python option) - -## 2012-02-11 0.5.7 -------------------- -* Fix 'g:pymode_lint_message' mode error -* Fix breakpoints -* Fix python paths and virtualenv detection - -## 2012-02-06 0.5.6 -------------------- -* Fix 'g:pymode_syntax' option -* Show error message in bottom part of screen - see 'g:pymode_lint_message' -* Fix pylint for windows users -* Fix breakpoint command (Use pdb when idpb not installed) - -## 2012-01-17 0.5.5 -------------------- -* Add a sign for info messages from pylint. - (c) Fredrik Henrysson -* Change motion keys: vic - viC, dam - daM and etc -* Add 'g:pymode_lint_onfly' option - -## 2012-01-09 0.5.3 -------------------- -* Prevent the configuration from breaking python-mode - (c) Dirk Wallenstein - -## 2012-01-08 0.5.2 -------------------- -* Fix ropeomnicompletion -* Add preview documentation - -## 2012-01-06 0.5.1 -------------------- -* Happy new year! -* Objects and motion fixes - -## 2011-11-30 0.5.0 -------------------- -* Add python objects and motions (beta) - :h pymode_motion - -## 2011-11-27 0.4.8 -------------------- -* Add `PyLintWindowToggle` command -* Fix some bugs - -## 2011-11-23 0.4.6 -------------------- -* Enable all syntax highlighting - For old settings set in your vimrc: - - :: - - let g:pymode_syntax_builtin_objs = 0 - let g:pymode_syntax_builtin_funcs = 0 - -* Change namespace of syntax variables - See README - -## 2011-11-18 0.4.5 -------------------- -* Add 'g:pymode_syntax' option -* Highlight 'self' keyword - -## 2011-11-16 0.4.4 -------------------- -* Minor fixes - -## 2011-11-11 0.4.3 -------------------- -* Fix pyflakes - -## 2011-11-09 0.4.2 -------------------- -* Add FAQ -* Some refactoring and fixes - -## 2011-11-08 0.4.0 -------------------- -* Add alternative code checker "pyflakes" - See :h 'pymode_lint_checker' -* Update install docs - -## 2011-10-30 0.3.3 -------------------- -* Fix RopeShowDoc - -## 2011-10-28 0.3.2 -------------------- -* Add 'g:pymode_options_*' stuff, for ability - to disable default pymode options for python buffers - -## 2011-10-27 0.3.1 -------------------- -* Add 'g:pymode_rope_always_show_complete_menu' option -* Some pylint fixes - -## 2011-10-25 0.3.0 -------------------- -* Add g:pymode_lint_minheight and g:pymode_lint_maxheight - options -* Fix PyLintToggle -* Fix Rope and PyLint libs loading - -## 2011-10-21 0.2.12 --------------------- -* Auto open cwindow with results - on rope find operations - -## 2011-10-20 0.2.11 --------------------- -* Add 'pymode_lint_jump' option - -## 2011-10-19 0.2.10 --------------------- -* Minor fixes (virtualenv loading, buffer commands) - -## 2011-10-18 0.2.6 -------------------- -* Add shortcut for macvim users. -* Add VIRTUALENV support - -## 2011-10-17 0.2.4 -------------------- -* Add current work path to sys.path -* Add 'g:pymode' option (disable/enable pylint and rope) -* Fix pylint copyright -* Hotfix rope autocomplete - -## 2011-10-15 0.2.1 -------------------- -* Change rope variables (ropevim_ -> pymode_rope_) -* Add "pymode_rope_auto_project" option (default: 1) -* Update and fix docs -* 'pymode_rope_extended_complete' set by default -* Auto generate rope project and cache -* "r a" for RopeAutoImport - -## 2011-10-12 0.1.4 -------------------- -* Add default pylint configuration - -## 2011-10-12 0.1.3 -------------------- -* Fix pylint and update docs - -## 2011-10-11 0.1.2 -------------------- -* First public release diff --git a/after/ftplugin/python.vim b/after/ftplugin/python.vim index 4f29b753..6b5a8839 100644 --- a/after/ftplugin/python.vim +++ b/after/ftplugin/python.vim @@ -11,36 +11,38 @@ if g:pymode_motion finish endif - nnoremap ]] :call pymode#motion#move('v^(classdef)s', '') - nnoremap [[ :call pymode#motion#move('v^(classdef)s', 'b') - nnoremap ]C :call pymode#motion#move('v^(classdef)s', '') - nnoremap [C :call pymode#motion#move('v^(classdef)s', 'b') - nnoremap ]M :call pymode#motion#move('^s*defs', '') - nnoremap [M :call pymode#motion#move('^s*defs', 'b') - - onoremap ]] :call pymode#motion#move('v^(classdef)s', '') - onoremap [[ :call pymode#motion#move('v^(classdef)s', 'b') - onoremap ]C :call pymode#motion#move('v^(classdef)s', '') - onoremap [C :call pymode#motion#move('v^(classdef)s', 'b') - onoremap ]M :call pymode#motion#move('^s*defs', '') - onoremap [M :call pymode#motion#move('^s*defs', 'b') - - vnoremap ]] :call pymode#motion#vmove('v^(classdef)s', '') - vnoremap [[ :call pymode#motion#vmove('v^(classdef)s', 'b') - vnoremap ]M :call pymode#motion#vmove('^s*defs', '') - vnoremap [M :call pymode#motion#vmove('^s*defs', 'b') - - onoremap C :call pymode#motion#select('^s*classs', 0) - onoremap aC :call pymode#motion#select('^s*classs', 0) - onoremap iC :call pymode#motion#select('^s*classs', 1) - vnoremap aC :call pymode#motion#select('^s*classs', 0) - vnoremap iC :call pymode#motion#select('^s*classs', 1) - - onoremap M :call pymode#motion#select('^s*defs', 0) - onoremap aM :call pymode#motion#select('^s*defs', 0) - onoremap iM :call pymode#motion#select('^s*defs', 1) - vnoremap aM :call pymode#motion#select('^s*defs', 0) - vnoremap iM :call pymode#motion#select('^s*defs', 1) + nnoremap ]] :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + nnoremap [[ :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + nnoremap ]C :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + nnoremap [C :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + nnoremap ]M :call pymode#motion#move('^s*(asyncs+)=defs', '') + nnoremap [M :call pymode#motion#move('^s*(asyncs+)=defs', 'b') + + onoremap ]] :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + onoremap [[ :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + onoremap ]C :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + onoremap [C :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + onoremap ]M :call pymode#motion#move('^s*(asyncs+)=defs', '') + onoremap [M :call pymode#motion#move('^s*(asyncs+)=defs', 'b') + + vnoremap ]] :call pymode#motion#vmove('^(class%(asyncs+)=def)s', '') + vnoremap [[ :call pymode#motion#vmove('^(class%(asyncs+)=def)s', 'b') + vnoremap ]M :call pymode#motion#vmove('^s*(asyncs+)=defs', '') + vnoremap [M :call pymode#motion#vmove('^s*(asyncs+)=defs', 'b') + + onoremap C :call pymode#motion#select_c('^s*classs', 0) + onoremap aC :call pymode#motion#select_c('^s*classs', 0) + onoremap iC :call pymode#motion#select_c('^s*classs', 1) + vnoremap aC :call pymode#motion#select_c('^s*classs', 0) + vnoremap iC :call pymode#motion#select_c('^s*classs', 1) + + onoremap M :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 0) + onoremap aM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 0) + onoremap iM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 1) + vnoremap aM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 0) + vnoremap iM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 1) + + onoremap V :call pymode#rope#select_logical_line() endif diff --git a/autoload/pymode.vim b/autoload/pymode.vim index 6beb89a8..b637289a 100644 --- a/autoload/pymode.vim +++ b/autoload/pymode.vim @@ -62,7 +62,7 @@ endfunction "}}} " DESC: Open temp buffer. fun! pymode#tempbuffer_open(name) "{{{ pclose - exe "botright 8new " . a:name + exe g:pymode_preview_position . " " . g:pymode_preview_height . "new " . a:name setlocal buftype=nofile bufhidden=delete noswapfile nowrap previewwindow redraw endfunction "}}} diff --git a/autoload/pymode/breakpoint.vim b/autoload/pymode/breakpoint.vim index 823a538a..98639b57 100644 --- a/autoload/pymode/breakpoint.vim +++ b/autoload/pymode/breakpoint.vim @@ -9,16 +9,16 @@ fun! pymode#breakpoint#init() "{{{ PymodePython << EOF -from imp import find_module +from importlib.util import find_spec -for module in ('wdb', 'pudb', 'ipdb', 'pdb'): - try: - find_module(module) - vim.command('let g:pymode_breakpoint_cmd = "import %s; %s.set_trace() # XXX BREAKPOINT"' % (module, module)) - break - except ImportError: - continue +if sys.version_info >= (3, 7): + vim.command('let g:pymode_breakpoint_cmd = "breakpoint()"') +else: + for module in ('wdb', 'pudb', 'ipdb', 'pdb'): + if find_spec(module): + vim.command('let g:pymode_breakpoint_cmd = "import %s; %s.set_trace() # XXX BREAKPOINT"' % (module, module)) + break EOF endif diff --git a/autoload/pymode/debug.vim b/autoload/pymode/debug.vim index cf139b07..2be5149c 100644 --- a/autoload/pymode/debug.vim +++ b/autoload/pymode/debug.vim @@ -30,7 +30,7 @@ fun! pymode#debug#sysinfo() "{{{ echom pymodevar endfor " }}} - " Github commit info. {{{ + " Git commit info. {{{ " Find in the scriptnames the first occurence of 'python-mode'. Then parse " the result outputting its path. This is in turn fed into the git command. call pymode#debug("Git commit: ") @@ -44,6 +44,13 @@ fun! pymode#debug#sysinfo() "{{{ let l:git_head_sha1 = system('git -C ' . expand(l:pymode_folder). ' rev-parse HEAD ' ) echom join(filter(split(l:git_head_sha1, '\zs'), 'v:val =~? "[0-9A-Fa-f]"'), '') " }}} + " Git submodules status. {{{ + call pymode#debug("Git submodule status:") + let l:git_submodule_status = system('git -C ' . expand(l:pymode_folder). ' submodule status') + for submodule in split(l:git_submodule_status, '\n') + echom submodule + endfor + " }}} call pymode#debug("End of pymode#debug#sysinfo") endfunction "}}} diff --git a/autoload/pymode/doc.vim b/autoload/pymode/doc.vim index b89eb0e7..a7a753c5 100644 --- a/autoload/pymode/doc.vim +++ b/autoload/pymode/doc.vim @@ -32,6 +32,9 @@ fun! pymode#doc#show(word) "{{{ if g:pymode_doc_vertical wincmd L endif + + normal gg + wincmd p endfunction "}}} diff --git a/autoload/pymode/indent.vim b/autoload/pymode/indent.vim index efd41f29..e964f378 100644 --- a/autoload/pymode/indent.vim +++ b/autoload/pymode/indent.vim @@ -24,7 +24,9 @@ function! pymode#indent#get_indent(lnum) if closing_paren return indent(parlnum) else - return indent(parlnum) + &shiftwidth + let l:indent_width = (g:pymode_indent_hanging_width > 0 ? + \ g:pymode_indent_hanging_width : &shiftwidth) + return indent(parlnum) + l:indent_width endif else return parcol diff --git a/autoload/pymode/motion.vim b/autoload/pymode/motion.vim index 17377c96..c88fb913 100644 --- a/autoload/pymode/motion.vim +++ b/autoload/pymode/motion.vim @@ -28,57 +28,65 @@ fun! pymode#motion#pos_le(pos1, pos2) "{{{ return ((a:pos1[0] < a:pos2[0]) || (a:pos1[0] == a:pos2[0] && a:pos1[1] <= a:pos2[1])) endfunction "}}} - -fun! pymode#motion#select(pattern, inner) "{{{ +fun! pymode#motion#select(first_pattern, second_pattern, inner) "{{{ let cnt = v:count1 - 1 let orig = getpos('.')[1:2] - let snum = s:BlockStart(orig[0], a:pattern) - if getline(snum) !~ a:pattern + let posns = s:BlockStart(orig[0], a:first_pattern, a:second_pattern) + if getline(posns[0]) !~ a:first_pattern && getline(posns[0]) !~ a:second_pattern return 0 endif - let enum = s:BlockEnd(snum, indent(snum)) + let snum = posns[0] + let enum = s:BlockEnd(posns[1], indent(posns[1])) while cnt - let lnum = search(a:pattern, 'nW') + let lnum = search(a:second_pattern, 'nW') if lnum let enum = s:BlockEnd(lnum, indent(lnum)) call cursor(enum, 1) endif let cnt = cnt - 1 endwhile - if pymode#motion#pos_le([snum, 0], orig) && pymode#motion#pos_le(orig, [enum, 1]) + if pymode#motion#pos_le([snum, 0], orig) && pymode#motion#pos_le(orig, [enum+1, 0]) if a:inner - let snum = snum + 1 - let enum = prevnonblank(enum) + let snum = posns[1] + 1 endif call cursor(snum, 1) - normal! v + normal! V call cursor(enum, len(getline(enum))) endif endfunction "}}} +fun! pymode#motion#select_c(pattern, inner) "{{{ + call pymode#motion#select(a:pattern, a:pattern, a:inner) +endfunction "}}} -fun! s:BlockStart(lnum, ...) "{{{ - let pattern = a:0 ? a:1 : '^\s*\(@\|class\s.*:\|def\s\)' +fun! s:BlockStart(lnum, first_pattern, second_pattern) "{{{ let lnum = a:lnum + 1 let indent = 100 while lnum let lnum = prevnonblank(lnum - 1) let test = indent(lnum) let line = getline(lnum) - if line =~ '^\s*#' " Skip comments - continue - elseif !test " Zero-level regular line - return lnum - elseif test >= indent " Skip deeper or equal lines + " Skip comments, deeper or equal lines + if line =~ '^\s*#' || test >= indent continue - " Indent is strictly less at this point: check for def/class - elseif line =~ pattern && line !~ '^\s*@' - return lnum endif let indent = indent(lnum) + + " Indent is strictly less at this point: check for def/class/@ + if line =~ a:first_pattern || line =~ a:second_pattern + while getline(lnum-1) =~ a:first_pattern + let lnum = lnum - 1 + endwhile + let first_pos = lnum + while getline(lnum) !~ a:second_pattern + let lnum = lnum + 1 + endwhile + let second_pos = lnum + return [first_pos, second_pos] + endif endwhile - return 0 + return [0, 0] endfunction "}}} @@ -89,7 +97,7 @@ fun! s:BlockEnd(lnum, ...) "{{{ let lnum = nextnonblank(lnum + 1) if getline(lnum) =~ '^\s*#' | continue elseif lnum && indent(lnum) <= indent - return lnum - 1 + return prevnonblank(lnum - 1) endif endwhile return line('$') diff --git a/autoload/pymode/rope.vim b/autoload/pymode/rope.vim index ab330f0a..36344d0a 100644 --- a/autoload/pymode/rope.vim +++ b/autoload/pymode/rope.vim @@ -15,14 +15,18 @@ endfunction fun! pymode#rope#complete(dot) if pumvisible() - return "\" + if stridx('noselect', &completeopt) != -1 + return "\" + else + return "" + endif endif if a:dot PymodePython rope.complete(True) else PymodePython rope.complete() endif - return pumvisible() ? "\\" : "" + return pumvisible() && stridx('noselect', &completeopt) != -1 ? "\\" : "" endfunction fun! pymode#rope#complete_on_dot() "{{{ @@ -76,6 +80,9 @@ fun! pymode#rope#show_doc() setlocal nomodifiable setlocal nomodified setlocal filetype=rst + + normal gg + wincmd p endif endfunction @@ -187,3 +194,7 @@ fun! pymode#rope#generate_package() "{{{ endif PymodePython rope.GenerateElementRefactoring('package').run() endfunction "}}} + +fun! pymode#rope#select_logical_line() "{{{ + PymodePython rope.select_logical_line() +endfunction "}}} diff --git a/doc/pymode.txt b/doc/pymode.txt index 37d5fe91..7235b5d5 100644 --- a/doc/pymode.txt +++ b/doc/pymode.txt @@ -1,4 +1,4 @@ -*pymode.txt* For Vim Version 8.0 Last change: 2017 November 20 +*pymode.txt* For Vim Version 8.0 Last change: 2019 March 08 ____ _ _ ____ _ _ _____ _ _ __ __ _____ ____ ____ ~ ( _ \( \/ )(_ _)( )_( )( _ )( \( )___( \/ )( _ )( _ \( ___) ~ @@ -6,7 +6,7 @@ (__) (__) (__) (_) (_)(_____)(_)\_) (_/\/\_)(_____)(____/(____) ~ - Version: 0.9.4 + Version: 0.14.0 =============================================================================== CONTENTS *pymode-contents* @@ -40,7 +40,8 @@ CONTENTS *pymode-contents XXX IMPORTANT: As of 2017-11-18 python-mode is going through a major redesign. Thus some of its functionality may not work as expected. Please be patient and -do report bugs or inconsistencies in its documentation. +do report bugs or inconsistencies in its documentation. But remember to look +for already openned bug reports for the same issue before creating a new one. Python-mode is a vim plugin that allows you to use the pylint, rope, and pydoc libraries in vim to provide features like python code bug checking, @@ -131,18 +132,30 @@ Setup pymode |quickfix| window. let g:pymode_quickfix_minheight = 3 let g:pymode_quickfix_maxheight = 6 +Set pymode |preview| window height. *'g:pymode_preview_height'* +Preview window is used to show documentation and ouput from |pymode-run|. +> + let g:pymode_preview_height = &previewheight + +Set where pymode |preview| window will appear. *'g:pymode_preview_position'* +> + let g:pymode_preview_position = 'botright' + +Value is command which can influcece where new window created by `:new` command +will appear, eg. `:botright`. + ------------------------------------------------------------------------------- 2.1. Python version ~ *pymode-python-version* -By default pymode looks for current python version supported in your Vim. -You could choose prefer version, but value will be tested on loading. +By default pymode will attempt to use Python 3, if available. However, you can +also disable all Python features of pymode. *'g:pymode_python'* > - let g:pymode_python = 'python' + let g:pymode_python = 'python3' -Values are `python`, `python3`, `disable`. If value set to `disable` most +Values are `python3`, `disable`. If value set to `disable` most python-features of **pymode** will be disabled. Set value to `python3` if you are working with python3 projects. You could use @@ -157,14 +170,26 @@ Enable pymode indentation *'g:pymode_indent' > let g:pymode_indent = 1 + +Customization: + +Hanging indent size after an open parenthesis or bracket (but nothing after the +parenthesis), when vertical alignment is not used. Defaults to `&shiftwidth`. + *'g:pymode_indent_hanging_width'* +> + let g:pymode_indent_hanging_width = &shiftwidth + let g:pymode_indent_hanging_width = 4 + ------------------------------------------------------------------------------- 2.3 Python folding ~ *pymode-folding* -Fast and usual python folding in Vim. Enable pymode folding *'g:pymode_folding'* > - let g:pymode_folding = 1 + let g:pymode_folding = 0 + +Currently folding is considered experimental. There are several issues with +its implementation. ------------------------------------------------------------------------------- 2.4 Vim motion ~ @@ -184,10 +209,11 @@ Key Command ]] Jump to next class or function (normal, visual, operator modes) [M Jump to previous class or method (normal, visual, operator modes) ]M Jump to next class or method (normal, visual, operator modes) -aC Select a class. Ex: vaC, daC, yaC, caC (normal, operator modes) -iC Select inner class. Ex: viC, diC, yiC, ciC (normal, operator modes) -aM Select a function or method. Ex: vaM, daM, yaM, caM (normal, operator modes) -iM Select inner function or method. Ex: viM, diM, yiM, ciM (normal, operator modes) +aC Select a class. Ex: vaC, daC, yaC, caC (operator modes) +iC Select inner class. Ex: viC, diC, yiC, ciC (operator modes) +aM Select a function or method. Ex: vaM, daM, yaM, caM (operator modes) +iM Select inner function or method. Ex: viM, diM, yiM, ciM (operator modes) +V Select logical line. Ex: dV, yV, cV (operator modes), also works with count ==== ============================ Enable pymode-motion *'g:pymode_motion'* @@ -267,7 +293,7 @@ Manually set breakpoint command (leave empty for automatic detection) 3. Code checking ~ *pymode-lint* -Pymode supports `pylint`, `pep257`, `pep8`, `pyflakes`, `mccabe` code +Pymode supports `pylint`, `pep257`, `pycodestyle`, `pyflakes`, `mccabe` code checkers. You could run several similar checkers. Pymode uses Pylama library for code checking. Many options like skip @@ -304,9 +330,9 @@ Show error message if cursor placed at the error line *'g:pymode_lint_message' Default code checkers (you could set several) *'g:pymode_lint_checkers'* > - let g:pymode_lint_checkers = ['pyflakes', 'pep8', 'mccabe'] + let g:pymode_lint_checkers = ['pyflakes', 'pycodestyle', 'mccabe'] -Values may be chosen from: `pylint`, `pep8`, `mccabe`, `pep257`, `pyflakes`. +Values may be chosen from: `pylint`, `pycodestyle`, `mccabe`, `pep257`, `pyflakes`. Skip errors and warnings *'g:pymode_lint_ignore'* E.g. ["W", "E2"] (Skip all Warnings and the Errors starting with E2) etc. @@ -350,10 +376,10 @@ Definitions for |signs| Pymode has the ability to set code checkers options from pymode variables: -Set PEP8 options *'g:pymode_lint_options_pep8'* +Set PEP8 options *'g:pymode_lint_options_pycodestyle'* > - let g:pymode_lint_options_pep8 = - \ {'max_line_length': g:pymode_options_max_line_length}) + let g:pymode_lint_options_pycodestyle = + \ {'max_line_length': g:pymode_options_max_line_length} See https://pep8.readthedocs.org/en/1.4.6/intro.html#configuration for more info. @@ -373,7 +399,7 @@ Set pep257 options *'g:pymode_lint_options_pep257' Set pylint options *'g:pymode_lint_options_pylint'* > let g:pymode_lint_options_pylint = - \ {'max-line-length': g:pymode_options_max_line_length}) + \ {'max-line-length': g:pymode_options_max_line_length} See http://docs.pylint.org/features.html#options for more info. @@ -398,6 +424,10 @@ Turn on the rope script *'g:pymode_rope' > let g:pymode_rope = 1 +Set the prefix for rope commands *'g:pymode_rope_prefix'* +> + let g:pymode_rope_refix = '' + .ropeproject Folder ~ *.ropeproject* @@ -468,6 +498,12 @@ your code. and / works too. Autocompletion is also called by typing a period in |Insert| mode by default. +If there's only one complete item, vim may be inserting it automatically +instead of using a popup menu. If the complete item which inserted is not +your wanted, you can roll it back use '' in |Insert| mode or setup +'completeopt' with `menuone` and `noinsert` in your vimrc. .e.g. +> + set completeopt=menuone,noinsert Turn on code completion support in the plugin *'g:pymode_rope_completion'* > @@ -582,14 +618,31 @@ code to call it instead. let g:pymode_rope_use_function_bind = 'ru' -Move method/fields ~ +Move refactoring ~ *pymode-rope-move* +Moving method/fields + It happens when you perform move refactoring on a method of a class. In this refactoring, a method of a class is moved to the class of one of its attributes. The old method will call the new method. If you want to change all of the occurrences of the old method to use the new method you can inline it afterwards. + +Moving global variable/class/function into another module + +It happens when you perform move refactoring on global variable/class/function. +In this refactoring, the object being refactored will be moved to a destination +module. All references to the object being moved will be updated to point to +the new location. + +Moving module variable/class/function into a package + +It happens when you perform move refactoring on a name referencing a module. +In this refactoring, the module being refactored will be moved to a destination +package. All references to the object being moved will be updated to point to +the new location. + > let g:pymode_rope_move_bind = 'rv' @@ -639,6 +692,10 @@ Highlight '=' operator *'g:pymode_syntax_highlight_equal_operator' > let g:pymode_syntax_highlight_equal_operator = g:pymode_syntax_all +Highlight ':=' operator *'g:pymode_syntax_highlight_walrus_operator'* +> + let g:pymode_syntax_highlight_walrus_operator = g:pymode_syntax_all + Highlight '*' operator *'g:pymode_syntax_highlight_stars_operator'* > let g:pymode_syntax_highlight_stars_operator = g:pymode_syntax_all @@ -691,11 +748,22 @@ Highlight docstrings as pythonDocstring (otherwise as pythonString) 1. Python-mode doesn't work --------------------------- -Start python mode with: +First remember to get the latest and updated version of the project source +code and also update the project submodules. + +Clear all python cache/compiled files (`*.pyc` files and `__pycache__` +directory and everything under it). In Linux/Unix/MacOS you can run: + +`find . -type f -name '*.pyc' -delete && find . -type d -name '__pycache__' -delete` + +Then start python mode with: `vim -i NONE -u /debugvimrc.vim` + Reproduce the error and submit your python mode debug file. You can check its location with `:messages` for something like: + `pymode debug msg 1: Starting debug on: 2017-11-18 16:44:13 with file /tmp/pymode_debug_file.txt` + Please submit the entire content of the file along with a reasoning of why the plugin seems broken. @@ -703,6 +771,7 @@ plugin seems broken. *Underlined submitting! + 2. Rope completion is very slow *pymode-rope-slow* ------------------------------- diff --git a/ftplugin/python/pymode.vim b/ftplugin/python/pymode.vim index c13aff71..a1370669 100644 --- a/ftplugin/python/pymode.vim +++ b/ftplugin/python/pymode.vim @@ -5,7 +5,7 @@ endif if g:pymode_python == 'disable' if g:pymode_warning - call pymode#error("Pymode requires vim compiled with +python. Most of features will be disabled.") + call pymode#error("Pymode requires vim compiled with +python3 (exclusively). Most of features will be disabled.") endif finish diff --git a/plugin/pymode.vim b/plugin/pymode.vim index e6ee50f8..b0d99270 100644 --- a/plugin/pymode.vim +++ b/plugin/pymode.vim @@ -1,5 +1,6 @@ " vi: fdl=1 -let g:pymode_version = "0.9.4" +let g:pymode_version = "0.14.0" + " Enable pymode by default :) call pymode#default('g:pymode', 1) @@ -19,7 +20,11 @@ filetype plugin on " OPTIONS: {{{ " Vim Python interpreter. Set to 'disable' for remove python features. -call pymode#default('g:pymode_python', '') +if has("python3") && executable('python3') + call pymode#default('g:pymode_python', 'python3') +else + call pymode#default('g:pymode_python', 'disable') +endif " Disable pymode warnings call pymode#default('g:pymode_warning', 1) @@ -34,8 +39,13 @@ call pymode#default('g:pymode_doc_bind', 'K') " Enable/Disable pymode PEP8 indentation call pymode#default("g:pymode_indent", 1) +" Customize hanging indent size different than &shiftwidth +call pymode#default("g:pymode_indent_hanging_width", -1) + +" TODO: currently folding suffers from a bad performance and incorrect +" implementation. This feature should be considered experimental. " Enable/disable pymode folding for pyfiles. -call pymode#default("g:pymode_folding", 1) +call pymode#default("g:pymode_folding", 0) " Maximum file length to check for nested class/def statements call pymode#default("g:pymode_folding_nest_limit", 1000) " Change for folding customization (by example enable fold for 'if', 'for') @@ -62,6 +72,12 @@ call pymode#default('g:pymode_quickfix_maxheight', 6) " Maximal height of pymode quickfix window call pymode#default('g:pymode_quickfix_minheight', 3) +" Height of preview window +call pymode#default('g:pymode_preview_height', &previewheight) + +" Position of preview window +call pymode#default('g:pymode_preview_position', 'botright') + " LOAD VIRTUALENV {{{ " " Enable virtualenv support @@ -106,8 +122,8 @@ call pymode#default("g:pymode_lint_on_fly", 0) " Show message about error in command line call pymode#default("g:pymode_lint_message", 1) -" Choices are: pylint, pyflakes, pep8, mccabe and pep257 -call pymode#default("g:pymode_lint_checkers", ['pyflakes', 'pep8', 'mccabe']) +" Choices are: pylint, pyflakes, pycodestyle, mccabe and pep257 +call pymode#default("g:pymode_lint_checkers", ['pyflakes', 'pycodestyle', 'mccabe']) " Skip errors and warnings (e.g. E4,W) call pymode#default("g:pymode_lint_ignore", []) @@ -136,8 +152,8 @@ call pymode#default("g:pymode_lint_info_symbol", "II") call pymode#default("g:pymode_lint_pyflakes_symbol", "FF") " Code checkers options -" TODO: check if most adequate name name is pep8 or pycodestyle. -call pymode#default("g:pymode_lint_options_pep8", +" TODO: check if most adequate name name is pycodestyle. +call pymode#default("g:pymode_lint_options_pycodestyle", \ {'max_line_length': g:pymode_options_max_line_length}) call pymode#default("g:pymode_lint_options_pylint", @@ -170,6 +186,7 @@ call pymode#default('g:pymode_breakpoint_cmd', '') " " Rope support call pymode#default('g:pymode_rope', 0) +call pymode#default('g:pymode_rope_prefix', '') " System plugin variable if g:pymode_rope @@ -198,7 +215,7 @@ if g:pymode_rope call pymode#default('g:pymode_rope_autoimport_modules', ['os', 'shutil', 'datetime']) " Bind keys to autoimport module for object under cursor - call pymode#default('g:pymode_rope_autoimport_bind', 'ra') + call pymode#default('g:pymode_rope_autoimport_bind', g:pymode_rope_prefix . 'ra') " Automatic completion on dot call pymode#default('g:pymode_rope_complete_on_dot', 1) @@ -207,56 +224,56 @@ if g:pymode_rope call pymode#default('g:pymode_rope_completion_bind', '') " Bind keys for goto definition (leave empty for disable) - call pymode#default('g:pymode_rope_goto_definition_bind', 'g') + call pymode#default('g:pymode_rope_goto_definition_bind', g:pymode_rope_prefix . 'g') " set command for open definition (e, new, vnew) call pymode#default('g:pymode_rope_goto_definition_cmd', 'new') " Bind keys for show documentation (leave empty for disable) - call pymode#default('g:pymode_rope_show_doc_bind', 'd') + call pymode#default('g:pymode_rope_show_doc_bind', g:pymode_rope_prefix . 'd') " Bind keys for find occurencies (leave empty for disable) - call pymode#default('g:pymode_rope_find_it_bind', 'f') + call pymode#default('g:pymode_rope_find_it_bind', g:pymode_rope_prefix . 'f') " Bind keys for organize imports (leave empty for disable) - call pymode#default('g:pymode_rope_organize_imports_bind', 'ro') + call pymode#default('g:pymode_rope_organize_imports_bind', g:pymode_rope_prefix . 'ro') " Bind keys for rename variable/method/class in the project (leave empty for disable) - call pymode#default('g:pymode_rope_rename_bind', 'rr') + call pymode#default('g:pymode_rope_rename_bind', g:pymode_rope_prefix . 'rr') " Bind keys for rename module - call pymode#default('g:pymode_rope_rename_module_bind', 'r1r') + call pymode#default('g:pymode_rope_rename_module_bind', g:pymode_rope_prefix . 'r1r') " Bind keys for convert module to package - call pymode#default('g:pymode_rope_module_to_package_bind', 'r1p') + call pymode#default('g:pymode_rope_module_to_package_bind', g:pymode_rope_prefix . 'r1p') " Creates a new function or method (depending on the context) from the selected lines - call pymode#default('g:pymode_rope_extract_method_bind', 'rm') + call pymode#default('g:pymode_rope_extract_method_bind', g:pymode_rope_prefix . 'rm') " Creates a variable from the selected lines - call pymode#default('g:pymode_rope_extract_variable_bind', 'rl') + call pymode#default('g:pymode_rope_extract_variable_bind', g:pymode_rope_prefix . 'rl') " Inline refactoring - call pymode#default('g:pymode_rope_inline_bind', 'ri') + call pymode#default('g:pymode_rope_inline_bind', g:pymode_rope_prefix . 'ri') " Move refactoring - call pymode#default('g:pymode_rope_move_bind', 'rv') + call pymode#default('g:pymode_rope_move_bind', g:pymode_rope_prefix . 'rv') " Generate function - call pymode#default('g:pymode_rope_generate_function_bind', 'rnf') + call pymode#default('g:pymode_rope_generate_function_bind', g:pymode_rope_prefix . 'rnf') " Generate class - call pymode#default('g:pymode_rope_generate_class_bind', 'rnc') + call pymode#default('g:pymode_rope_generate_class_bind', g:pymode_rope_prefix . 'rnc') " Generate package - call pymode#default('g:pymode_rope_generate_package_bind', 'rnp') + call pymode#default('g:pymode_rope_generate_package_bind', g:pymode_rope_prefix . 'rnp') " Change signature - call pymode#default('g:pymode_rope_change_signature_bind', 'rs') + call pymode#default('g:pymode_rope_change_signature_bind', g:pymode_rope_prefix . 'rs') " Tries to find the places in which a function can be used and changes the " code to call it instead - call pymode#default('g:pymode_rope_use_function_bind', 'ru') + call pymode#default('g:pymode_rope_use_function_bind', g:pymode_rope_prefix . 'ru') " Regenerate project cache on every save call pymode#default('g:pymode_rope_regenerate_on_write', 1) @@ -272,31 +289,16 @@ 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' - let g:pymode_python = 'python' - elseif exists('g:_uspy') && g:_uspy == ':py3' - let g:pymode_python = 'python3' - elseif has("python") - let g:pymode_python = 'python' - elseif has("python3") + if (exists('g:_uspy') && g:_uspy == ':py3') || has("python3") let g:pymode_python = 'python3' else let g:pymode_python = 'disable' endif endif -if g:pymode_python == 'python' - - command! -nargs=1 PymodePython python - let g:UltiSnipsUsePythonVersion = 2 - -elseif g:pymode_python == 'python3' +if g:pymode_python == 'python3' command! -nargs=1 PymodePython python3 let g:UltiSnipsUsePythonVersion = 3 diff --git a/pymode/__init__.py b/pymode/__init__.py index 2a5dac3d..906d7059 100644 --- a/pymode/__init__.py +++ b/pymode/__init__.py @@ -1,10 +1,19 @@ """Pymode support functions.""" -from __future__ import absolute_import - import sys +from importlib.machinery import PathFinder as _PathFinder + import vim # noqa +if not hasattr(vim, 'find_module'): + try: + vim.find_module = _PathFinder.find_module # deprecated + except AttributeError: + def _find_module(package_name): + spec = _PathFinder.find_spec(package_name) + return spec.loader if spec else None + vim.find_module = _find_module + def auto(): """Fix PEP8 erorrs in current buffer. @@ -34,7 +43,7 @@ class Options(object): def get_documentation(): """Search documentation and append to current buffer.""" - from ._compat import StringIO + from io import StringIO sys.stdout, _ = StringIO(), sys.stdout help(vim.eval('a:word')) diff --git a/pymode/_compat.py b/pymode/_compat.py deleted file mode 100644 index d859f152..00000000 --- a/pymode/_compat.py +++ /dev/null @@ -1,98 +0,0 @@ -""" Compatibility. - - Some py2/py3 compatibility support based on a stripped down - version of six so we don't have to depend on a specific version - of it. - - :copyright: (c) 2014 by Armin Ronacher. - :license: BSD -""" -import sys - -PY2 = sys.version_info[0] == 2 -_identity = lambda x: x - - -if not PY2: - text_type = str - string_types = (str,) - integer_types = (int, ) - - iterkeys = lambda d: iter(d.keys()) - itervalues = lambda d: iter(d.values()) - iteritems = lambda d: iter(d.items()) - - from io import StringIO - from queue import Queue # noqa - - def reraise(tp, value, tb=None): - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - - implements_to_string = _identity - -else: - text_type = unicode - string_types = (str, unicode) - integer_types = (int, long) - - iterkeys = lambda d: d.iterkeys() - itervalues = lambda d: d.itervalues() - iteritems = lambda d: d.iteritems() - - from cStringIO import StringIO - from Queue import Queue - - exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') - - def implements_to_string(cls): - cls.__unicode__ = cls.__str__ - cls.__str__ = lambda x: x.__unicode__().encode('utf-8') - return cls - - -def with_metaclass(meta, *bases): - # This requires a bit of explanation: the basic idea is to make a - # dummy metaclass for one level of class instantiation that replaces - # itself with the actual metaclass. Because of internal type checks - # we also need to make sure that we downgrade the custom metaclass - # for one level to something closer to type (that's why __call__ and - # __init__ comes back from type etc.). - # - # This has the advantage over six.with_metaclass in that it does not - # introduce dummy classes into the final MRO. - class metaclass(meta): - __call__ = type.__call__ - __init__ = type.__init__ - def __new__(cls, name, this_bases, d): - if this_bases is None: - return type.__new__(cls, name, (), d) - return meta(name, bases, d) - return metaclass('temporary_class', None, {}) - - -# Certain versions of pypy have a bug where clearing the exception stack -# breaks the __exit__ function in a very peculiar way. This is currently -# true for pypy 2.2.1 for instance. The second level of exception blocks -# is necessary because pypy seems to forget to check if an exception -# happend until the next bytecode instruction? -BROKEN_PYPY_CTXMGR_EXIT = False -if hasattr(sys, 'pypy_version_info'): - class _Mgr(object): - def __enter__(self): - return self - def __exit__(self, *args): - sys.exc_clear() - try: - try: - with _Mgr(): - raise AssertionError() - except: - raise - except TypeError: - BROKEN_PYPY_CTXMGR_EXIT = True - except AssertionError: - pass - -# pylama:skip=1 diff --git a/pymode/async.py b/pymode/async.py index dd314d76..d211ac4a 100644 --- a/pymode/async.py +++ b/pymode/async.py @@ -1,6 +1,6 @@ """ Python-mode async support. """ -from ._compat import Queue +from queue import Queue # noqa RESULTS = Queue() diff --git a/pymode/environment.py b/pymode/environment.py index 56f49b4a..86527f56 100644 --- a/pymode/environment.py +++ b/pymode/environment.py @@ -1,14 +1,10 @@ """Define interfaces.""" -from __future__ import print_function - import json import os.path import time import vim # noqa -from ._compat import PY2 - class VimPymodeEnviroment(object): @@ -53,10 +49,7 @@ def lines(self): :return list: """ - if not PY2: - return self.curbuf - - return [l.decode(self.options.get('encoding')) for l in self.curbuf] + return self.curbuf @staticmethod def var(name, to_bool=False, silence=False, default=None): @@ -91,24 +84,31 @@ def message(msg, history=False): return vim.command('call pymode#wide_message("%s")' % str(msg)) - def user_input(self, msg, default=''): + def user_input(self, msg='', default=''): """Return user input or default. :return str: """ - msg = '%s %s ' % (self.prefix, msg) + prompt = [] + prompt.append(str(self.prefix.strip())) + prompt.append(str(msg).strip()) if default != '': - msg += '[%s] ' % default + prompt.append('[%s]' % default) + + prompt.append('> ') + prompt = ' '.join([s for s in prompt if s]) + + vim.command('echohl Debug') try: - vim.command('echohl Debug') - input_str = vim.eval('input("%s> ")' % msg) - vim.command('echohl none') + input_str = vim.eval('input(%r)' % (prompt,)) except KeyboardInterrupt: input_str = '' + vim.command('echohl none') + return input_str or default def user_confirm(self, msg, yes=False): @@ -201,9 +201,6 @@ def prepare_value(self, value, dumps=True): if dumps: value = json.dumps(value) - if PY2: - value = value.decode('utf-8').encode(self.options.get('encoding')) - return value def get_offset_params(self, cursor=None, base=""): @@ -245,5 +242,8 @@ def goto_buffer(bufnr): if str(bufnr) != '-1': vim.command('buffer %s' % bufnr) + def select_line(self, start, end): + vim.command('normal %sggV%sgg' % (start, end)) + env = VimPymodeEnviroment() diff --git a/pymode/libs/astroid/interpreter/__init__.py b/pymode/libs/__init__.py similarity index 100% rename from pymode/libs/astroid/interpreter/__init__.py rename to pymode/libs/__init__.py diff --git a/pymode/libs/_markerlib/__init__.py b/pymode/libs/_markerlib/__init__.py deleted file mode 100644 index e2b237b1..00000000 --- a/pymode/libs/_markerlib/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -try: - import ast - from _markerlib.markers import default_environment, compile, interpret -except ImportError: - if 'ast' in globals(): - raise - def default_environment(): - return {} - def compile(marker): - def marker_fn(environment=None, override=None): - # 'empty markers are True' heuristic won't install extra deps. - return not marker.strip() - marker_fn.__doc__ = marker - return marker_fn - def interpret(marker, environment=None, override=None): - return compile(marker)() diff --git a/pymode/libs/_markerlib/markers.py b/pymode/libs/_markerlib/markers.py deleted file mode 100644 index fa837061..00000000 --- a/pymode/libs/_markerlib/markers.py +++ /dev/null @@ -1,119 +0,0 @@ -# -*- coding: utf-8 -*- -"""Interpret PEP 345 environment markers. - -EXPR [in|==|!=|not in] EXPR [or|and] ... - -where EXPR belongs to any of those: - - python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1]) - python_full_version = sys.version.split()[0] - os.name = os.name - sys.platform = sys.platform - platform.version = platform.version() - platform.machine = platform.machine() - platform.python_implementation = platform.python_implementation() - a free string, like '2.6', or 'win32' -""" - -__all__ = ['default_environment', 'compile', 'interpret'] - -import ast -import os -import platform -import sys -import weakref - -_builtin_compile = compile - -try: - from platform import python_implementation -except ImportError: - if os.name == "java": - # Jython 2.5 has ast module, but not platform.python_implementation() function. - def python_implementation(): - return "Jython" - else: - raise - - -# restricted set of variables -_VARS = {'sys.platform': sys.platform, - 'python_version': '%s.%s' % sys.version_info[:2], - # FIXME parsing sys.platform is not reliable, but there is no other - # way to get e.g. 2.7.2+, and the PEP is defined with sys.version - 'python_full_version': sys.version.split(' ', 1)[0], - 'os.name': os.name, - 'platform.version': platform.version(), - 'platform.machine': platform.machine(), - 'platform.python_implementation': python_implementation(), - 'extra': None # wheel extension - } - -for var in list(_VARS.keys()): - if '.' in var: - _VARS[var.replace('.', '_')] = _VARS[var] - -def default_environment(): - """Return copy of default PEP 385 globals dictionary.""" - return dict(_VARS) - -class ASTWhitelist(ast.NodeTransformer): - def __init__(self, statement): - self.statement = statement # for error messages - - ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str) - # Bool operations - ALLOWED += (ast.And, ast.Or) - # Comparison operations - ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn) - - def visit(self, node): - """Ensure statement only contains allowed nodes.""" - if not isinstance(node, self.ALLOWED): - raise SyntaxError('Not allowed in environment markers.\n%s\n%s' % - (self.statement, - (' ' * node.col_offset) + '^')) - return ast.NodeTransformer.visit(self, node) - - def visit_Attribute(self, node): - """Flatten one level of attribute access.""" - new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx) - return ast.copy_location(new_node, node) - -def parse_marker(marker): - tree = ast.parse(marker, mode='eval') - new_tree = ASTWhitelist(marker).generic_visit(tree) - return new_tree - -def compile_marker(parsed_marker): - return _builtin_compile(parsed_marker, '', 'eval', - dont_inherit=True) - -_cache = weakref.WeakValueDictionary() - -def compile(marker): - """Return compiled marker as a function accepting an environment dict.""" - try: - return _cache[marker] - except KeyError: - pass - if not marker.strip(): - def marker_fn(environment=None, override=None): - """""" - return True - else: - compiled_marker = compile_marker(parse_marker(marker)) - def marker_fn(environment=None, override=None): - """override updates environment""" - if override is None: - override = {} - if environment is None: - environment = default_environment() - environment.update(override) - return eval(compiled_marker, environment) - marker_fn.__doc__ = marker - _cache[marker] = marker_fn - return _cache[marker] - -def interpret(marker, environment=None): - return compile(marker)(environment) diff --git a/pymode/libs/appdirs.py b/pymode/libs/appdirs.py new file mode 120000 index 00000000..da7cbf20 --- /dev/null +++ b/pymode/libs/appdirs.py @@ -0,0 +1 @@ +../../submodules/appdirs/appdirs.py \ No newline at end of file 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/__init__.py b/pymode/libs/astroid/__init__.py deleted file mode 100644 index 69b2ff0c..00000000 --- a/pymode/libs/astroid/__init__.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2006-2013, 2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Google, Inc. -# 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 - -"""Python Abstract Syntax Tree New Generation - -The aim of this module is to provide a common base representation of -python source code for projects such as pychecker, pyreverse, -pylint... Well, actually the development of this library is essentially -governed by pylint's needs. - -It extends class defined in the python's _ast module with some -additional methods and attributes. Instance attributes are added by a -builder object, which can either generate extended ast (let's call -them astroid ;) by visiting an existent ast tree or by inspecting living -object. Methods are added by monkey patching ast classes. - -Main modules are: - -* nodes and scoped_nodes for more information about methods and - attributes added to different node classes - -* the manager contains a high level object to get astroid trees from - source files and living objects. It maintains a cache of previously - constructed tree for quick access - -* builder contains the class responsible to build astroid trees -""" - -import os -import sys -import re -from operator import attrgetter - -import enum - - -_Context = enum.Enum('Context', 'Load Store Del') -Load = _Context.Load -Store = _Context.Store -Del = _Context.Del -del _Context - - -from .__pkginfo__ import version as __version__ -# WARNING: internal imports order matters ! - -# pylint: disable=redefined-builtin, wildcard-import - -# make all exception classes accessible from astroid package -from astroid.exceptions import * - -# make all node classes accessible from astroid package -from astroid.nodes import * - -# trigger extra monkey-patching -from astroid import inference - -# more stuff available -from astroid import raw_building -from astroid.bases import BaseInstance, Instance, BoundMethod, UnboundMethod -from astroid.node_classes import are_exclusive, unpack_infer -from astroid.scoped_nodes import builtin_lookup -from astroid.builder import parse, extract_node -from astroid.util import Uninferable, YES - -# make a manager instance (borg) accessible from astroid package -from astroid.manager import AstroidManager -MANAGER = AstroidManager() -del AstroidManager - -# transform utilities (filters and decorator) - -class AsStringRegexpPredicate(object): - """ClassDef to be used as predicate that may be given to `register_transform` - - First argument is a regular expression that will be searched against the `as_string` - representation of the node onto which it's applied. - - If specified, the second argument is an `attrgetter` expression that will be - applied on the node first to get the actual node on which `as_string` should - be called. - - WARNING: This can be fairly slow, as it has to convert every AST node back - to Python code; you should consider examining the AST directly instead. - """ - def __init__(self, regexp, expression=None): - self.regexp = re.compile(regexp) - self.expression = expression - - def __call__(self, node): - if self.expression is not None: - node = attrgetter(self.expression)(node) - # pylint: disable=no-member; github.com/pycqa/astroid/126 - return self.regexp.search(node.as_string()) - -def inference_tip(infer_function): - """Given an instance specific inference function, return a function to be - given to MANAGER.register_transform to set this inference function. - - Typical usage - - .. sourcecode:: python - - MANAGER.register_transform(Call, inference_tip(infer_named_tuple), - predicate) - """ - def transform(node, infer_function=infer_function): - node._explicit_inference = infer_function - return node - return transform - - -def register_module_extender(manager, module_name, get_extension_mod): - def transform(node): - extension_module = get_extension_mod() - for name, objs in extension_module.locals.items(): - node.locals[name] = objs - for obj in objs: - if obj.parent is extension_module: - obj.parent = node - - manager.register_transform(Module, transform, lambda n: n.name == module_name) - - -# load brain plugins -BRAIN_MODULES_DIR = os.path.join(os.path.dirname(__file__), 'brain') -if BRAIN_MODULES_DIR not in sys.path: - # add it to the end of the list so user path take precedence - sys.path.append(BRAIN_MODULES_DIR) -# load modules in this directory -for module in os.listdir(BRAIN_MODULES_DIR): - if module.endswith('.py'): - __import__(module[:-3]) 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/arguments.py b/pymode/libs/astroid/arguments.py deleted file mode 100644 index f81ccbd6..00000000 --- a/pymode/libs/astroid/arguments.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# 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 - - -import six - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import nodes -from astroid import util - - - -class CallSite(object): - """Class for understanding arguments passed into a call site - - It needs a call context, which contains the arguments and the - keyword arguments that were passed into a given call site. - In order to infer what an argument represents, call - :meth:`infer_argument` with the corresponding function node - and the argument name. - """ - - def __init__(self, callcontext): - args = callcontext.args - keywords = callcontext.keywords - self.duplicated_keywords = set() - self._unpacked_args = self._unpack_args(args) - self._unpacked_kwargs = self._unpack_keywords(keywords) - - self.positional_arguments = [ - arg for arg in self._unpacked_args - if arg is not util.Uninferable - ] - self.keyword_arguments = { - key: value for key, value in self._unpacked_kwargs.items() - if value is not util.Uninferable - } - - @classmethod - def from_call(cls, call_node): - """Get a CallSite object from the given Call node.""" - callcontext = contextmod.CallContext(call_node.args, - call_node.keywords) - return cls(callcontext) - - def has_invalid_arguments(self): - """Check if in the current CallSite were passed *invalid* arguments - - This can mean multiple things. For instance, if an unpacking - of an invalid object was passed, then this method will return True. - Other cases can be when the arguments can't be inferred by astroid, - for example, by passing objects which aren't known statically. - """ - return len(self.positional_arguments) != len(self._unpacked_args) - - def has_invalid_keywords(self): - """Check if in the current CallSite were passed *invalid* keyword arguments - - For instance, unpacking a dictionary with integer keys is invalid - (**{1:2}), because the keys must be strings, which will make this - method to return True. Other cases where this might return True if - objects which can't be inferred were passed. - """ - return len(self.keyword_arguments) != len(self._unpacked_kwargs) - - def _unpack_keywords(self, keywords): - values = {} - context = contextmod.InferenceContext() - for name, value in keywords: - if name is None: - # Then it's an unpacking operation (**) - try: - inferred = next(value.infer(context=context)) - except exceptions.InferenceError: - values[name] = util.Uninferable - continue - - if not isinstance(inferred, nodes.Dict): - # Not something we can work with. - values[name] = util.Uninferable - continue - - for dict_key, dict_value in inferred.items: - try: - dict_key = next(dict_key.infer(context=context)) - except exceptions.InferenceError: - values[name] = util.Uninferable - continue - if not isinstance(dict_key, nodes.Const): - values[name] = util.Uninferable - continue - if not isinstance(dict_key.value, six.string_types): - values[name] = util.Uninferable - continue - if dict_key.value in values: - # The name is already in the dictionary - values[dict_key.value] = util.Uninferable - self.duplicated_keywords.add(dict_key.value) - continue - values[dict_key.value] = dict_value - else: - values[name] = value - return values - - @staticmethod - def _unpack_args(args): - values = [] - context = contextmod.InferenceContext() - for arg in args: - if isinstance(arg, nodes.Starred): - try: - inferred = next(arg.value.infer(context=context)) - except exceptions.InferenceError: - values.append(util.Uninferable) - continue - - if inferred is util.Uninferable: - values.append(util.Uninferable) - continue - if not hasattr(inferred, 'elts'): - values.append(util.Uninferable) - continue - values.extend(inferred.elts) - else: - values.append(arg) - return values - - def infer_argument(self, funcnode, name, context): - """infer a function argument value according to the call context - - Arguments: - funcnode: The function being called. - name: The name of the argument whose value is being inferred. - context: TODO - """ - if name in self.duplicated_keywords: - raise exceptions.InferenceError('The arguments passed to {func!r} ' - ' have duplicate keywords.', - call_site=self, func=funcnode, - arg=name, context=context) - - # Look into the keywords first, maybe it's already there. - try: - return self.keyword_arguments[name].infer(context) - except KeyError: - pass - - # Too many arguments given and no variable arguments. - if len(self.positional_arguments) > len(funcnode.args.args): - if not funcnode.args.vararg: - raise exceptions.InferenceError('Too many positional arguments ' - 'passed to {func!r} that does ' - 'not have *args.', - call_site=self, func=funcnode, - arg=name, context=context) - - positional = self.positional_arguments[:len(funcnode.args.args)] - vararg = self.positional_arguments[len(funcnode.args.args):] - argindex = funcnode.args.find_argname(name)[0] - kwonlyargs = set(arg.name for arg in funcnode.args.kwonlyargs) - kwargs = { - key: value for key, value in self.keyword_arguments.items() - if key not in kwonlyargs - } - # If there are too few positionals compared to - # what the function expects to receive, check to see - # if the missing positional arguments were passed - # as keyword arguments and if so, place them into the - # positional args list. - if len(positional) < len(funcnode.args.args): - for func_arg in funcnode.args.args: - if func_arg.name in kwargs: - arg = kwargs.pop(func_arg.name) - positional.append(arg) - - if argindex is not None: - # 2. first argument of instance/class method - if argindex == 0 and funcnode.type in ('method', 'classmethod'): - if context.boundnode is not None: - boundnode = context.boundnode - else: - # XXX can do better ? - boundnode = funcnode.parent.frame() - - if isinstance(boundnode, nodes.ClassDef): - # Verify that we're accessing a method - # of the metaclass through a class, as in - # `cls.metaclass_method`. In this case, the - # first argument is always the class. - method_scope = funcnode.parent.scope() - if method_scope is boundnode.metaclass(): - return iter((boundnode, )) - - if funcnode.type == 'method': - if not isinstance(boundnode, bases.Instance): - boundnode = bases.Instance(boundnode) - return iter((boundnode,)) - if funcnode.type == 'classmethod': - return iter((boundnode,)) - # if we have a method, extract one position - # from the index, so we'll take in account - # the extra parameter represented by `self` or `cls` - if funcnode.type in ('method', 'classmethod'): - argindex -= 1 - # 2. search arg index - try: - return self.positional_arguments[argindex].infer(context) - except IndexError: - pass - - if funcnode.args.kwarg == name: - # It wants all the keywords that were passed into - # the call site. - if self.has_invalid_keywords(): - raise exceptions.InferenceError( - "Inference failed to find values for all keyword arguments " - "to {func!r}: {unpacked_kwargs!r} doesn't correspond to " - "{keyword_arguments!r}.", - keyword_arguments=self.keyword_arguments, - unpacked_kwargs=self._unpacked_kwargs, - call_site=self, func=funcnode, arg=name, context=context) - kwarg = nodes.Dict(lineno=funcnode.args.lineno, - col_offset=funcnode.args.col_offset, - parent=funcnode.args) - kwarg.postinit([(nodes.const_factory(key), value) - for key, value in kwargs.items()]) - return iter((kwarg, )) - elif funcnode.args.vararg == name: - # It wants all the args that were passed into - # the call site. - if self.has_invalid_arguments(): - raise exceptions.InferenceError( - "Inference failed to find values for all positional " - "arguments to {func!r}: {unpacked_args!r} doesn't " - "correspond to {positional_arguments!r}.", - positional_arguments=self.positional_arguments, - unpacked_args=self._unpacked_args, - call_site=self, func=funcnode, arg=name, context=context) - args = nodes.Tuple(lineno=funcnode.args.lineno, - col_offset=funcnode.args.col_offset, - parent=funcnode.args) - args.postinit(vararg) - return iter((args, )) - - # Check if it's a default parameter. - try: - return funcnode.args.default_value(name).infer(context) - except exceptions.NoDefault: - pass - raise exceptions.InferenceError('No value found for argument {name} to ' - '{func!r}', call_site=self, - func=funcnode, arg=name, context=context) diff --git a/pymode/libs/astroid/as_string.py b/pymode/libs/astroid/as_string.py deleted file mode 100644 index 85afdc30..00000000 --- a/pymode/libs/astroid/as_string.py +++ /dev/null @@ -1,527 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""This module renders Astroid nodes as string: - -* :func:`to_code` function return equivalent (hopefully valid) python string - -* :func:`dump` function return an internal representation of nodes found - in the tree, useful for debugging or understanding the tree structure -""" -import sys - -import six - - -# pylint: disable=unused-argument - - -class AsStringVisitor(object): - """Visitor to render an Astroid node as a valid python code string""" - - def __init__(self, indent): - self.indent = indent - - def __call__(self, node): - """Makes this visitor behave as a simple function""" - return node.accept(self) - - def _stmt_list(self, stmts): - """return a list of nodes to string""" - stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr]) - return self.indent + stmts.replace('\n', '\n'+self.indent) - - - ## visit_ methods ########################################### - - def visit_arguments(self, node): - """return an astroid.Function node as string""" - return node.format_args() - - def visit_assignattr(self, node): - """return an astroid.AssAttr node as string""" - return self.visit_attribute(node) - - def visit_assert(self, node): - """return an astroid.Assert node as string""" - if node.fail: - return 'assert %s, %s' % (node.test.accept(self), - node.fail.accept(self)) - return 'assert %s' % node.test.accept(self) - - def visit_assignname(self, node): - """return an astroid.AssName node as string""" - return node.name - - def visit_assign(self, node): - """return an astroid.Assign node as string""" - lhs = ' = '.join([n.accept(self) for n in node.targets]) - return '%s = %s' % (lhs, node.value.accept(self)) - - def visit_augassign(self, node): - """return an astroid.AugAssign node as string""" - return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) - - def visit_annassign(self, node): - """Return an astroid.AugAssign node as string""" - - target = node.target.accept(self) - annotation = node.annotation.accept(self) - if node.value is None: - return '%s: %s' % (target, annotation) - return '%s: %s = %s' % (target, annotation, node.value.accept(self)) - - def visit_repr(self, node): - """return an astroid.Repr node as string""" - return '`%s`' % node.value.accept(self) - - def visit_binop(self, node): - """return an astroid.BinOp node as string""" - return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self)) - - def visit_boolop(self, node): - """return an astroid.BoolOp node as string""" - return (' %s ' % node.op).join(['(%s)' % n.accept(self) - for n in node.values]) - - def visit_break(self, node): - """return an astroid.Break node as string""" - return 'break' - - def visit_call(self, node): - """return an astroid.Call node as string""" - expr_str = node.func.accept(self) - args = [arg.accept(self) for arg in node.args] - if node.keywords: - keywords = [kwarg.accept(self) for kwarg in node.keywords] - else: - keywords = [] - - args.extend(keywords) - return '%s(%s)' % (expr_str, ', '.join(args)) - - def visit_classdef(self, node): - """return an astroid.ClassDef node as string""" - decorate = node.decorators.accept(self) if node.decorators else '' - bases = ', '.join([n.accept(self) for n in node.bases]) - if sys.version_info[0] == 2: - bases = '(%s)' % bases if bases else '' - else: - metaclass = node.metaclass() - if metaclass and not node.has_metaclass_hack(): - if bases: - bases = '(%s, metaclass=%s)' % (bases, metaclass.name) - else: - bases = '(metaclass=%s)' % metaclass.name - else: - bases = '(%s)' % bases if bases else '' - docs = '\n%s"""%s"""' % (self.indent, node.doc) if node.doc else '' - return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, - self._stmt_list(node.body)) - - def visit_compare(self, node): - """return an astroid.Compare node as string""" - rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) - for op, expr in node.ops]) - return '%s %s' % (node.left.accept(self), rhs_str) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - ifs = ''.join([' if %s' % n.accept(self) for n in node.ifs]) - return 'for %s in %s%s' % (node.target.accept(self), - node.iter.accept(self), ifs) - - def visit_const(self, node): - """return an astroid.Const node as string""" - return repr(node.value) - - def visit_continue(self, node): - """return an astroid.Continue node as string""" - return 'continue' - - def visit_delete(self, node): # XXX check if correct - """return an astroid.Delete node as string""" - return 'del %s' % ', '.join([child.accept(self) - for child in node.targets]) - - def visit_delattr(self, node): - """return an astroid.DelAttr node as string""" - return self.visit_attribute(node) - - def visit_delname(self, node): - """return an astroid.DelName node as string""" - return node.name - - def visit_decorators(self, node): - """return an astroid.Decorators node as string""" - return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) - - def visit_dict(self, node): - """return an astroid.Dict node as string""" - return '{%s}' % ', '.join(self._visit_dict(node)) - - def _visit_dict(self, node): - for key, value in node.items: - key = key.accept(self) - value = value.accept(self) - if key == '**': - # It can only be a DictUnpack node. - yield key + value - else: - yield '%s: %s' % (key, value) - - def visit_dictunpack(self, node): - return '**' - - def visit_dictcomp(self, node): - """return an astroid.DictComp node as string""" - return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_expr(self, node): - """return an astroid.Discard node as string""" - return node.value.accept(self) - - def visit_emptynode(self, node): - """dummy method for visiting an Empty node""" - return '' - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s, %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_ellipsis(self, node): - """return an astroid.Ellipsis node as string""" - return '...' - - def visit_empty(self, node): - """return an Empty node as string""" - return '' - - def visit_exec(self, node): - """return an astroid.Exec node as string""" - if node.locals: - return 'exec %s in %s, %s' % (node.expr.accept(self), - node.locals.accept(self), - node.globals.accept(self)) - if node.globals: - return 'exec %s in %s' % (node.expr.accept(self), - node.globals.accept(self)) - return 'exec %s' % node.expr.accept(self) - - def visit_extslice(self, node): - """return an astroid.ExtSlice node as string""" - return ','.join([dim.accept(self) for dim in node.dims]) - - def visit_for(self, node): - """return an astroid.For node as string""" - fors = 'for %s in %s:\n%s' % (node.target.accept(self), - node.iter.accept(self), - self._stmt_list(node.body)) - if node.orelse: - fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) - return fors - - def visit_importfrom(self, node): - """return an astroid.ImportFrom node as string""" - return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, - _import_string(node.names)) - - def visit_functiondef(self, node): - """return an astroid.Function node as string""" - decorate = node.decorators.accept(self) if node.decorators else '' - docs = '\n%s"""%s"""' % (self.indent, node.doc) if node.doc else '' - return_annotation = '' - if six.PY3 and node.returns: - return_annotation = '->' + node.returns.as_string() - trailer = return_annotation + ":" - else: - trailer = ":" - def_format = "\n%sdef %s(%s)%s%s\n%s" - return def_format % (decorate, node.name, - node.args.accept(self), - trailer, docs, - self._stmt_list(node.body)) - - def visit_generatorexp(self, node): - """return an astroid.GeneratorExp node as string""" - return '(%s %s)' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_attribute(self, node): - """return an astroid.Getattr node as string""" - return '%s.%s' % (node.expr.accept(self), node.attrname) - - def visit_global(self, node): - """return an astroid.Global node as string""" - return 'global %s' % ', '.join(node.names) - - def visit_if(self, node): - """return an astroid.If node as string""" - ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))] - if node.orelse:# XXX use elif ??? - ifs.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(ifs) - - def visit_ifexp(self, node): - """return an astroid.IfExp node as string""" - return '%s if %s else %s' % (node.body.accept(self), - node.test.accept(self), - node.orelse.accept(self)) - - def visit_import(self, node): - """return an astroid.Import node as string""" - return 'import %s' % _import_string(node.names) - - def visit_keyword(self, node): - """return an astroid.Keyword node as string""" - if node.arg is None: - return '**%s' % node.value.accept(self) - return '%s=%s' % (node.arg, node.value.accept(self)) - - def visit_lambda(self, node): - """return an astroid.Lambda node as string""" - return 'lambda %s: %s' % (node.args.accept(self), - node.body.accept(self)) - - def visit_list(self, node): - """return an astroid.List node as string""" - return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_listcomp(self, node): - """return an astroid.ListComp node as string""" - return '[%s %s]' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_module(self, node): - """return an astroid.Module node as string""" - docs = '"""%s"""\n\n' % node.doc if node.doc else '' - return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' - - def visit_name(self, node): - """return an astroid.Name node as string""" - return node.name - - def visit_pass(self, node): - """return an astroid.Pass node as string""" - return 'pass' - - def visit_print(self, node): - """return an astroid.Print node as string""" - nodes = ', '.join([n.accept(self) for n in node.values]) - if not node.nl: - nodes = '%s,' % nodes - if node.dest: - return 'print >> %s, %s' % (node.dest.accept(self), nodes) - return 'print %s' % nodes - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.inst: - if node.tback: - return 'raise %s, %s, %s' % (node.exc.accept(self), - node.inst.accept(self), - node.tback.accept(self)) - return 'raise %s, %s' % (node.exc.accept(self), - node.inst.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_return(self, node): - """return an astroid.Return node as string""" - if node.value: - return 'return %s' % node.value.accept(self) - - return 'return' - - def visit_index(self, node): - """return a astroid.Index node as string""" - return node.value.accept(self) - - def visit_set(self, node): - """return an astroid.Set node as string""" - return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_setcomp(self, node): - """return an astroid.SetComp node as string""" - return '{%s %s}' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_slice(self, node): - """return a astroid.Slice node as string""" - lower = node.lower.accept(self) if node.lower else '' - upper = node.upper.accept(self) if node.upper else'' - step = node.step.accept(self) if node.step else '' - if step: - return '%s:%s:%s' % (lower, upper, step) - return '%s:%s' % (lower, upper) - - def visit_subscript(self, node): - """return an astroid.Subscript node as string""" - return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) - - def visit_tryexcept(self, node): - """return an astroid.TryExcept node as string""" - trys = ['try:\n%s' % self._stmt_list(node.body)] - for handler in node.handlers: - trys.append(handler.accept(self)) - if node.orelse: - trys.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(trys) - - def visit_tryfinally(self, node): - """return an astroid.TryFinally node as string""" - return 'try:\n%s\nfinally:\n%s' % (self._stmt_list(node.body), - self._stmt_list(node.finalbody)) - - def visit_tuple(self, node): - """return an astroid.Tuple node as string""" - if len(node.elts) == 1: - return '(%s, )' % node.elts[0].accept(self) - return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_unaryop(self, node): - """return an astroid.UnaryOp node as string""" - if node.op == 'not': - operator = 'not ' - else: - operator = node.op - return '%s%s' % (operator, node.operand.accept(self)) - - def visit_while(self, node): - """return an astroid.While node as string""" - whiles = 'while %s:\n%s' % (node.test.accept(self), - self._stmt_list(node.body)) - if node.orelse: - whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) - return whiles - - def visit_with(self, node): # 'with' without 'as' is possible - """return an astroid.With node as string""" - items = ', '.join(('(%s)' % expr.accept(self)) + - (vars and ' as (%s)' % (vars.accept(self)) or '') - for expr, vars in node.items) - return 'with %s:\n%s' % (items, self._stmt_list(node.body)) - - def visit_yield(self, node): - """yield an ast.Yield node as string""" - yi_val = (" " + node.value.accept(self)) if node.value else "" - expr = 'yield' + yi_val - if node.parent.is_statement: - return expr - - return "(%s)" % (expr,) - - def visit_starred(self, node): - """return Starred node as string""" - return "*" + node.value.accept(self) - - # These aren't for real AST nodes, but for inference objects. - - def visit_frozenset(self, node): - return node.parent.accept(self) - - def visit_super(self, node): - return node.parent.accept(self) - - def visit_uninferable(self, node): - return str(node) - - -class AsStringVisitor3(AsStringVisitor): - """AsStringVisitor3 overwrites some AsStringVisitor methods""" - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s as %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_nonlocal(self, node): - """return an astroid.Nonlocal node as string""" - return 'nonlocal %s' % ', '.join(node.names) - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.cause: - return 'raise %s from %s' % (node.exc.accept(self), - node.cause.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_yieldfrom(self, node): - """ Return an astroid.YieldFrom node as string. """ - yi_val = (" " + node.value.accept(self)) if node.value else "" - expr = 'yield from' + yi_val - if node.parent.is_statement: - return expr - - return "(%s)" % (expr,) - - def visit_asyncfunctiondef(self, node): - function = super(AsStringVisitor3, self).visit_functiondef(node) - return 'async ' + function.strip() - - def visit_await(self, node): - return 'await %s' % node.value.accept(self) - - def visit_asyncwith(self, node): - return 'async %s' % self.visit_with(node) - - def visit_asyncfor(self, node): - return 'async %s' % self.visit_for(node) - - def visit_joinedstr(self, node): - # Special treatment for constants, - # as we want to join literals not reprs - string = ''.join( - value.value if type(value).__name__ == 'Const' - else value.accept(self) - for value in node.values - ) - return "f'%s'" % string - - def visit_formattedvalue(self, node): - return '{%s}' % node.value.accept(self) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - return '%s%s' % ('async ' if node.is_async else '', - super(AsStringVisitor3, self).visit_comprehension(node)) - - -def _import_string(names): - """return a list of (name, asname) formatted as a string""" - _names = [] - for name, asname in names: - if asname is not None: - _names.append('%s as %s' % (name, asname)) - else: - _names.append(name) - return ', '.join(_names) - - -if sys.version_info >= (3, 0): - AsStringVisitor = AsStringVisitor3 - -# This sets the default indent to 4 spaces. -to_code = AsStringVisitor(' ') 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/bases.py b/pymode/libs/astroid/bases.py deleted file mode 100644 index aefbaa1a..00000000 --- a/pymode/libs/astroid/bases.py +++ /dev/null @@ -1,450 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""This module contains base classes and functions for the nodes and some -inference utils. -""" - -import collections -import sys - -import six - -from astroid import context as contextmod -from astroid import exceptions -from astroid import util - -objectmodel = util.lazy_import('interpreter.objectmodel') -BUILTINS = six.moves.builtins.__name__ -manager = util.lazy_import('manager') -MANAGER = manager.AstroidManager() - -if sys.version_info >= (3, 0): - BUILTINS = 'builtins' - BOOL_SPECIAL_METHOD = '__bool__' -else: - BUILTINS = '__builtin__' - BOOL_SPECIAL_METHOD = '__nonzero__' -PROPERTIES = {BUILTINS + '.property', 'abc.abstractproperty'} -# List of possible property names. We use this list in order -# to see if a method is a property or not. This should be -# pretty reliable and fast, the alternative being to check each -# decorator to see if its a real property-like descriptor, which -# can be too complicated. -# Also, these aren't qualified, because each project can -# define them, we shouldn't expect to know every possible -# property-like decorator! -# TODO(cpopa): just implement descriptors already. -POSSIBLE_PROPERTIES = {"cached_property", "cachedproperty", - "lazyproperty", "lazy_property", "reify", - "lazyattribute", "lazy_attribute", - "LazyProperty", "lazy"} - - -def _is_property(meth): - if PROPERTIES.intersection(meth.decoratornames()): - return True - stripped = {name.split(".")[-1] for name in meth.decoratornames() - if name is not util.Uninferable} - return any(name in stripped for name in POSSIBLE_PROPERTIES) - - -class Proxy(object): - """a simple proxy object""" - - _proxied = None # proxied object may be set by class or by instance - - def __init__(self, proxied=None): - if proxied is not None: - self._proxied = proxied - - def __getattr__(self, name): - if name == '_proxied': - return getattr(self.__class__, '_proxied') - if name in self.__dict__: - return self.__dict__[name] - return getattr(self._proxied, name) - - def infer(self, context=None): - yield self - - -def _infer_stmts(stmts, context, frame=None): - """Return an iterator on statements inferred by each statement in *stmts*.""" - stmt = None - inferred = False - if context is not None: - name = context.lookupname - context = context.clone() - else: - name = None - context = contextmod.InferenceContext() - - for stmt in stmts: - if stmt is util.Uninferable: - yield stmt - inferred = True - continue - context.lookupname = stmt._infer_name(frame, name) - try: - for inferred in stmt.infer(context=context): - yield inferred - inferred = True - except exceptions.NameInferenceError: - continue - except exceptions.InferenceError: - yield util.Uninferable - inferred = True - if not inferred: - raise exceptions.InferenceError( - 'Inference failed for all members of {stmts!r}.', - stmts=stmts, frame=frame, context=context) - - -def _infer_method_result_truth(instance, method_name, context): - # Get the method from the instance and try to infer - # its return's truth value. - meth = next(instance.igetattr(method_name, context=context), None) - if meth and hasattr(meth, 'infer_call_result'): - if not meth.callable(): - return util.Uninferable - for value in meth.infer_call_result(instance, context=context): - if value is util.Uninferable: - return value - - inferred = next(value.infer(context=context)) - return inferred.bool_value() - return util.Uninferable - - -class BaseInstance(Proxy): - """An instance base class, which provides lookup methods for potential instances.""" - - special_attributes = None - - def display_type(self): - return 'Instance of' - - def getattr(self, name, context=None, lookupclass=True): - try: - values = self._proxied.instance_attr(name, context) - except exceptions.AttributeInferenceError: - if self.special_attributes and name in self.special_attributes: - return [self.special_attributes.lookup(name)] - - if lookupclass: - # Class attributes not available through the instance - # unless they are explicitly defined. - return self._proxied.getattr(name, context, - class_context=False) - - util.reraise(exceptions.AttributeInferenceError(target=self, - attribute=name, - context=context)) - # since we've no context information, return matching class members as - # well - if lookupclass: - try: - return values + self._proxied.getattr(name, context, - class_context=False) - except exceptions.AttributeInferenceError: - pass - return values - - def igetattr(self, name, context=None): - """inferred getattr""" - if not context: - context = contextmod.InferenceContext() - try: - # avoid recursively inferring the same attr on the same class - if context.push((self._proxied, name)): - return - - # XXX frame should be self._proxied, or not ? - get_attr = self.getattr(name, context, lookupclass=False) - for stmt in _infer_stmts(self._wrap_attr(get_attr, context), - context, frame=self): - yield stmt - except exceptions.AttributeInferenceError: - try: - # fallback to class.igetattr since it has some logic to handle - # descriptors - attrs = self._proxied.igetattr(name, context, class_context=False) - for stmt in self._wrap_attr(attrs, context): - yield stmt - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError(**vars(error))) - - def _wrap_attr(self, attrs, context=None): - """wrap bound methods of attrs in a InstanceMethod proxies""" - for attr in attrs: - if isinstance(attr, UnboundMethod): - if _is_property(attr): - for inferred in attr.infer_call_result(self, context): - yield inferred - else: - yield BoundMethod(attr, self) - elif hasattr(attr, 'name') and attr.name == '': - # This is a lambda function defined at class level, - # since its scope is the underlying _proxied class. - # Unfortunately, we can't do an isinstance check here, - # because of the circular dependency between astroid.bases - # and astroid.scoped_nodes. - if attr.statement().scope() == self._proxied: - if attr.args.args and attr.args.args[0].name == 'self': - yield BoundMethod(attr, self) - continue - yield attr - else: - yield attr - - def infer_call_result(self, caller, context=None): - """infer what a class instance is returning when called""" - inferred = False - for node in self._proxied.igetattr('__call__', context): - if node is util.Uninferable or not node.callable(): - continue - for res in node.infer_call_result(caller, context): - inferred = True - yield res - if not inferred: - raise exceptions.InferenceError(node=self, caller=caller, - context=context) - - -class Instance(BaseInstance): - """A special node representing a class instance.""" - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.InstanceModel()) - - def __repr__(self): - return '' % (self._proxied.root().name, - self._proxied.name, - id(self)) - def __str__(self): - return 'Instance of %s.%s' % (self._proxied.root().name, - self._proxied.name) - - def callable(self): - try: - self._proxied.getattr('__call__', class_context=False) - return True - except exceptions.AttributeInferenceError: - return False - - def pytype(self): - return self._proxied.qname() - - def display_type(self): - return 'Instance of' - - def bool_value(self): - """Infer the truth value for an Instance - - The truth value of an instance is determined by these conditions: - - * if it implements __bool__ on Python 3 or __nonzero__ - on Python 2, then its bool value will be determined by - calling this special method and checking its result. - * when this method is not defined, __len__() is called, if it - is defined, and the object is considered true if its result is - nonzero. If a class defines neither __len__() nor __bool__(), - all its instances are considered true. - """ - context = contextmod.InferenceContext() - context.callcontext = contextmod.CallContext(args=[]) - context.boundnode = self - - try: - result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context) - except (exceptions.InferenceError, exceptions.AttributeInferenceError): - # Fallback to __len__. - try: - result = _infer_method_result_truth(self, '__len__', context) - except (exceptions.AttributeInferenceError, exceptions.InferenceError): - return True - return result - - # TODO(cpopa): this is set in inference.py - # The circular dependency hell goes deeper and deeper. - def getitem(self, index, context=None): - pass - - -class UnboundMethod(Proxy): - """a special node representing a method not bound to an instance""" - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.UnboundMethodModel()) - - def __repr__(self): - frame = self._proxied.parent.frame() - return '<%s %s of %s at 0x%s' % (self.__class__.__name__, - self._proxied.name, - frame.qname(), id(self)) - - def is_bound(self): - return False - - def getattr(self, name, context=None): - if name in self.special_attributes: - return [self.special_attributes.lookup(name)] - return self._proxied.getattr(name, context) - - def igetattr(self, name, context=None): - if name in self.special_attributes: - return iter((self.special_attributes.lookup(name), )) - return self._proxied.igetattr(name, context) - - def infer_call_result(self, caller, context): - # If we're unbound method __new__ of builtin object, the result is an - # instance of the class given as first argument. - if (self._proxied.name == '__new__' and - self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - infer = caller.args[0].infer() if caller.args else [] - return (Instance(x) if x is not util.Uninferable else x for x in infer) - return self._proxied.infer_call_result(caller, context) - - def bool_value(self): - return True - - -class BoundMethod(UnboundMethod): - """a special node representing a method bound to an instance""" - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.BoundMethodModel()) - - def __init__(self, proxy, bound): - UnboundMethod.__init__(self, proxy) - self.bound = bound - - def is_bound(self): - return True - - def _infer_type_new_call(self, caller, context): - """Try to infer what type.__new__(mcs, name, bases, attrs) returns. - - In order for such call to be valid, the metaclass needs to be - a subtype of ``type``, the name needs to be a string, the bases - needs to be a tuple of classes and the attributes a dictionary - of strings to values. - """ - from astroid import node_classes - # Verify the metaclass - mcs = next(caller.args[0].infer(context=context)) - if mcs.__class__.__name__ != 'ClassDef': - # Not a valid first argument. - return - if not mcs.is_subtype_of("%s.type" % BUILTINS): - # Not a valid metaclass. - return - - # Verify the name - name = next(caller.args[1].infer(context=context)) - if name.__class__.__name__ != 'Const': - # Not a valid name, needs to be a const. - return - if not isinstance(name.value, str): - # Needs to be a string. - return - - # Verify the bases - bases = next(caller.args[2].infer(context=context)) - if bases.__class__.__name__ != 'Tuple': - # Needs to be a tuple. - return - inferred_bases = [next(elt.infer(context=context)) - for elt in bases.elts] - if any(base.__class__.__name__ != 'ClassDef' - for base in inferred_bases): - # All the bases needs to be Classes - return - - # Verify the attributes. - attrs = next(caller.args[3].infer(context=context)) - if attrs.__class__.__name__ != 'Dict': - # Needs to be a dictionary. - return - cls_locals = collections.defaultdict(list) - for key, value in attrs.items: - key = next(key.infer(context=context)) - value = next(value.infer(context=context)) - if key.__class__.__name__ != 'Const': - # Something invalid as an attribute. - return - if not isinstance(key.value, str): - # Not a proper attribute. - return - cls_locals[key.value].append(value) - - # Build the class from now. - cls = mcs.__class__(name=name.value, lineno=caller.lineno, - col_offset=caller.col_offset, - parent=caller) - empty = node_classes.Pass() - cls.postinit(bases=bases.elts, body=[empty], decorators=[], - newstyle=True, metaclass=mcs, keywords=[]) - cls.locals = cls_locals - return cls - - def infer_call_result(self, caller, context=None): - if context is None: - context = contextmod.InferenceContext() - context = context.clone() - context.boundnode = self.bound - - if (self.bound.__class__.__name__ == 'ClassDef' - and self.bound.name == 'type' - and self.name == '__new__' - and len(caller.args) == 4 - # TODO(cpopa): this check shouldn't be needed. - and self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - - # Check if we have an ``type.__new__(mcs, name, bases, attrs)`` call. - new_cls = self._infer_type_new_call(caller, context) - if new_cls: - return iter((new_cls, )) - - return super(BoundMethod, self).infer_call_result(caller, context) - - def bool_value(self): - return True - - -class Generator(BaseInstance): - """a special node representing a generator. - - Proxied class is set once for all in raw_building. - """ - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.GeneratorModel()) - - # pylint: disable=super-init-not-called - def __init__(self, parent=None): - self.parent = parent - - def callable(self): - return False - - def pytype(self): - return '%s.generator' % BUILTINS - - def display_type(self): - return 'Generator' - - def bool_value(self): - return True - - def __repr__(self): - return '' % (self._proxied.name, self.lineno, id(self)) - - def __str__(self): - return 'Generator(%s)' % (self._proxied.name) diff --git a/pymode/libs/astroid/brain/brain_builtin_inference.py b/pymode/libs/astroid/brain/brain_builtin_inference.py deleted file mode 100644 index 717dc78c..00000000 --- a/pymode/libs/astroid/brain/brain_builtin_inference.py +++ /dev/null @@ -1,496 +0,0 @@ -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson - -# 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 various builtins.""" - -from functools import partial -import sys -from textwrap import dedent - -import six -from astroid import (MANAGER, UseInferenceDefault, AttributeInferenceError, - inference_tip, InferenceError, NameInferenceError) -from astroid import arguments -from astroid.builder import AstroidBuilder -from astroid import helpers -from astroid import nodes -from astroid import objects -from astroid import scoped_nodes -from astroid import util - -def _extend_str(class_node, rvalue): - """function to extend builtin str/unicode class""" - # TODO(cpopa): this approach will make astroid to believe - # that some arguments can be passed by keyword, but - # unfortunately, strings and bytes don't accept keyword arguments. - code = dedent(''' - class whatever(object): - def join(self, iterable): - return {rvalue} - def replace(self, old, new, count=None): - return {rvalue} - def format(self, *args, **kwargs): - return {rvalue} - def encode(self, encoding='ascii', errors=None): - return '' - def decode(self, encoding='ascii', errors=None): - return u'' - def capitalize(self): - return {rvalue} - def title(self): - return {rvalue} - def lower(self): - return {rvalue} - def upper(self): - return {rvalue} - def swapcase(self): - return {rvalue} - def index(self, sub, start=None, end=None): - return 0 - def find(self, sub, start=None, end=None): - return 0 - def count(self, sub, start=None, end=None): - return 0 - def strip(self, chars=None): - return {rvalue} - def lstrip(self, chars=None): - return {rvalue} - def rstrip(self, chars=None): - return {rvalue} - def rjust(self, width, fillchar=None): - return {rvalue} - def center(self, width, fillchar=None): - return {rvalue} - def ljust(self, width, fillchar=None): - return {rvalue} - ''') - code = code.format(rvalue=rvalue) - fake = AstroidBuilder(MANAGER).string_build(code)['whatever'] - for method in fake.mymethods(): - class_node.locals[method.name] = [method] - method.parent = class_node - -def extend_builtins(class_transforms): - from astroid.bases import BUILTINS - builtin_ast = MANAGER.astroid_cache[BUILTINS] - for class_name, transform in class_transforms.items(): - transform(builtin_ast[class_name]) - -if sys.version_info > (3, 0): - extend_builtins({'bytes': partial(_extend_str, rvalue="b''"), - 'str': partial(_extend_str, rvalue="''")}) -else: - extend_builtins({'str': partial(_extend_str, rvalue="''"), - 'unicode': partial(_extend_str, rvalue="u''")}) - - -def register_builtin_transform(transform, builtin_name): - """Register a new transform function for the given *builtin_name*. - - The transform function must accept two parameters, a node and - an optional context. - """ - def _transform_wrapper(node, context=None): - result = transform(node, context=context) - if result: - if not result.parent: - # Let the transformation function determine - # the parent for its result. Otherwise, - # we set it to be the node we transformed from. - result.parent = node - - result.lineno = node.lineno - result.col_offset = node.col_offset - return iter([result]) - - MANAGER.register_transform(nodes.Call, - inference_tip(_transform_wrapper), - lambda n: (isinstance(n.func, nodes.Name) and - n.func.name == builtin_name)) - - -def _generic_inference(node, context, node_type, transform): - args = node.args - if not args: - return node_type() - if len(node.args) > 1: - raise UseInferenceDefault() - - arg, = args - transformed = transform(arg) - if not transformed: - try: - inferred = next(arg.infer(context=context)) - except (InferenceError, StopIteration): - raise UseInferenceDefault() - if inferred is util.Uninferable: - raise UseInferenceDefault() - transformed = transform(inferred) - if not transformed or transformed is util.Uninferable: - raise UseInferenceDefault() - return transformed - - -def _generic_transform(arg, klass, iterables, build_elts): - if isinstance(arg, klass): - return arg - elif isinstance(arg, iterables): - if not all(isinstance(elt, nodes.Const) - for elt in arg.elts): - # TODO(cpopa): Don't support heterogenous elements. - # Not yet, though. - raise UseInferenceDefault() - elts = [elt.value for elt in arg.elts] - elif isinstance(arg, nodes.Dict): - if not all(isinstance(elt[0], nodes.Const) - for elt in arg.items): - raise UseInferenceDefault() - elts = [item[0].value for item in arg.items] - elif (isinstance(arg, nodes.Const) and - isinstance(arg.value, (six.string_types, six.binary_type))): - elts = arg.value - else: - return - return klass.from_constants(elts=build_elts(elts)) - - -def _infer_builtin(node, context, - klass=None, iterables=None, - build_elts=None): - transform_func = partial( - _generic_transform, - klass=klass, - iterables=iterables, - build_elts=build_elts) - - return _generic_inference(node, context, klass, transform_func) - -# pylint: disable=invalid-name -infer_tuple = partial( - _infer_builtin, - klass=nodes.Tuple, - iterables=(nodes.List, nodes.Set, objects.FrozenSet, - objects.DictItems, objects.DictKeys, - objects.DictValues), - build_elts=tuple) - -infer_list = partial( - _infer_builtin, - klass=nodes.List, - iterables=(nodes.Tuple, nodes.Set, objects.FrozenSet, - objects.DictItems, objects.DictKeys, - objects.DictValues), - build_elts=list) - -infer_set = partial( - _infer_builtin, - klass=nodes.Set, - iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, - objects.DictKeys), - build_elts=set) - -infer_frozenset = partial( - _infer_builtin, - klass=objects.FrozenSet, - iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, - objects.DictKeys), - build_elts=frozenset) - - -def _get_elts(arg, context): - is_iterable = lambda n: isinstance(n, - (nodes.List, nodes.Tuple, nodes.Set)) - try: - inferred = next(arg.infer(context)) - except (InferenceError, NameInferenceError): - raise UseInferenceDefault() - if isinstance(inferred, nodes.Dict): - items = inferred.items - elif is_iterable(inferred): - items = [] - for elt in inferred.elts: - # If an item is not a pair of two items, - # then fallback to the default inference. - # Also, take in consideration only hashable items, - # tuples and consts. We are choosing Names as well. - if not is_iterable(elt): - raise UseInferenceDefault() - if len(elt.elts) != 2: - raise UseInferenceDefault() - if not isinstance(elt.elts[0], - (nodes.Tuple, nodes.Const, nodes.Name)): - raise UseInferenceDefault() - items.append(tuple(elt.elts)) - else: - raise UseInferenceDefault() - return items - -def infer_dict(node, context=None): - """Try to infer a dict call to a Dict node. - - The function treats the following cases: - - * dict() - * dict(mapping) - * dict(iterable) - * dict(iterable, **kwargs) - * dict(mapping, **kwargs) - * dict(**kwargs) - - If a case can't be inferred, we'll fallback to default inference. - """ - call = arguments.CallSite.from_call(node) - if call.has_invalid_arguments() or call.has_invalid_keywords(): - raise UseInferenceDefault - - args = call.positional_arguments - kwargs = list(call.keyword_arguments.items()) - - if not args and not kwargs: - # dict() - return nodes.Dict() - elif kwargs and not args: - # dict(a=1, b=2, c=4) - items = [(nodes.Const(key), value) for key, value in kwargs] - elif len(args) == 1 and kwargs: - # dict(some_iterable, b=2, c=4) - elts = _get_elts(args[0], context) - keys = [(nodes.Const(key), value) for key, value in kwargs] - items = elts + keys - elif len(args) == 1: - items = _get_elts(args[0], context) - else: - raise UseInferenceDefault() - - value = nodes.Dict(col_offset=node.col_offset, - lineno=node.lineno, - parent=node.parent) - value.postinit(items) - return value - - -def infer_super(node, context=None): - """Understand super calls. - - There are some restrictions for what can be understood: - - * unbounded super (one argument form) is not understood. - - * if the super call is not inside a function (classmethod or method), - then the default inference will be used. - - * if the super arguments can't be inferred, the default inference - will be used. - """ - if len(node.args) == 1: - # Ignore unbounded super. - raise UseInferenceDefault - - scope = node.scope() - if not isinstance(scope, nodes.FunctionDef): - # Ignore non-method uses of super. - raise UseInferenceDefault - if scope.type not in ('classmethod', 'method'): - # Not interested in staticmethods. - raise UseInferenceDefault - - cls = scoped_nodes.get_wrapping_class(scope) - if not len(node.args): - mro_pointer = cls - # In we are in a classmethod, the interpreter will fill - # automatically the class as the second argument, not an instance. - if scope.type == 'classmethod': - mro_type = cls - else: - mro_type = cls.instantiate_class() - else: - # TODO(cpopa): support flow control (multiple inference values). - try: - mro_pointer = next(node.args[0].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - try: - mro_type = next(node.args[1].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - - if mro_pointer is util.Uninferable or mro_type is util.Uninferable: - # No way we could understand this. - raise UseInferenceDefault - - super_obj = objects.Super(mro_pointer=mro_pointer, - mro_type=mro_type, - self_class=cls, - scope=scope) - super_obj.parent = node - return super_obj - - -def _infer_getattr_args(node, context): - if len(node.args) not in (2, 3): - # Not a valid getattr call. - raise UseInferenceDefault - - try: - # TODO(cpopa): follow all the values of the first argument? - obj = next(node.args[0].infer(context=context)) - attr = next(node.args[1].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - - if obj is util.Uninferable or attr is util.Uninferable: - # If one of the arguments is something we can't infer, - # then also make the result of the getattr call something - # which is unknown. - return util.Uninferable, util.Uninferable - - is_string = (isinstance(attr, nodes.Const) and - isinstance(attr.value, six.string_types)) - if not is_string: - raise UseInferenceDefault - - return obj, attr.value - - -def infer_getattr(node, context=None): - """Understand getattr calls - - If one of the arguments is an Uninferable object, then the - result will be an Uninferable object. Otherwise, the normal attribute - lookup will be done. - """ - obj, attr = _infer_getattr_args(node, context) - if obj is util.Uninferable or attr is util.Uninferable or not hasattr(obj, 'igetattr'): - return util.Uninferable - - try: - return next(obj.igetattr(attr, context=context)) - except (StopIteration, InferenceError, AttributeInferenceError): - if len(node.args) == 3: - # Try to infer the default and return it instead. - try: - return next(node.args[2].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - - raise UseInferenceDefault - - -def infer_hasattr(node, context=None): - """Understand hasattr calls - - This always guarantees three possible outcomes for calling - hasattr: Const(False) when we are sure that the object - doesn't have the intended attribute, Const(True) when - we know that the object has the attribute and Uninferable - when we are unsure of the outcome of the function call. - """ - try: - obj, attr = _infer_getattr_args(node, context) - if obj is util.Uninferable or attr is util.Uninferable or not hasattr(obj, 'getattr'): - return util.Uninferable - obj.getattr(attr, context=context) - except UseInferenceDefault: - # Can't infer something from this function call. - return util.Uninferable - except AttributeInferenceError: - # Doesn't have it. - return nodes.Const(False) - return nodes.Const(True) - - -def infer_callable(node, context=None): - """Understand callable calls - - This follows Python's semantics, where an object - is callable if it provides an attribute __call__, - even though that attribute is something which can't be - called. - """ - if len(node.args) != 1: - # Invalid callable call. - raise UseInferenceDefault - - argument = node.args[0] - try: - inferred = next(argument.infer(context=context)) - except InferenceError: - return util.Uninferable - if inferred is util.Uninferable: - return util.Uninferable - return nodes.Const(inferred.callable()) - - -def infer_bool(node, context=None): - """Understand bool calls.""" - if len(node.args) > 1: - # Invalid bool call. - raise UseInferenceDefault - - if not node.args: - return nodes.Const(False) - - argument = node.args[0] - try: - inferred = next(argument.infer(context=context)) - except InferenceError: - return util.Uninferable - if inferred is util.Uninferable: - return util.Uninferable - - bool_value = inferred.bool_value() - if bool_value is util.Uninferable: - return util.Uninferable - return nodes.Const(bool_value) - - -def infer_type(node, context=None): - """Understand the one-argument form of *type*.""" - if len(node.args) != 1: - raise UseInferenceDefault - - return helpers.object_type(node.args[0], context) - - -def infer_slice(node, context=None): - """Understand `slice` calls.""" - args = node.args - if not 0 < len(args) <= 3: - raise UseInferenceDefault - - args = list(map(helpers.safe_infer, args)) - for arg in args: - if not arg or arg is util.Uninferable: - raise UseInferenceDefault - if not isinstance(arg, nodes.Const): - raise UseInferenceDefault - if not isinstance(arg.value, (type(None), int)): - raise UseInferenceDefault - - if len(args) < 3: - # Make sure we have 3 arguments. - args.extend([None] * (3 - len(args))) - - slice_node = nodes.Slice(lineno=node.lineno, - col_offset=node.col_offset, - parent=node.parent) - slice_node.postinit(*args) - return slice_node - - -# Builtins inference -register_builtin_transform(infer_bool, 'bool') -register_builtin_transform(infer_super, 'super') -register_builtin_transform(infer_callable, 'callable') -register_builtin_transform(infer_getattr, 'getattr') -register_builtin_transform(infer_hasattr, 'hasattr') -register_builtin_transform(infer_tuple, 'tuple') -register_builtin_transform(infer_set, 'set') -register_builtin_transform(infer_list, 'list') -register_builtin_transform(infer_dict, 'dict') -register_builtin_transform(infer_frozenset, 'frozenset') -register_builtin_transform(infer_type, 'type') -register_builtin_transform(infer_slice, 'slice') diff --git a/pymode/libs/astroid/brain/brain_collections.py b/pymode/libs/astroid/brain/brain_collections.py deleted file mode 100644 index 77343377..00000000 --- a/pymode/libs/astroid/brain/brain_collections.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 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 - -import astroid - - -def _collections_transform(): - return astroid.parse(''' - class defaultdict(dict): - default_factory = None - def __missing__(self, key): pass - def __getitem__(self, key): return default_factory - - class deque(object): - maxlen = 0 - def __init__(self, iterable=None, maxlen=None): - self.iterable = iterable - def append(self, x): pass - def appendleft(self, x): pass - def clear(self): pass - def count(self, x): return 0 - def extend(self, iterable): pass - def extendleft(self, iterable): pass - def pop(self): pass - def popleft(self): pass - def remove(self, value): pass - def reverse(self): pass - def rotate(self, n): pass - def __iter__(self): return self - def __reversed__(self): return self.iterable[::-1] - def __getitem__(self, index): pass - def __setitem__(self, index, value): pass - def __delitem__(self, index): pass - - def OrderedDict(dict): - def __reversed__(self): return self[::-1] - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'collections', _collections_transform) - diff --git a/pymode/libs/astroid/brain/brain_dateutil.py b/pymode/libs/astroid/brain/brain_dateutil.py deleted file mode 100644 index e9aafd25..00000000 --- a/pymode/libs/astroid/brain/brain_dateutil.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa -# Copyright (c) 2016 Cara Vinson - -# 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 dateutil""" - -import textwrap - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - -def dateutil_transform(): - return AstroidBuilder(MANAGER).string_build(textwrap.dedent(''' - import datetime - def parse(timestr, parserinfo=None, **kwargs): - return datetime.datetime() - ''')) - -register_module_extender(MANAGER, 'dateutil.parser', dateutil_transform) diff --git a/pymode/libs/astroid/brain/brain_fstrings.py b/pymode/libs/astroid/brain/brain_fstrings.py deleted file mode 100644 index e4c93571..00000000 --- a/pymode/libs/astroid/brain/brain_fstrings.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2017 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 - -import sys - -import astroid - - -def _clone_node_with_lineno(node, parent, lineno): - cls = node.__class__ - other_fields = node._other_fields - _astroid_fields = node._astroid_fields - init_params = { - 'lineno': lineno, - 'col_offset': node.col_offset, - 'parent': parent - } - postinit_params = { - param: getattr(node, param) - for param in _astroid_fields - } - if other_fields: - init_params.update({ - param: getattr(node, param) - for param in other_fields - }) - new_node = cls(**init_params) - if hasattr(node, 'postinit'): - new_node.postinit(**postinit_params) - return new_node - - -def _transform_formatted_value(node): - if node.value and node.value.lineno == 1: - if node.lineno != node.value.lineno: - new_node = astroid.FormattedValue( - lineno=node.lineno, - col_offset=node.col_offset, - parent=node.parent - ) - new_value = _clone_node_with_lineno( - node=node.value, - lineno=node.lineno, - parent=new_node - ) - new_node.postinit(value=new_value, - format_spec=node.format_spec) - return new_node - - -if sys.version_info[:2] >= (3, 6): - # TODO: this fix tries to *patch* http://bugs.python.org/issue29051 - # The problem is that FormattedValue.value, which is a Name node, - # has wrong line numbers, usually 1. This creates problems for pylint, - # which expects correct line numbers for things such as message control. - astroid.MANAGER.register_transform( - astroid.FormattedValue, - _transform_formatted_value) - diff --git a/pymode/libs/astroid/brain/brain_functools.py b/pymode/libs/astroid/brain/brain_functools.py deleted file mode 100644 index 6dcf7cd9..00000000 --- a/pymode/libs/astroid/brain/brain_functools.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -"""Astroid hooks for understanding functools library module.""" - -import astroid -from astroid import BoundMethod -from astroid import extract_node -from astroid import helpers -from astroid.interpreter import objectmodel -from astroid import MANAGER - - -LRU_CACHE = 'functools.lru_cache' - - -class LruWrappedModel(objectmodel.FunctionModel): - """Special attribute model for functions decorated with functools.lru_cache. - - The said decorators patches at decoration time some functions onto - the decorated function. - """ - - @property - def py__wrapped__(self): - return self._instance - - @property - def pycache_info(self): - cache_info = extract_node(''' - from functools import _CacheInfo - _CacheInfo(0, 0, 0, 0) - ''') - class CacheInfoBoundMethod(BoundMethod): - def infer_call_result(self, caller, context=None): - yield helpers.safe_infer(cache_info) - - return CacheInfoBoundMethod(proxy=self._instance, bound=self._instance) - - @property - def pycache_clear(self): - node = extract_node('''def cache_clear(self): pass''') - return BoundMethod(proxy=node, bound=self._instance.parent.scope()) - - -def _transform_lru_cache(node, context=None): - # TODO: this is not ideal, since the node should be immutable, - # but due to https://github.com/PyCQA/astroid/issues/354, - # there's not much we can do now. - # Replacing the node would work partially, because, - # in pylint, the old node would still be available, leading - # to spurious false positives. - node.special_attributes = LruWrappedModel()(node) - return - - -def _looks_like_lru_cache(node): - """Check if the given function node is decorated with lru_cache.""" - if not node.decorators: - return False - - for decorator in node.decorators.nodes: - if not isinstance(decorator, astroid.Call): - continue - - func = helpers.safe_infer(decorator.func) - if func in (None, astroid.Uninferable): - continue - - if isinstance(func, astroid.FunctionDef) and func.qname() == LRU_CACHE: - return True - return False - - -MANAGER.register_transform(astroid.FunctionDef, _transform_lru_cache, - _looks_like_lru_cache) diff --git a/pymode/libs/astroid/brain/brain_gi.py b/pymode/libs/astroid/brain/brain_gi.py deleted file mode 100644 index 03c19c0a..00000000 --- a/pymode/libs/astroid/brain/brain_gi.py +++ /dev/null @@ -1,201 +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 the Python 2 GObject introspection bindings. - -Helps with understanding everything imported from 'gi.repository' -""" - -import inspect -import itertools -import sys -import re -import warnings - -from astroid import MANAGER, AstroidBuildingError, nodes -from astroid.builder import AstroidBuilder - - -_inspected_modules = {} - -_identifier_re = r'^[A-Za-z_]\w*$' - -def _gi_build_stub(parent): - """ - Inspect the passed module recursively and build stubs for functions, - classes, etc. - """ - classes = {} - functions = {} - constants = {} - methods = {} - for name in dir(parent): - if name.startswith("__"): - continue - - # Check if this is a valid name in python - if not re.match(_identifier_re, name): - continue - - try: - obj = getattr(parent, name) - except: - continue - - if inspect.isclass(obj): - classes[name] = obj - elif (inspect.isfunction(obj) or - inspect.isbuiltin(obj)): - functions[name] = obj - elif (inspect.ismethod(obj) or - inspect.ismethoddescriptor(obj)): - methods[name] = obj - elif (str(obj).startswith(", ) - # Only accept function calls with two constant arguments - if len(node.args) != 2: - return False - - if not all(isinstance(arg, nodes.Const) for arg in node.args): - return False - - func = node.func - if isinstance(func, nodes.Attribute): - if func.attrname != 'require_version': - return False - if isinstance(func.expr, nodes.Name) and func.expr.name == 'gi': - return True - - return False - - if isinstance(func, nodes.Name): - return func.name == 'require_version' - - return False - -def _register_require_version(node): - # Load the gi.require_version locally - try: - import gi - gi.require_version(node.args[0].value, node.args[1].value) - except Exception: - pass - - return node - -MANAGER.register_failed_import_hook(_import_gi_module) -MANAGER.register_transform(nodes.Call, _register_require_version, _looks_like_require_version) diff --git a/pymode/libs/astroid/brain/brain_hashlib.py b/pymode/libs/astroid/brain/brain_hashlib.py deleted file mode 100644 index aacd1743..00000000 --- a/pymode/libs/astroid/brain/brain_hashlib.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 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 - -import six - -import astroid - - -def _hashlib_transform(): - template = ''' - class %(name)s(object): - def __init__(self, value=''): pass - def digest(self): - return %(digest)s - def copy(self): - return self - def update(self, value): pass - def hexdigest(self): - return '' - @property - def name(self): - return %(name)r - @property - def block_size(self): - return 1 - @property - def digest_size(self): - return 1 - ''' - algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') - classes = "".join( - template % {'name': hashfunc, 'digest': 'b""' if six.PY3 else '""'} - for hashfunc in algorithms) - return astroid.parse(classes) - - -astroid.register_module_extender(astroid.MANAGER, 'hashlib', _hashlib_transform) - diff --git a/pymode/libs/astroid/brain/brain_io.py b/pymode/libs/astroid/brain/brain_io.py deleted file mode 100644 index be8d30c8..00000000 --- a/pymode/libs/astroid/brain/brain_io.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 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 brain hints for some of the _io C objects.''' - -import astroid - - -BUFFERED = {'BufferedWriter', 'BufferedReader'} -TextIOWrapper = 'TextIOWrapper' -FileIO = 'FileIO' -BufferedWriter = 'BufferedWriter' - - -def _generic_io_transform(node, name, cls): - '''Transform the given name, by adding the given *class* as a member of the node.''' - - io_module = astroid.MANAGER.ast_from_module_name('_io') - attribute_object = io_module[cls] - instance = attribute_object.instantiate_class() - node.locals[name] = [instance] - - -def _transform_text_io_wrapper(node): - # This is not always correct, since it can vary with the type of the descriptor, - # being stdout, stderr or stdin. But we cannot get access to the name of the - # stream, which is why we are using the BufferedWriter class as a default - # value - return _generic_io_transform(node, name='buffer', cls=BufferedWriter) - - -def _transform_buffered(node): - return _generic_io_transform(node, name='raw', cls=FileIO) - - -astroid.MANAGER.register_transform(astroid.ClassDef, - _transform_buffered, - lambda node: node.name in BUFFERED) -astroid.MANAGER.register_transform(astroid.ClassDef, - _transform_text_io_wrapper, - lambda node: node.name == TextIOWrapper) diff --git a/pymode/libs/astroid/brain/brain_mechanize.py b/pymode/libs/astroid/brain/brain_mechanize.py deleted file mode 100644 index afc65059..00000000 --- a/pymode/libs/astroid/brain/brain_mechanize.py +++ /dev/null @@ -1,23 +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 - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - -def mechanize_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -class Browser(object): - def open(self, url, data=None, timeout=None): - return None - def open_novisit(self, url, data=None, timeout=None): - return None - def open_local_file(self, filename): - return None - -''') - - -register_module_extender(MANAGER, 'mechanize', mechanize_transform) diff --git a/pymode/libs/astroid/brain/brain_multiprocessing.py b/pymode/libs/astroid/brain/brain_multiprocessing.py deleted file mode 100644 index a19bac69..00000000 --- a/pymode/libs/astroid/brain/brain_multiprocessing.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 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 - -import sys - -import astroid -from astroid import exceptions - - -PY34 = sys.version_info >= (3, 4) - - -def _multiprocessing_transform(): - module = astroid.parse(''' - from multiprocessing.managers import SyncManager - def Manager(): - return SyncManager() - ''') - if not PY34: - return module - - # On Python 3.4, multiprocessing uses a getattr lookup inside contexts, - # in order to get the attributes they need. Since it's extremely - # dynamic, we use this approach to fake it. - node = astroid.parse(''' - from multiprocessing.context import DefaultContext, BaseContext - default = DefaultContext() - base = BaseContext() - ''') - try: - context = next(node['default'].infer()) - base = next(node['base'].infer()) - except exceptions.InferenceError: - return module - - for node in (context, base): - for key, value in node.locals.items(): - if key.startswith("_"): - continue - - value = value[0] - if isinstance(value, astroid.FunctionDef): - # We need to rebound this, since otherwise - # it will have an extra argument (self). - value = astroid.BoundMethod(value, node) - module[key] = value - return module - - -def _multiprocessing_managers_transform(): - return astroid.parse(''' - import array - import threading - import multiprocessing.pool as pool - - import six - - class Namespace(object): - pass - - class Value(object): - def __init__(self, typecode, value, lock=True): - self._typecode = typecode - self._value = value - def get(self): - return self._value - def set(self, value): - self._value = value - def __repr__(self): - return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value) - value = property(get, set) - - def Array(typecode, sequence, lock=True): - return array.array(typecode, sequence) - - class SyncManager(object): - Queue = JoinableQueue = six.moves.queue.Queue - Event = threading.Event - RLock = threading.RLock - BoundedSemaphore = threading.BoundedSemaphore - Condition = threading.Condition - Barrier = threading.Barrier - Pool = pool.Pool - list = list - dict = dict - Value = Value - Array = Array - Namespace = Namespace - __enter__ = lambda self: self - __exit__ = lambda *args: args - - def start(self, initializer=None, initargs=None): - pass - def shutdown(self): - pass - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'multiprocessing.managers', - _multiprocessing_managers_transform) -astroid.register_module_extender(astroid.MANAGER, 'multiprocessing', - _multiprocessing_transform) diff --git a/pymode/libs/astroid/brain/brain_namedtuple_enum.py b/pymode/libs/astroid/brain/brain_namedtuple_enum.py deleted file mode 100644 index b99cb761..00000000 --- a/pymode/libs/astroid/brain/brain_namedtuple_enum.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) 2012-2015 LOGILAB S.A. (Paris, FRANCE) -# 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 the Python standard library.""" - -import functools -import sys -import keyword -from textwrap import dedent - -from astroid import ( - MANAGER, UseInferenceDefault, inference_tip, - InferenceError) -from astroid import arguments -from astroid import exceptions -from astroid import nodes -from astroid.builder import AstroidBuilder, extract_node -from astroid import util - -PY3K = sys.version_info > (3, 0) -PY33 = sys.version_info >= (3, 3) -PY34 = sys.version_info >= (3, 4) - -# general function - -def infer_func_form(node, base_type, context=None, enum=False): - """Specific inference function for namedtuple or Python 3 enum. """ - def infer_first(node): - if node is util.Uninferable: - raise UseInferenceDefault - try: - value = next(node.infer(context=context)) - if value is util.Uninferable: - raise UseInferenceDefault() - else: - return value - except StopIteration: - raise InferenceError() - - # node is a Call node, class name as first argument and generated class - # attributes as second argument - if len(node.args) != 2: - # something weird here, go back to class implementation - raise UseInferenceDefault() - # namedtuple or enums list of attributes can be a list of strings or a - # whitespace-separate string - try: - name = infer_first(node.args[0]).value - names = infer_first(node.args[1]) - try: - attributes = names.value.replace(',', ' ').split() - except AttributeError: - if not enum: - attributes = [infer_first(const).value for const in names.elts] - else: - # Enums supports either iterator of (name, value) pairs - # or mappings. - # TODO: support only list, tuples and mappings. - if hasattr(names, 'items') and isinstance(names.items, list): - attributes = [infer_first(const[0]).value - for const in names.items - if isinstance(const[0], nodes.Const)] - elif hasattr(names, 'elts'): - # Enums can support either ["a", "b", "c"] - # or [("a", 1), ("b", 2), ...], but they can't - # be mixed. - if all(isinstance(const, nodes.Tuple) - for const in names.elts): - attributes = [infer_first(const.elts[0]).value - for const in names.elts - if isinstance(const, nodes.Tuple)] - else: - attributes = [infer_first(const).value - for const in names.elts] - else: - raise AttributeError - if not attributes: - raise AttributeError - except (AttributeError, exceptions.InferenceError): - raise UseInferenceDefault() - - # If we can't infer the name of the class, don't crash, up to this point - # we know it is a namedtuple anyway. - name = name or 'Uninferable' - # we want to return a Class node instance with proper attributes set - class_node = nodes.ClassDef(name, 'docstring') - class_node.parent = node.parent - # set base class=tuple - class_node.bases.append(base_type) - # XXX add __init__(*attributes) method - for attr in attributes: - fake_node = nodes.EmptyNode() - fake_node.parent = class_node - fake_node.attrname = attr - class_node.instance_attrs[attr] = [fake_node] - return class_node, name, attributes - - -def _looks_like(node, name): - func = node.func - if isinstance(func, nodes.Attribute): - return func.attrname == name - if isinstance(func, nodes.Name): - return func.name == name - return False - -_looks_like_namedtuple = functools.partial(_looks_like, name='namedtuple') -_looks_like_enum = functools.partial(_looks_like, name='Enum') - - -def infer_named_tuple(node, context=None): - """Specific inference function for namedtuple Call node""" - class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, - context=context) - call_site = arguments.CallSite.from_call(node) - func = next(extract_node('import collections; collections.namedtuple').infer()) - try: - rename = next(call_site.infer_argument(func, 'rename', context)).bool_value() - except InferenceError: - rename = False - - if rename: - attributes = _get_renamed_namedtuple_atributes(attributes) - - field_def = (" {name} = property(lambda self: self[{index:d}], " - "doc='Alias for field number {index:d}')") - field_defs = '\n'.join(field_def.format(name=name, index=index) - for index, name in enumerate(attributes)) - fake = AstroidBuilder(MANAGER).string_build(''' -class %(name)s(tuple): - __slots__ = () - _fields = %(fields)r - def _asdict(self): - return self.__dict__ - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - return new(cls, iterable) - def _replace(self, **kwds): - return self - def __getnewargs__(self): - return tuple(self) -%(field_defs)s - ''' % {'name': name, 'fields': attributes, 'field_defs': field_defs}) - class_node.locals['_asdict'] = fake.body[0].locals['_asdict'] - class_node.locals['_make'] = fake.body[0].locals['_make'] - class_node.locals['_replace'] = fake.body[0].locals['_replace'] - class_node.locals['_fields'] = fake.body[0].locals['_fields'] - for attr in attributes: - class_node.locals[attr] = fake.body[0].locals[attr] - # we use UseInferenceDefault, we can't be a generator so return an iterator - return iter([class_node]) - - -def _get_renamed_namedtuple_atributes(field_names): - names = list(field_names) - seen = set() - for i, name in enumerate(field_names): - if (not all(c.isalnum() or c == '_' for c in name) or keyword.iskeyword(name) - or not name or name[0].isdigit() or name.startswith('_') or name in seen): - names[i] = '_%d' % i - seen.add(name) - return tuple(names) - - -def infer_enum(node, context=None): - """ Specific inference function for enum Call node. """ - enum_meta = extract_node(''' - class EnumMeta(object): - 'docstring' - def __call__(self, node): - class EnumAttribute(object): - name = '' - value = 0 - return EnumAttribute() - ''') - class_node = infer_func_form(node, enum_meta, - context=context, enum=True)[0] - return iter([class_node.instantiate_class()]) - - -def infer_enum_class(node): - """ Specific inference for enums. """ - names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) - for basename in node.basenames: - # TODO: doesn't handle subclasses yet. This implementation - # is a hack to support enums. - if basename not in names: - continue - if node.root().name == 'enum': - # Skip if the class is directly from enum module. - break - for local, values in node.locals.items(): - if any(not isinstance(value, nodes.AssignName) - for value in values): - continue - - stmt = values[0].statement() - if isinstance(stmt.targets[0], nodes.Tuple): - targets = stmt.targets[0].itered() - else: - targets = stmt.targets - - new_targets = [] - for target in targets: - # Replace all the assignments with our mocked class. - classdef = dedent(''' - class %(name)s(%(types)s): - @property - def value(self): - # Not the best return. - return None - @property - def name(self): - return %(name)r - ''' % {'name': target.name, 'types': ', '.join(node.basenames)}) - fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] - fake.parent = target.parent - for method in node.mymethods(): - fake.locals[method.name] = [method] - new_targets.append(fake.instantiate_class()) - node.locals[local] = new_targets - break - return node - - -MANAGER.register_transform(nodes.Call, inference_tip(infer_named_tuple), - _looks_like_namedtuple) -MANAGER.register_transform(nodes.Call, inference_tip(infer_enum), - _looks_like_enum) -MANAGER.register_transform(nodes.ClassDef, infer_enum_class) diff --git a/pymode/libs/astroid/brain/brain_nose.py b/pymode/libs/astroid/brain/brain_nose.py deleted file mode 100644 index cc8939bd..00000000 --- a/pymode/libs/astroid/brain/brain_nose.py +++ /dev/null @@ -1,70 +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 - - -"""Hooks for nose library.""" - -import re -import textwrap - -import astroid -import astroid.builder - -_BUILDER = astroid.builder.AstroidBuilder(astroid.MANAGER) - - -def _pep8(name, caps=re.compile('([A-Z])')): - return caps.sub(lambda m: '_' + m.groups()[0].lower(), name) - - -def _nose_tools_functions(): - """Get an iterator of names and bound methods.""" - module = _BUILDER.string_build(textwrap.dedent(''' - import unittest - - class Test(unittest.TestCase): - pass - a = Test() - ''')) - try: - case = next(module['a'].infer()) - except astroid.InferenceError: - return - for method in case.methods(): - if method.name.startswith('assert') and '_' not in method.name: - pep8_name = _pep8(method.name) - yield pep8_name, astroid.BoundMethod(method, case) - if method.name == 'assertEqual': - # nose also exports assert_equals. - yield 'assert_equals', astroid.BoundMethod(method, case) - - -def _nose_tools_transform(node): - for method_name, method in _nose_tools_functions(): - node.locals[method_name] = [method] - - -def _nose_tools_trivial_transform(): - """Custom transform for the nose.tools module.""" - stub = _BUILDER.string_build('''__all__ = []''') - all_entries = ['ok_', 'eq_'] - - for pep8_name, method in _nose_tools_functions(): - all_entries.append(pep8_name) - stub[pep8_name] = method - - # Update the __all__ variable, since nose.tools - # does this manually with .append. - all_assign = stub['__all__'].parent - all_object = astroid.List(all_entries) - all_object.parent = all_assign - all_assign.value = all_object - return stub - - -astroid.register_module_extender(astroid.MANAGER, 'nose.tools.trivial', - _nose_tools_trivial_transform) -astroid.MANAGER.register_transform(astroid.Module, _nose_tools_transform, - lambda n: n.name == 'nose.tools') 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_pkg_resources.py b/pymode/libs/astroid/brain/brain_pkg_resources.py deleted file mode 100644 index bd60ecd3..00000000 --- a/pymode/libs/astroid/brain/brain_pkg_resources.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 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 - - -import astroid -from astroid import parse -from astroid import inference_tip -from astroid import register_module_extender -from astroid import MANAGER - - -def pkg_resources_transform(): - return parse(''' -def require(*requirements): - return pkg_resources.working_set.require(*requirements) - -def run_script(requires, script_name): - return pkg_resources.working_set.run_script(requires, script_name) - -def iter_entry_points(group, name=None): - return pkg_resources.working_set.iter_entry_points(group, name) - -def resource_exists(package_or_requirement, resource_name): - return get_provider(package_or_requirement).has_resource(resource_name) - -def resource_isdir(package_or_requirement, resource_name): - return get_provider(package_or_requirement).resource_isdir( - resource_name) - -def resource_filename(package_or_requirement, resource_name): - return get_provider(package_or_requirement).get_resource_filename( - self, resource_name) - -def resource_stream(package_or_requirement, resource_name): - return get_provider(package_or_requirement).get_resource_stream( - self, resource_name) - -def resource_string(package_or_requirement, resource_name): - return get_provider(package_or_requirement).get_resource_string( - self, resource_name) - -def resource_listdir(package_or_requirement, resource_name): - return get_provider(package_or_requirement).resource_listdir( - resource_name) - -def extraction_error(): - pass - -def get_cache_path(archive_name, names=()): - extract_path = self.extraction_path or get_default_cache() - target_path = os.path.join(extract_path, archive_name+'-tmp', *names) - return target_path - -def postprocess(tempname, filename): - pass - -def set_extraction_path(path): - pass - -def cleanup_resources(force=False): - pass - -def get_distribution(dist): - return Distribution(dist) - -''') - -register_module_extender(MANAGER, 'pkg_resources', pkg_resources_transform) diff --git a/pymode/libs/astroid/brain/brain_pytest.py b/pymode/libs/astroid/brain/brain_pytest.py deleted file mode 100644 index 23e2db8e..00000000 --- a/pymode/libs/astroid/brain/brain_pytest.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2016 Cara Vinson - -# 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 pytest.""" -from __future__ import absolute_import -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - - -def pytest_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -try: - import _pytest.mark - import _pytest.recwarn - import _pytest.runner - import _pytest.python - import _pytest.skipping - import _pytest.assertion -except ImportError: - pass -else: - deprecated_call = _pytest.recwarn.deprecated_call - warns = _pytest.recwarn.warns - - exit = _pytest.runner.exit - fail = _pytest.runner.fail - skip = _pytest.runner.skip - importorskip = _pytest.runner.importorskip - - xfail = _pytest.skipping.xfail - mark = _pytest.mark.MarkGenerator() - raises = _pytest.python.raises - - # New in pytest 3.0 - try: - approx = _pytest.python.approx - register_assert_rewrite = _pytest.assertion.register_assert_rewrite - except AttributeError: - pass - - -# Moved in pytest 3.0 - -try: - import _pytest.freeze_support - freeze_includes = _pytest.freeze_support.freeze_includes -except ImportError: - try: - import _pytest.genscript - freeze_includes = _pytest.genscript.freeze_includes - except ImportError: - pass - -try: - import _pytest.debugging - set_trace = _pytest.debugging.pytestPDB().set_trace -except ImportError: - try: - import _pytest.pdb - set_trace = _pytest.pdb.pytestPDB().set_trace - except ImportError: - pass - -try: - import _pytest.fixtures - fixture = _pytest.fixtures.fixture - yield_fixture = _pytest.fixtures.yield_fixture -except ImportError: - try: - import _pytest.python - fixture = _pytest.python.fixture - yield_fixture = _pytest.python.yield_fixture - except ImportError: - pass -''') - -register_module_extender(MANAGER, 'pytest', pytest_transform) -register_module_extender(MANAGER, 'py.test', pytest_transform) diff --git a/pymode/libs/astroid/brain/brain_qt.py b/pymode/libs/astroid/brain/brain_qt.py deleted file mode 100644 index bb1565fe..00000000 --- a/pymode/libs/astroid/brain/brain_qt.py +++ /dev/null @@ -1,53 +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 the PyQT library.""" - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid import nodes -from astroid import parse - - -def _looks_like_signal(node, signal_name='pyqtSignal'): - if '__class__' in node.instance_attrs: - try: - cls = node.instance_attrs['__class__'][0] - return cls.name == signal_name - except AttributeError: - # return False if the cls does not have a name attribute - pass - return False - - -def transform_pyqt_signal(node): - module = parse(''' - class pyqtSignal(object): - def connect(self, slot, type=None, no_receiver_check=False): - pass - def disconnect(self, slot): - pass - def emit(self, *args): - pass - ''') - signal_cls = module['pyqtSignal'] - node.instance_attrs['emit'] = signal_cls['emit'] - node.instance_attrs['disconnect'] = signal_cls['disconnect'] - node.instance_attrs['connect'] = signal_cls['connect'] - - -def pyqt4_qtcore_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -def SIGNAL(signal_name): pass - -class QObject(object): - def emit(self, signal): pass -''') - - -register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform) -MANAGER.register_transform(nodes.FunctionDef, transform_pyqt_signal, - _looks_like_signal) \ No newline at end of file diff --git a/pymode/libs/astroid/brain/brain_re.py b/pymode/libs/astroid/brain/brain_re.py deleted file mode 100644 index f6e1befd..00000000 --- a/pymode/libs/astroid/brain/brain_re.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 -import sys -import astroid - -PY36 = sys.version_info >= (3, 6) - -if PY36: - # Since Python 3.6 there is the RegexFlag enum - # where every entry will be exposed via updating globals() - - def _re_transform(): - return astroid.parse(''' - import sre_compile - ASCII = sre_compile.SRE_FLAG_ASCII - IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE - LOCALE = sre_compile.SRE_FLAG_LOCALE - UNICODE = sre_compile.SRE_FLAG_UNICODE - MULTILINE = sre_compile.SRE_FLAG_MULTILINE - DOTALL = sre_compile.SRE_FLAG_DOTALL - VERBOSE = sre_compile.SRE_FLAG_VERBOSE - A = ASCII - I = IGNORECASE - L = LOCALE - U = UNICODE - M = MULTILINE - S = DOTALL - X = VERBOSE - TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE - T = TEMPLATE - DEBUG = sre_compile.SRE_FLAG_DEBUG - ''') - - astroid.register_module_extender(astroid.MANAGER, 're', _re_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_ssl.py b/pymode/libs/astroid/brain/brain_ssl.py deleted file mode 100644 index 78c20c99..00000000 --- a/pymode/libs/astroid/brain/brain_ssl.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 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 the ssl library.""" - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid import nodes -from astroid import parse - - -def ssl_transform(): - return parse(''' - from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION - from _ssl import _SSLContext, MemoryBIO - from _ssl import ( - SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, - SSLSyscallError, SSLEOFError, - ) - from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED - from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj - from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes - try: - from _ssl import RAND_egd - except ImportError: - # LibreSSL does not provide RAND_egd - pass - from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE, - OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3, - OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2, - OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE) - - from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE, - ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE, - ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE, - ALERT_DESCRIPTION_BAD_RECORD_MAC, - ALERT_DESCRIPTION_CERTIFICATE_EXPIRED, - ALERT_DESCRIPTION_CERTIFICATE_REVOKED, - ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN, - ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE, - ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR, - ALERT_DESCRIPTION_DECOMPRESSION_FAILURE, - ALERT_DESCRIPTION_DECRYPT_ERROR, - ALERT_DESCRIPTION_HANDSHAKE_FAILURE, - ALERT_DESCRIPTION_ILLEGAL_PARAMETER, - ALERT_DESCRIPTION_INSUFFICIENT_SECURITY, - ALERT_DESCRIPTION_INTERNAL_ERROR, - ALERT_DESCRIPTION_NO_RENEGOTIATION, - ALERT_DESCRIPTION_PROTOCOL_VERSION, - ALERT_DESCRIPTION_RECORD_OVERFLOW, - ALERT_DESCRIPTION_UNEXPECTED_MESSAGE, - ALERT_DESCRIPTION_UNKNOWN_CA, - ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY, - ALERT_DESCRIPTION_UNRECOGNIZED_NAME, - ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE, - ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION, - ALERT_DESCRIPTION_USER_CANCELLED) - from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL, - SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ, - SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN) - from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT - from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN - from _ssl import _OPENSSL_API_VERSION - from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 - ''') - - -register_module_extender(MANAGER, 'ssl', ssl_transform) diff --git a/pymode/libs/astroid/brain/brain_subprocess.py b/pymode/libs/astroid/brain/brain_subprocess.py deleted file mode 100644 index db800e78..00000000 --- a/pymode/libs/astroid/brain/brain_subprocess.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 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 - -import sys -import textwrap - -import six - -import astroid - - -PY33 = sys.version_info >= (3, 3) -PY36 = sys.version_info >= (3, 6) - - -def _subprocess_transform(): - if six.PY3: - communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) - communicate_signature = 'def communicate(self, input=None, timeout=None)' - if PY36: - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=(), *, - encoding=None, errors=None): - pass - """ - else: - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=()): - pass - """ - else: - communicate = ('string', 'string') - communicate_signature = 'def communicate(self, input=None)' - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - pass - """ - if PY33: - wait_signature = 'def wait(self, timeout=None)' - else: - wait_signature = 'def wait(self)' - if six.PY3: - ctx_manager = ''' - def __enter__(self): return self - def __exit__(self, *args): pass - ''' - else: - ctx_manager = '' - code = textwrap.dedent(''' - class Popen(object): - returncode = pid = 0 - stdin = stdout = stderr = file() - - %(communicate_signature)s: - return %(communicate)r - %(wait_signature)s: - return self.returncode - def poll(self): - return self.returncode - def send_signal(self, signal): - pass - def terminate(self): - pass - def kill(self): - pass - %(ctx_manager)s - ''' % {'communicate': communicate, - 'communicate_signature': communicate_signature, - 'wait_signature': wait_signature, - 'ctx_manager': ctx_manager}) - - init_lines = textwrap.dedent(init).splitlines() - indented_init = '\n'.join([' ' * 4 + line for line in init_lines]) - code += indented_init - return astroid.parse(code) - - -astroid.register_module_extender(astroid.MANAGER, 'subprocess', _subprocess_transform) diff --git a/pymode/libs/astroid/brain/brain_threading.py b/pymode/libs/astroid/brain/brain_threading.py deleted file mode 100644 index bbc0c612..00000000 --- a/pymode/libs/astroid/brain/brain_threading.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 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 - -import astroid - - -def _thread_transform(): - return astroid.parse(''' - class lock(object): - def acquire(self, blocking=True): - pass - def release(self): - pass - def __enter__(self): - return True - def __exit__(self, *args): - pass - - def Lock(): - return lock() - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'threading', _thread_transform) 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/builder.py b/pymode/libs/astroid/builder.py deleted file mode 100644 index afb9daf1..00000000 --- a/pymode/libs/astroid/builder.py +++ /dev/null @@ -1,437 +0,0 @@ -# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2015 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""The AstroidBuilder makes astroid from living object and / or from _ast - -The builder is not thread safe and can't be used to parse different sources -at the same time. -""" - -import re -import os -import sys -import textwrap -import _ast - -from astroid import bases -from astroid import exceptions -from astroid import manager -from astroid import modutils -from astroid import raw_building -from astroid import rebuilder -from astroid import nodes -from astroid import util - -# The name of the transient function that is used to -# wrap expressions to be extracted when calling -# extract_node. -_TRANSIENT_FUNCTION = '__' - -# The comment used to select a statement to be extracted -# when calling extract_node. -_STATEMENT_SELECTOR = '#@' - - -def _parse(string): - return compile(string, "", 'exec', _ast.PyCF_ONLY_AST) - - -if sys.version_info >= (3, 0): - from tokenize import detect_encoding - - def open_source_file(filename): - with open(filename, 'rb') as byte_stream: - encoding = detect_encoding(byte_stream.readline)[0] - stream = open(filename, 'r', newline=None, encoding=encoding) - data = stream.read() - return stream, encoding, data - -else: - _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)") - - def _guess_encoding(string): - """get encoding from a python file as string or return None if not found""" - # check for UTF-8 byte-order mark - if string.startswith('\xef\xbb\xbf'): - return 'UTF-8' - for line in string.split('\n', 2)[:2]: - # check for encoding declaration - match = _ENCODING_RGX.match(line) - if match is not None: - return match.group(1) - - def open_source_file(filename): - """get data for parsing a file""" - stream = open(filename, 'U') - data = stream.read() - encoding = _guess_encoding(data) - return stream, encoding, data - - -MANAGER = manager.AstroidManager() - - -def _can_assign_attr(node, attrname): - try: - slots = node.slots() - except NotImplementedError: - pass - else: - if slots and attrname not in set(slot.value for slot in slots): - return False - return True - - -class AstroidBuilder(raw_building.InspectBuilder): - """Class for building an astroid tree from source code or from a live module. - - The param *manager* specifies the manager class which should be used. - If no manager is given, then the default one will be used. The - param *apply_transforms* determines if the transforms should be - applied after the tree was built from source or from a live object, - by default being True. - """ - # pylint: disable=redefined-outer-name - def __init__(self, manager=None, apply_transforms=True): - super(AstroidBuilder, self).__init__() - self._manager = manager or MANAGER - self._apply_transforms = apply_transforms - - def module_build(self, module, modname=None): - """Build an astroid from a living module instance.""" - node = None - path = getattr(module, '__file__', None) - if path is not None: - path_, ext = os.path.splitext(modutils._path_from_filename(path)) - if ext in ('.py', '.pyc', '.pyo') and os.path.exists(path_ + '.py'): - node = self.file_build(path_ + '.py', modname) - if node is None: - # this is a built-in module - # get a partial representation by introspection - node = self.inspect_build(module, modname=modname, path=path) - if self._apply_transforms: - # We have to handle transformation by ourselves since the - # rebuilder isn't called for builtin nodes - node = self._manager.visit_transforms(node) - return node - - def file_build(self, path, modname=None): - """Build astroid from a source code file (i.e. from an ast) - - *path* is expected to be a python source file - """ - try: - stream, encoding, data = open_source_file(path) - except IOError as exc: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to load file {path}:\n{error}', - modname=modname, path=path, error=exc)) - except (SyntaxError, LookupError) as exc: - util.reraise(exceptions.AstroidSyntaxError( - 'Python 3 encoding specification error or unknown encoding:\n' - '{error}', modname=modname, path=path, error=exc)) - except UnicodeError: # wrong encoding - # detect_encoding returns utf-8 if no encoding specified - util.reraise(exceptions.AstroidBuildingError( - 'Wrong or no encoding specified for {filename}.', - filename=path)) - with stream: - # get module name if necessary - if modname is None: - try: - modname = '.'.join(modutils.modpath_from_file(path)) - except ImportError: - modname = os.path.splitext(os.path.basename(path))[0] - # build astroid representation - module = self._data_build(data, modname, path) - return self._post_build(module, encoding) - - def string_build(self, data, modname='', path=None): - """Build astroid from source code string.""" - module = self._data_build(data, modname, path) - module.file_bytes = data.encode('utf-8') - return self._post_build(module, 'utf-8') - - def _post_build(self, module, encoding): - """Handles encoding and delayed nodes after a module has been built""" - module.file_encoding = encoding - self._manager.cache_module(module) - # post tree building steps after we stored the module in the cache: - for from_node in module._import_from_nodes: - if from_node.modname == '__future__': - for symbol, _ in from_node.names: - module.future_imports.add(symbol) - self.add_from_names_to_locals(from_node) - # handle delayed assattr nodes - for delayed in module._delayed_assattr: - self.delayed_assattr(delayed) - - # Visit the transforms - if self._apply_transforms: - module = self._manager.visit_transforms(module) - return module - - def _data_build(self, data, modname, path): - """Build tree node from data and add some informations""" - try: - node = _parse(data + '\n') - except (TypeError, ValueError, SyntaxError) as exc: - util.reraise(exceptions.AstroidSyntaxError( - 'Parsing Python code failed:\n{error}', - source=data, modname=modname, path=path, error=exc)) - if path is not None: - node_file = os.path.abspath(path) - else: - node_file = '' - if modname.endswith('.__init__'): - modname = modname[:-9] - package = True - else: - package = path is not None and os.path.splitext(os.path.basename(path))[0] == '__init__' - builder = rebuilder.TreeRebuilder(self._manager) - module = builder.visit_module(node, modname, node_file, package) - module._import_from_nodes = builder._import_from_nodes - module._delayed_assattr = builder._delayed_assattr - return module - - def add_from_names_to_locals(self, node): - """Store imported names to the locals - - Resort the locals if coming from a delayed node - """ - _key_func = lambda node: node.fromlineno - def sort_locals(my_list): - my_list.sort(key=_key_func) - - for (name, asname) in node.names: - if name == '*': - try: - imported = node.do_import_module() - except exceptions.AstroidBuildingError: - continue - for name in imported.public_names(): - node.parent.set_local(name, node) - sort_locals(node.parent.scope().locals[name]) - else: - node.parent.set_local(asname or name, node) - sort_locals(node.parent.scope().locals[asname or name]) - - def delayed_assattr(self, node): - """Visit a AssAttr node - - This adds name to locals and handle members definition. - """ - try: - frame = node.frame() - for inferred in node.expr.infer(): - if inferred is util.Uninferable: - continue - try: - if inferred.__class__ is bases.Instance: - inferred = inferred._proxied - iattrs = inferred.instance_attrs - if not _can_assign_attr(inferred, node.attrname): - continue - elif isinstance(inferred, bases.Instance): - # Const, Tuple, ... we may be wrong, may be not, but - # anyway we don't want to pollute builtin's namespace - continue - elif inferred.is_function: - iattrs = inferred.instance_attrs - else: - iattrs = inferred.locals - except AttributeError: - # XXX log error - continue - values = iattrs.setdefault(node.attrname, []) - if node in values: - continue - # get assign in __init__ first XXX useful ? - if (frame.name == '__init__' and values and - values[0].frame().name != '__init__'): - values.insert(0, node) - else: - values.append(node) - except exceptions.InferenceError: - pass - - -def build_namespace_package_module(name, path): - return nodes.Module(name, doc='', path=path, package=True) - - -def parse(code, module_name='', path=None, apply_transforms=True): - """Parses a source string in order to obtain an astroid AST from it - - :param str code: The code for the module. - :param str module_name: The name for the module, if any - :param str path: The path for the module - :param bool apply_transforms: - Apply the transforms for the give code. Use it if you - don't want the default transforms to be applied. - """ - code = textwrap.dedent(code) - builder = AstroidBuilder(manager=MANAGER, - apply_transforms=apply_transforms) - return builder.string_build(code, modname=module_name, path=path) - - -def _extract_expressions(node): - """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. - - The function walks the AST recursively to search for expressions that - are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an - expression, it completely removes the function call node from the tree, - replacing it by the wrapped expression inside the parent. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :yields: The sequence of wrapped expressions on the modified tree - expression can be found. - """ - if (isinstance(node, nodes.Call) - and isinstance(node.func, nodes.Name) - and node.func.name == _TRANSIENT_FUNCTION): - real_expr = node.args[0] - real_expr.parent = node.parent - # Search for node in all _astng_fields (the fields checked when - # get_children is called) of its parent. Some of those fields may - # be lists or tuples, in which case the elements need to be checked. - # When we find it, replace it by real_expr, so that the AST looks - # like no call to _TRANSIENT_FUNCTION ever took place. - for name in node.parent._astroid_fields: - child = getattr(node.parent, name) - if isinstance(child, (list, tuple)): - for idx, compound_child in enumerate(child): - if compound_child is node: - child[idx] = real_expr - elif child is node: - setattr(node.parent, name, real_expr) - yield real_expr - else: - for child in node.get_children(): - for result in _extract_expressions(child): - yield result - - -def _find_statement_by_line(node, line): - """Extracts the statement on a specific line from an AST. - - If the line number of node matches line, it will be returned; - otherwise its children are iterated and the function is called - recursively. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :param line: The line number of the statement to extract. - :type line: int - :returns: The statement on the line, or None if no statement for the line - can be found. - :rtype: astroid.bases.NodeNG or None - """ - if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)): - # This is an inaccuracy in the AST: the nodes that can be - # decorated do not carry explicit information on which line - # the actual definition (class/def), but .fromline seems to - # be close enough. - node_line = node.fromlineno - else: - node_line = node.lineno - - if node_line == line: - return node - - for child in node.get_children(): - result = _find_statement_by_line(child, line) - if result: - return result - - return None - - -def extract_node(code, module_name=''): - """Parses some Python code as a module and extracts a designated AST node. - - Statements: - To extract one or more statement nodes, append #@ to the end of the line - - Examples: - >>> def x(): - >>> def y(): - >>> return 1 #@ - - The return statement will be extracted. - - >>> class X(object): - >>> def meth(self): #@ - >>> pass - - The function object 'meth' will be extracted. - - Expressions: - To extract arbitrary expressions, surround them with the fake - function call __(...). After parsing, the surrounded expression - will be returned and the whole AST (accessible via the returned - node's parent attribute) will look like the function call was - never there in the first place. - - Examples: - >>> a = __(1) - - The const node will be extracted. - - >>> def x(d=__(foo.bar)): pass - - The node containing the default argument will be extracted. - - >>> def foo(a, b): - >>> return 0 < __(len(a)) < b - - The node containing the function call 'len' will be extracted. - - If no statements or expressions are selected, the last toplevel - statement will be returned. - - If the selected statement is a discard statement, (i.e. an expression - turned into a statement), the wrapped expression is returned instead. - - For convenience, singleton lists are unpacked. - - :param str code: A piece of Python code that is parsed as - a module. Will be passed through textwrap.dedent first. - :param str module_name: The name of the module. - :returns: The designated node from the parse tree, or a list of nodes. - :rtype: astroid.bases.NodeNG, or a list of nodes. - """ - def _extract(node): - if isinstance(node, nodes.Expr): - return node.value - - return node - - requested_lines = [] - for idx, line in enumerate(code.splitlines()): - if line.strip().endswith(_STATEMENT_SELECTOR): - requested_lines.append(idx + 1) - - tree = parse(code, module_name=module_name) - extracted = [] - if requested_lines: - for line in requested_lines: - extracted.append(_find_statement_by_line(tree, line)) - - # Modifies the tree. - extracted.extend(_extract_expressions(tree)) - - if not extracted: - extracted.append(tree.body[-1]) - - extracted = [_extract(node) for node in extracted] - if len(extracted) == 1: - return extracted[0] - return extracted diff --git a/pymode/libs/astroid/context.py b/pymode/libs/astroid/context.py deleted file mode 100644 index 627bae5d..00000000 --- a/pymode/libs/astroid/context.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# 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 - -"""Various context related utilities, including inference and call contexts.""" - -import contextlib -import pprint - - -class InferenceContext(object): - __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'inferred') - - def __init__(self, path=None, inferred=None): - self.path = path or set() - self.lookupname = None - self.callcontext = None - self.boundnode = None - self.inferred = inferred or {} - - def push(self, node): - name = self.lookupname - if (node, name) in self.path: - return True - - self.path.add((node, name)) - return False - - def clone(self): - # XXX copy lookupname/callcontext ? - clone = InferenceContext(self.path, inferred=self.inferred) - clone.callcontext = self.callcontext - clone.boundnode = self.boundnode - return clone - - def cache_generator(self, key, generator): - results = [] - for result in generator: - results.append(result) - yield result - - self.inferred[key] = tuple(results) - return - - @contextlib.contextmanager - def restore_path(self): - path = set(self.path) - yield - self.path = path - - def __str__(self): - state = ('%s=%s' % (field, pprint.pformat(getattr(self, field), - width=80 - len(field))) - for field in self.__slots__) - return '%s(%s)' % (type(self).__name__, ',\n '.join(state)) - - -class CallContext(object): - """Holds information for a call site.""" - - __slots__ = ('args', 'keywords') - - def __init__(self, args, keywords=None): - self.args = args - if keywords: - keywords = [(arg.arg, arg.value) for arg in keywords] - else: - keywords = [] - self.keywords = keywords - - -def copy_context(context): - if context is not None: - return context.clone() - - return InferenceContext() diff --git a/pymode/libs/astroid/decorators.py b/pymode/libs/astroid/decorators.py deleted file mode 100644 index a59cab22..00000000 --- a/pymode/libs/astroid/decorators.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015 Florian Bruhin -# 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 - -""" A few useful function/method decorators.""" - -import functools - -import wrapt - -from astroid import context as contextmod -from astroid import exceptions -from astroid import util - - -@wrapt.decorator -def cached(func, instance, args, kwargs): - """Simple decorator to cache result of method calls without args.""" - cache = getattr(instance, '__cache', None) - if cache is None: - instance.__cache = cache = {} - try: - return cache[func] - except KeyError: - cache[func] = result = func(*args, **kwargs) - return result - - -class cachedproperty(object): - """ Provides a cached property equivalent to the stacking of - @cached and @property, but more efficient. - - After first usage, the becomes part of the object's - __dict__. Doing: - - del obj. empties the cache. - - Idea taken from the pyramid_ framework and the mercurial_ project. - - .. _pyramid: http://pypi.python.org/pypi/pyramid - .. _mercurial: http://pypi.python.org/pypi/Mercurial - """ - __slots__ = ('wrapped',) - - def __init__(self, wrapped): - try: - wrapped.__name__ - except AttributeError: - util.reraise(TypeError('%s must have a __name__ attribute' - % wrapped)) - self.wrapped = wrapped - - @property - def __doc__(self): - doc = getattr(self.wrapped, '__doc__', None) - return ('%s' - % ('\n%s' % doc if doc else '')) - - def __get__(self, inst, objtype=None): - if inst is None: - return self - val = self.wrapped(inst) - setattr(inst, self.wrapped.__name__, val) - return val - - -def path_wrapper(func): - """return the given infer function wrapped to handle the path""" - # TODO: switch this to wrapt after the monkey-patching is fixed (ceridwen) - @functools.wraps(func) - def wrapped(node, context=None, _func=func, **kwargs): - """wrapper function handling context""" - if context is None: - context = contextmod.InferenceContext() - if context.push(node): - return - - yielded = set() - generator = _func(node, context, **kwargs) - try: - while True: - res = next(generator) - # unproxy only true instance, not const, tuple, dict... - if res.__class__.__name__ == 'Instance': - ares = res._proxied - else: - ares = res - if ares not in yielded: - yield res - yielded.add(ares) - except StopIteration as error: - # Explicit StopIteration to return error information, see - # comment in raise_if_nothing_inferred. - if error.args: - raise StopIteration(error.args[0]) - else: - raise StopIteration - - return wrapped - - -@wrapt.decorator -def yes_if_nothing_inferred(func, instance, args, kwargs): - inferred = False - for node in func(*args, **kwargs): - inferred = True - yield node - if not inferred: - yield util.Uninferable - - -@wrapt.decorator -def raise_if_nothing_inferred(func, instance, args, kwargs): - '''All generators wrapped with raise_if_nothing_inferred *must* - explicitly raise StopIteration with information to create an - appropriate structured InferenceError. - - ''' - # TODO: Explicitly raising StopIteration in a generator will cause - # a RuntimeError in Python >=3.7, as per - # http://legacy.python.org/dev/peps/pep-0479/ . Before 3.7 is - # released, this code will need to use one of four possible - # solutions: a decorator that restores the current behavior as - # described in - # http://legacy.python.org/dev/peps/pep-0479/#sub-proposal-decorator-to-explicitly-request-current-behaviour - # , dynamic imports or exec to generate different code for - # different versions, drop support for all Python versions <3.3, - # or refactoring to change how these decorators work. In any - # event, after dropping support for Python <3.3 this code should - # be refactored to use `yield from`. - inferred = False - try: - generator = func(*args, **kwargs) - while True: - yield next(generator) - inferred = True - except StopIteration as error: - if not inferred: - if error.args: - # pylint: disable=not-a-mapping - raise exceptions.InferenceError(**error.args[0]) - else: - raise exceptions.InferenceError( - 'StopIteration raised without any error information.') diff --git a/pymode/libs/astroid/exceptions.py b/pymode/libs/astroid/exceptions.py deleted file mode 100644 index 8b3f2522..00000000 --- a/pymode/libs/astroid/exceptions.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (c) 2007, 2009-2010, 2013 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson -# 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 - -"""this module contains exceptions used in the astroid library -""" -from astroid import util - - -class AstroidError(Exception): - """base exception class for all astroid related exceptions - - AstroidError and its subclasses are structured, intended to hold - objects representing state when the exception is thrown. Field - values are passed to the constructor as keyword-only arguments. - Each subclass has its own set of standard fields, but use your - best judgment to decide whether a specific exception instance - needs more or fewer fields for debugging. Field values may be - used to lazily generate the error message: self.message.format() - will be called with the field names and values supplied as keyword - arguments. - """ - def __init__(self, message='', **kws): - super(AstroidError, self).__init__(message) - self.message = message - for key, value in kws.items(): - setattr(self, key, value) - - def __str__(self): - return self.message.format(**vars(self)) - - -class AstroidBuildingError(AstroidError): - """exception class when we are unable to build an astroid representation - - Standard attributes: - modname: Name of the module that AST construction failed for. - error: Exception raised during construction. - """ - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='Failed to import module {modname}.', **kws): - super(AstroidBuildingError, self).__init__(message, **kws) - - -class AstroidImportError(AstroidBuildingError): - """Exception class used when a module can't be imported by astroid.""" - - -class TooManyLevelsError(AstroidImportError): - """Exception class which is raised when a relative import was beyond the top-level. - - Standard attributes: - level: The level which was attempted. - name: the name of the module on which the relative import was attempted. - """ - level = None - name = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='Relative import with too many levels ' - '({level}) for module {name!r}', **kws): - super(TooManyLevelsError, self).__init__(message, **kws) - - -class AstroidSyntaxError(AstroidBuildingError): - """Exception class used when a module can't be parsed.""" - - -class NoDefault(AstroidError): - """raised by function's `default_value` method when an argument has - no default value - - Standard attributes: - func: Function node. - name: Name of argument without a default. - """ - func = None - name = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='{func!r} has no default for {name!r}.', **kws): - super(NoDefault, self).__init__(message, **kws) - - -class ResolveError(AstroidError): - """Base class of astroid resolution/inference error. - - ResolveError is not intended to be raised. - - Standard attributes: - context: InferenceContext object. - """ - context = None - - -class MroError(ResolveError): - """Error raised when there is a problem with method resolution of a class. - - Standard attributes: - mros: A sequence of sequences containing ClassDef nodes. - cls: ClassDef node whose MRO resolution failed. - context: InferenceContext object. - """ - mros = () - cls = None - - def __str__(self): - mro_names = ", ".join("({})".format(", ".join(b.name for b in m)) - for m in self.mros) - return self.message.format(mros=mro_names, cls=self.cls) - - -class DuplicateBasesError(MroError): - """Error raised when there are duplicate bases in the same class bases.""" - - -class InconsistentMroError(MroError): - """Error raised when a class's MRO is inconsistent.""" - - -class SuperError(ResolveError): - - """Error raised when there is a problem with a super call. - - Standard attributes: - super_: The Super instance that raised the exception. - context: InferenceContext object. - """ - super_ = None - - def __str__(self): - return self.message.format(**vars(self.super_)) - - -class InferenceError(ResolveError): - """raised when we are unable to infer a node - - Standard attributes: - node: The node inference was called on. - context: InferenceContext object. - """ - node = None - context = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='Inference failed for {node!r}.', **kws): - super(InferenceError, self).__init__(message, **kws) - - -# Why does this inherit from InferenceError rather than ResolveError? -# Changing it causes some inference tests to fail. -class NameInferenceError(InferenceError): - """Raised when a name lookup fails, corresponds to NameError. - - Standard attributes: - name: The name for which lookup failed, as a string. - scope: The node representing the scope in which the lookup occurred. - context: InferenceContext object. - """ - name = None - scope = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='{name!r} not found in {scope!r}.', **kws): - super(NameInferenceError, self).__init__(message, **kws) - - -class AttributeInferenceError(ResolveError): - """Raised when an attribute lookup fails, corresponds to AttributeError. - - Standard attributes: - target: The node for which lookup failed. - attribute: The attribute for which lookup failed, as a string. - context: InferenceContext object. - """ - target = None - attribute = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='{attribute!r} not found on {target!r}.', **kws): - super(AttributeInferenceError, self).__init__(message, **kws) - - -class UseInferenceDefault(Exception): - """exception to be raised in custom inference function to indicate that it - should go back to the default behaviour - """ - - -class _NonDeducibleTypeHierarchy(Exception): - """Raised when is_subtype / is_supertype can't deduce the relation between two types.""" - - -class AstroidIndexError(AstroidError): - """Raised when an Indexable / Mapping does not have an index / key.""" - - -class AstroidTypeError(AstroidError): - """Raised when a TypeError would be expected in Python code.""" - - -# Backwards-compatibility aliases -OperationError = util.BadOperationMessage -UnaryOperationError = util.BadUnaryOperationMessage -BinaryOperationError = util.BadBinaryOperationMessage - -SuperArgumentTypeError = SuperError -UnresolvableName = NameInferenceError -NotFoundError = AttributeInferenceError -AstroidBuildingException = AstroidBuildingError diff --git a/pymode/libs/astroid/helpers.py b/pymode/libs/astroid/helpers.py deleted file mode 100644 index 62da56ea..00000000 --- a/pymode/libs/astroid/helpers.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# 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 - - -""" -Various helper utilities. -""" - -import six - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import manager -from astroid import nodes -from astroid import raw_building -from astroid import scoped_nodes -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ - - -def _build_proxy_class(cls_name, builtins): - proxy = raw_building.build_class(cls_name) - proxy.parent = builtins - return proxy - - -def _function_type(function, builtins): - if isinstance(function, scoped_nodes.Lambda): - if function.root().name == BUILTINS: - cls_name = 'builtin_function_or_method' - else: - cls_name = 'function' - elif isinstance(function, bases.BoundMethod): - if six.PY2: - cls_name = 'instancemethod' - else: - cls_name = 'method' - elif isinstance(function, bases.UnboundMethod): - if six.PY2: - cls_name = 'instancemethod' - else: - cls_name = 'function' - return _build_proxy_class(cls_name, builtins) - - -def _object_type(node, context=None): - astroid_manager = manager.AstroidManager() - builtins = astroid_manager.astroid_cache[BUILTINS] - context = context or contextmod.InferenceContext() - - for inferred in node.infer(context=context): - if isinstance(inferred, scoped_nodes.ClassDef): - if inferred.newstyle: - metaclass = inferred.metaclass() - if metaclass: - yield metaclass - continue - yield builtins.getattr('type')[0] - elif isinstance(inferred, (scoped_nodes.Lambda, bases.UnboundMethod)): - yield _function_type(inferred, builtins) - elif isinstance(inferred, scoped_nodes.Module): - yield _build_proxy_class('module', builtins) - else: - yield inferred._proxied - - -def object_type(node, context=None): - """Obtain the type of the given node - - This is used to implement the ``type`` builtin, which means that it's - used for inferring type calls, as well as used in a couple of other places - in the inference. - The node will be inferred first, so this function can support all - sorts of objects, as long as they support inference. - """ - - try: - types = set(_object_type(node, context)) - except exceptions.InferenceError: - return util.Uninferable - if len(types) > 1 or not types: - return util.Uninferable - return list(types)[0] - - -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 exceptions.InferenceError: - return - try: - next(inferit) - return # None if there is ambiguity on the inferred node - except exceptions.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, scoped_nodes.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 _type_check(type1, type2): - if not all(map(has_known_bases, (type1, type2))): - raise exceptions._NonDeducibleTypeHierarchy - - if not all([type1.newstyle, type2.newstyle]): - return False - try: - return type1 in type2.mro()[:-1] - except exceptions.MroError: - # The MRO is invalid. - raise exceptions._NonDeducibleTypeHierarchy - - -def is_subtype(type1, type2): - """Check if *type1* is a subtype of *typ2*.""" - return _type_check(type2, type1) - - -def is_supertype(type1, type2): - """Check if *type2* is a supertype of *type1*.""" - return _type_check(type1, type2) - - -def class_instance_as_index(node): - """Get the value as an index for the given instance. - - If an instance provides an __index__ method, then it can - be used in some scenarios where an integer is expected, - for instance when multiplying or subscripting a list. - """ - context = contextmod.InferenceContext() - context.callcontext = contextmod.CallContext(args=[node]) - - try: - for inferred in node.igetattr('__index__', context=context): - if not isinstance(inferred, bases.BoundMethod): - continue - - for result in inferred.infer_call_result(node, context=context): - if (isinstance(result, nodes.Const) - and isinstance(result.value, int)): - return result - except exceptions.InferenceError: - pass diff --git a/pymode/libs/astroid/inference.py b/pymode/libs/astroid/inference.py deleted file mode 100644 index f77ad19a..00000000 --- a/pymode/libs/astroid/inference.py +++ /dev/null @@ -1,816 +0,0 @@ -# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""this module contains a set of functions to handle inference on astroid trees -""" - -import functools -import itertools -import operator - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import decorators -from astroid import helpers -from astroid import manager -from astroid import nodes -from astroid.interpreter import dunder_lookup -from astroid import protocols -from astroid import util - - -MANAGER = manager.AstroidManager() - - -# .infer method ############################################################### - - -def infer_end(self, context=None): - """inference's end for node such as Module, ClassDef, FunctionDef, - Const... - - """ - yield self -nodes.Module._infer = infer_end -nodes.ClassDef._infer = infer_end -nodes.FunctionDef._infer = infer_end -nodes.Lambda._infer = infer_end -nodes.Const._infer = infer_end -nodes.Slice._infer = infer_end - - -def infer_seq(self, context=None): - if not any(isinstance(e, nodes.Starred) for e in self.elts): - yield self - else: - values = _infer_seq(self, context) - new_seq = type(self)(self.lineno, self.col_offset, self.parent) - new_seq.postinit(values) - yield new_seq - - -def _infer_seq(node, context=None): - """Infer all values based on _BaseContainer.elts""" - values = [] - - for elt in node.elts: - if isinstance(elt, nodes.Starred): - starred = helpers.safe_infer(elt.value, context) - if starred in (None, util.Uninferable): - raise exceptions.InferenceError(node=node, - context=context) - if not hasattr(starred, 'elts'): - raise exceptions.InferenceError(node=node, - context=context) - values.extend(_infer_seq(starred)) - else: - values.append(elt) - return values - - -nodes.List._infer = infer_seq -nodes.Tuple._infer = infer_seq -nodes.Set._infer = infer_seq - - -def infer_map(self, context=None): - if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items): - yield self - else: - items = _infer_map(self, context) - new_seq = type(self)(self.lineno, self.col_offset, self.parent) - new_seq.postinit(list(items.items())) - yield new_seq - - -def _infer_map(node, context): - """Infer all values based on Dict.items""" - values = {} - for name, value in node.items: - if isinstance(name, nodes.DictUnpack): - double_starred = helpers.safe_infer(value, context) - if double_starred in (None, util.Uninferable): - raise exceptions.InferenceError - if not isinstance(double_starred, nodes.Dict): - raise exceptions.InferenceError(node=node, - context=context) - values.update(_infer_map(double_starred, context)) - else: - key = helpers.safe_infer(name, context=context) - value = helpers.safe_infer(value, context=context) - if key is None or value is None: - raise exceptions.InferenceError(node=node, - context=context) - values[key] = value - return values - - -nodes.Dict._infer = infer_map - - -def _higher_function_scope(node): - """ Search for the first function which encloses the given - scope. This can be used for looking up in that function's - scope, in case looking up in a lower scope for a particular - name fails. - - :param node: A scope node. - :returns: - ``None``, if no parent function scope was found, - otherwise an instance of :class:`astroid.scoped_nodes.Function`, - which encloses the given node. - """ - current = node - while current.parent and not isinstance(current.parent, nodes.FunctionDef): - current = current.parent - if current and current.parent: - return current.parent - -def infer_name(self, context=None): - """infer a Name: use name lookup rules""" - frame, stmts = self.lookup(self.name) - if not stmts: - # Try to see if the name is enclosed in a nested function - # and use the higher (first function) scope for searching. - # TODO: should this be promoted to other nodes as well? - parent_function = _higher_function_scope(self.scope()) - if parent_function: - _, stmts = parent_function.lookup(self.name) - - if not stmts: - raise exceptions.NameInferenceError(name=self.name, - scope=self.scope(), - context=context) - context = context.clone() - context.lookupname = self.name - return bases._infer_stmts(stmts, context, frame) -nodes.Name._infer = decorators.path_wrapper(infer_name) -nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper - - -@decorators.raise_if_nothing_inferred -@decorators.path_wrapper -def infer_call(self, context=None): - """infer a Call node by trying to guess what the function returns""" - callcontext = context.clone() - callcontext.callcontext = contextmod.CallContext(args=self.args, - keywords=self.keywords) - callcontext.boundnode = None - for callee in self.func.infer(context): - if callee is util.Uninferable: - yield callee - continue - try: - if hasattr(callee, 'infer_call_result'): - for inferred in callee.infer_call_result(self, callcontext): - yield inferred - except exceptions.InferenceError: - ## XXX log error ? - continue - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) -nodes.Call._infer = infer_call - - -@decorators.path_wrapper -def infer_import(self, context=None, asname=True): - """infer an Import node: return the imported module/object""" - name = context.lookupname - if name is None: - raise exceptions.InferenceError(node=self, context=context) - - try: - if asname: - yield self.do_import_module(self.real_name(name)) - else: - yield self.do_import_module(name) - except exceptions.AstroidBuildingError as exc: - util.reraise(exceptions.InferenceError(node=self, error=exc, - context=context)) - -nodes.Import._infer = infer_import - - -def infer_name_module(self, name): - context = contextmod.InferenceContext() - context.lookupname = name - return self.infer(context, asname=False) -nodes.Import.infer_name_module = infer_name_module - - -@decorators.path_wrapper -def infer_import_from(self, context=None, asname=True): - """infer a ImportFrom node: return the imported module/object""" - name = context.lookupname - if name is None: - raise exceptions.InferenceError(node=self, context=context) - if asname: - name = self.real_name(name) - - try: - module = self.do_import_module() - except exceptions.AstroidBuildingError as exc: - util.reraise(exceptions.InferenceError(node=self, error=exc, - context=context)) - - try: - context = contextmod.copy_context(context) - context.lookupname = name - stmts = module.getattr(name, ignore_locals=module is self.root()) - return bases._infer_stmts(stmts, context) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) -nodes.ImportFrom._infer = infer_import_from - - -@decorators.raise_if_nothing_inferred -def infer_attribute(self, context=None): - """infer an Attribute node by using getattr on the associated object""" - for owner in self.expr.infer(context): - if owner is util.Uninferable: - yield owner - continue - - if context and context.boundnode: - # This handles the situation where the attribute is accessed through a subclass - # of a base class and the attribute is defined at the base class's level, - # by taking in consideration a redefinition in the subclass. - if (isinstance(owner, bases.Instance) - and isinstance(context.boundnode, bases.Instance)): - try: - if helpers.is_subtype(helpers.object_type(context.boundnode), - helpers.object_type(owner)): - owner = context.boundnode - except exceptions._NonDeducibleTypeHierarchy: - # Can't determine anything useful. - pass - - try: - context.boundnode = owner - for obj in owner.igetattr(self.attrname, context): - yield obj - context.boundnode = None - except (exceptions.AttributeInferenceError, exceptions.InferenceError): - context.boundnode = None - except AttributeError: - # XXX method / function - context.boundnode = None - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) -nodes.Attribute._infer = decorators.path_wrapper(infer_attribute) -nodes.AssignAttr.infer_lhs = infer_attribute # # won't work with a path wrapper - - -@decorators.path_wrapper -def infer_global(self, context=None): - if context.lookupname is None: - raise exceptions.InferenceError(node=self, context=context) - try: - return bases._infer_stmts(self.root().getattr(context.lookupname), - context) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=context.lookupname, - context=context)) -nodes.Global._infer = infer_global - - -_SUBSCRIPT_SENTINEL = object() - - -@decorators.raise_if_nothing_inferred -def infer_subscript(self, context=None): - """Inference for subscripts - - We're understanding if the index is a Const - or a slice, passing the result of inference - to the value's `getitem` method, which should - handle each supported index type accordingly. - """ - - value = next(self.value.infer(context)) - if value is util.Uninferable: - yield util.Uninferable - return - - index = next(self.slice.infer(context)) - if index is util.Uninferable: - yield util.Uninferable - return - - # Try to deduce the index value. - index_value = _SUBSCRIPT_SENTINEL - if value.__class__ == bases.Instance: - index_value = index - else: - if index.__class__ == bases.Instance: - instance_as_index = helpers.class_instance_as_index(index) - if instance_as_index: - index_value = instance_as_index - else: - index_value = index - if index_value is _SUBSCRIPT_SENTINEL: - raise exceptions.InferenceError(node=self, context=context) - - try: - assigned = value.getitem(index_value, context) - except (exceptions.AstroidTypeError, - exceptions.AstroidIndexError, - exceptions.AttributeInferenceError, - AttributeError) as exc: - util.reraise(exceptions.InferenceError(node=self, error=exc, - context=context)) - - # Prevent inferring if the inferred subscript - # is the same as the original subscripted object. - if self is assigned or assigned is util.Uninferable: - yield util.Uninferable - return - for inferred in assigned.infer(context): - yield inferred - - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) - -nodes.Subscript._infer = decorators.path_wrapper(infer_subscript) -nodes.Subscript.infer_lhs = infer_subscript - - -@decorators.raise_if_nothing_inferred -@decorators.path_wrapper -def _infer_boolop(self, context=None): - """Infer a boolean operation (and / or / not). - - The function will calculate the boolean operation - for all pairs generated through inference for each component - node. - """ - values = self.values - if self.op == 'or': - predicate = operator.truth - else: - predicate = operator.not_ - - try: - values = [value.infer(context=context) for value in values] - except exceptions.InferenceError: - yield util.Uninferable - return - - for pair in itertools.product(*values): - if any(item is util.Uninferable for item in pair): - # Can't infer the final result, just yield Uninferable. - yield util.Uninferable - continue - - bool_values = [item.bool_value() for item in pair] - if any(item is util.Uninferable for item in bool_values): - # Can't infer the final result, just yield Uninferable. - yield util.Uninferable - continue - - # Since the boolean operations are short circuited operations, - # this code yields the first value for which the predicate is True - # and if no value respected the predicate, then the last value will - # be returned (or Uninferable if there was no last value). - # This is conforming to the semantics of `and` and `or`: - # 1 and 0 -> 1 - # 0 and 1 -> 0 - # 1 or 0 -> 1 - # 0 or 1 -> 1 - value = util.Uninferable - for value, bool_value in zip(pair, bool_values): - if predicate(bool_value): - yield value - break - else: - yield value - - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) - -nodes.BoolOp._infer = _infer_boolop - - -# UnaryOp, BinOp and AugAssign inferences - -def _filter_operation_errors(self, infer_callable, context, error): - for result in infer_callable(self, context): - if isinstance(result, error): - # For the sake of .infer(), we don't care about operation - # errors, which is the job of pylint. So return something - # which shows that we can't infer the result. - yield util.Uninferable - else: - yield result - - -def _infer_unaryop(self, context=None): - """Infer what an UnaryOp should return when evaluated.""" - for operand in self.operand.infer(context): - try: - yield operand.infer_unary_op(self.op) - except TypeError as exc: - # The operand doesn't support this operation. - yield util.BadUnaryOperationMessage(operand, self.op, exc) - except AttributeError as exc: - meth = protocols.UNARY_OP_METHOD[self.op] - if meth is None: - # `not node`. Determine node's boolean - # value and negate its result, unless it is - # Uninferable, which will be returned as is. - bool_value = operand.bool_value() - if bool_value is not util.Uninferable: - yield nodes.const_factory(not bool_value) - else: - yield util.Uninferable - else: - if not isinstance(operand, (bases.Instance, nodes.ClassDef)): - # The operation was used on something which - # doesn't support it. - yield util.BadUnaryOperationMessage(operand, self.op, exc) - continue - - try: - try: - methods = dunder_lookup.lookup(operand, meth) - except exceptions.AttributeInferenceError: - yield util.BadUnaryOperationMessage(operand, self.op, exc) - continue - - meth = methods[0] - inferred = next(meth.infer(context=context)) - if inferred is util.Uninferable or not inferred.callable(): - continue - - context = contextmod.copy_context(context) - context.callcontext = contextmod.CallContext(args=[operand]) - call_results = inferred.infer_call_result(self, context=context) - result = next(call_results, None) - if result is None: - # Failed to infer, return the same type. - yield operand - else: - yield result - except exceptions.AttributeInferenceError as exc: - # The unary operation special method was not found. - yield util.BadUnaryOperationMessage(operand, self.op, exc) - except exceptions.InferenceError: - yield util.Uninferable - - -@decorators.raise_if_nothing_inferred -@decorators.path_wrapper -def infer_unaryop(self, context=None): - """Infer what an UnaryOp should return when evaluated.""" - for inferred in _filter_operation_errors(self, _infer_unaryop, context, - util.BadUnaryOperationMessage): - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) - -nodes.UnaryOp._infer_unaryop = _infer_unaryop -nodes.UnaryOp._infer = infer_unaryop - - -def _is_not_implemented(const): - """Check if the given const node is NotImplemented.""" - return isinstance(const, nodes.Const) and const.value is NotImplemented - - -def _invoke_binop_inference(instance, opnode, op, other, context, method_name): - """Invoke binary operation inference on the given instance.""" - methods = dunder_lookup.lookup(instance, method_name) - method = methods[0] - inferred = next(method.infer(context=context)) - return instance.infer_binary_op(opnode, op, other, context, inferred) - - -def _aug_op(instance, opnode, op, other, context, reverse=False): - """Get an inference callable for an augmented binary operation.""" - method_name = protocols.AUGMENTED_OP_METHOD[op] - return functools.partial(_invoke_binop_inference, - instance=instance, - op=op, opnode=opnode, other=other, - context=context, - method_name=method_name) - - -def _bin_op(instance, opnode, op, other, context, reverse=False): - """Get an inference callable for a normal binary operation. - - If *reverse* is True, then the reflected method will be used instead. - """ - if reverse: - method_name = protocols.REFLECTED_BIN_OP_METHOD[op] - else: - method_name = protocols.BIN_OP_METHOD[op] - return functools.partial(_invoke_binop_inference, - instance=instance, - op=op, opnode=opnode, other=other, - context=context, - method_name=method_name) - - -def _get_binop_contexts(context, left, right): - """Get contexts for binary operations. - - This will return two inferrence contexts, the first one - for x.__op__(y), the other one for y.__rop__(x), where - only the arguments are inversed. - """ - # The order is important, since the first one should be - # left.__op__(right). - for arg in (right, left): - new_context = context.clone() - new_context.callcontext = contextmod.CallContext(args=[arg]) - new_context.boundnode = None - yield new_context - - -def _same_type(type1, type2): - """Check if type1 is the same as type2.""" - return type1.qname() == type2.qname() - - -def _get_binop_flow(left, left_type, binary_opnode, right, right_type, - context, reverse_context): - """Get the flow for binary operations. - - The rules are a bit messy: - - * if left and right have the same type, then only one - method will be called, left.__op__(right) - * if left and right are unrelated typewise, then first - left.__op__(right) is tried and if this does not exist - or returns NotImplemented, then right.__rop__(left) is tried. - * if left is a subtype of right, then only left.__op__(right) - is tried. - * if left is a supertype of right, then right.__rop__(left) - is first tried and then left.__op__(right) - """ - op = binary_opnode.op - if _same_type(left_type, right_type): - methods = [_bin_op(left, binary_opnode, op, right, context)] - elif helpers.is_subtype(left_type, right_type): - methods = [_bin_op(left, binary_opnode, op, right, context)] - elif helpers.is_supertype(left_type, right_type): - methods = [_bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), - _bin_op(left, binary_opnode, op, right, context)] - else: - methods = [_bin_op(left, binary_opnode, op, right, context), - _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True)] - return methods - - -def _get_aug_flow(left, left_type, aug_opnode, right, right_type, - context, reverse_context): - """Get the flow for augmented binary operations. - - The rules are a bit messy: - - * if left and right have the same type, then left.__augop__(right) - is first tried and then left.__op__(right). - * if left and right are unrelated typewise, then - left.__augop__(right) is tried, then left.__op__(right) - is tried and then right.__rop__(left) is tried. - * if left is a subtype of right, then left.__augop__(right) - is tried and then left.__op__(right). - * if left is a supertype of right, then left.__augop__(right) - is tried, then right.__rop__(left) and then - left.__op__(right) - """ - bin_op = aug_opnode.op.strip("=") - aug_op = aug_opnode.op - if _same_type(left_type, right_type): - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(left, aug_opnode, bin_op, right, context)] - elif helpers.is_subtype(left_type, right_type): - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(left, aug_opnode, bin_op, right, context)] - elif helpers.is_supertype(left_type, right_type): - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True), - _bin_op(left, aug_opnode, bin_op, right, context)] - else: - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(left, aug_opnode, bin_op, right, context), - _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True)] - return methods - - -def _infer_binary_operation(left, right, binary_opnode, context, flow_factory): - """Infer a binary operation between a left operand and a right operand - - This is used by both normal binary operations and augmented binary - operations, the only difference is the flow factory used. - """ - - context, reverse_context = _get_binop_contexts(context, left, right) - left_type = helpers.object_type(left) - right_type = helpers.object_type(right) - methods = flow_factory(left, left_type, binary_opnode, right, right_type, - context, reverse_context) - for method in methods: - try: - results = list(method()) - except AttributeError: - continue - except exceptions.AttributeInferenceError: - continue - except exceptions.InferenceError: - yield util.Uninferable - return - else: - if any(result is util.Uninferable for result in results): - yield util.Uninferable - return - - # TODO(cpopa): since the inference engine might return - # more values than are actually possible, we decide - # to return util.Uninferable if we have union types. - if all(map(_is_not_implemented, results)): - continue - not_implemented = sum(1 for result in results - if _is_not_implemented(result)) - if not_implemented and not_implemented != len(results): - # Can't decide yet what this is, not yet though. - yield util.Uninferable - return - - for result in results: - yield result - return - # TODO(cpopa): yield a BadBinaryOperationMessage here, - # since the operation is not supported - yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type) - - -def _infer_binop(self, context): - """Binary operation inferrence logic.""" - if context is None: - context = contextmod.InferenceContext() - left = self.left - right = self.right - - # we use two separate contexts for evaluating lhs and rhs because - # 1. evaluating lhs may leave some undesired entries in context.path - # which may not let us infer right value of rhs - lhs_context = context.clone() - rhs_context = context.clone() - - for lhs in left.infer(context=lhs_context): - if lhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - for rhs in right.infer(context=rhs_context): - if rhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - try: - for result in _infer_binary_operation(lhs, rhs, self, - context, _get_binop_flow): - yield result - except exceptions._NonDeducibleTypeHierarchy: - yield util.Uninferable - - -@decorators.yes_if_nothing_inferred -@decorators.path_wrapper -def infer_binop(self, context=None): - return _filter_operation_errors(self, _infer_binop, context, - util.BadBinaryOperationMessage) - -nodes.BinOp._infer_binop = _infer_binop -nodes.BinOp._infer = infer_binop - - -def _infer_augassign(self, context=None): - """Inference logic for augmented binary operations.""" - if context is None: - context = contextmod.InferenceContext() - - for lhs in self.target.infer_lhs(context=context): - if lhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - # TODO(cpopa): if we have A() * A(), trying to infer - # the rhs with the same context will result in an - # inference error, so we create another context for it. - # This is a bug which should be fixed in InferenceContext at some point. - rhs_context = context.clone() - rhs_context.path = set() - for rhs in self.value.infer(context=rhs_context): - if rhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - try: - results = _infer_binary_operation(lhs, rhs, self, context, _get_aug_flow) - except exceptions._NonDeducibleTypeHierarchy: - yield util.Uninferable - else: - for result in results: - yield result - - -@decorators.path_wrapper -def infer_augassign(self, context=None): - return _filter_operation_errors(self, _infer_augassign, context, - util.BadBinaryOperationMessage) - -nodes.AugAssign._infer_augassign = _infer_augassign -nodes.AugAssign._infer = infer_augassign - -# End of binary operation inference. - - -def infer_arguments(self, context=None): - name = context.lookupname - if name is None: - raise exceptions.InferenceError(node=self, context=context) - return protocols._arguments_infer_argname(self, name, context) -nodes.Arguments._infer = infer_arguments - - -@decorators.path_wrapper -def infer_assign(self, context=None): - """infer a AssignName/AssignAttr: need to inspect the RHS part of the - assign node - """ - stmt = self.statement() - if isinstance(stmt, nodes.AugAssign): - return stmt.infer(context) - - stmts = list(self.assigned_stmts(context=context)) - return bases._infer_stmts(stmts, context) -nodes.AssignName._infer = infer_assign -nodes.AssignAttr._infer = infer_assign - - -# no infer method on DelName and DelAttr (expected InferenceError) - -@decorators.path_wrapper -def infer_empty_node(self, context=None): - if not self.has_underlying_object(): - yield util.Uninferable - else: - try: - for inferred in MANAGER.infer_ast_from_something(self.object, - context=context): - yield inferred - except exceptions.AstroidError: - yield util.Uninferable -nodes.EmptyNode._infer = infer_empty_node - - -def infer_index(self, context=None): - return self.value.infer(context) -nodes.Index._infer = infer_index - -# TODO: move directly into bases.Instance when the dependency hell -# will be solved. -def instance_getitem(self, index, context=None): - # Rewrap index to Const for this case - if context: - new_context = context.clone() - else: - context = new_context = contextmod.InferenceContext() - - # Create a new callcontext for providing index as an argument. - new_context.callcontext = contextmod.CallContext(args=[index]) - new_context.boundnode = self - - method = next(self.igetattr('__getitem__', context=context)) - if not isinstance(method, bases.BoundMethod): - raise exceptions.InferenceError( - 'Could not find __getitem__ for {node!r}.', - node=self, context=context) - - try: - return next(method.infer_call_result(self, new_context)) - except StopIteration: - util.reraise(exceptions.InferenceError( - message='Inference for {node!r}[{index!s}] failed.', - node=self, index=index, context=context)) - -bases.Instance.getitem = instance_getitem diff --git a/pymode/libs/astroid/interpreter/_import/__init__.py b/pymode/libs/astroid/interpreter/_import/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pymode/libs/astroid/interpreter/_import/spec.py b/pymode/libs/astroid/interpreter/_import/spec.py deleted file mode 100644 index 1edd23d6..00000000 --- a/pymode/libs/astroid/interpreter/_import/spec.py +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -import abc -import collections -import enum -import imp -import os -import sys -import zipimport -try: - import importlib.machinery - _HAS_MACHINERY = True -except ImportError: - _HAS_MACHINERY = False - -try: - from functools import lru_cache -except ImportError: - from backports.functools_lru_cache import lru_cache - -from . import util - -ModuleType = enum.Enum('ModuleType', 'C_BUILTIN C_EXTENSION PKG_DIRECTORY ' - 'PY_CODERESOURCE PY_COMPILED PY_FROZEN PY_RESOURCE ' - 'PY_SOURCE PY_ZIPMODULE PY_NAMESPACE') -_ImpTypes = {imp.C_BUILTIN: ModuleType.C_BUILTIN, - imp.C_EXTENSION: ModuleType.C_EXTENSION, - imp.PKG_DIRECTORY: ModuleType.PKG_DIRECTORY, - imp.PY_COMPILED: ModuleType.PY_COMPILED, - imp.PY_FROZEN: ModuleType.PY_FROZEN, - imp.PY_SOURCE: ModuleType.PY_SOURCE, - } -if hasattr(imp, 'PY_RESOURCE'): - _ImpTypes[imp.PY_RESOURCE] = ModuleType.PY_RESOURCE -if hasattr(imp, 'PY_CODERESOURCE'): - _ImpTypes[imp.PY_CODERESOURCE] = ModuleType.PY_CODERESOURCE - -def _imp_type_to_module_type(imp_type): - return _ImpTypes[imp_type] - -_ModuleSpec = collections.namedtuple('_ModuleSpec', 'name type location ' - 'origin submodule_search_locations') - -class ModuleSpec(_ModuleSpec): - """Defines a class similar to PEP 420's ModuleSpec - - A module spec defines a name of a module, its type, location - and where submodules can be found, if the module is a package. - """ - - def __new__(cls, name, module_type, location=None, origin=None, - submodule_search_locations=None): - return _ModuleSpec.__new__(cls, name=name, type=module_type, - location=location, origin=origin, - submodule_search_locations=submodule_search_locations) - - -class Finder(object): - """A finder is a class which knows how to find a particular module.""" - - def __init__(self, path=None): - self._path = path or sys.path - - @abc.abstractmethod - def find_module(self, modname, module_parts, processed, submodule_path): - """Find the given module - - Each finder is responsible for each protocol of finding, as long as - they all return a ModuleSpec. - - :param str modname: The module which needs to be searched. - :param list module_parts: It should be a list of strings, - where each part contributes to the module's - namespace. - :param list processed: What parts from the module parts were processed - so far. - :param list submodule_path: A list of paths where the module - can be looked into. - :returns: A ModuleSpec, describing how and where the module was found, - None, otherwise. - """ - - def contribute_to_path(self, spec, processed): - """Get a list of extra paths where this finder can search.""" - - -class ImpFinder(Finder): - """A finder based on the imp module.""" - - def find_module(self, modname, module_parts, processed, submodule_path): - if submodule_path is not None: - submodule_path = list(submodule_path) - try: - stream, mp_filename, mp_desc = imp.find_module(modname, submodule_path) - except ImportError: - return None - - # Close resources. - if stream: - stream.close() - - return ModuleSpec(name=modname, location=mp_filename, - module_type=_imp_type_to_module_type(mp_desc[2])) - - def contribute_to_path(self, spec, processed): - if spec.location is None: - # Builtin. - return None - - if _is_setuptools_namespace(spec.location): - # extend_path is called, search sys.path for module/packages - # of this name see pkgutil.extend_path documentation - path = [os.path.join(p, *processed) for p in sys.path - if os.path.isdir(os.path.join(p, *processed))] - else: - path = [spec.location] - return path - - -class ExplicitNamespacePackageFinder(ImpFinder): - """A finder for the explicit namespace packages, generated through pkg_resources.""" - - def find_module(self, modname, module_parts, processed, submodule_path): - if util.is_namespace(modname) and modname in sys.modules: - submodule_path = sys.modules[modname].__path__ - return ModuleSpec(name=modname, location='', - origin='namespace', - module_type=ModuleType.PY_NAMESPACE, - submodule_search_locations=submodule_path) - - - def contribute_to_path(self, spec, processed): - return spec.submodule_search_locations - - -class ZipFinder(Finder): - """Finder that knows how to find a module inside zip files.""" - - def __init__(self, path): - super(ZipFinder, self).__init__(path) - self._zipimporters = _precache_zipimporters(path) - - def find_module(self, modname, module_parts, processed, submodule_path): - try: - file_type, filename, path = _search_zip(module_parts, self._zipimporters) - except ImportError: - return None - - return ModuleSpec(name=modname, location=filename, - origin='egg', module_type=file_type, - submodule_search_locations=path) - - -class PathSpecFinder(Finder): - """Finder based on importlib.machinery.PathFinder.""" - - def find_module(self, modname, module_parts, processed, submodule_path): - spec = importlib.machinery.PathFinder.find_spec(modname, path=submodule_path) - if spec: - location = spec.origin if spec.origin != 'namespace' else None - module_type = ModuleType.PY_NAMESPACE if spec.origin == 'namespace' else None - spec = ModuleSpec(name=spec.name, location=location, - origin=spec.origin, module_type=module_type, - submodule_search_locations=list(spec.submodule_search_locations - or [])) - return spec - - def contribute_to_path(self, spec, processed): - if spec.type == ModuleType.PY_NAMESPACE: - return spec.submodule_search_locations - return None - - -_SPEC_FINDERS = ( - ImpFinder, - ZipFinder, -) -if _HAS_MACHINERY and sys.version_info[:2] > (3, 3): - _SPEC_FINDERS += (PathSpecFinder, ) -_SPEC_FINDERS += (ExplicitNamespacePackageFinder, ) - - -def _is_setuptools_namespace(location): - try: - with open(os.path.join(location, '__init__.py'), 'rb') as stream: - data = stream.read(4096) - except IOError: - pass - else: - extend_path = b'pkgutil' in data and b'extend_path' in data - declare_namespace = ( - b"pkg_resources" in data - and b"declare_namespace(__name__)" in data) - return extend_path or declare_namespace - - -@lru_cache() -def _cached_set_diff(left, right): - result = set(left) - result.difference_update(right) - return result - - -def _precache_zipimporters(path=None): - pic = sys.path_importer_cache - - # When measured, despite having the same complexity (O(n)), - # converting to tuples and then caching the conversion to sets - # and the set difference is faster than converting to sets - # and then only caching the set difference. - - req_paths = tuple(path or sys.path) - cached_paths = tuple(pic) - new_paths = _cached_set_diff(req_paths, cached_paths) - for entry_path in new_paths: - try: - pic[entry_path] = zipimport.zipimporter(entry_path) - except zipimport.ZipImportError: - continue - return pic - - -def _search_zip(modpath, pic): - for filepath, importer in list(pic.items()): - if importer is not None: - found = importer.find_module(modpath[0]) - if found: - if not importer.find_module(os.path.sep.join(modpath)): - raise ImportError('No module named %s in %s/%s' % ( - '.'.join(modpath[1:]), filepath, modpath)) - #import code; code.interact(local=locals()) - return (ModuleType.PY_ZIPMODULE, - os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), - filepath) - raise ImportError('No module named %s' % '.'.join(modpath)) - - -def _find_spec_with_path(search_path, modname, module_parts, processed, submodule_path): - finders = [finder(search_path) for finder in _SPEC_FINDERS] - for finder in finders: - spec = finder.find_module(modname, module_parts, processed, submodule_path) - if spec is None: - continue - return finder, spec - - raise ImportError('No module named %s' % '.'.join(module_parts)) - - -def find_spec(modpath, path=None): - """Find a spec for the given module. - - :type modpath: list or tuple - :param modpath: - split module's name (i.e name of a module or package split - on '.'), with leading empty strings for explicit relative import - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :rtype: ModuleSpec - :return: A module spec, which describes how the module was - found and where. - """ - _path = path or sys.path - - # Need a copy for not mutating the argument. - modpath = modpath[:] - - submodule_path = None - module_parts = modpath[:] - processed = [] - - while modpath: - modname = modpath.pop(0) - finder, spec = _find_spec_with_path(_path, modname, - module_parts, processed, - submodule_path or path) - processed.append(modname) - if modpath: - submodule_path = finder.contribute_to_path(spec, processed) - - if spec.type == ModuleType.PKG_DIRECTORY: - spec = spec._replace(submodule_search_locations=submodule_path) - - return spec diff --git a/pymode/libs/astroid/interpreter/_import/util.py b/pymode/libs/astroid/interpreter/_import/util.py deleted file mode 100644 index 79ff4e0a..00000000 --- a/pymode/libs/astroid/interpreter/_import/util.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -try: - import pkg_resources -except ImportError: - pkg_resources = None - - -def is_namespace(modname): - # pylint: disable=no-member; astroid issue #290, modifying globals at runtime. - return (pkg_resources is not None - and modname in pkg_resources._namespace_packages) diff --git a/pymode/libs/astroid/interpreter/dunder_lookup.py b/pymode/libs/astroid/interpreter/dunder_lookup.py deleted file mode 100644 index 424ed01f..00000000 --- a/pymode/libs/astroid/interpreter/dunder_lookup.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 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 - -"""Contains logic for retrieving special methods. - -This implementation does not rely on the dot attribute access -logic, found in ``.getattr()``. The difference between these two -is that the dunder methods are looked with the type slots -(you can find more about these here -http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/) -As such, the lookup for the special methods is actually simpler than -the dot attribute access. -""" -import itertools - -import astroid -from astroid import exceptions - - -def _lookup_in_mro(node, name): - attrs = node.locals.get(name, []) - - nodes = itertools.chain.from_iterable( - ancestor.locals.get(name, []) - for ancestor in node.ancestors(recurs=True) - ) - values = list(itertools.chain(attrs, nodes)) - if not values: - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - return values - - -def lookup(node, name): - """Lookup the given special method name in the given *node* - - If the special method was found, then a list of attributes - will be returned. Otherwise, `astroid.AttributeInferenceError` - is going to be raised. - """ - if isinstance(node, (astroid.List, - astroid.Tuple, - astroid.Const, - astroid.Dict, - astroid.Set)): - return _builtin_lookup(node, name) - elif isinstance(node, astroid.Instance): - return _lookup_in_mro(node, name) - elif isinstance(node, astroid.ClassDef): - return _class_lookup(node, name) - - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - -def _class_lookup(node, name): - metaclass = node.metaclass() - if metaclass is None: - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - return _lookup_in_mro(metaclass, name) - - -def _builtin_lookup(node, name): - values = node.locals.get(name, []) - if not values: - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - return values diff --git a/pymode/libs/astroid/interpreter/objectmodel.py b/pymode/libs/astroid/interpreter/objectmodel.py deleted file mode 100644 index 067095cc..00000000 --- a/pymode/libs/astroid/interpreter/objectmodel.py +++ /dev/null @@ -1,632 +0,0 @@ -# Copyright (c) 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 -""" -Data object model, as per https://docs.python.org/3/reference/datamodel.html. - -This module describes, at least partially, a data object model for some -of astroid's nodes. The model contains special attributes that nodes such -as functions, classes, modules etc have, such as __doc__, __class__, -__module__ etc, being used when doing attribute lookups over nodes. - -For instance, inferring `obj.__class__` will first trigger an inference -of the `obj` variable. If it was succesfully inferred, then an attribute -`__class__ will be looked for in the inferred object. This is the part -where the data model occurs. The model is attached to those nodes -and the lookup mechanism will try to see if attributes such as -`__class__` are defined by the model or not. If they are defined, -the model will be requested to return the corresponding value of that -attribute. Thus the model can be viewed as a special part of the lookup -mechanism. -""" - -try: - from functools import lru_cache -except ImportError: - from backports.functools_lru_cache import lru_cache - -import itertools -import pprint -import os -import types - -import six - -import astroid -from astroid import context as contextmod -from astroid import exceptions -from astroid import node_classes - - -def _dunder_dict(instance, attributes): - obj = node_classes.Dict(parent=instance) - - # Convert the keys to node strings - keys = [node_classes.Const(value=value, parent=obj) - for value in list(attributes.keys())] - - # The original attribute has a list of elements for each key, - # but that is not useful for retrieving the special attribute's value. - # In this case, we're picking the last value from each list. - values = [elem[-1] for elem in attributes.values()] - - obj.postinit(list(zip(keys, values))) - return obj - - -class ObjectModel(object): - - def __init__(self): - self._instance = None - - def __repr__(self): - result = [] - cname = type(self).__name__ - string = '%(cname)s(%(fields)s)' - alignment = len(cname) + 1 - for field in sorted(self.attributes()): - width = 80 - len(field) - alignment - lines = pprint.pformat(field, indent=2, - width=width).splitlines(True) - - inner = [lines[0]] - for line in lines[1:]: - inner.append(' ' * alignment + line) - result.append(field) - - return string % {'cname': cname, - 'fields': (',\n' + ' ' * alignment).join(result)} - - def __call__(self, instance): - self._instance = instance - return self - - def __get__(self, instance, cls=None): - # ObjectModel needs to be a descriptor so that just doing - # `special_attributes = SomeObjectModel` should be enough in the body of a node. - # But at the same time, node.special_attributes should return an object - # which can be used for manipulating the special attributes. That's the reason - # we pass the instance through which it got accessed to ObjectModel.__call__, - # returning itself afterwards, so we can still have access to the - # underlying data model and to the instance for which it got accessed. - return self(instance) - - def __contains__(self, name): - return name in self.attributes() - - @lru_cache(maxsize=None) - def attributes(self): - """Get the attributes which are exported by this object model.""" - return [obj[2:] for obj in dir(self) if obj.startswith('py')] - - def lookup(self, name): - """Look up the given *name* in the current model - - It should return an AST or an interpreter object, - but if the name is not found, then an AttributeInferenceError will be raised. - """ - - if name in self.attributes(): - return getattr(self, "py" + name) - raise exceptions.AttributeInferenceError(target=self._instance, attribute=name) - - -class ModuleModel(ObjectModel): - - def _builtins(self): - builtins = astroid.MANAGER.astroid_cache[six.moves.builtins.__name__] - return builtins.special_attributes.lookup('__dict__') - - if six.PY3: - @property - def pybuiltins(self): - return self._builtins() - - else: - @property - def py__builtin__(self): - return self._builtins() - - # __path__ is a standard attribute on *packages* not - # non-package modules. The only mention of it in the - # official 2.7 documentation I can find is in the - # tutorial. - - @property - def py__path__(self): - if not self._instance.package: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='__path__') - - path = os.path.dirname(self._instance.file) - path_obj = node_classes.Const(value=path, parent=self._instance) - - container = node_classes.List(parent=self._instance) - container.postinit([path_obj]) - - return container - - @property - def py__name__(self): - return node_classes.Const(value=self._instance.name, - parent=self._instance) - - @property - def py__doc__(self): - return node_classes.Const(value=self._instance.doc, - parent=self._instance) - - @property - def py__file__(self): - return node_classes.Const(value=self._instance.file, - parent=self._instance) - - @property - def py__dict__(self): - return _dunder_dict(self._instance, self._instance.globals) - - # __package__ isn't mentioned anywhere outside a PEP: - # https://www.python.org/dev/peps/pep-0366/ - @property - def py__package__(self): - if not self._instance.package: - value = '' - else: - value = self._instance.name - - return node_classes.Const(value=value, parent=self._instance) - - # These are related to the Python 3 implementation of the - # import system, - # https://docs.python.org/3/reference/import.html#import-related-module-attributes - - @property - def py__spec__(self): - # No handling for now. - return node_classes.Unknown() - - @property - def py__loader__(self): - # No handling for now. - return node_classes.Unknown() - - @property - def py__cached__(self): - # No handling for now. - return node_classes.Unknown() - - -class FunctionModel(ObjectModel): - - @property - def py__name__(self): - return node_classes.Const(value=self._instance.name, - parent=self._instance) - - @property - def py__doc__(self): - return node_classes.Const(value=self._instance.doc, - parent=self._instance) - - @property - def py__qualname__(self): - return node_classes.Const(value=self._instance.qname(), - parent=self._instance) - - @property - def py__defaults__(self): - func = self._instance - if not func.args.defaults: - return node_classes.Const(value=None, parent=func) - - defaults_obj = node_classes.Tuple(parent=func) - defaults_obj.postinit(func.args.defaults) - return defaults_obj - - @property - def py__annotations__(self): - obj = node_classes.Dict(parent=self._instance) - - if not self._instance.returns: - returns = None - else: - returns = self._instance.returns - - args = self._instance.args - pair_annotations = itertools.chain( - six.moves.zip(args.args, args.annotations), - six.moves.zip(args.kwonlyargs, args.kwonlyargs_annotations) - ) - - annotations = { - arg.name: annotation - for (arg, annotation) in pair_annotations - if annotation - } - if args.varargannotation: - annotations[args.vararg] = args.varargannotation - if args.kwargannotation: - annotations[args.kwarg] = args.kwargannotation - if returns: - annotations['return'] = returns - - items = [(node_classes.Const(key, parent=obj), value) - for (key, value) in annotations.items()] - - obj.postinit(items) - return obj - - @property - def py__dict__(self): - return node_classes.Dict(parent=self._instance) - - py__globals__ = py__dict__ - - @property - def py__kwdefaults__(self): - - def _default_args(args, parent): - for arg in args.kwonlyargs: - try: - default = args.default_value(arg.name) - except exceptions.NoDefault: - continue - - name = node_classes.Const(arg.name, parent=parent) - yield name, default - - args = self._instance.args - obj = node_classes.Dict(parent=self._instance) - defaults = dict(_default_args(args, obj)) - - obj.postinit(list(defaults.items())) - return obj - - @property - def py__module__(self): - return node_classes.Const(self._instance.root().qname()) - - @property - def py__get__(self): - from astroid import bases - - func = self._instance - - class DescriptorBoundMethod(bases.BoundMethod): - """Bound method which knows how to understand calling descriptor binding.""" - def infer_call_result(self, caller, context=None): - if len(caller.args) != 2: - raise exceptions.InferenceError( - "Invalid arguments for descriptor binding", - target=self, context=context) - - context = contextmod.copy_context(context) - cls = next(caller.args[0].infer(context=context)) - - # Rebuild the original value, but with the parent set as the - # class where it will be bound. - new_func = func.__class__(name=func.name, doc=func.doc, - lineno=func.lineno, col_offset=func.col_offset, - parent=cls) - # pylint: disable=no-member - new_func.postinit(func.args, func.body, - func.decorators, func.returns) - - # Build a proper bound method that points to our newly built function. - proxy = bases.UnboundMethod(new_func) - yield bases.BoundMethod(proxy=proxy, bound=cls) - - return DescriptorBoundMethod(proxy=self._instance, bound=self._instance) - - # These are here just for completion. - @property - def py__ne__(self): - return node_classes.Unknown() - - py__subclasshook__ = py__ne__ - py__str__ = py__ne__ - py__sizeof__ = py__ne__ - py__setattr__ = py__ne__ - py__repr__ = py__ne__ - py__reduce__ = py__ne__ - py__reduce_ex__ = py__ne__ - py__new__ = py__ne__ - py__lt__ = py__ne__ - py__eq__ = py__ne__ - py__gt__ = py__ne__ - py__format__ = py__ne__ - py__delattr__ = py__ne__ - py__getattribute__ = py__ne__ - py__hash__ = py__ne__ - py__init__ = py__ne__ - py__dir__ = py__ne__ - py__call__ = py__ne__ - py__class__ = py__ne__ - py__closure__ = py__ne__ - py__code__ = py__ne__ - - if six.PY2: - pyfunc_name = py__name__ - pyfunc_doc = py__doc__ - pyfunc_globals = py__globals__ - pyfunc_dict = py__dict__ - pyfunc_defaults = py__defaults__ - pyfunc_code = py__code__ - pyfunc_closure = py__closure__ - - -class ClassModel(ObjectModel): - - @property - def py__module__(self): - return node_classes.Const(self._instance.root().qname()) - - @property - def py__name__(self): - return node_classes.Const(self._instance.name) - - @property - def py__qualname__(self): - return node_classes.Const(self._instance.qname()) - - @property - def py__doc__(self): - return node_classes.Const(self._instance.doc) - - @property - def py__mro__(self): - if not self._instance.newstyle: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='__mro__') - - mro = self._instance.mro() - obj = node_classes.Tuple(parent=self._instance) - obj.postinit(mro) - return obj - - @property - def pymro(self): - if not self._instance.newstyle: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='mro') - - from astroid import bases - - other_self = self - - # Cls.mro is a method and we need to return one in order to have a proper inference. - # The method we're returning is capable of inferring the underlying MRO though. - class MroBoundMethod(bases.BoundMethod): - def infer_call_result(self, caller, context=None): - yield other_self.py__mro__ - - implicit_metaclass = self._instance.implicit_metaclass() - mro_method = implicit_metaclass.locals['mro'][0] - return MroBoundMethod(proxy=mro_method, bound=implicit_metaclass) - - @property - def py__bases__(self): - obj = node_classes.Tuple() - context = contextmod.InferenceContext() - elts = list(self._instance._inferred_bases(context)) - obj.postinit(elts=elts) - return obj - - @property - def py__class__(self): - from astroid import helpers - return helpers.object_type(self._instance) - - @property - def py__subclasses__(self): - """Get the subclasses of the underlying class - - This looks only in the current module for retrieving the subclasses, - thus it might miss a couple of them. - """ - from astroid import bases - from astroid import scoped_nodes - - if not self._instance.newstyle: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='__subclasses__') - - qname = self._instance.qname() - root = self._instance.root() - classes = [cls for cls in root.nodes_of_class(scoped_nodes.ClassDef) - if cls != self._instance and cls.is_subtype_of(qname)] - - obj = node_classes.List(parent=self._instance) - obj.postinit(classes) - - class SubclassesBoundMethod(bases.BoundMethod): - def infer_call_result(self, caller, context=None): - yield obj - - implicit_metaclass = self._instance.implicit_metaclass() - subclasses_method = implicit_metaclass.locals['__subclasses__'][0] - return SubclassesBoundMethod(proxy=subclasses_method, - bound=implicit_metaclass) - - @property - def py__dict__(self): - return node_classes.Dict(parent=self._instance) - - -class SuperModel(ObjectModel): - - @property - def py__thisclass__(self): - return self._instance.mro_pointer - - @property - def py__self_class__(self): - return self._instance._self_class - - @property - def py__self__(self): - return self._instance.type - - @property - def py__class__(self): - return self._instance._proxied - - -class UnboundMethodModel(ObjectModel): - - @property - def py__class__(self): - from astroid import helpers - return helpers.object_type(self._instance) - - @property - def py__func__(self): - return self._instance._proxied - - @property - def py__self__(self): - return node_classes.Const(value=None, parent=self._instance) - - pyim_func = py__func__ - pyim_class = py__class__ - pyim_self = py__self__ - - -class BoundMethodModel(FunctionModel): - - @property - def py__func__(self): - return self._instance._proxied._proxied - - @property - def py__self__(self): - return self._instance.bound - - -class GeneratorModel(FunctionModel): - - def __new__(cls, *args, **kwargs): - # Append the values from the GeneratorType unto this object. - ret = super(GeneratorModel, cls).__new__(cls, *args, **kwargs) - generator = astroid.MANAGER.astroid_cache[six.moves.builtins.__name__]['generator'] - for name, values in generator.locals.items(): - print(name, values) - method = values[0] - patched = lambda cls, meth=method: meth - - setattr(type(ret), 'py' + name, property(patched)) - - return ret - - @property - def py__name__(self): - return node_classes.Const(value=self._instance.parent.name, - parent=self._instance) - - @property - def py__doc__(self): - return node_classes.Const(value=self._instance.parent.doc, - parent=self._instance) - - -class InstanceModel(ObjectModel): - - @property - def py__class__(self): - return self._instance._proxied - - @property - def py__module__(self): - return node_classes.Const(self._instance.root().qname()) - - @property - def py__doc__(self): - return node_classes.Const(self._instance.doc) - - @property - def py__dict__(self): - return _dunder_dict(self._instance, self._instance.instance_attrs) - - -class ExceptionInstanceModel(InstanceModel): - - @property - def pyargs(self): - message = node_classes.Const('') - args = node_classes.Tuple(parent=self._instance) - args.postinit((message, )) - return args - - if six.PY3: - # It's available only on Python 3. - - @property - def py__traceback__(self): - builtins = astroid.MANAGER.astroid_cache[six.moves.builtins.__name__] - traceback_type = builtins[types.TracebackType.__name__] - return traceback_type.instantiate_class() - - if six.PY2: - # It's available only on Python 2. - - @property - def pymessage(self): - return node_classes.Const('') - - -class DictModel(ObjectModel): - - @property - def py__class__(self): - return self._instance._proxied - - def _generic_dict_attribute(self, obj, name): - """Generate a bound method that can infer the given *obj*.""" - - class DictMethodBoundMethod(astroid.BoundMethod): - def infer_call_result(self, caller, context=None): - yield obj - - meth = next(self._instance._proxied.igetattr(name)) - return DictMethodBoundMethod(proxy=meth, bound=self._instance) - - @property - def pyitems(self): - elems = [] - obj = node_classes.List(parent=self._instance) - for key, value in self._instance.items: - elem = node_classes.Tuple(parent=obj) - elem.postinit((key, value)) - elems.append(elem) - obj.postinit(elts=elems) - - if six.PY3: - from astroid import objects - obj = objects.DictItems(obj) - - return self._generic_dict_attribute(obj, 'items') - - @property - def pykeys(self): - keys = [key for (key, _) in self._instance.items] - obj = node_classes.List(parent=self._instance) - obj.postinit(elts=keys) - - if six.PY3: - from astroid import objects - obj = objects.DictKeys(obj) - - return self._generic_dict_attribute(obj, 'keys') - - @property - def pyvalues(self): - - values = [value for (_, value) in self._instance.items] - obj = node_classes.List(parent=self._instance) - obj.postinit(values) - - if six.PY3: - from astroid import objects - obj = objects.DictValues(obj) - - return self._generic_dict_attribute(obj, 'values') diff --git a/pymode/libs/astroid/manager.py b/pymode/libs/astroid/manager.py deleted file mode 100644 index 4e7e35d0..00000000 --- a/pymode/libs/astroid/manager.py +++ /dev/null @@ -1,292 +0,0 @@ -# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 manager: avoid multiple astroid build of a same module when -possible by providing a class responsible to get astroid representation -from various source and using a cache of built modules) -""" - -import os -import sys -import zipimport - -import six - -from astroid import exceptions -from astroid.interpreter._import import spec -from astroid import modutils -from astroid import transforms -from astroid import util - - -def safe_repr(obj): - try: - return repr(obj) - except Exception: # pylint: disable=broad-except - return '???' - - -class AstroidManager(object): - """the astroid manager, responsible to build astroid from files - or modules. - - Use the Borg pattern. - """ - - name = 'astroid loader' - brain = {} - - def __init__(self): - self.__dict__ = AstroidManager.brain - if not self.__dict__: - # NOTE: cache entries are added by the [re]builder - self.astroid_cache = {} - self._mod_file_cache = {} - self._failed_import_hooks = [] - self.always_load_extensions = False - self.optimize_ast = False - self.extension_package_whitelist = set() - self._transform = transforms.TransformVisitor() - - # Export these APIs for convenience - self.register_transform = self._transform.register_transform - self.unregister_transform = self._transform.unregister_transform - - def visit_transforms(self, node): - """Visit the transforms and apply them to the given *node*.""" - return self._transform.visit(node) - - def ast_from_file(self, filepath, modname=None, fallback=True, source=False): - """given a module name, return the astroid object""" - try: - filepath = modutils.get_source_file(filepath, include_no_ext=True) - source = True - except modutils.NoSourceFile: - pass - if modname is None: - try: - modname = '.'.join(modutils.modpath_from_file(filepath)) - except ImportError: - modname = filepath - if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: - return self.astroid_cache[modname] - if source: - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).file_build(filepath, modname) - elif fallback and modname: - return self.ast_from_module_name(modname) - raise exceptions.AstroidBuildingError( - 'Unable to build an AST for {path}.', path=filepath) - - def _build_stub_module(self, modname): - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).string_build('', modname) - - def _build_namespace_module(self, modname, path): - from astroid.builder import build_namespace_package_module - return build_namespace_package_module(modname, path) - - def _can_load_extension(self, modname): - if self.always_load_extensions: - return True - if modutils.is_standard_module(modname): - return True - parts = modname.split('.') - return any( - '.'.join(parts[:x]) in self.extension_package_whitelist - for x in range(1, len(parts) + 1)) - - def ast_from_module_name(self, modname, context_file=None): - """given a module name, return the astroid object""" - if modname in self.astroid_cache: - return self.astroid_cache[modname] - if modname == '__main__': - return self._build_stub_module(modname) - old_cwd = os.getcwd() - if context_file: - os.chdir(os.path.dirname(context_file)) - try: - found_spec = self.file_from_module_name(modname, context_file) - # pylint: disable=no-member - if found_spec.type == spec.ModuleType.PY_ZIPMODULE: - # pylint: disable=no-member - module = self.zip_import_data(found_spec.location) - if module is not None: - return module - - elif found_spec.type in (spec.ModuleType.C_BUILTIN, - spec.ModuleType.C_EXTENSION): - # pylint: disable=no-member - if (found_spec.type == spec.ModuleType.C_EXTENSION - and not self._can_load_extension(modname)): - return self._build_stub_module(modname) - try: - module = modutils.load_module_from_name(modname) - except Exception as ex: # pylint: disable=broad-except - util.reraise(exceptions.AstroidImportError( - 'Loading {modname} failed with:\n{error}', - modname=modname, path=found_spec.location, error=ex)) - return self.ast_from_module(module, modname) - - elif found_spec.type == spec.ModuleType.PY_COMPILED: - raise exceptions.AstroidImportError( - "Unable to load compiled module {modname}.", - # pylint: disable=no-member - modname=modname, path=found_spec.location) - - elif found_spec.type == spec.ModuleType.PY_NAMESPACE: - return self._build_namespace_module(modname, - # pylint: disable=no-member - found_spec.submodule_search_locations) - - # pylint: disable=no-member - if found_spec.location is None: - raise exceptions.AstroidImportError( - "Can't find a file for module {modname}.", - modname=modname) - - # pylint: disable=no-member - return self.ast_from_file(found_spec.location, modname, fallback=False) - except exceptions.AstroidBuildingError as e: - for hook in self._failed_import_hooks: - try: - return hook(modname) - except exceptions.AstroidBuildingError: - pass - raise e - finally: - os.chdir(old_cwd) - - def zip_import_data(self, filepath): - if zipimport is None: - return None - from astroid.builder import AstroidBuilder - builder = AstroidBuilder(self) - for ext in ('.zip', '.egg'): - try: - eggpath, resource = filepath.rsplit(ext + os.path.sep, 1) - except ValueError: - continue - try: - importer = zipimport.zipimporter(eggpath + ext) - zmodname = resource.replace(os.path.sep, '.') - if importer.is_package(resource): - zmodname = zmodname + '.__init__' - module = builder.string_build(importer.get_source(resource), - zmodname, filepath) - return module - except Exception: # pylint: disable=broad-except - continue - return None - - def file_from_module_name(self, modname, contextfile): - try: - value = self._mod_file_cache[(modname, contextfile)] - traceback = sys.exc_info()[2] - except KeyError: - try: - value = modutils.file_info_from_modpath( - modname.split('.'), context_file=contextfile) - traceback = sys.exc_info()[2] - except ImportError as ex: - value = exceptions.AstroidImportError( - 'Failed to import module {modname} with error:\n{error}.', - modname=modname, error=ex) - traceback = sys.exc_info()[2] - self._mod_file_cache[(modname, contextfile)] = value - if isinstance(value, exceptions.AstroidBuildingError): - six.reraise(exceptions.AstroidBuildingError, - value, traceback) - return value - - def ast_from_module(self, module, modname=None): - """given an imported module, return the astroid object""" - modname = modname or module.__name__ - if modname in self.astroid_cache: - return self.astroid_cache[modname] - try: - # some builtin modules don't have __file__ attribute - filepath = module.__file__ - if modutils.is_python_source(filepath): - return self.ast_from_file(filepath, modname) - except AttributeError: - pass - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).module_build(module, modname) - - def ast_from_class(self, klass, modname=None): - """get astroid for the given class""" - if modname is None: - try: - modname = klass.__module__ - except AttributeError: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to get module for class {class_name}.', - cls=klass, class_repr=safe_repr(klass), modname=modname)) - modastroid = self.ast_from_module_name(modname) - return modastroid.getattr(klass.__name__)[0] # XXX - - def infer_ast_from_something(self, obj, context=None): - """infer astroid for the given class""" - if hasattr(obj, '__class__') and not isinstance(obj, type): - klass = obj.__class__ - else: - klass = obj - try: - modname = klass.__module__ - except AttributeError: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to get module for {class_repr}.', - cls=klass, class_repr=safe_repr(klass))) - except Exception as ex: # pylint: disable=broad-except - util.reraise(exceptions.AstroidImportError( - 'Unexpected error while retrieving module for {class_repr}:\n' - '{error}', cls=klass, class_repr=safe_repr(klass), error=ex)) - try: - name = klass.__name__ - except AttributeError: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to get name for {class_repr}:\n', - cls=klass, class_repr=safe_repr(klass))) - except Exception as ex: # pylint: disable=broad-except - util.reraise(exceptions.AstroidImportError( - 'Unexpected error while retrieving name for {class_repr}:\n' - '{error}', cls=klass, class_repr=safe_repr(klass), error=ex)) - # take care, on living object __module__ is regularly wrong :( - modastroid = self.ast_from_module_name(modname) - if klass is obj: - for inferred in modastroid.igetattr(name, context): - yield inferred - else: - for inferred in modastroid.igetattr(name, context): - yield inferred.instantiate_class() - - def register_failed_import_hook(self, hook): - """Registers a hook to resolve imports that cannot be found otherwise. - - `hook` must be a function that accepts a single argument `modname` which - contains the name of the module or package that could not be imported. - If `hook` can resolve the import, must return a node of type `astroid.Module`, - otherwise, it must raise `AstroidBuildingError`. - """ - self._failed_import_hooks.append(hook) - - def cache_module(self, module): - """Cache a module if no module with the same name is known yet.""" - self.astroid_cache.setdefault(module.name, module) - - def clear_cache(self, astroid_builtin=None): - # XXX clear transforms - self.astroid_cache.clear() - # force bootstrap again, else we may ends up with cache inconsistency - # between the manager and CONST_PROXY, making - # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the - # test order - import astroid.raw_building - astroid.raw_building._astroid_bootstrapping( - astroid_builtin=astroid_builtin) diff --git a/pymode/libs/astroid/mixins.py b/pymode/libs/astroid/mixins.py deleted file mode 100644 index 739828d5..00000000 --- a/pymode/libs/astroid/mixins.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright (c) 2010-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""This module contains some mixins for the different nodes. -""" - -import warnings - -from astroid import decorators -from astroid import exceptions - - -class BlockRangeMixIn(object): - """override block range """ - - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.lineno - - def _elsed_block_range(self, lineno, orelse, last=None): - """handle block line numbers range for try/finally, for, if and while - statements - """ - if lineno == self.fromlineno: - return lineno, lineno - if orelse: - if lineno >= orelse[0].fromlineno: - return lineno, orelse[-1].tolineno - return lineno, orelse[0].fromlineno - 1 - return lineno, last or self.tolineno - - -class FilterStmtsMixin(object): - """Mixin for statement filtering and assignment type""" - - def _get_filtered_stmts(self, _, node, _stmts, mystmt): - """method used in _filter_stmts to get statements and trigger break""" - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - def 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() - - -class AssignTypeMixin(object): - - 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: - return _stmts, True - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - -class ParentAssignTypeMixin(AssignTypeMixin): - - def assign_type(self): - return self.parent.assign_type() - - 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() - - -class ImportFromMixin(FilterStmtsMixin): - """MixIn for From and Import Nodes""" - - def _infer_name(self, frame, name): - return name - - def do_import_module(self, modname=None): - """return the ast for a module whose name is imported by - """ - # handle special case where we are on a package node importing a module - # using the same name as the package, which may end in an infinite loop - # on relative imports - # XXX: no more needed ? - mymodule = self.root() - level = getattr(self, 'level', None) # Import as no level - if modname is None: - modname = self.modname - # XXX we should investigate deeper if we really want to check - # importing itself: modname and mymodule.name be relative or absolute - if mymodule.relative_to_absolute_name(modname, level) == mymodule.name: - # FIXME: we used to raise InferenceError here, but why ? - return mymodule - - return mymodule.import_module(modname, level=level, - relative_only=level and level >= 1) - - def real_name(self, asname): - """get name from 'as' name""" - for name, _asname in self.names: - if name == '*': - return asname - if not _asname: - name = name.split('.', 1)[0] - _asname = name - if asname == _asname: - return name - raise exceptions.AttributeInferenceError( - 'Could not find original name for {attribute} in {target!r}', - target=self, attribute=asname) diff --git a/pymode/libs/astroid/modutils.py b/pymode/libs/astroid/modutils.py deleted file mode 100644 index f17bfdab..00000000 --- a/pymode/libs/astroid/modutils.py +++ /dev/null @@ -1,645 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015 Florian Bruhin -# Copyright (c) 2015 Radosław Ganczarek -# 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 - -"""Python modules manipulation utility functions. - -:type PY_SOURCE_EXTS: tuple(str) -:var PY_SOURCE_EXTS: list of possible python source file extension - -:type STD_LIB_DIRS: set of str -:var STD_LIB_DIRS: directories where standard modules are located - -:type BUILTIN_MODULES: dict -:var BUILTIN_MODULES: dictionary with builtin module names has key -""" -import imp -import os -import platform -import sys -from distutils.sysconfig import get_python_lib # pylint: disable=import-error -# pylint: disable=import-error, no-name-in-module -from distutils.errors import DistutilsPlatformError -# distutils is replaced by virtualenv with a module that does -# weird path manipulations in order to get to the -# real distutils module. - -import six - -from .interpreter._import import spec -from .interpreter._import import util - -if sys.platform.startswith('win'): - PY_SOURCE_EXTS = ('py', 'pyw') - PY_COMPILED_EXTS = ('dll', 'pyd') -else: - PY_SOURCE_EXTS = ('py',) - PY_COMPILED_EXTS = ('so',) - - -try: - # The explicit sys.prefix is to work around a patch in virtualenv that - # replaces the 'real' sys.prefix (i.e. the location of the binary) - # with the prefix from which the virtualenv was created. This throws - # off the detection logic for standard library modules, thus the - # workaround. - STD_LIB_DIRS = set([ - get_python_lib(standard_lib=True, prefix=sys.prefix), - # Take care of installations where exec_prefix != prefix. - get_python_lib(standard_lib=True, prefix=sys.exec_prefix), - get_python_lib(standard_lib=True)]) -# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to -# non-valid path, see https://bugs.pypy.org/issue1164 -except DistutilsPlatformError: - STD_LIB_DIRS = set() - -if os.name == 'nt': - STD_LIB_DIRS.add(os.path.join(sys.prefix, 'dlls')) - try: - # real_prefix is defined when running inside virtual environments, - # created with the **virtualenv** library. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'dlls')) - except AttributeError: - # sys.base_exec_prefix is always defined, but in a virtual environment - # created with the stdlib **venv** module, it points to the original - # installation, if the virtual env is activated. - try: - STD_LIB_DIRS.add(os.path.join(sys.base_exec_prefix, 'dlls')) - except AttributeError: - pass - -if platform.python_implementation() == 'PyPy': - _root = os.path.join(sys.prefix, 'lib_pypy') - STD_LIB_DIRS.add(_root) - try: - # real_prefix is defined when running inside virtualenv. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'lib_pypy')) - except AttributeError: - pass - del _root -if os.name == 'posix': - # Need the real prefix is we're under a virtualenv, otherwise - # the usual one will do. - try: - prefix = sys.real_prefix - except AttributeError: - prefix = sys.prefix - - def _posix_path(path): - base_python = 'python%d.%d' % sys.version_info[:2] - return os.path.join(prefix, path, base_python) - - STD_LIB_DIRS.add(_posix_path('lib')) - if sys.maxsize > 2**32: - # This tries to fix a problem with /usr/lib64 builds, - # where systems are running both 32-bit and 64-bit code - # on the same machine, which reflects into the places where - # standard library could be found. More details can be found - # here http://bugs.python.org/issue1294959. - # An easy reproducing case would be - # https://github.com/PyCQA/pylint/issues/712#issuecomment-163178753 - STD_LIB_DIRS.add(_posix_path('lib64')) - -EXT_LIB_DIR = get_python_lib() -IS_JYTHON = platform.python_implementation() == 'Jython' -BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - -def _normalize_path(path): - return os.path.normcase(os.path.abspath(path)) - - -def _canonicalize_path(path): - return os.path.realpath(os.path.expanduser(path)) - - -def _path_from_filename(filename, is_jython=IS_JYTHON): - if not is_jython: - if sys.version_info > (3, 0): - return filename - else: - if filename.endswith(".pyc"): - return filename[:-1] - return filename - head, has_pyclass, _ = filename.partition("$py.class") - if has_pyclass: - return head + ".py" - return filename - - -def _handle_blacklist(blacklist, dirnames, filenames): - """remove files/directories in the black list - - dirnames/filenames are usually from os.walk - """ - for norecurs in blacklist: - if norecurs in dirnames: - dirnames.remove(norecurs) - elif norecurs in filenames: - filenames.remove(norecurs) - - -_NORM_PATH_CACHE = {} - -def _cache_normalize_path(path): - """abspath with caching""" - # _module_file calls abspath on every path in sys.path every time it's - # called; on a larger codebase this easily adds up to half a second just - # assembling path components. This cache alleviates that. - try: - return _NORM_PATH_CACHE[path] - except KeyError: - if not path: # don't cache result for '' - return _normalize_path(path) - result = _NORM_PATH_CACHE[path] = _normalize_path(path) - return result - -def load_module_from_name(dotted_name, path=None, use_sys=True): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=1): - """Load a python module from its split name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package split on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = imp.find_module(part, path) - module = imp.load_module(curname, mp_file, mp_filename, mp_desc) - # mp_file still needs to be closed. - if mp_file: - mp_file.close() - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, '__file__', '') - prevmodule = module - if not _file and util.is_namespace(curname): - continue - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):])) - path = [os.path.dirname(_file)] - return module - - -def load_module_from_file(filepath, path=None, use_sys=True, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def check_modpath_has_init(path, mod_path): - """check there are some __init__.py all along the way""" - modpath = [] - for part in mod_path: - modpath.append(part) - path = os.path.join(path, part) - if not _has_init(path): - old_namespace = util.is_namespace('.'.join(modpath)) - if not old_namespace: - return False - return True - - -def modpath_from_file_with_callback(filename, extrapath=None, is_package_cb=None): - filename = _path_from_filename(filename) - filename = os.path.realpath(os.path.expanduser(filename)) - base = os.path.splitext(filename)[0] - - if extrapath is not None: - for path_ in six.moves.map(_canonicalize_path, extrapath): - path = os.path.abspath(path_) - if path and os.path.normcase(base[:len(path)]) == os.path.normcase(path): - submodpath = [pkg for pkg in base[len(path):].split(os.sep) - if pkg] - if is_package_cb(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - - for path in six.moves.map(_canonicalize_path, sys.path): - path = _cache_normalize_path(path) - if path and os.path.normcase(base).startswith(path): - modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] - if is_package_cb(path, modpath[:-1]): - return modpath - - raise ImportError('Unable to find module for %s in %s' % ( - filename, ', \n'.join(sys.path))) - - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding split module's name - (i.e name of a module or package split on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package split in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding split module's name - """ - return modpath_from_file_with_callback(filename, extrapath, check_modpath_has_init) - - -def file_from_modpath(modpath, path=None, context_file=None): - return file_info_from_modpath(modpath, path, context_file).location - -def file_info_from_modpath(modpath, path=None, context_file=None): - """given a mod path (i.e. split module / package name), return the - corresponding file, giving priority to source file over precompiled - file if it exists - - :type modpath: list or tuple - :param modpath: - split module's name (i.e name of a module or package split - on '.') - (this means explicit relative imports that start with dots have - empty strings in this list!) - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - :raise ImportError: if there is no such module in the directory - - :rtype: (str or None, import type) - :return: - the path to the module's file or None if it's an integrated - builtin module such as 'sys' - """ - if context_file is not None: - context = os.path.dirname(context_file) - else: - context = context_file - if modpath[0] == 'xml': - # handle _xmlplus - try: - return _spec_from_modpath(['_xmlplus'] + modpath[1:], path, context) - except ImportError: - return _spec_from_modpath(modpath, path, context) - elif modpath == ['os', 'path']: - # FIXME: currently ignoring search_path... - return spec.ModuleSpec(name='os.path', location=os.path.__file__, module_type=imp.PY_SOURCE) - return _spec_from_modpath(modpath, path, context) - - -def get_module_part(dotted_name, context_file=None): - """given a dotted name return the module part of the name : - - >>> get_module_part('astroid.as_string.dump') - 'astroid.as_string' - - :type dotted_name: str - :param dotted_name: full name of the identifier we are interested in - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the module part of the name or None if we have not been able at - all to import the given name - - XXX: deprecated, since it doesn't handle package precedence over module - (see #10066) - """ - # os.path trick - if dotted_name.startswith('os.path'): - return 'os.path' - parts = dotted_name.split('.') - if context_file is not None: - # first check for builtin module which won't be considered latter - # in that case (path != None) - if parts[0] in BUILTIN_MODULES: - if len(parts) > 2: - raise ImportError(dotted_name) - return parts[0] - # don't use += or insert, we want a new list to be created ! - path = None - starti = 0 - if parts[0] == '': - assert context_file is not None, \ - 'explicit relative import, but no context_file?' - path = [] # prevent resolving the import non-relatively - starti = 1 - while parts[starti] == '': # for all further dots: change context - starti += 1 - context_file = os.path.dirname(context_file) - for i in range(starti, len(parts)): - try: - file_from_modpath(parts[starti:i+1], path=path, - context_file=context_file) - except ImportError: - if i < max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_module_files(src_directory, blacklist, list_all=False): - """given a package directory return a list of all available python - module's files in the package and its subpackages - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: iterable - list of files or directories to ignore. - - :type list_all: bool - :param list_all: - get files from all paths, including ones without __init__.py - - :rtype: list - :return: - the list of all available python module's files in the package and - its subpackages - """ - files = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not list_all and '__init__.py' not in filenames: - dirnames[:] = () - continue - for filename in filenames: - if _is_python_file(filename): - src = os.path.join(directory, filename) - files.append(src) - return files - - -def get_source_file(filename, include_no_ext=False): - """given a python module's file name return the matching source file - name (the filename will be returned identically if it's a already an - absolute path to a python source file...) - - :type filename: str - :param filename: python module's file name - - - :raise NoSourceFile: if no source file exists on the file system - - :rtype: str - :return: the absolute path of the source file if it exists - """ - filename = os.path.abspath(_path_from_filename(filename)) - base, orig_ext = os.path.splitext(filename) - for ext in PY_SOURCE_EXTS: - source_path = '%s.%s' % (base, ext) - if os.path.exists(source_path): - return source_path - if include_no_ext and not orig_ext and os.path.exists(base): - return base - raise NoSourceFile(filename) - - -def is_python_source(filename): - """ - rtype: bool - return: True if the filename is a python source file - """ - return os.path.splitext(filename)[1][1:] in PY_SOURCE_EXTS - - -def is_standard_module(modname, std_path=None): - """try to guess if a module is a standard python module (by default, - see `std_path` parameter's description) - - :type modname: str - :param modname: name of the module we are interested in - - :type std_path: list(str) or tuple(str) - :param std_path: list of path considered has standard - - - :rtype: bool - :return: - true if the module: - - is located on the path listed in one of the directory in `std_path` - - is a built-in module - """ - modname = modname.split('.')[0] - try: - filename = file_from_modpath([modname]) - except ImportError: - # import failed, i'm probably not so wrong by supposing it's - # not standard... - return False - # modules which are not living in a file are considered standard - # (sys and __builtin__ for instance) - if filename is None: - # we assume there are no namespaces in stdlib - return not util.is_namespace(modname) - filename = _normalize_path(filename) - if filename.startswith(_cache_normalize_path(EXT_LIB_DIR)): - return False - if std_path is None: - std_path = STD_LIB_DIRS - for path in std_path: - if filename.startswith(_cache_normalize_path(path)): - return True - return False - - - -def is_relative(modname, from_file): - """return true if the given module name is relative to the given - file name - - :type modname: str - :param modname: name of the module we are interested in - - :type from_file: str - :param from_file: - path of the module from which modname has been imported - - :rtype: bool - :return: - true if the module has been imported relatively to `from_file` - """ - if not os.path.isdir(from_file): - from_file = os.path.dirname(from_file) - if from_file in sys.path: - return False - try: - stream, _, _ = imp.find_module(modname.split('.')[0], [from_file]) - - # Close the stream to avoid ResourceWarnings. - if stream: - stream.close() - return True - except ImportError: - return False - - -# internal only functions ##################################################### - -def _spec_from_modpath(modpath, path=None, context=None): - """given a mod path (i.e. split module / package name), return the - corresponding spec - - this function is used internally, see `file_from_modpath`'s - documentation for more information - """ - assert modpath - location = None - if context is not None: - try: - found_spec = spec.find_spec(modpath, [context]) - location = found_spec.location - except ImportError: - found_spec = spec.find_spec(modpath, path) - location = found_spec.location - else: - found_spec = spec.find_spec(modpath, path) - if found_spec.type == spec.ModuleType.PY_COMPILED: - try: - location = get_source_file(found_spec.location) - return found_spec._replace(location=location, type=spec.ModuleType.PY_SOURCE) - except NoSourceFile: - return found_spec._replace(location=location) - elif found_spec.type == spec.ModuleType.C_BUILTIN: - # integrated builtin module - return found_spec._replace(location=None) - elif found_spec.type == spec.ModuleType.PKG_DIRECTORY: - location = _has_init(found_spec.location) - return found_spec._replace(location=location, type=spec.ModuleType.PY_SOURCE) - return found_spec - - -def _is_python_file(filename): - """return true if the given filename should be considered as a python file - - .pyc and .pyo are ignored - """ - for ext in ('.py', '.so', '.pyd', '.pyw'): - if filename.endswith(ext): - return True - return False - - -def _has_init(directory): - """if the given directory has a valid __init__ file, return its path, - else return None - """ - mod_or_pack = os.path.join(directory, '__init__') - for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'): - if os.path.exists(mod_or_pack + '.' + ext): - return mod_or_pack + '.' + ext - return None - - -def is_namespace(specobj): - return specobj.type == spec.ModuleType.PY_NAMESPACE - -def is_directory(specobj): - return specobj.type == spec.ModuleType.PKG_DIRECTORY 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/astroid/nodes.py b/pymode/libs/astroid/nodes.py deleted file mode 100644 index 2b207d02..00000000 --- a/pymode/libs/astroid/nodes.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2006-2011, 2013 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -""" -on all nodes : - .is_statement, returning true if the node should be considered as a - statement node - .root(), returning the root node of the tree (i.e. a Module) - .previous_sibling(), returning previous sibling statement node - .next_sibling(), returning next sibling statement node - .statement(), returning the first parent node marked as statement node - .frame(), returning the first node defining a new local scope (i.e. - Module, FunctionDef or ClassDef) - .set_local(name, node), define an identifier on the first parent frame, - with the node defining it. This is used by the astroid builder and should not - be used from out there. - -on ImportFrom and Import : - .real_name(name), - - -""" -# pylint: disable=unused-import,redefined-builtin - -from astroid.node_classes import ( - Arguments, AssignAttr, Assert, Assign, AnnAssign, - AssignName, AugAssign, Repr, BinOp, BoolOp, Break, Call, Compare, - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, - Dict, Expr, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, - ImportFrom, Attribute, Global, If, IfExp, Import, Index, Keyword, - List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, - const_factory, - AsyncFor, Await, AsyncWith, - FormattedValue, JoinedStr, - # Backwards-compatibility aliases - Backquote, Discard, AssName, AssAttr, Getattr, CallFunc, From, - # Node not present in the builtin ast module. - DictUnpack, - Unknown, -) -from astroid.scoped_nodes import ( - Module, GeneratorExp, Lambda, DictComp, - ListComp, SetComp, FunctionDef, ClassDef, - AsyncFunctionDef, - # Backwards-compatibility aliases - Class, Function, GenExpr, -) - - - -ALL_NODE_CLASSES = ( - AsyncFunctionDef, AsyncFor, AsyncWith, Await, - - Arguments, AssignAttr, Assert, Assign, AnnAssign, AssignName, AugAssign, - Repr, BinOp, BoolOp, Break, - Call, ClassDef, Compare, Comprehension, Const, Continue, - Decorators, DelAttr, DelName, Delete, - Dict, DictComp, DictUnpack, Expr, - Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, - For, ImportFrom, FunctionDef, - Attribute, GeneratorExp, Global, - If, IfExp, Import, Index, - Keyword, - Lambda, List, ListComp, - Name, Nonlocal, - Module, - Pass, Print, - Raise, Return, - Set, SetComp, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, - UnaryOp, - While, With, - Yield, YieldFrom, - FormattedValue, JoinedStr, - ) diff --git a/pymode/libs/astroid/objects.py b/pymode/libs/astroid/objects.py deleted file mode 100644 index 7a873449..00000000 --- a/pymode/libs/astroid/objects.py +++ /dev/null @@ -1,220 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# 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 - - -""" -Inference objects are a way to represent composite AST nodes, -which are used only as inference results, so they can't be found in the -original AST tree. For instance, inferring the following frozenset use, -leads to an inferred FrozenSet: - - Call(func=Name('frozenset'), args=Tuple(...)) -""" - -import six - -from astroid import bases -from astroid import decorators -from astroid import exceptions -from astroid import MANAGER -from astroid import node_classes -from astroid import scoped_nodes -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ -objectmodel = util.lazy_import('interpreter.objectmodel') - - -class FrozenSet(node_classes._BaseContainer): - """class representing a FrozenSet composite node""" - - def pytype(self): - return '%s.frozenset' % BUILTINS - - def _infer(self, context=None): - yield self - - @decorators.cachedproperty - def _proxied(self): # pylint: disable=method-hidden - builtins = MANAGER.astroid_cache[BUILTINS] - return builtins.getattr('frozenset')[0] - - -class Super(node_classes.NodeNG): - """Proxy class over a super call. - - This class offers almost the same behaviour as Python's super, - which is MRO lookups for retrieving attributes from the parents. - - The *mro_pointer* is the place in the MRO from where we should - start looking, not counting it. *mro_type* is the object which - provides the MRO, it can be both a type or an instance. - *self_class* is the class where the super call is, while - *scope* is the function where the super call is. - """ - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.SuperModel()) - - # pylint: disable=super-init-not-called - def __init__(self, mro_pointer, mro_type, self_class, scope): - self.type = mro_type - self.mro_pointer = mro_pointer - self._class_based = False - self._self_class = self_class - self._scope = scope - - def _infer(self, context=None): - yield self - - def super_mro(self): - """Get the MRO which will be used to lookup attributes in this super.""" - if not isinstance(self.mro_pointer, scoped_nodes.ClassDef): - raise exceptions.SuperError( - "The first argument to super must be a subtype of " - "type, not {mro_pointer}.", super_=self) - - if isinstance(self.type, scoped_nodes.ClassDef): - # `super(type, type)`, most likely in a class method. - self._class_based = True - mro_type = self.type - else: - mro_type = getattr(self.type, '_proxied', None) - if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)): - raise exceptions.SuperError( - "The second argument to super must be an " - "instance or subtype of type, not {type}.", - super_=self) - - if not mro_type.newstyle: - raise exceptions.SuperError("Unable to call super on old-style classes.", super_=self) - - mro = mro_type.mro() - if self.mro_pointer not in mro: - raise exceptions.SuperError( - "The second argument to super must be an " - "instance or subtype of type, not {type}.", - super_=self) - - index = mro.index(self.mro_pointer) - return mro[index + 1:] - - @decorators.cachedproperty - def _proxied(self): - builtins = MANAGER.astroid_cache[BUILTINS] - return builtins.getattr('super')[0] - - def pytype(self): - return '%s.super' % BUILTINS - - def display_type(self): - return 'Super of' - - @property - def name(self): - """Get the name of the MRO pointer.""" - return self.mro_pointer.name - - def igetattr(self, name, context=None): - """Retrieve the inferred values of the given attribute name.""" - - if name in self.special_attributes: - yield self.special_attributes.lookup(name) - return - - try: - mro = self.super_mro() - # Don't let invalid MROs or invalid super calls - # leak out as is from this function. - except exceptions.SuperError as exc: - util.reraise(exceptions.AttributeInferenceError( - ('Lookup for {name} on {target!r} because super call {super!r} ' - 'is invalid.'), - target=self, attribute=name, context=context, super_=exc.super_)) - except exceptions.MroError as exc: - util.reraise(exceptions.AttributeInferenceError( - ('Lookup for {name} on {target!r} failed because {cls!r} has an ' - 'invalid MRO.'), - target=self, attribute=name, context=context, mros=exc.mros, - cls=exc.cls)) - found = False - for cls in mro: - if name not in cls.locals: - continue - - found = True - for inferred in bases._infer_stmts([cls[name]], context, frame=self): - if not isinstance(inferred, scoped_nodes.FunctionDef): - yield inferred - continue - - # We can obtain different descriptors from a super depending - # on what we are accessing and where the super call is. - if inferred.type == 'classmethod': - yield bases.BoundMethod(inferred, cls) - elif self._scope.type == 'classmethod' and inferred.type == 'method': - yield inferred - elif self._class_based or inferred.type == 'staticmethod': - yield inferred - elif bases._is_property(inferred): - # TODO: support other descriptors as well. - for value in inferred.infer_call_result(self, context): - yield value - else: - yield bases.BoundMethod(inferred, cls) - - if not found: - raise exceptions.AttributeInferenceError(target=self, - attribute=name, - context=context) - - def getattr(self, name, context=None): - return list(self.igetattr(name, context=context)) - - -class ExceptionInstance(bases.Instance): - """Class for instances of exceptions - - It has special treatment for some of the exceptions's attributes, - which are transformed at runtime into certain concrete objects, such as - the case of .args. - """ - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.ExceptionInstanceModel()) - - -class DictInstance(bases.Instance): - """Special kind of instances for dictionaries - - This instance knows the underlying object model of the dictionaries, which means - that methods such as .values or .items can be properly inferred. - """ - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.DictModel()) - - -# Custom objects tailored for dictionaries, which are used to -# disambiguate between the types of Python 2 dict's method returns -# and Python 3 (where they return set like objects). -class DictItems(bases.Proxy): - __str__ = node_classes.NodeNG.__str__ - __repr__ = node_classes.NodeNG.__repr__ - - -class DictKeys(bases.Proxy): - __str__ = node_classes.NodeNG.__str__ - __repr__ = node_classes.NodeNG.__repr__ - - -class DictValues(bases.Proxy): - __str__ = node_classes.NodeNG.__str__ - __repr__ = node_classes.NodeNG.__repr__ - -# TODO: Hack to solve the circular import problem between node_classes and objects -# This is not needed in 2.0, which has a cleaner design overall -node_classes.Dict.__bases__ = (node_classes.NodeNG, DictInstance) diff --git a/pymode/libs/astroid/protocols.py b/pymode/libs/astroid/protocols.py deleted file mode 100644 index f19a9f41..00000000 --- a/pymode/libs/astroid/protocols.py +++ /dev/null @@ -1,600 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""this module contains a set of functions to handle python protocols for nodes -where it makes sense. -""" - -import collections -import operator as operator_mod -import sys - -import six - -from astroid import arguments -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import decorators -from astroid import node_classes -from astroid import helpers -from astroid import nodes -from astroid import util - -raw_building = util.lazy_import('raw_building') -objects = util.lazy_import('objects') - -def _reflected_name(name): - return "__r" + name[2:] - -def _augmented_name(name): - return "__i" + name[2:] - - -_CONTEXTLIB_MGR = 'contextlib.contextmanager' -BIN_OP_METHOD = {'+': '__add__', - '-': '__sub__', - '/': '__div__' if six.PY2 else '__truediv__', - '//': '__floordiv__', - '*': '__mul__', - '**': '__pow__', - '%': '__mod__', - '&': '__and__', - '|': '__or__', - '^': '__xor__', - '<<': '__lshift__', - '>>': '__rshift__', - '@': '__matmul__' - } - -REFLECTED_BIN_OP_METHOD = { - key: _reflected_name(value) - for (key, value) in BIN_OP_METHOD.items() -} -AUGMENTED_OP_METHOD = { - key + "=": _augmented_name(value) - for (key, value) in BIN_OP_METHOD.items() -} - -UNARY_OP_METHOD = {'+': '__pos__', - '-': '__neg__', - '~': '__invert__', - 'not': None, # XXX not '__nonzero__' - } -_UNARY_OPERATORS = { - '+': operator_mod.pos, - '-': operator_mod.neg, - '~': operator_mod.invert, - 'not': operator_mod.not_, -} - - -def _infer_unary_op(obj, op): - func = _UNARY_OPERATORS[op] - value = func(obj) - return nodes.const_factory(value) - -nodes.Tuple.infer_unary_op = lambda self, op: _infer_unary_op(tuple(self.elts), op) -nodes.List.infer_unary_op = lambda self, op: _infer_unary_op(self.elts, op) -nodes.Set.infer_unary_op = lambda self, op: _infer_unary_op(set(self.elts), op) -nodes.Const.infer_unary_op = lambda self, op: _infer_unary_op(self.value, op) -nodes.Dict.infer_unary_op = lambda self, op: _infer_unary_op(dict(self.items), op) - -# Binary operations - -BIN_OP_IMPL = {'+': lambda a, b: a + b, - '-': lambda a, b: a - b, - '/': lambda a, b: a / b, - '//': lambda a, b: a // b, - '*': lambda a, b: a * b, - '**': lambda a, b: a ** b, - '%': lambda a, b: a % b, - '&': lambda a, b: a & b, - '|': lambda a, b: a | b, - '^': lambda a, b: a ^ b, - '<<': lambda a, b: a << b, - '>>': lambda a, b: a >> b, - } -if sys.version_info >= (3, 5): - # MatMult is available since Python 3.5+. - BIN_OP_IMPL['@'] = operator_mod.matmul - -for _KEY, _IMPL in list(BIN_OP_IMPL.items()): - BIN_OP_IMPL[_KEY + '='] = _IMPL - - -@decorators.yes_if_nothing_inferred -def const_infer_binary_op(self, opnode, operator, other, context, _): - not_implemented = nodes.Const(NotImplemented) - if isinstance(other, nodes.Const): - try: - impl = BIN_OP_IMPL[operator] - try: - yield nodes.const_factory(impl(self.value, other.value)) - except TypeError: - # ArithmeticError is not enough: float >> float is a TypeError - yield not_implemented - except Exception: # pylint: disable=broad-except - yield util.Uninferable - except TypeError: - yield not_implemented - elif isinstance(self.value, six.string_types) and operator == '%': - # TODO(cpopa): implement string interpolation later on. - yield util.Uninferable - else: - yield not_implemented - -nodes.Const.infer_binary_op = const_infer_binary_op - - -def _multiply_seq_by_int(self, opnode, other, context): - node = self.__class__(parent=opnode) - elts = [] - for elt in self.elts: - infered = helpers.safe_infer(elt, context) - if infered is None: - infered = util.Uninferable - elts.append(infered) - node.elts = elts * other.value - return node - - -def _filter_uninferable_nodes(elts, context): - for elt in elts: - if elt is util.Uninferable: - yield nodes.Unknown() - else: - for inferred in elt.infer(context): - if inferred is not util.Uninferable: - yield inferred - else: - yield nodes.Unknown() - - -@decorators.yes_if_nothing_inferred -def tl_infer_binary_op(self, opnode, operator, other, context, method): - not_implemented = nodes.Const(NotImplemented) - if isinstance(other, self.__class__) and operator == '+': - node = self.__class__(parent=opnode) - elts = list(_filter_uninferable_nodes(self.elts, context)) - elts += list(_filter_uninferable_nodes(other.elts, context)) - node.elts = elts - yield node - elif isinstance(other, nodes.Const) and operator == '*': - if not isinstance(other.value, int): - yield not_implemented - return - yield _multiply_seq_by_int(self, opnode, other, context) - elif isinstance(other, bases.Instance) and operator == '*': - # Verify if the instance supports __index__. - as_index = helpers.class_instance_as_index(other) - if not as_index: - yield util.Uninferable - else: - yield _multiply_seq_by_int(self, opnode, as_index, context) - else: - yield not_implemented - -nodes.Tuple.infer_binary_op = tl_infer_binary_op -nodes.List.infer_binary_op = tl_infer_binary_op - - -@decorators.yes_if_nothing_inferred -def instance_class_infer_binary_op(self, opnode, operator, other, context, method): - return method.infer_call_result(self, context) - -bases.Instance.infer_binary_op = instance_class_infer_binary_op -nodes.ClassDef.infer_binary_op = instance_class_infer_binary_op - - -# assignment ################################################################## - -"""the assigned_stmts method is responsible to return the assigned statement -(e.g. not inferred) according to the assignment type. - -The `asspath` argument is used to record the lhs path of the original node. -For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath -will be [1, 1] once arrived to the Assign node. - -The `context` argument is the current inference context which should be given -to any intermediary inference necessary. -""" - -def _resolve_looppart(parts, asspath, context): - """recursive function to resolve multiple assignments on loops""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if part is util.Uninferable: - continue - # XXX handle __iter__ and log potentially detected errors - if not hasattr(part, 'itered'): - continue - try: - itered = part.itered() - except TypeError: - continue # XXX log error - for stmt in itered: - index_node = nodes.Const(index) - try: - assigned = stmt.getitem(index_node, context) - except (AttributeError, - exceptions.AstroidTypeError, - exceptions.AstroidIndexError): - continue - if not asspath: - # we achieved to resolved the assignment path, - # don't infer the last part - yield assigned - elif assigned is util.Uninferable: - break - else: - # we are not yet on the last part of the path - # search on each possibly inferred value - try: - for inferred in _resolve_looppart(assigned.infer(context), - asspath, context): - yield inferred - except exceptions.InferenceError: - break - -@decorators.raise_if_nothing_inferred -def for_assigned_stmts(self, node=None, context=None, asspath=None): - if isinstance(self, nodes.AsyncFor) or getattr(self, 'is_async', False): - # Skip inferring of async code for now - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - if asspath is None: - for lst in self.iter.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.elts: - yield item - else: - for inferred in _resolve_looppart(self.iter.infer(context), - asspath, context): - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - -nodes.For.assigned_stmts = for_assigned_stmts -nodes.Comprehension.assigned_stmts = for_assigned_stmts - - -def sequence_assigned_stmts(self, node=None, context=None, asspath=None): - if asspath is None: - asspath = [] - try: - index = self.elts.index(node) - except ValueError: - util.reraise(exceptions.InferenceError( - 'Tried to retrieve a node {node!r} which does not exist', - node=self, assign_path=asspath, context=context)) - - asspath.insert(0, index) - return self.parent.assigned_stmts(node=self, context=context, asspath=asspath) - -nodes.Tuple.assigned_stmts = sequence_assigned_stmts -nodes.List.assigned_stmts = sequence_assigned_stmts - - -def assend_assigned_stmts(self, node=None, context=None, asspath=None): - return self.parent.assigned_stmts(node=self, context=context) -nodes.AssignName.assigned_stmts = assend_assigned_stmts -nodes.AssignAttr.assigned_stmts = assend_assigned_stmts - - -def _arguments_infer_argname(self, name, context): - # arguments information may be missing, in which case we can't do anything - # more - if not (self.args or self.vararg or self.kwarg): - yield util.Uninferable - return - # first argument of instance/class method - if self.args and getattr(self.args[0], 'name', None) == name: - functype = self.parent.type - cls = self.parent.parent.scope() - is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == 'metaclass' - # If this is a metaclass, then the first argument will always - # be the class, not an instance. - if is_metaclass or functype == 'classmethod': - yield cls - return - if functype == 'method': - yield bases.Instance(self.parent.parent.frame()) - return - - if context and context.callcontext: - call_site = arguments.CallSite(context.callcontext) - for value in call_site.infer_argument(self.parent, name, context): - yield value - return - - # TODO: just provide the type here, no need to have an empty Dict. - if name == self.vararg: - vararg = nodes.const_factory(()) - vararg.parent = self - yield vararg - return - if name == self.kwarg: - kwarg = nodes.const_factory({}) - kwarg.parent = self - yield kwarg - return - # if there is a default value, yield it. And then yield Uninferable to reflect - # we can't guess given argument value - try: - context = contextmod.copy_context(context) - for inferred in self.default_value(name).infer(context): - yield inferred - yield util.Uninferable - except exceptions.NoDefault: - yield util.Uninferable - - -def arguments_assigned_stmts(self, node=None, context=None, asspath=None): - if context.callcontext: - # reset call context/name - callcontext = context.callcontext - context = contextmod.copy_context(context) - context.callcontext = None - args = arguments.CallSite(callcontext) - return args.infer_argument(self.parent, node.name, context) - return _arguments_infer_argname(self, node.name, context) - -nodes.Arguments.assigned_stmts = arguments_assigned_stmts - - -@decorators.raise_if_nothing_inferred -def assign_assigned_stmts(self, node=None, context=None, asspath=None): - if not asspath: - yield self.value - return - for inferred in _resolve_asspart(self.value.infer(context), asspath, context): - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - - -def assign_annassigned_stmts(self, node=None, context=None, asspath=None): - for inferred in assign_assigned_stmts(self, node, context, asspath): - if inferred is None: - yield util.Uninferable - else: - yield inferred - -nodes.Assign.assigned_stmts = assign_assigned_stmts -nodes.AnnAssign.assigned_stmts = assign_annassigned_stmts -nodes.AugAssign.assigned_stmts = assign_assigned_stmts - - -def _resolve_asspart(parts, asspath, context): - """recursive function to resolve multiple assignments""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if hasattr(part, 'getitem'): - index_node = nodes.Const(index) - try: - assigned = part.getitem(index_node, context) - # XXX raise a specific exception to avoid potential hiding of - # unexpected exception ? - except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): - return - if not asspath: - # we achieved to resolved the assignment path, don't infer the - # last part - yield assigned - elif assigned is util.Uninferable: - return - else: - # we are not yet on the last part of the path search on each - # possibly inferred value - try: - for inferred in _resolve_asspart(assigned.infer(context), - asspath, context): - yield inferred - except exceptions.InferenceError: - return - - -@decorators.raise_if_nothing_inferred -def excepthandler_assigned_stmts(self, node=None, context=None, asspath=None): - for assigned in node_classes.unpack_infer(self.type): - if isinstance(assigned, nodes.ClassDef): - assigned = objects.ExceptionInstance(assigned) - - yield assigned - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - - -nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts - - -def _infer_context_manager(self, mgr, context): - try: - inferred = next(mgr.infer(context=context)) - except exceptions.InferenceError: - return - if isinstance(inferred, bases.Generator): - # Check if it is decorated with contextlib.contextmanager. - func = inferred.parent - if not func.decorators: - return - for decorator_node in func.decorators.nodes: - decorator = next(decorator_node.infer(context)) - if isinstance(decorator, nodes.FunctionDef): - if decorator.qname() == _CONTEXTLIB_MGR: - break - else: - # It doesn't interest us. - return - - # Get the first yield point. If it has multiple yields, - # then a RuntimeError will be raised. - # TODO(cpopa): Handle flows. - yield_point = next(func.nodes_of_class(nodes.Yield), None) - if yield_point: - if not yield_point.value: - # TODO(cpopa): an empty yield. Should be wrapped to Const. - const = nodes.Const(None) - const.parent = yield_point - const.lineno = yield_point.lineno - yield const - else: - for inferred in yield_point.value.infer(context=context): - yield inferred - elif isinstance(inferred, bases.Instance): - try: - enter = next(inferred.igetattr('__enter__', context=context)) - except (exceptions.InferenceError, exceptions.AttributeInferenceError): - return - if not isinstance(enter, bases.BoundMethod): - return - if not context.callcontext: - context.callcontext = contextmod.CallContext(args=[inferred]) - for result in enter.infer_call_result(self, context): - yield result - - -@decorators.raise_if_nothing_inferred -def with_assigned_stmts(self, node=None, context=None, asspath=None): - """Infer names and other nodes from a *with* statement. - - This enables only inference for name binding in a *with* statement. - For instance, in the following code, inferring `func` will return - the `ContextManager` class, not whatever ``__enter__`` returns. - We are doing this intentionally, because we consider that the context - manager result is whatever __enter__ returns and what it is binded - using the ``as`` keyword. - - class ContextManager(object): - def __enter__(self): - return 42 - with ContextManager() as f: - pass - # ContextManager().infer() will return ContextManager - # f.infer() will return 42. - - Arguments: - self: nodes.With - node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`. - context: TODO - asspath: TODO - """ - mgr = next(mgr for (mgr, vars) in self.items if vars == node) - if asspath is None: - for result in _infer_context_manager(self, mgr, context): - yield result - else: - for result in _infer_context_manager(self, mgr, context): - # Walk the asspath and get the item at the final index. - obj = result - for index in asspath: - if not hasattr(obj, 'elts'): - raise exceptions.InferenceError( - 'Wrong type ({targets!r}) for {node!r} assignment', - node=self, targets=node, assign_path=asspath, - context=context) - try: - obj = obj.elts[index] - except IndexError: - util.reraise(exceptions.InferenceError( - 'Tried to infer a nonexistent target with index {index} ' - 'in {node!r}.', node=self, targets=node, - assign_path=asspath, context=context)) - except TypeError: - util.reraise(exceptions.InferenceError( - 'Tried to unpack an non-iterable value ' - 'in {node!r}.', node=self, targets=node, - assign_path=asspath, context=context)) - yield obj - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - -nodes.With.assigned_stmts = with_assigned_stmts - - -@decorators.yes_if_nothing_inferred -def starred_assigned_stmts(self, node=None, context=None, asspath=None): - """ - Arguments: - self: nodes.Starred - node: TODO - context: TODO - asspath: TODO - """ - stmt = self.statement() - if not isinstance(stmt, (nodes.Assign, nodes.For)): - raise exceptions.InferenceError('Statement {stmt!r} enclosing {node!r} ' - 'must be an Assign or For node.', - node=self, stmt=stmt, unknown=node, - context=context) - - if isinstance(stmt, nodes.Assign): - value = stmt.value - lhs = stmt.targets[0] - - if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1: - raise exceptions.InferenceError('Too many starred arguments in the ' - ' assignment targets {lhs!r}.', - node=self, targets=lhs, - unknown=node, context=context) - - if context is None: - context = contextmod.InferenceContext() - try: - rhs = next(value.infer(context)) - except exceptions.InferenceError: - yield util.Uninferable - return - if rhs is util.Uninferable or not hasattr(rhs, 'elts'): - # Not interested in inferred values without elts. - yield util.Uninferable - return - - elts = collections.deque(rhs.elts[:]) - if len(lhs.elts) > len(rhs.elts): - raise exceptions.InferenceError('More targets, {targets!r}, than ' - 'values to unpack, {values!r}.', - node=self, targets=lhs, - values=rhs, unknown=node, - context=context) - - # Unpack iteratively the values from the rhs of the assignment, - # until the find the starred node. What will remain will - # be the list of values which the Starred node will represent - # This is done in two steps, from left to right to remove - # anything before the starred node and from right to left - # to remove anything after the starred node. - - for index, left_node in enumerate(lhs.elts): - if not isinstance(left_node, nodes.Starred): - elts.popleft() - continue - lhs_elts = collections.deque(reversed(lhs.elts[index:])) - for right_node in lhs_elts: - if not isinstance(right_node, nodes.Starred): - elts.pop() - continue - # We're done - packed = nodes.List() - packed.elts = elts - packed.parent = self - yield packed - break - -nodes.Starred.assigned_stmts = starred_assigned_stmts diff --git a/pymode/libs/astroid/raw_building.py b/pymode/libs/astroid/raw_building.py deleted file mode 100644 index 21e60d11..00000000 --- a/pymode/libs/astroid/raw_building.py +++ /dev/null @@ -1,416 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""this module contains a set of functions to create astroid trees from scratch -(build_* functions) or from living object (object_build_* functions) -""" - -import inspect -import logging -import os -import sys -import types - -import six - -from astroid import bases -from astroid import manager -from astroid import node_classes -from astroid import nodes - - -MANAGER = manager.AstroidManager() -# the keys of CONST_CLS eg python builtin types -_CONSTANTS = tuple(node_classes.CONST_CLS) -_JYTHON = os.name == 'java' -_BUILTINS = vars(six.moves.builtins) -_LOG = logging.getLogger(__name__) - - -def _io_discrepancy(member): - # _io module names itself `io`: http://bugs.python.org/issue18602 - member_self = getattr(member, '__self__', None) - return (member_self and - inspect.ismodule(member_self) and - member_self.__name__ == '_io' and - member.__module__ == 'io') - -def _attach_local_node(parent, node, name): - node.name = name # needed by add_local_node - parent.add_local_node(node) - - -def _add_dunder_class(func, member): - """Add a __class__ member to the given func node, if we can determine it.""" - python_cls = member.__class__ - cls_name = getattr(python_cls, '__name__', None) - if not cls_name: - return - cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__] - ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__) - func.instance_attrs['__class__'] = [ast_klass] - - -_marker = object() - - -def attach_dummy_node(node, name, runtime_object=_marker): - """create a dummy node and register it in the locals of the given - node with the specified name - """ - enode = nodes.EmptyNode() - enode.object = runtime_object - _attach_local_node(node, enode, name) - -def _has_underlying_object(self): - return self.object is not None and self.object is not _marker - -nodes.EmptyNode.has_underlying_object = _has_underlying_object - -def attach_const_node(node, name, value): - """create a Const node and register it in the locals of the given - node with the specified name - """ - if name not in node.special_attributes: - _attach_local_node(node, nodes.const_factory(value), name) - -def attach_import_node(node, modname, membername): - """create a ImportFrom node and register it in the locals of the given - node with the specified name - """ - from_node = nodes.ImportFrom(modname, [(membername, None)]) - _attach_local_node(node, from_node, membername) - - -def build_module(name, doc=None): - """create and initialize a astroid Module node""" - node = nodes.Module(name, doc, pure_python=False) - node.package = False - node.parent = None - return node - - -def build_class(name, basenames=(), doc=None): - """create and initialize a astroid ClassDef node""" - node = nodes.ClassDef(name, doc) - for base in basenames: - basenode = nodes.Name() - basenode.name = base - node.bases.append(basenode) - basenode.parent = node - return node - - -def build_function(name, args=None, defaults=None, doc=None): - """create and initialize a astroid FunctionDef node""" - args, defaults = args or [], defaults or [] - # first argument is now a list of decorators - func = nodes.FunctionDef(name, doc) - func.args = argsnode = nodes.Arguments() - argsnode.args = [] - for arg in args: - argsnode.args.append(nodes.Name()) - argsnode.args[-1].name = arg - argsnode.args[-1].parent = argsnode - argsnode.defaults = [] - for default in defaults: - argsnode.defaults.append(nodes.const_factory(default)) - argsnode.defaults[-1].parent = argsnode - argsnode.kwarg = None - argsnode.vararg = None - argsnode.parent = func - if args: - register_arguments(func) - return func - - -def build_from_import(fromname, names): - """create and initialize an astroid ImportFrom import statement""" - return nodes.ImportFrom(fromname, [(name, None) for name in names]) - -def register_arguments(func, args=None): - """add given arguments to local - - args is a list that may contains nested lists - (i.e. def func(a, (b, c, d)): ...) - """ - if args is None: - args = func.args.args - if func.args.vararg: - func.set_local(func.args.vararg, func.args) - if func.args.kwarg: - func.set_local(func.args.kwarg, func.args) - for arg in args: - if isinstance(arg, nodes.Name): - func.set_local(arg.name, arg) - else: - register_arguments(func, arg.elts) - - -def object_build_class(node, member, localname): - """create astroid for a living class object""" - basenames = [base.__name__ for base in member.__bases__] - return _base_class_object_build(node, member, basenames, - localname=localname) - - -def object_build_function(node, member, localname): - """create astroid for a living function object""" - # pylint: disable=deprecated-method; completely removed in 2.0 - args, varargs, varkw, defaults = inspect.getargspec(member) - if varargs is not None: - args.append(varargs) - if varkw is not None: - args.append(varkw) - func = build_function(getattr(member, '__name__', None) or localname, args, - defaults, member.__doc__) - node.add_local_node(func, localname) - - -def object_build_datadescriptor(node, member, name): - """create astroid for a living data descriptor object""" - return _base_class_object_build(node, member, [], name) - - -def object_build_methoddescriptor(node, member, localname): - """create astroid for a living method descriptor object""" - # FIXME get arguments ? - func = build_function(getattr(member, '__name__', None) or localname, - doc=member.__doc__) - # set node's arguments to None to notice that we have no information, not - # and empty argument list - func.args.args = None - node.add_local_node(func, localname) - _add_dunder_class(func, member) - - -def _base_class_object_build(node, member, basenames, name=None, localname=None): - """create astroid for a living class object, with a given set of base names - (e.g. ancestors) - """ - klass = build_class(name or getattr(member, '__name__', None) or localname, - basenames, member.__doc__) - klass._newstyle = isinstance(member, type) - node.add_local_node(klass, localname) - try: - # limit the instantiation trick since it's too dangerous - # (such as infinite test execution...) - # this at least resolves common case such as Exception.args, - # OSError.errno - if issubclass(member, Exception): - instdict = member().__dict__ - else: - raise TypeError - except: # pylint: disable=bare-except - pass - else: - for item_name, obj in instdict.items(): - valnode = nodes.EmptyNode() - valnode.object = obj - valnode.parent = klass - valnode.lineno = 1 - klass.instance_attrs[item_name] = [valnode] - return klass - - -def _build_from_function(node, name, member, module): - # verify this is not an imported function - try: - code = six.get_function_code(member) - except AttributeError: - # Some implementations don't provide the code object, - # such as Jython. - code = None - filename = getattr(code, 'co_filename', None) - if filename is None: - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif filename != getattr(module, '__file__', None): - attach_dummy_node(node, name, member) - else: - object_build_function(node, member, name) - - -class InspectBuilder(object): - """class for building nodes from living object - - this is actually a really minimal representation, including only Module, - FunctionDef and ClassDef nodes and some others as guessed. - """ - - # astroid from living objects ############################################### - - def __init__(self): - self._done = {} - self._module = None - - def inspect_build(self, module, modname=None, path=None): - """build astroid from a living module (i.e. using inspect) - this is used when there is no python source code available (either - because it's a built-in module or because the .py is not available) - """ - self._module = module - if modname is None: - modname = module.__name__ - try: - node = build_module(modname, module.__doc__) - except AttributeError: - # in jython, java modules have no __doc__ (see #109562) - node = build_module(modname) - node.file = node.path = os.path.abspath(path) if path else path - node.name = modname - MANAGER.cache_module(node) - node.package = hasattr(module, '__path__') - self._done = {} - self.object_build(node, module) - return node - - def object_build(self, node, obj): - """recursive method which create a partial ast from real objects - (only function, class, and method are handled) - """ - if obj in self._done: - return self._done[obj] - self._done[obj] = node - for name in dir(obj): - try: - member = getattr(obj, name) - except AttributeError: - # damned ExtensionClass.Base, I know you're there ! - attach_dummy_node(node, name) - continue - if inspect.ismethod(member): - member = six.get_method_function(member) - if inspect.isfunction(member): - _build_from_function(node, name, member, self._module) - elif inspect.isbuiltin(member): - if (not _io_discrepancy(member) and - self.imported_member(node, member, name)): - continue - object_build_methoddescriptor(node, member, name) - elif inspect.isclass(member): - if self.imported_member(node, member, name): - continue - if member in self._done: - class_node = self._done[member] - if class_node not in node.locals.get(name, ()): - node.add_local_node(class_node, name) - else: - class_node = object_build_class(node, member, name) - # recursion - self.object_build(class_node, member) - if name == '__class__' and class_node.parent is None: - class_node.parent = self._done[self._module] - elif inspect.ismethoddescriptor(member): - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif inspect.isdatadescriptor(member): - assert isinstance(member, object) - object_build_datadescriptor(node, member, name) - elif isinstance(member, _CONSTANTS): - attach_const_node(node, name, member) - elif inspect.isroutine(member): - # This should be called for Jython, where some builtin - # methods aren't caught by isbuiltin branch. - _build_from_function(node, name, member, self._module) - else: - # create an empty node so that the name is actually defined - attach_dummy_node(node, name, member) - - def imported_member(self, node, member, name): - """verify this is not an imported class or handle it""" - # /!\ some classes like ExtensionClass doesn't have a __module__ - # attribute ! Also, this may trigger an exception on badly built module - # (see http://www.logilab.org/ticket/57299 for instance) - try: - modname = getattr(member, '__module__', None) - except: # pylint: disable=bare-except - _LOG.exception('unexpected error while building ' - 'astroid from living object') - modname = None - if modname is None: - if (name in ('__new__', '__subclasshook__') - or (name in _BUILTINS and _JYTHON)): - # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14) - # >>> print object.__new__.__module__ - # None - modname = six.moves.builtins.__name__ - else: - attach_dummy_node(node, name, member) - return True - - real_name = { - 'gtk': 'gtk_gtk', - '_io': 'io', - }.get(modname, modname) - - if real_name != self._module.__name__: - # check if it sounds valid and then add an import node, else use a - # dummy node - try: - getattr(sys.modules[modname], name) - except (KeyError, AttributeError): - attach_dummy_node(node, name, member) - else: - attach_import_node(node, modname, name) - return True - return False - - -### astroid bootstrapping ###################################################### -Astroid_BUILDER = InspectBuilder() - -_CONST_PROXY = {} -def _astroid_bootstrapping(astroid_builtin=None): - """astroid boot strapping the builtins module""" - # this boot strapping is necessary since we need the Const nodes to - # inspect_build builtins, and then we can proxy Const - if astroid_builtin is None: - from six.moves import builtins - astroid_builtin = Astroid_BUILDER.inspect_build(builtins) - - # pylint: disable=redefined-outer-name - for cls, node_cls in node_classes.CONST_CLS.items(): - if cls is type(None): - proxy = build_class('NoneType') - proxy.parent = astroid_builtin - elif cls is type(NotImplemented): - proxy = build_class('NotImplementedType') - proxy.parent = astroid_builtin - else: - proxy = astroid_builtin.getattr(cls.__name__)[0] - if cls in (dict, list, set, tuple): - node_cls._proxied = proxy - else: - _CONST_PROXY[cls] = proxy - -_astroid_bootstrapping() - -# TODO : find a nicer way to handle this situation; -# However __proxied introduced an -# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870) -def _set_proxied(const): - return _CONST_PROXY[const.value.__class__] -nodes.Const._proxied = property(_set_proxied) - -_GeneratorType = nodes.ClassDef(types.GeneratorType.__name__, types.GeneratorType.__doc__) -_GeneratorType.parent = MANAGER.astroid_cache[six.moves.builtins.__name__] -bases.Generator._proxied = _GeneratorType -Astroid_BUILDER.object_build(bases.Generator._proxied, types.GeneratorType) - -_builtins = MANAGER.astroid_cache[six.moves.builtins.__name__] -BUILTIN_TYPES = (types.GetSetDescriptorType, types.GeneratorType, - types.MemberDescriptorType, type(None), type(NotImplemented), - types.FunctionType, types.MethodType, - types.BuiltinFunctionType, types.ModuleType, types.TracebackType) -for _type in BUILTIN_TYPES: - if _type.__name__ not in _builtins: - cls = nodes.ClassDef(_type.__name__, _type.__doc__) - cls.parent = MANAGER.astroid_cache[six.moves.builtins.__name__] - Astroid_BUILDER.object_build(cls, _type) - _builtins[_type.__name__] = cls diff --git a/pymode/libs/astroid/rebuilder.py b/pymode/libs/astroid/rebuilder.py deleted file mode 100644 index 60a1ad77..00000000 --- a/pymode/libs/astroid/rebuilder.py +++ /dev/null @@ -1,914 +0,0 @@ -# Copyright (c) 2009-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# 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 - -"""this module contains utilities for rebuilding a _ast tree in -order to get a single Astroid representation -""" - -import sys -import _ast - -import astroid -from astroid import astpeephole -from astroid import nodes - - - -_BIN_OP_CLASSES = {_ast.Add: '+', - _ast.BitAnd: '&', - _ast.BitOr: '|', - _ast.BitXor: '^', - _ast.Div: '/', - _ast.FloorDiv: '//', - _ast.Mod: '%', - _ast.Mult: '*', - _ast.Pow: '**', - _ast.Sub: '-', - _ast.LShift: '<<', - _ast.RShift: '>>', - } -if sys.version_info >= (3, 5): - _BIN_OP_CLASSES[_ast.MatMult] = '@' - -_BOOL_OP_CLASSES = {_ast.And: 'and', - _ast.Or: 'or', - } - -_UNARY_OP_CLASSES = {_ast.UAdd: '+', - _ast.USub: '-', - _ast.Not: 'not', - _ast.Invert: '~', - } - -_CMP_OP_CLASSES = {_ast.Eq: '==', - _ast.Gt: '>', - _ast.GtE: '>=', - _ast.In: 'in', - _ast.Is: 'is', - _ast.IsNot: 'is not', - _ast.Lt: '<', - _ast.LtE: '<=', - _ast.NotEq: '!=', - _ast.NotIn: 'not in', - } - -CONST_NAME_TRANSFORMS = {'None': None, - 'True': True, - 'False': False, - } - -REDIRECT = {'arguments': 'Arguments', - 'comprehension': 'Comprehension', - "ListCompFor": 'Comprehension', - "GenExprFor": 'Comprehension', - 'excepthandler': 'ExceptHandler', - 'keyword': 'Keyword', - } -PY3 = sys.version_info >= (3, 0) -PY34 = sys.version_info >= (3, 4) -CONTEXTS = {_ast.Load: astroid.Load, - _ast.Store: astroid.Store, - _ast.Del: astroid.Del, - _ast.Param: astroid.Store} - - -def _get_doc(node): - try: - if isinstance(node.body[0], _ast.Expr) and isinstance(node.body[0].value, _ast.Str): - doc = node.body[0].value.s - node.body = node.body[1:] - return node, doc - except IndexError: - pass # ast built from scratch - return node, None - -def _visit_or_none(node, attr, visitor, parent, visit='visit', - **kws): - """If the given node has an attribute, visits the attribute, and - otherwise returns None. - - """ - value = getattr(node, attr, None) - if value: - return getattr(visitor, visit)(value, parent, **kws) - - return None - - -def _get_context(node): - return CONTEXTS.get(type(node.ctx), astroid.Load) - - -class TreeRebuilder(object): - """Rebuilds the _ast tree to become an Astroid tree""" - - def __init__(self, manager): - self._manager = manager - self._global_names = [] - self._import_from_nodes = [] - self._delayed_assattr = [] - self._visit_meths = {} - self._peepholer = astpeephole.ASTPeepholeOptimizer() - - def visit_module(self, node, modname, modpath, package): - """visit a Module node by returning a fresh instance of it""" - node, doc = _get_doc(node) - newnode = nodes.Module(name=modname, doc=doc, file=modpath, path=modpath, - package=package, parent=None) - newnode.postinit([self.visit(child, newnode) for child in node.body]) - return newnode - - def visit(self, node, parent): - cls = node.__class__ - if cls in self._visit_meths: - visit_method = self._visit_meths[cls] - else: - cls_name = cls.__name__ - visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower() - visit_method = getattr(self, visit_name) - self._visit_meths[cls] = visit_method - return visit_method(node, parent) - - def _save_assignment(self, node, name=None): - """save assignement situation since node.parent is not available yet""" - if self._global_names and node.name in self._global_names[-1]: - node.root().set_local(node.name, node) - else: - node.parent.set_local(node.name, node) - - def visit_arguments(self, node, parent): - """visit a Arguments node by returning a fresh instance of it""" - vararg, kwarg = node.vararg, node.kwarg - if PY34: - newnode = nodes.Arguments(vararg.arg if vararg else None, - kwarg.arg if kwarg else None, - parent) - else: - newnode = nodes.Arguments(vararg, kwarg, parent) - args = [self.visit(child, newnode) for child in node.args] - defaults = [self.visit(child, newnode) - for child in node.defaults] - varargannotation = None - kwargannotation = None - # change added in 82732 (7c5c678e4164), vararg and kwarg - # are instances of `_ast.arg`, not strings - if vararg: - if PY34: - if node.vararg.annotation: - varargannotation = self.visit(node.vararg.annotation, - newnode) - vararg = vararg.arg - elif PY3 and node.varargannotation: - varargannotation = self.visit(node.varargannotation, - newnode) - if kwarg: - if PY34: - if node.kwarg.annotation: - kwargannotation = self.visit(node.kwarg.annotation, - newnode) - kwarg = kwarg.arg - elif PY3: - if node.kwargannotation: - kwargannotation = self.visit(node.kwargannotation, - newnode) - if PY3: - kwonlyargs = [self.visit(child, newnode) for child - in node.kwonlyargs] - kw_defaults = [self.visit(child, newnode) if child else - None for child in node.kw_defaults] - annotations = [self.visit(arg.annotation, newnode) if - arg.annotation else None for arg in node.args] - kwonlyargs_annotations = [ - self.visit(arg.annotation, newnode) if arg.annotation else None - for arg in node.kwonlyargs - ] - else: - kwonlyargs = [] - kw_defaults = [] - annotations = [] - kwonlyargs_annotations = [] - - newnode.postinit( - args=args, - defaults=defaults, - kwonlyargs=kwonlyargs, - kw_defaults=kw_defaults, - annotations=annotations, - kwonlyargs_annotations=kwonlyargs_annotations, - varargannotation=varargannotation, - kwargannotation=kwargannotation - ) - # save argument names in locals: - if vararg: - newnode.parent.set_local(vararg, newnode) - if kwarg: - newnode.parent.set_local(kwarg, newnode) - return newnode - - def visit_assert(self, node, parent): - """visit a Assert node by returning a fresh instance of it""" - newnode = nodes.Assert(node.lineno, node.col_offset, parent) - if node.msg: - msg = self.visit(node.msg, newnode) - else: - msg = None - newnode.postinit(self.visit(node.test, newnode), msg) - return newnode - - def visit_assign(self, node, parent): - """visit a Assign node by returning a fresh instance of it""" - newnode = nodes.Assign(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.targets], - self.visit(node.value, newnode)) - return newnode - - def visit_assignname(self, node, parent, node_name=None): - '''visit a node and return a AssignName node''' - newnode = nodes.AssignName(node_name, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - self._save_assignment(newnode) - return newnode - - def visit_augassign(self, node, parent): - """visit a AugAssign node by returning a fresh instance of it""" - newnode = nodes.AugAssign(_BIN_OP_CLASSES[type(node.op)] + "=", - node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.target, newnode), - self.visit(node.value, newnode)) - return newnode - - def visit_repr(self, node, parent): - """visit a Backquote node by returning a fresh instance of it""" - newnode = nodes.Repr(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_binop(self, node, parent): - """visit a BinOp node by returning a fresh instance of it""" - if isinstance(node.left, _ast.BinOp) and self._manager.optimize_ast: - # Optimize BinOp operations in order to remove - # redundant recursion. For instance, if the - # following code is parsed in order to obtain - # its ast, then the rebuilder will fail with an - # infinite recursion, the same will happen with the - # inference engine as well. There's no need to hold - # so many objects for the BinOp if they can be reduced - # to something else (also, the optimization - # might handle only Const binops, which isn't a big - # problem for the correctness of the program). - # - # ("a" + "b" + # one thousand more + "c") - optimized = self._peepholer.optimize_binop(node, parent) - if optimized: - return optimized - - newnode = nodes.BinOp(_BIN_OP_CLASSES[type(node.op)], - node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.left, newnode), - self.visit(node.right, newnode)) - return newnode - - def visit_boolop(self, node, parent): - """visit a BoolOp node by returning a fresh instance of it""" - newnode = nodes.BoolOp(_BOOL_OP_CLASSES[type(node.op)], - node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.values]) - return newnode - - def visit_break(self, node, parent): - """visit a Break node by returning a fresh instance of it""" - return nodes.Break(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), - parent) - - def visit_call(self, node, parent): - """visit a CallFunc node by returning a fresh instance of it""" - newnode = nodes.Call(node.lineno, node.col_offset, parent) - starargs = _visit_or_none(node, 'starargs', self, newnode) - kwargs = _visit_or_none(node, 'kwargs', self, newnode) - args = [self.visit(child, newnode) - for child in node.args] - - if node.keywords: - keywords = [self.visit(child, newnode) - for child in node.keywords] - else: - keywords = None - if starargs: - new_starargs = nodes.Starred(col_offset=starargs.col_offset, - lineno=starargs.lineno, - parent=starargs.parent) - new_starargs.postinit(value=starargs) - args.append(new_starargs) - if kwargs: - new_kwargs = nodes.Keyword(arg=None, col_offset=kwargs.col_offset, - lineno=kwargs.lineno, - parent=kwargs.parent) - new_kwargs.postinit(value=kwargs) - if keywords: - keywords.append(new_kwargs) - else: - keywords = [new_kwargs] - - newnode.postinit(self.visit(node.func, newnode), - args, keywords) - return newnode - - def visit_classdef(self, node, parent, newstyle=None): - """visit a ClassDef node to become astroid""" - node, doc = _get_doc(node) - newnode = nodes.ClassDef(node.name, doc, node.lineno, - node.col_offset, parent) - metaclass = None - if PY3: - for keyword in node.keywords: - if keyword.arg == 'metaclass': - metaclass = self.visit(keyword, newnode).value - break - if node.decorator_list: - decorators = self.visit_decorators(node, newnode) - else: - decorators = None - newnode.postinit([self.visit(child, newnode) - for child in node.bases], - [self.visit(child, newnode) - for child in node.body], - decorators, newstyle, metaclass, - [self.visit(kwd, newnode) for kwd in node.keywords - if kwd.arg != 'metaclass'] if PY3 else []) - return newnode - - def visit_const(self, node, parent): - """visit a Const node by returning a fresh instance of it""" - return nodes.Const(node.value, - getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - def visit_continue(self, node, parent): - """visit a Continue node by returning a fresh instance of it""" - return nodes.Continue(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), - parent) - - def visit_compare(self, node, parent): - """visit a Compare node by returning a fresh instance of it""" - newnode = nodes.Compare(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.left, newnode), - [(_CMP_OP_CLASSES[op.__class__], - self.visit(expr, newnode)) - for (op, expr) in zip(node.ops, node.comparators)]) - return newnode - - def visit_comprehension(self, node, parent): - """visit a Comprehension node by returning a fresh instance of it""" - newnode = nodes.Comprehension(parent) - newnode.postinit(self.visit(node.target, newnode), - self.visit(node.iter, newnode), - [self.visit(child, newnode) - for child in node.ifs], - getattr(node, 'is_async', None)) - return newnode - - def visit_decorators(self, node, parent): - """visit a Decorators node by returning a fresh instance of it""" - # /!\ node is actually a _ast.FunctionDef node while - # parent is a astroid.nodes.FunctionDef node - newnode = nodes.Decorators(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.decorator_list]) - return newnode - - def visit_delete(self, node, parent): - """visit a Delete node by returning a fresh instance of it""" - newnode = nodes.Delete(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.targets]) - return newnode - - def _visit_dict_items(self, node, parent, newnode): - for key, value in zip(node.keys, node.values): - rebuilt_value = self.visit(value, newnode) - if not key: - # Python 3.5 and extended unpacking - rebuilt_key = nodes.DictUnpack(rebuilt_value.lineno, - rebuilt_value.col_offset, - parent) - else: - rebuilt_key = self.visit(key, newnode) - yield rebuilt_key, rebuilt_value - - def visit_dict(self, node, parent): - """visit a Dict node by returning a fresh instance of it""" - newnode = nodes.Dict(node.lineno, node.col_offset, parent) - items = list(self._visit_dict_items(node, parent, newnode)) - newnode.postinit(items) - return newnode - - def visit_dictcomp(self, node, parent): - """visit a DictComp node by returning a fresh instance of it""" - newnode = nodes.DictComp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.key, newnode), - self.visit(node.value, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_expr(self, node, parent): - """visit a Expr node by returning a fresh instance of it""" - newnode = nodes.Expr(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_ellipsis(self, node, parent): - """visit an Ellipsis node by returning a fresh instance of it""" - return nodes.Ellipsis(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - - def visit_emptynode(self, node, parent): - """visit an EmptyNode node by returning a fresh instance of it""" - return nodes.EmptyNode(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent) - # /!\ node.name can be a tuple - newnode.postinit(_visit_or_none(node, 'type', self, newnode), - _visit_or_none(node, 'name', self, newnode), - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_exec(self, node, parent): - """visit an Exec node by returning a fresh instance of it""" - newnode = nodes.Exec(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.body, newnode), - _visit_or_none(node, 'globals', self, newnode), - _visit_or_none(node, 'locals', self, newnode)) - return newnode - - def visit_extslice(self, node, parent): - """visit an ExtSlice node by returning a fresh instance of it""" - newnode = nodes.ExtSlice(parent=parent) - newnode.postinit([self.visit(dim, newnode) - for dim in node.dims]) - return newnode - - def _visit_for(self, cls, node, parent): - """visit a For node by returning a fresh instance of it""" - newnode = cls(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.target, newnode), - self.visit(node.iter, newnode), - [self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_for(self, node, parent): - return self._visit_for(nodes.For, node, parent) - - def visit_importfrom(self, node, parent): - """visit an ImportFrom node by returning a fresh instance of it""" - names = [(alias.name, alias.asname) for alias in node.names] - newnode = nodes.ImportFrom(node.module or '', names, node.level or None, - getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - # store From names to add them to locals after building - self._import_from_nodes.append(newnode) - return newnode - - def _visit_functiondef(self, cls, node, parent): - """visit an FunctionDef node to become astroid""" - self._global_names.append({}) - node, doc = _get_doc(node) - newnode = cls(node.name, doc, node.lineno, - node.col_offset, parent) - if node.decorator_list: - decorators = self.visit_decorators(node, newnode) - else: - decorators = None - if PY3 and node.returns: - returns = self.visit(node.returns, newnode) - else: - returns = None - newnode.postinit(self.visit(node.args, newnode), - [self.visit(child, newnode) - for child in node.body], - decorators, returns) - self._global_names.pop() - return newnode - - def visit_functiondef(self, node, parent): - return self._visit_functiondef(nodes.FunctionDef, node, parent) - - def visit_generatorexp(self, node, parent): - """visit a GeneratorExp node by returning a fresh instance of it""" - newnode = nodes.GeneratorExp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.elt, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_attribute(self, node, parent): - """visit an Attribute node by returning a fresh instance of it""" - context = _get_context(node) - if context == astroid.Del: - # FIXME : maybe we should reintroduce and visit_delattr ? - # for instance, deactivating assign_ctx - newnode = nodes.DelAttr(node.attr, node.lineno, node.col_offset, - parent) - elif context == astroid.Store: - newnode = nodes.AssignAttr(node.attr, node.lineno, node.col_offset, - parent) - # Prohibit a local save if we are in an ExceptHandler. - if not isinstance(parent, astroid.ExceptHandler): - self._delayed_assattr.append(newnode) - else: - newnode = nodes.Attribute(node.attr, node.lineno, node.col_offset, - parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_global(self, node, parent): - """visit a Global node to become astroid""" - newnode = nodes.Global(node.names, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - if self._global_names: # global at the module level, no effect - for name in node.names: - self._global_names[-1].setdefault(name, []).append(newnode) - return newnode - - def visit_if(self, node, parent): - """visit an If node by returning a fresh instance of it""" - newnode = nodes.If(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.test, newnode), - [self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_ifexp(self, node, parent): - """visit a IfExp node by returning a fresh instance of it""" - newnode = nodes.IfExp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.test, newnode), - self.visit(node.body, newnode), - self.visit(node.orelse, newnode)) - return newnode - - def visit_import(self, node, parent): - """visit a Import node by returning a fresh instance of it""" - names = [(alias.name, alias.asname) for alias in node.names] - newnode = nodes.Import(names, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - # save import names in parent's locals: - for (name, asname) in newnode.names: - name = asname or name - parent.set_local(name.split('.')[0], newnode) - return newnode - - def visit_index(self, node, parent): - """visit a Index node by returning a fresh instance of it""" - newnode = nodes.Index(parent=parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_keyword(self, node, parent): - """visit a Keyword node by returning a fresh instance of it""" - newnode = nodes.Keyword(node.arg, parent=parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_lambda(self, node, parent): - """visit a Lambda node by returning a fresh instance of it""" - newnode = nodes.Lambda(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.args, newnode), - self.visit(node.body, newnode)) - return newnode - - def visit_list(self, node, parent): - """visit a List node by returning a fresh instance of it""" - context = _get_context(node) - newnode = nodes.List(ctx=context, - lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit([self.visit(child, newnode) - for child in node.elts]) - return newnode - - def visit_listcomp(self, node, parent): - """visit a ListComp node by returning a fresh instance of it""" - newnode = nodes.ListComp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.elt, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_name(self, node, parent): - """visit a Name node by returning a fresh instance of it""" - context = _get_context(node) - # True and False can be assigned to something in py2x, so we have to - # check first the context. - if context == astroid.Del: - newnode = nodes.DelName(node.id, node.lineno, node.col_offset, - parent) - elif context == astroid.Store: - newnode = nodes.AssignName(node.id, node.lineno, node.col_offset, - parent) - elif node.id in CONST_NAME_TRANSFORMS: - newnode = nodes.Const(CONST_NAME_TRANSFORMS[node.id], - getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - return newnode - else: - newnode = nodes.Name(node.id, node.lineno, node.col_offset, parent) - # XXX REMOVE me : - if context in (astroid.Del, astroid.Store): # 'Aug' ?? - self._save_assignment(newnode) - return newnode - - def visit_str(self, node, parent): - """visit a String/Bytes node by returning a fresh instance of Const""" - return nodes.Const(node.s, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - visit_bytes = visit_str - - def visit_num(self, node, parent): - """visit a Num node by returning a fresh instance of Const""" - return nodes.Const(node.n, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - def visit_pass(self, node, parent): - """visit a Pass node by returning a fresh instance of it""" - return nodes.Pass(node.lineno, node.col_offset, parent) - - def visit_print(self, node, parent): - """visit a Print node by returning a fresh instance of it""" - newnode = nodes.Print(node.nl, node.lineno, node.col_offset, parent) - newnode.postinit(_visit_or_none(node, 'dest', self, newnode), - [self.visit(child, newnode) - for child in node.values]) - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = nodes.Raise(node.lineno, node.col_offset, parent) - newnode.postinit(_visit_or_none(node, 'type', self, newnode), - _visit_or_none(node, 'inst', self, newnode), - _visit_or_none(node, 'tback', self, newnode)) - return newnode - - def visit_return(self, node, parent): - """visit a Return node by returning a fresh instance of it""" - newnode = nodes.Return(node.lineno, node.col_offset, parent) - if node.value is not None: - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_set(self, node, parent): - """visit a Set node by returning a fresh instance of it""" - newnode = nodes.Set(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.elts]) - return newnode - - def visit_setcomp(self, node, parent): - """visit a SetComp node by returning a fresh instance of it""" - newnode = nodes.SetComp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.elt, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_slice(self, node, parent): - """visit a Slice node by returning a fresh instance of it""" - newnode = nodes.Slice(parent=parent) - newnode.postinit(_visit_or_none(node, 'lower', self, newnode), - _visit_or_none(node, 'upper', self, newnode), - _visit_or_none(node, 'step', self, newnode)) - return newnode - - def visit_subscript(self, node, parent): - """visit a Subscript node by returning a fresh instance of it""" - context = _get_context(node) - newnode = nodes.Subscript(ctx=context, - lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit(self.visit(node.value, newnode), - self.visit(node.slice, newnode)) - return newnode - - def visit_tryexcept(self, node, parent): - """visit a TryExcept node by returning a fresh instance of it""" - newnode = nodes.TryExcept(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.handlers], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_tryfinally(self, node, parent): - """visit a TryFinally node by returning a fresh instance of it""" - newnode = nodes.TryFinally(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.body], - [self.visit(n, newnode) - for n in node.finalbody]) - return newnode - - def visit_tuple(self, node, parent): - """visit a Tuple node by returning a fresh instance of it""" - context = _get_context(node) - newnode = nodes.Tuple(ctx=context, - lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit([self.visit(child, newnode) - for child in node.elts]) - return newnode - - def visit_unaryop(self, node, parent): - """visit a UnaryOp node by returning a fresh instance of it""" - newnode = nodes.UnaryOp(_UNARY_OP_CLASSES[node.op.__class__], - node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.operand, newnode)) - return newnode - - def visit_while(self, node, parent): - """visit a While node by returning a fresh instance of it""" - newnode = nodes.While(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.test, newnode), - [self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_with(self, node, parent): - newnode = nodes.With(node.lineno, node.col_offset, parent) - expr = self.visit(node.context_expr, newnode) - if node.optional_vars is not None: - optional_vars = self.visit(node.optional_vars, newnode) - else: - optional_vars = None - newnode.postinit([(expr, optional_vars)], - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_yield(self, node, parent): - """visit a Yield node by returning a fresh instance of it""" - newnode = nodes.Yield(node.lineno, node.col_offset, parent) - if node.value is not None: - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - -class TreeRebuilder3(TreeRebuilder): - """extend and overwrite TreeRebuilder for python3k""" - - def visit_arg(self, node, parent): - """visit a arg node by returning a fresh AssName instance""" - # TODO(cpopa): introduce an Arg node instead of using AssignName. - return self.visit_assignname(node, parent, node.arg) - - def visit_nameconstant(self, node, parent): - # in Python 3.4 we have NameConstant for True / False / None - return nodes.Const(node.value, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent) - if node.name: - name = self.visit_assignname(node, newnode, node.name) - else: - name = None - newnode.postinit(_visit_or_none(node, 'type', self, newnode), - name, - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_nonlocal(self, node, parent): - """visit a Nonlocal node and return a new instance of it""" - return nodes.Nonlocal(node.names, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = nodes.Raise(node.lineno, node.col_offset, parent) - # no traceback; anyway it is not used in Pylint - newnode.postinit(_visit_or_none(node, 'exc', self, newnode), - _visit_or_none(node, 'cause', self, newnode)) - return newnode - - def visit_starred(self, node, parent): - """visit a Starred node and return a new instance of it""" - context = _get_context(node) - newnode = nodes.Starred(ctx=context, lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_try(self, node, parent): - # python 3.3 introduce a new Try node replacing - # TryFinally/TryExcept nodes - if node.finalbody: - newnode = nodes.TryFinally(node.lineno, node.col_offset, parent) - if node.handlers: - body = [self.visit_tryexcept(node, newnode)] - else: - body = [self.visit(child, newnode) - for child in node.body] - newnode.postinit(body, - [self.visit(n, newnode) - for n in node.finalbody]) - return newnode - elif node.handlers: - return self.visit_tryexcept(node, parent) - - def visit_annassign(self, node, parent): - """visit an AnnAssign node by returning a fresh instance of it""" - newnode = nodes.AnnAssign(node.lineno, node.col_offset, parent) - annotation = _visit_or_none(node, 'annotation', self, newnode) - newnode.postinit(target=self.visit(node.target, newnode), - annotation=annotation, - simple=node.simple, - value=_visit_or_none(node, 'value', self, newnode)) - return newnode - - def _visit_with(self, cls, node, parent): - if 'items' not in node._fields: - # python < 3.3 - return super(TreeRebuilder3, self).visit_with(node, parent) - - newnode = cls(node.lineno, node.col_offset, parent) - def visit_child(child): - expr = self.visit(child.context_expr, newnode) - var = _visit_or_none(child, 'optional_vars', self, newnode) - return expr, var - newnode.postinit([visit_child(child) for child in node.items], - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_with(self, node, parent): - return self._visit_with(nodes.With, node, parent) - - def visit_yieldfrom(self, node, parent): - newnode = nodes.YieldFrom(node.lineno, node.col_offset, parent) - if node.value is not None: - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_classdef(self, node, parent, newstyle=True): - return super(TreeRebuilder3, self).visit_classdef(node, parent, - newstyle=newstyle) - - # Async structs added in Python 3.5 - def visit_asyncfunctiondef(self, node, parent): - return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent) - - def visit_asyncfor(self, node, parent): - return self._visit_for(nodes.AsyncFor, node, parent) - - def visit_await(self, node, parent): - newnode = nodes.Await(node.lineno, node.col_offset, parent) - newnode.postinit(value=self.visit(node.value, newnode)) - return newnode - - def visit_asyncwith(self, node, parent): - return self._visit_with(nodes.AsyncWith, node, parent) - - def visit_joinedstr(self, node, parent): - newnode = nodes.JoinedStr(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.values]) - return newnode - - def visit_formattedvalue(self, node, parent): - newnode = nodes.FormattedValue(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.value, newnode), - node.conversion, - _visit_or_none(node, 'format_spec', self, newnode)) - return newnode - -if sys.version_info >= (3, 0): - TreeRebuilder = TreeRebuilder3 diff --git a/pymode/libs/astroid/scoped_nodes.py b/pymode/libs/astroid/scoped_nodes.py deleted file mode 100644 index 052ec688..00000000 --- a/pymode/libs/astroid/scoped_nodes.py +++ /dev/null @@ -1,1832 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2011, 2013-2015 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015 Rene Zhang - -# 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 - - -""" -This module contains the classes for "scoped" node, i.e. which are opening a -new local scope in the language definition : Module, ClassDef, FunctionDef (and -Lambda, GeneratorExp, DictComp and SetComp to some extent). -""" - -import sys -import io -import itertools -import warnings - -import six - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import decorators as decorators_mod -from astroid.interpreter import objectmodel -from astroid.interpreter import dunder_lookup -from astroid import manager -from astroid import mixins -from astroid import node_classes -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ -ITER_METHODS = ('__iter__', '__getitem__') - - -def _c3_merge(sequences, cls, context): - """Merges MROs in *sequences* to a single MRO using the C3 algorithm. - - Adapted from http://www.python.org/download/releases/2.3/mro/. - - """ - result = [] - while True: - sequences = [s for s in sequences if s] # purge empty sequences - if not sequences: - return result - for s1 in sequences: # find merge candidates among seq heads - candidate = s1[0] - for s2 in sequences: - if candidate in s2[1:]: - candidate = None - break # reject the current head, it appears later - else: - break - if not candidate: - # Show all the remaining bases, which were considered as - # candidates for the next mro sequence. - raise exceptions.InconsistentMroError( - message="Cannot create a consistent method resolution order " - "for MROs {mros} of class {cls!r}.", - mros=sequences, cls=cls, context=context) - - result.append(candidate) - # remove the chosen candidate - for seq in sequences: - if seq[0] == candidate: - del seq[0] - - -def _verify_duplicates_mro(sequences, cls, context): - for sequence in sequences: - names = [node.qname() for node in sequence] - if len(names) != len(set(names)): - raise exceptions.DuplicateBasesError( - message='Duplicates found in MROs {mros} for {cls!r}.', - mros=sequences, cls=cls, context=context) - - -def function_to_method(n, klass): - if isinstance(n, FunctionDef): - if n.type == 'classmethod': - return bases.BoundMethod(n, klass) - if n.type != 'staticmethod': - return bases.UnboundMethod(n) - return n - - -MANAGER = manager.AstroidManager() -def builtin_lookup(name): - """lookup a name into the builtin module - return the list of matching statements and the astroid for the builtin - module - """ - builtin_astroid = MANAGER.ast_from_module(six.moves.builtins) - if name == '__dict__': - return builtin_astroid, () - try: - stmts = builtin_astroid.locals[name] - except KeyError: - stmts = () - return builtin_astroid, stmts - - -# TODO move this Mixin to mixins.py; problem: 'FunctionDef' in _scope_lookup -class LocalsDictNodeNG(node_classes.LookupMixIn, - node_classes.NodeNG): - """ this class provides locals handling common to Module, FunctionDef - and ClassDef nodes, including a dict like interface for direct access - to locals information - """ - - # attributes below are set by the builder module or by raw factories - - # dictionary of locals with name as key and node defining the local as - # value - - locals = {} - - def qname(self): - """return the 'qualified' name of the node, eg module.name, - module.class.name ... - """ - # pylint: disable=no-member; github.com/pycqa/astroid/issues/278 - if self.parent is None: - return self.name - return '%s.%s' % (self.parent.frame().qname(), self.name) - - def frame(self): - """return the first parent frame node (i.e. Module, FunctionDef or ClassDef) - """ - return self - - def scope(self): - """return the first node defining a new scope (i.e. Module, - FunctionDef, ClassDef, Lambda but also GeneratorExp, DictComp and SetComp) - """ - return self - - def _scope_lookup(self, node, name, offset=0): - """XXX method for interfacing the scope lookup""" - try: - stmts = node._filter_stmts(self.locals[name], self, offset) - except KeyError: - stmts = () - if stmts: - return self, stmts - if self.parent: # i.e. not Module - # nested scope: if parent scope is a function, that's fine - # else jump to the module - pscope = self.parent.scope() - if not pscope.is_function: - pscope = pscope.root() - return pscope.scope_lookup(node, name) - return builtin_lookup(name) # Module - - def set_local(self, name, stmt): - """define in locals ( is the node defining the name) - if the node is a Module node (i.e. has globals), add the name to - globals - - if the name is already defined, ignore it - """ - #assert not stmt in self.locals.get(name, ()), (self, stmt) - self.locals.setdefault(name, []).append(stmt) - - __setitem__ = set_local - - def _append_node(self, child): - """append a child, linking it in the tree""" - # pylint: disable=no-member; depending by the class - # which uses the current class as a mixin or base class. - # It's rewritten in 2.0, so it makes no sense for now - # to spend development time on it. - self.body.append(child) - child.parent = self - - def add_local_node(self, child_node, name=None): - """append a child which should alter locals to the given node""" - if name != '__class__': - # add __class__ node as a child will cause infinite recursion later! - self._append_node(child_node) - self.set_local(name or child_node.name, child_node) - - def __getitem__(self, item): - """method from the `dict` interface returning the first node - associated with the given name in the locals dictionary - - :type item: str - :param item: the name of the locally defined object - :raises KeyError: if the name is not defined - """ - return self.locals[item][0] - - def __iter__(self): - """method from the `dict` interface returning an iterator on - `self.keys()` - """ - return iter(self.keys()) - - def keys(self): - """method from the `dict` interface returning a tuple containing - locally defined names - """ - return list(self.locals.keys()) - - def values(self): - """method from the `dict` interface returning a tuple containing - locally defined nodes which are instance of `FunctionDef` or `ClassDef` - """ - return [self[key] for key in self.keys()] - - def items(self): - """method from the `dict` interface returning a list of tuple - containing each locally defined name with its associated node, - which is an instance of `FunctionDef` or `ClassDef` - """ - return list(zip(self.keys(), self.values())) - - def __contains__(self, name): - return name in self.locals - - -class Module(LocalsDictNodeNG): - _astroid_fields = ('body',) - - fromlineno = 0 - lineno = 0 - - # attributes below are set by the builder module or by raw factories - - # the file from which as been extracted the astroid representation. It may - # be None if the representation has been built from a built-in module - file = None - # Alternatively, if built from a string/bytes, this can be set - file_bytes = None - # encoding of python source file, so we can get unicode out of it (python2 - # only) - file_encoding = None - # the module name - name = None - # boolean for astroid built from source (i.e. ast) - pure_python = None - # boolean for package module - package = None - # dictionary of globals with name as key and node defining the global - # as value - globals = None - - # Future imports - future_imports = None - special_attributes = objectmodel.ModuleModel() - - # names of python special attributes (handled by getattr impl.) - - # names of module attributes available through the global scope - scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) - - _other_fields = ('name', 'doc', 'file', 'path', 'package', - 'pure_python', 'future_imports') - _other_other_fields = ('locals', 'globals') - - def __init__(self, name, doc, file=None, path=None, package=None, - parent=None, pure_python=True): - self.name = name - self.doc = doc - self.file = file - self.path = path - self.package = package - self.parent = parent - self.pure_python = pure_python - self.locals = self.globals = {} - self.body = [] - self.future_imports = set() - # pylint: enable=redefined-builtin - - def postinit(self, body=None): - self.body = body - - def _get_stream(self): - if self.file_bytes is not None: - return io.BytesIO(self.file_bytes) - if self.file is not None: - stream = open(self.file, 'rb') - return stream - return None - - @property - def file_stream(self): - warnings.warn("file_stream property is deprecated and " - "it is slated for removal in astroid 1.6." - "Use the new method 'stream' instead.", - PendingDeprecationWarning, - stacklevel=2) - return self._get_stream() - - def stream(self): - """Get a stream to the underlying file or bytes.""" - return self._get_stream() - - def close(self): - """Close the underlying file streams.""" - warnings.warn("close method is deprecated and it is " - "slated for removal in astroid 1.6, along " - "with 'file_stream' property. " - "Its behaviour is replaced by managing each " - "file stream returned by the 'stream' method.", - PendingDeprecationWarning, - stacklevel=2) - - def block_range(self, lineno): - """return block line numbers. - - start from the beginning whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def scope_lookup(self, node, name, offset=0): - if name in self.scope_attrs and name not in self.locals: - try: - return self, self.getattr(name) - except exceptions.AttributeInferenceError: - return self, () - return self._scope_lookup(node, name, offset) - - def pytype(self): - return '%s.module' % BUILTINS - - def display_type(self): - return 'Module' - - def getattr(self, name, context=None, ignore_locals=False): - result = [] - name_in_locals = name in self.locals - - if name in self.special_attributes and not ignore_locals and not name_in_locals: - result = [self.special_attributes.lookup(name)] - elif not ignore_locals and name_in_locals: - result = self.locals[name] - elif self.package: - try: - result = [self.import_module(name, relative_only=True)] - except (exceptions.AstroidBuildingError, SyntaxError): - util.reraise(exceptions.AttributeInferenceError(target=self, - attribute=name, - context=context)) - result = [n for n in result if not isinstance(n, node_classes.DelName)] - if result: - return result - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - - def igetattr(self, name, context=None): - """inferred getattr""" - # set lookup name since this is necessary to infer on import nodes for - # instance - context = contextmod.copy_context(context) - context.lookupname = name - try: - return bases._infer_stmts(self.getattr(name, context), - context, frame=self) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) - - def fully_defined(self): - """return True if this module has been built from a .py file - and so contains a complete representation including the code - """ - return self.file is not None and self.file.endswith('.py') - - def statement(self): - """return the first parent node marked as statement node - consider a module as a statement... - """ - return self - - def previous_sibling(self): - """module has no sibling""" - return - - def next_sibling(self): - """module has no sibling""" - return - - if six.PY2: - @decorators_mod.cachedproperty - def _absolute_import_activated(self): - for stmt in self.locals.get('absolute_import', ()): - if isinstance(stmt, node_classes.ImportFrom) and stmt.modname == '__future__': - return True - return False - else: - _absolute_import_activated = True - - def absolute_import_activated(self): - return self._absolute_import_activated - - def import_module(self, modname, relative_only=False, level=None): - """import the given module considering self as context""" - if relative_only and level is None: - level = 0 - absmodname = self.relative_to_absolute_name(modname, level) - - try: - return MANAGER.ast_from_module_name(absmodname) - except exceptions.AstroidBuildingError: - # we only want to import a sub module or package of this module, - # skip here - if relative_only: - raise - return MANAGER.ast_from_module_name(modname) - - def relative_to_absolute_name(self, modname, level): - """return the absolute module name for a relative import. - - The relative import can be implicit or explicit. - """ - # XXX this returns non sens when called on an absolute import - # like 'pylint.checkers.astroid.utils' - # XXX doesn't return absolute name if self.name isn't absolute name - if self.absolute_import_activated() and level is None: - return modname - if level: - if self.package: - level = level - 1 - if level and self.name.count('.') < level: - raise exceptions.TooManyLevelsError( - level=level, name=self.name) - - package_name = self.name.rsplit('.', level)[0] - elif self.package: - package_name = self.name - else: - package_name = self.name.rsplit('.', 1)[0] - - if package_name: - if not modname: - return package_name - return '%s.%s' % (package_name, modname) - return modname - - def wildcard_import_names(self): - """return the list of imported names when this module is 'wildcard - imported' - - It doesn't include the '__builtins__' name which is added by the - current CPython implementation of wildcard imports. - """ - # We separate the different steps of lookup in try/excepts - # to avoid catching too many Exceptions - default = [name for name in self.keys() if not name.startswith('_')] - try: - all_values = self['__all__'] - except KeyError: - return default - - try: - explicit = next(all_values.assigned_stmts()) - except exceptions.InferenceError: - return default - except AttributeError: - # not an assignment node - # XXX infer? - return default - - # Try our best to detect the exported name. - inferred = [] - try: - explicit = next(explicit.infer()) - except exceptions.InferenceError: - return default - if not isinstance(explicit, (node_classes.Tuple, node_classes.List)): - return default - - str_const = lambda node: (isinstance(node, node_classes.Const) and - isinstance(node.value, six.string_types)) - for node in explicit.elts: - if str_const(node): - inferred.append(node.value) - else: - try: - inferred_node = next(node.infer()) - except exceptions.InferenceError: - continue - if str_const(inferred_node): - inferred.append(inferred_node.value) - return inferred - - def public_names(self): - """Get the list of the names which are publicly available in this module.""" - return [name for name in self.keys() if not name.startswith('_')] - - def bool_value(self): - return True - - -class ComprehensionScope(LocalsDictNodeNG): - def frame(self): - return self.parent.frame() - - scope_lookup = LocalsDictNodeNG._scope_lookup - - -class GeneratorExp(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - _other_other_fields = ('locals',) - elt = None - generators = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(GeneratorExp, self).__init__(lineno, col_offset, parent) - - def postinit(self, elt=None, generators=None): - self.elt = elt - if generators is None: - self.generators = [] - else: - self.generators = generators - - def bool_value(self): - return True - - -class DictComp(ComprehensionScope): - _astroid_fields = ('key', 'value', 'generators') - _other_other_fields = ('locals',) - key = None - value = None - generators = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(DictComp, self).__init__(lineno, col_offset, parent) - - def postinit(self, key=None, value=None, generators=None): - self.key = key - self.value = value - if generators is None: - self.generators = [] - else: - self.generators = generators - - def bool_value(self): - return util.Uninferable - - -class SetComp(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - _other_other_fields = ('locals',) - elt = None - generators = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(SetComp, self).__init__(lineno, col_offset, parent) - - def postinit(self, elt=None, generators=None): - self.elt = elt - if generators is None: - self.generators = [] - else: - self.generators = generators - - def bool_value(self): - return util.Uninferable - - -class _ListComp(node_classes.NodeNG): - """class representing a ListComp node""" - _astroid_fields = ('elt', 'generators') - elt = None - generators = None - - def postinit(self, elt=None, generators=None): - self.elt = elt - self.generators = generators - - def bool_value(self): - return util.Uninferable - - -if six.PY3: - class ListComp(_ListComp, ComprehensionScope): - """class representing a ListComp node""" - _other_other_fields = ('locals',) - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(ListComp, self).__init__(lineno, col_offset, parent) -else: - class ListComp(_ListComp): - """class representing a ListComp node""" - - -def _infer_decorator_callchain(node): - """Detect decorator call chaining and see if the end result is a - static or a classmethod. - """ - if not isinstance(node, FunctionDef): - return - if not node.parent: - return - try: - # TODO: We don't handle multiple inference results right now, - # because there's no flow to reason when the return - # is what we are looking for, a static or a class method. - result = next(node.infer_call_result(node.parent)) - except (StopIteration, exceptions.InferenceError): - return - if isinstance(result, bases.Instance): - result = result._proxied - if isinstance(result, ClassDef): - if result.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - if result.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - - -class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG): - _astroid_fields = ('args', 'body',) - _other_other_fields = ('locals',) - name = '' - - # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' - @property - def type(self): - # pylint: disable=no-member - if self.args.args and self.args.args[0].name == 'self': - if isinstance(self.parent.scope(), ClassDef): - return 'method' - return 'function' - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - self.args = [] - self.body = [] - super(Lambda, self).__init__(lineno, col_offset, parent) - - def postinit(self, args, body): - self.args = args - self.body = body - - def pytype(self): - if 'method' in self.type: - return '%s.instancemethod' % BUILTINS - return '%s.function' % BUILTINS - - def display_type(self): - if 'method' in self.type: - return 'Method' - return 'Function' - - def callable(self): - return True - - def argnames(self): - """return a list of argument names""" - # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 - # args is in fact redefined later on by postinit. Can't be changed - # to None due to a strong interaction between Lambda and FunctionDef. - - if self.args.args: # maybe None with builtin functions - names = _rec_get_names(self.args.args) - else: - names = [] - if self.args.vararg: - names.append(self.args.vararg) - if self.args.kwarg: - names.append(self.args.kwarg) - return names - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 - # args is in fact redefined later on by postinit. Can't be changed - # to None due to a strong interaction between Lambda and FunctionDef. - - return self.body.infer(context) - - def scope_lookup(self, node, name, offset=0): - # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 - # args is in fact redefined later on by postinit. Can't be changed - # to None due to a strong interaction between Lambda and FunctionDef. - - if node in self.args.defaults or node in self.args.kw_defaults: - frame = self.parent.frame() - # line offset to avoid that def func(f=func) resolve the default - # value to the defined function - offset = -1 - else: - # check this is not used in function decorators - frame = self - return frame._scope_lookup(node, name, offset) - - def bool_value(self): - return True - - -class FunctionDef(node_classes.Statement, Lambda): - if six.PY3: - _astroid_fields = ('decorators', 'args', 'returns', 'body') - returns = None - else: - _astroid_fields = ('decorators', 'args', 'body') - decorators = None - special_attributes = objectmodel.FunctionModel() - is_function = True - # attributes below are set by the builder module or by raw factories - _other_fields = ('name', 'doc') - _other_other_fields = ('locals', '_type') - _type = None - - def __init__(self, name=None, doc=None, lineno=None, - col_offset=None, parent=None): - self.name = name - self.doc = doc - self.instance_attrs = {} - super(FunctionDef, self).__init__(lineno, col_offset, parent) - if parent: - frame = parent.frame() - frame.set_local(name, self) - - # pylint: disable=arguments-differ; different than Lambdas - def postinit(self, args, body, decorators=None, returns=None): - self.args = args - self.body = body - self.decorators = decorators - self.returns = returns - - @decorators_mod.cachedproperty - def extra_decorators(self): - """Get the extra decorators that this function can haves - Additional decorators are considered when they are used as - assignments, as in `method = staticmethod(method)`. - The property will return all the callables that are used for - decoration. - """ - frame = self.parent.frame() - if not isinstance(frame, ClassDef): - return [] - - decorators = [] - for assign in frame.nodes_of_class(node_classes.Assign): - if (isinstance(assign.value, node_classes.Call) - and isinstance(assign.value.func, node_classes.Name)): - for assign_node in assign.targets: - if not isinstance(assign_node, node_classes.AssignName): - # Support only `name = callable(name)` - continue - - if assign_node.name != self.name: - # Interested only in the assignment nodes that - # decorates the current method. - continue - try: - meth = frame[self.name] - except KeyError: - continue - else: - # Must be a function and in the same frame as the - # original method. - if (isinstance(meth, FunctionDef) - and assign_node.frame() == frame): - decorators.append(assign.value) - return decorators - - @decorators_mod.cachedproperty - def type(self): - """Get the function type for this node. - - Possible values are: method, function, staticmethod, classmethod. - """ - builtin_descriptors = {'classmethod', 'staticmethod'} - - for decorator in self.extra_decorators: - if decorator.func.name in builtin_descriptors: - return decorator.func.name - - frame = self.parent.frame() - type_name = 'function' - if isinstance(frame, ClassDef): - if self.name == '__new__': - return 'classmethod' - elif sys.version_info >= (3, 6) and self.name == '__init_subclass__': - return 'classmethod' - else: - type_name = 'method' - - if not self.decorators: - return type_name - - for node in self.decorators.nodes: - if isinstance(node, node_classes.Name): - if node.name in builtin_descriptors: - return node.name - - if isinstance(node, node_classes.Call): - # Handle the following case: - # @some_decorator(arg1, arg2) - # def func(...) - # - try: - current = next(node.func.infer()) - except exceptions.InferenceError: - continue - _type = _infer_decorator_callchain(current) - if _type is not None: - return _type - - try: - for inferred in node.infer(): - # Check to see if this returns a static or a class method. - _type = _infer_decorator_callchain(inferred) - if _type is not None: - return _type - - if not isinstance(inferred, ClassDef): - continue - for ancestor in inferred.ancestors(): - if not isinstance(ancestor, ClassDef): - continue - if ancestor.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - except exceptions.InferenceError: - pass - return type_name - - @decorators_mod.cachedproperty - def fromlineno(self): - # lineno is the line number of the first decorator, we want the def - # statement lineno - lineno = self.lineno - if self.decorators is not None: - lineno += sum(node.tolineno - node.lineno + 1 - for node in self.decorators.nodes) - - return lineno - - @decorators_mod.cachedproperty - def blockstart_tolineno(self): - return self.args.tolineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "def" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - """ - if name in self.instance_attrs: - return self.instance_attrs[name] - if name in self.special_attributes: - return [self.special_attributes.lookup(name)] - raise exceptions.AttributeInferenceError(target=self, attribute=name) - - def igetattr(self, name, context=None): - """Inferred getattr, which returns an iterator of inferred statements.""" - try: - return bases._infer_stmts(self.getattr(name, context), - context, frame=self) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) - - def is_method(self): - """return true if the function node should be considered as a method""" - # check we are defined in a ClassDef, because this is usually expected - # (e.g. pylint...) when is_method() return True - return self.type != 'function' and isinstance(self.parent.frame(), ClassDef) - - @decorators_mod.cached - def decoratornames(self): - """return a list of decorator qualified names""" - result = set() - decoratornodes = [] - if self.decorators is not None: - decoratornodes += self.decorators.nodes - decoratornodes += self.extra_decorators - for decnode in decoratornodes: - try: - for infnode in decnode.infer(): - result.add(infnode.qname()) - except exceptions.InferenceError: - continue - return result - - def is_bound(self): - """return true if the function is bound to an Instance or a class""" - return self.type == 'classmethod' - - def is_abstract(self, pass_is_abstract=True): - """Returns True if the method is abstract. - - A method is considered abstract if - - the only statement is 'raise NotImplementedError', or - - the only statement is 'pass' and pass_is_abstract is True, or - - the method is annotated with abc.astractproperty/abc.abstractmethod - """ - if self.decorators: - for node in self.decorators.nodes: - try: - inferred = next(node.infer()) - except exceptions.InferenceError: - continue - if inferred and inferred.qname() in ('abc.abstractproperty', - 'abc.abstractmethod'): - return True - - for child_node in self.body: - if isinstance(child_node, node_classes.Raise): - if child_node.raises_not_implemented(): - return True - return pass_is_abstract and isinstance(child_node, node_classes.Pass) - # empty function is the same as function with a single "pass" statement - if pass_is_abstract: - return True - - def is_generator(self): - """return true if this is a generator function""" - yield_nodes = (node_classes.Yield, node_classes.YieldFrom) - return next(self.nodes_of_class(yield_nodes, - skip_klass=(FunctionDef, Lambda)), False) - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - if self.is_generator(): - result = bases.Generator(self) - yield result - return - # This is really a gigantic hack to work around metaclass generators - # that return transient class-generating functions. Pylint's AST structure - # cannot handle a base class object that is only used for calling __new__, - # but does not contribute to the inheritance structure itself. We inject - # a fake class into the hierarchy here for several well-known metaclass - # generators, and filter it out later. - if (self.name == 'with_metaclass' and - len(self.args.args) == 1 and - self.args.vararg is not None): - metaclass = next(caller.args[0].infer(context)) - if isinstance(metaclass, ClassDef): - c = ClassDef('temporary_class', None) - c.hide = True - c.parent = self - class_bases = [next(b.infer(context)) for b in caller.args[1:]] - c.bases = [base for base in class_bases if base != util.Uninferable] - c._metaclass = metaclass - yield c - return - returns = self.nodes_of_class(node_classes.Return, skip_klass=FunctionDef) - for returnnode in returns: - if returnnode.value is None: - yield node_classes.Const(None) - else: - try: - for inferred in returnnode.value.infer(context): - yield inferred - except exceptions.InferenceError: - yield util.Uninferable - - def bool_value(self): - return True - - -class AsyncFunctionDef(FunctionDef): - """Asynchronous function created with the `async` keyword.""" - - -def _rec_get_names(args, names=None): - """return a list of all argument names""" - if names is None: - names = [] - for arg in args: - if isinstance(arg, node_classes.Tuple): - _rec_get_names(arg.elts, names) - else: - names.append(arg.name) - return names - - -def _is_metaclass(klass, seen=None): - """ Return if the given class can be - used as a metaclass. - """ - if klass.name == 'type': - return True - if seen is None: - seen = set() - for base in klass.bases: - try: - for baseobj in base.infer(): - baseobj_name = baseobj.qname() - if baseobj_name in seen: - continue - else: - seen.add(baseobj_name) - if isinstance(baseobj, bases.Instance): - # not abstract - return False - if baseobj is util.Uninferable: - continue - if baseobj is klass: - continue - if not isinstance(baseobj, ClassDef): - continue - if baseobj._type == 'metaclass': - return True - if _is_metaclass(baseobj, seen): - return True - except exceptions.InferenceError: - continue - return False - - -def _class_type(klass, ancestors=None): - """return a ClassDef node type to differ metaclass and exception - from 'regular' classes - """ - # XXX we have to store ancestors in case we have a ancestor loop - if klass._type is not None: - return klass._type - if _is_metaclass(klass): - klass._type = 'metaclass' - elif klass.name.endswith('Exception'): - klass._type = 'exception' - else: - if ancestors is None: - ancestors = set() - klass_name = klass.qname() - if klass_name in ancestors: - # XXX we are in loop ancestors, and have found no type - klass._type = 'class' - return 'class' - ancestors.add(klass_name) - for base in klass.ancestors(recurs=False): - name = _class_type(base, ancestors) - if name != 'class': - if name == 'metaclass' and not _is_metaclass(klass): - # don't propagate it if the current class - # can't be a metaclass - continue - klass._type = base.type - break - if klass._type is None: - klass._type = 'class' - return klass._type - - -def get_wrapping_class(node): - """Obtain the class that *wraps* this node - - We consider that a class wraps a node if the class - is a parent for the said node. - """ - - klass = node.frame() - while klass is not None and not isinstance(klass, ClassDef): - if klass.parent is None: - klass = None - else: - klass = klass.parent.frame() - return klass - - - -class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, - node_classes.Statement): - - # some of the attributes below are set by the builder module or - # by a raw factories - - # a dictionary of class instances attributes - _astroid_fields = ('decorators', 'bases', 'body') # name - - decorators = None - special_attributes = objectmodel.ClassModel() - - _type = None - _metaclass_hack = False - hide = False - type = property(_class_type, - doc="class'type, possible values are 'class' | " - "'metaclass' | 'exception'") - _other_fields = ('name', 'doc') - _other_other_fields = ('locals', '_newstyle') - _newstyle = None - - def __init__(self, name=None, doc=None, lineno=None, - col_offset=None, parent=None): - self.instance_attrs = {} - self.locals = {} - self.keywords = [] - self.bases = [] - self.body = [] - self.name = name - self.doc = doc - super(ClassDef, self).__init__(lineno, col_offset, parent) - if parent is not None: - parent.frame().set_local(name, self) - - # pylint: disable=redefined-outer-name - def postinit(self, bases, body, decorators, newstyle=None, metaclass=None, keywords=None): - self.keywords = keywords - self.bases = bases - self.body = body - self.decorators = decorators - if newstyle is not None: - self._newstyle = newstyle - if metaclass is not None: - self._metaclass = metaclass - - def _newstyle_impl(self, context=None): - if context is None: - context = contextmod.InferenceContext() - if self._newstyle is not None: - return self._newstyle - for base in self.ancestors(recurs=False, context=context): - if base._newstyle_impl(context): - self._newstyle = True - break - klass = self.declared_metaclass() - # could be any callable, we'd need to infer the result of klass(name, - # bases, dict). punt if it's not a class node. - if klass is not None and isinstance(klass, ClassDef): - self._newstyle = klass._newstyle_impl(context) - if self._newstyle is None: - self._newstyle = False - return self._newstyle - - _newstyle = None - newstyle = property(_newstyle_impl, - doc="boolean indicating if it's a new style class" - "or not") - - @decorators_mod.cachedproperty - def blockstart_tolineno(self): - if self.bases: - return self.bases[-1].tolineno - - return self.fromlineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "class" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def pytype(self): - if self.newstyle: - return '%s.type' % BUILTINS - return '%s.classobj' % BUILTINS - - def display_type(self): - return 'Class' - - def callable(self): - return True - - def is_subtype_of(self, type_name, context=None): - if self.qname() == type_name: - return True - for anc in self.ancestors(context=context): - if anc.qname() == type_name: - return True - - def _infer_type_call(self, caller, context): - name_node = next(caller.args[0].infer(context)) - if (isinstance(name_node, node_classes.Const) and - isinstance(name_node.value, six.string_types)): - name = name_node.value - else: - return util.Uninferable - - result = ClassDef(name, None) - - # Get the bases of the class. - class_bases = next(caller.args[1].infer(context)) - if isinstance(class_bases, (node_classes.Tuple, node_classes.List)): - result.bases = class_bases.itered() - else: - # There is currently no AST node that can represent an 'unknown' - # node (Uninferable is not an AST node), therefore we simply return Uninferable here - # although we know at least the name of the class. - return util.Uninferable - - # Get the members of the class - try: - members = next(caller.args[2].infer(context)) - except exceptions.InferenceError: - members = None - - if members and isinstance(members, node_classes.Dict): - for attr, value in members.items: - if (isinstance(attr, node_classes.Const) and - isinstance(attr.value, six.string_types)): - result.locals[attr.value] = [value] - - result.parent = caller.parent - return result - - def infer_call_result(self, caller, context=None): - """infer what a class is returning when called""" - if (self.is_subtype_of('%s.type' % (BUILTINS,), context) - and len(caller.args) == 3): - result = self._infer_type_call(caller, context) - yield result - else: - yield bases.Instance(self) - - def scope_lookup(self, node, name, offset=0): - # If the name looks like a builtin name, just try to look - # into the upper scope of this class. We might have a - # decorator that it's poorly named after a builtin object - # inside this class. - lookup_upper_frame = ( - isinstance(node.parent, node_classes.Decorators) and - name in MANAGER.astroid_cache[six.moves.builtins.__name__] - ) - if any(node == base or base.parent_of(node) - for base in self.bases) or lookup_upper_frame: - # Handle the case where we have either a name - # in the bases of a class, which exists before - # the actual definition or the case where we have - # a Getattr node, with that name. - # - # name = ... - # class A(name): - # def name(self): ... - # - # import name - # class A(name.Name): - # def name(self): ... - - frame = self.parent.frame() - # line offset to avoid that class A(A) resolve the ancestor to - # the defined class - offset = -1 - else: - frame = self - return frame._scope_lookup(node, name, offset) - - @property - def basenames(self): - """Get the list of parent class names, as they appear in the class definition.""" - return [bnode.as_string() for bnode in self.bases] - - def ancestors(self, recurs=True, context=None): - """return an iterator on the node base classes in a prefixed - depth first order - - :param recurs: - boolean indicating if it should recurse or return direct - ancestors only - """ - # FIXME: should be possible to choose the resolution order - # FIXME: inference make infinite loops possible here - yielded = set([self]) - if context is None: - context = contextmod.InferenceContext() - if six.PY3: - if not self.bases and self.qname() != 'builtins.object': - yield builtin_lookup("object")[1][0] - return - - for stmt in self.bases: - with context.restore_path(): - try: - for baseobj in stmt.infer(context): - if not isinstance(baseobj, ClassDef): - if isinstance(baseobj, bases.Instance): - baseobj = baseobj._proxied - else: - continue - if not baseobj.hide: - if baseobj in yielded: - continue - yielded.add(baseobj) - yield baseobj - if recurs: - for grandpa in baseobj.ancestors(recurs=True, - context=context): - if grandpa is self: - # This class is the ancestor of itself. - break - if grandpa in yielded: - continue - yielded.add(grandpa) - yield grandpa - except exceptions.InferenceError: - continue - - def local_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their locals - """ - if self.newstyle and all(n.newstyle for n in self.ancestors(context)): - # Look up in the mro if we can. This will result in the - # attribute being looked up just as Python does it. - try: - ancestors = self.mro(context)[1:] - except exceptions.MroError: - # Fallback to use ancestors, we can't determine - # a sane MRO. - ancestors = self.ancestors(context=context) - else: - ancestors = self.ancestors(context=context) - for astroid in ancestors: - if name in astroid: - yield astroid - - def instance_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their instance attribute dictionary - """ - for astroid in self.ancestors(context=context): - if name in astroid.instance_attrs: - yield astroid - - def has_base(self, node): - return node in self.bases - - def local_attr(self, name, context=None): - """return the list of assign node associated to name in this class - locals or in its parents - - :raises `AttributeInferenceError`: - if no attribute with this name has been find in this class or - its parent classes - """ - result = [] - if name in self.locals: - result = self.locals[name] - else: - class_node = next(self.local_attr_ancestors(name, context), ()) - if class_node: - result = class_node.locals[name] - result = [n for n in result if not isinstance(n, node_classes.DelAttr)] - if result: - return result - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - - def instance_attr(self, name, context=None): - """return the astroid nodes associated to name in this class instance - attributes dictionary and in its parents - - :raises `AttributeInferenceError`: - if no attribute with this name has been find in this class or - its parent classes - """ - # Return a copy, so we don't modify self.instance_attrs, - # which could lead to infinite loop. - values = list(self.instance_attrs.get(name, [])) - # get all values from parents - for class_node in self.instance_attr_ancestors(name, context): - values += class_node.instance_attrs[name] - values = [n for n in values if not isinstance(n, node_classes.DelAttr)] - if values: - return values - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - - def instantiate_class(self): - """return Instance of ClassDef node, else return self""" - return bases.Instance(self) - - def instanciate_class(self): - warnings.warn('%s.instanciate_class() is deprecated and slated for ' - 'removal in astroid 2.0, use %s.instantiate_class() ' - 'instead.' % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.instantiate_class() - - def getattr(self, name, context=None, class_context=True): - """Get an attribute from this class, using Python's attribute semantic - - This method doesn't look in the instance_attrs dictionary - since it's done by an Instance proxy at inference time. It - may return a Uninferable object if the attribute has not been actually - found but a __getattr__ or __getattribute__ method is defined. - If *class_context* is given, then it's considered that the - attribute is accessed from a class context, - e.g. ClassDef.attribute, otherwise it might have been accessed - from an instance as well. If *class_context* is used in that - case, then a lookup in the implicit metaclass and the explicit - metaclass will be done. - - """ - values = self.locals.get(name, []) - if name in self.special_attributes and class_context and not values: - result = [self.special_attributes.lookup(name)] - if name == '__bases__': - # Need special treatment, since they are mutable - # and we need to return all the values. - result += values - return result - - # don't modify the list in self.locals! - values = list(values) - for classnode in self.ancestors(recurs=True, context=context): - values += classnode.locals.get(name, []) - - if class_context: - values += self._metaclass_lookup_attribute(name, context) - - if not values: - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - return values - - def _metaclass_lookup_attribute(self, name, context): - """Search the given name in the implicit and the explicit metaclass.""" - attrs = set() - implicit_meta = self.implicit_metaclass() - metaclass = self.metaclass() - for cls in {implicit_meta, metaclass}: - if cls and cls != self and isinstance(cls, ClassDef): - cls_attributes = self._get_attribute_from_metaclass( - cls, name, context) - attrs.update(set(cls_attributes)) - return attrs - - def _get_attribute_from_metaclass(self, cls, name, context): - try: - attrs = cls.getattr(name, context=context, - class_context=True) - except exceptions.AttributeInferenceError: - return - - for attr in bases._infer_stmts(attrs, context, frame=cls): - if not isinstance(attr, FunctionDef): - yield attr - continue - - if bases._is_property(attr): - # TODO(cpopa): don't use a private API. - for inferred in attr.infer_call_result(self, context): - yield inferred - continue - if attr.type == 'classmethod': - # If the method is a classmethod, then it will - # be bound to the metaclass, not to the class - # from where the attribute is retrieved. - # get_wrapping_class could return None, so just - # default to the current class. - frame = get_wrapping_class(attr) or self - yield bases.BoundMethod(attr, frame) - elif attr.type == 'staticmethod': - yield attr - else: - yield bases.BoundMethod(attr, self) - - def igetattr(self, name, context=None, class_context=True): - """inferred getattr, need special treatment in class to handle - descriptors - """ - # set lookup name since this is necessary to infer on import nodes for - # instance - context = contextmod.copy_context(context) - context.lookupname = name - try: - attrs = self.getattr(name, context, class_context=class_context) - for inferred in bases._infer_stmts(attrs, context, frame=self): - # yield Uninferable object instead of descriptors when necessary - if (not isinstance(inferred, node_classes.Const) - and isinstance(inferred, bases.Instance)): - try: - inferred._proxied.getattr('__get__', context) - except exceptions.AttributeInferenceError: - yield inferred - else: - yield util.Uninferable - else: - yield function_to_method(inferred, self) - except exceptions.AttributeInferenceError as error: - if not name.startswith('__') and self.has_dynamic_getattr(context): - # class handle some dynamic attributes, return a Uninferable object - yield util.Uninferable - else: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) - - def has_dynamic_getattr(self, context=None): - """ - Check if the current instance has a custom __getattr__ - or a custom __getattribute__. - - If any such method is found and it is not from - builtins, nor from an extension module, then the function - will return True. - """ - def _valid_getattr(node): - root = node.root() - return root.name != BUILTINS and getattr(root, 'pure_python', None) - - try: - return _valid_getattr(self.getattr('__getattr__', context)[0]) - except exceptions.AttributeInferenceError: - #if self.newstyle: XXX cause an infinite recursion error - try: - getattribute = self.getattr('__getattribute__', context)[0] - return _valid_getattr(getattribute) - except exceptions.AttributeInferenceError: - pass - return False - - def getitem(self, index, context=None): - """Return the inference of a subscript. - - This is basically looking up the method in the metaclass and calling it. - """ - try: - methods = dunder_lookup.lookup(self, '__getitem__') - except exceptions.AttributeInferenceError as exc: - util.reraise( - exceptions.AstroidTypeError( - node=self, error=exc, - context=context - ) - ) - - method = methods[0] - - # Create a new callcontext for providing index as an argument. - if context: - new_context = context.clone() - else: - new_context = contextmod.InferenceContext() - - new_context.callcontext = contextmod.CallContext(args=[index]) - new_context.boundnode = self - - return next(method.infer_call_result(self, new_context)) - - def methods(self): - """return an iterator on all methods defined in the class and - its ancestors - """ - done = {} - for astroid in itertools.chain(iter((self,)), self.ancestors()): - for meth in astroid.mymethods(): - if meth.name in done: - continue - done[meth.name] = None - yield meth - - def mymethods(self): - """return an iterator on all methods defined in the class""" - for member in self.values(): - if isinstance(member, FunctionDef): - yield member - - def implicit_metaclass(self): - """Get the implicit metaclass of the current class - - For newstyle classes, this will return an instance of builtins.type. - For oldstyle classes, it will simply return None, since there's - no implicit metaclass there. - """ - - if self.newstyle: - return builtin_lookup('type')[1][0] - - _metaclass = None - def declared_metaclass(self): - """Return the explicit declared metaclass for the current class. - - An explicit declared metaclass is defined - either by passing the ``metaclass`` keyword argument - in the class definition line (Python 3) or (Python 2) by - having a ``__metaclass__`` class attribute, or if there are - no explicit bases but there is a global ``__metaclass__`` variable. - """ - for base in self.bases: - try: - for baseobj in base.infer(): - if isinstance(baseobj, ClassDef) and baseobj.hide: - self._metaclass = baseobj._metaclass - self._metaclass_hack = True - break - except exceptions.InferenceError: - pass - - if self._metaclass: - # Expects this from Py3k TreeRebuilder - try: - return next(node for node in self._metaclass.infer() - if node is not util.Uninferable) - except (exceptions.InferenceError, StopIteration): - return None - if six.PY3: - return None - - if '__metaclass__' in self.locals: - assignment = self.locals['__metaclass__'][-1] - elif self.bases: - return None - elif '__metaclass__' in self.root().locals: - assignments = [ass for ass in self.root().locals['__metaclass__'] - if ass.lineno < self.lineno] - if not assignments: - return None - assignment = assignments[-1] - else: - return None - - try: - inferred = next(assignment.infer()) - except exceptions.InferenceError: - return - if inferred is util.Uninferable: # don't expose this - return None - return inferred - - def _find_metaclass(self, seen=None): - if seen is None: - seen = set() - seen.add(self) - - klass = self.declared_metaclass() - if klass is None: - for parent in self.ancestors(): - if parent not in seen: - klass = parent._find_metaclass(seen) - if klass is not None: - break - return klass - - def metaclass(self): - """Return the metaclass of this class. - - If this class does not define explicitly a metaclass, - then the first defined metaclass in ancestors will be used - instead. - """ - return self._find_metaclass() - - def has_metaclass_hack(self): - return self._metaclass_hack - - def _islots(self): - """ Return an iterator with the inferred slots. """ - if '__slots__' not in self.locals: - return - for slots in self.igetattr('__slots__'): - # check if __slots__ is a valid type - for meth in ITER_METHODS: - try: - slots.getattr(meth) - break - except exceptions.AttributeInferenceError: - continue - else: - continue - - if isinstance(slots, node_classes.Const): - # a string. Ignore the following checks, - # but yield the node, only if it has a value - if slots.value: - yield slots - continue - if not hasattr(slots, 'itered'): - # we can't obtain the values, maybe a .deque? - continue - - if isinstance(slots, node_classes.Dict): - values = [item[0] for item in slots.items] - else: - values = slots.itered() - if values is util.Uninferable: - continue - if not values: - # Stop the iteration, because the class - # has an empty list of slots. - raise StopIteration(values) - - for elt in values: - try: - for inferred in elt.infer(): - if inferred is util.Uninferable: - continue - if (not isinstance(inferred, node_classes.Const) or - not isinstance(inferred.value, - six.string_types)): - continue - if not inferred.value: - continue - yield inferred - except exceptions.InferenceError: - continue - - def _slots(self): - if not self.newstyle: - raise NotImplementedError( - "The concept of slots is undefined for old-style classes.") - - slots = self._islots() - try: - first = next(slots) - except StopIteration as exc: - # The class doesn't have a __slots__ definition or empty slots. - if exc.args and exc.args[0] not in ('', None): - return exc.args[0] - return None - return [first] + list(slots) - - # Cached, because inferring them all the time is expensive - @decorators_mod.cached - def slots(self): - """Get all the slots for this node. - - If the class doesn't define any slot, through `__slots__` - variable, then this function will return a None. - Also, it will return None in the case the slots weren't inferred. - Otherwise, it will return a list of slot names. - """ - def grouped_slots(): - # Not interested in object, since it can't have slots. - for cls in self.mro()[:-1]: - try: - cls_slots = cls._slots() - except NotImplementedError: - continue - if cls_slots is not None: - for slot in cls_slots: - yield slot - else: - yield None - - if not self.newstyle: - raise NotImplementedError( - "The concept of slots is undefined for old-style classes.") - - slots = list(grouped_slots()) - if not all(slot is not None for slot in slots): - return None - - return sorted(slots, key=lambda item: item.value) - - def _inferred_bases(self, context=None): - # TODO(cpopa): really similar with .ancestors, - # but the difference is when one base is inferred, - # only the first object is wanted. That's because - # we aren't interested in superclasses, as in the following - # example: - # - # class SomeSuperClass(object): pass - # class SomeClass(SomeSuperClass): pass - # class Test(SomeClass): pass - # - # Inferring SomeClass from the Test's bases will give - # us both SomeClass and SomeSuperClass, but we are interested - # only in SomeClass. - - if context is None: - context = contextmod.InferenceContext() - if six.PY3: - if not self.bases and self.qname() != 'builtins.object': - yield builtin_lookup("object")[1][0] - return - - for stmt in self.bases: - try: - baseobj = next(stmt.infer(context=context)) - except exceptions.InferenceError: - continue - if isinstance(baseobj, bases.Instance): - baseobj = baseobj._proxied - if not isinstance(baseobj, ClassDef): - continue - if not baseobj.hide: - yield baseobj - else: - for base in baseobj.bases: - yield base - - def _compute_mro(self, context=None): - inferred_bases = list(self._inferred_bases(context=context)) - bases_mro = [] - for base in inferred_bases: - if base is self: - continue - - try: - mro = base._compute_mro(context=context) - bases_mro.append(mro) - except NotImplementedError: - # Some classes have in their ancestors both newstyle and - # old style classes. For these we can't retrieve the .mro, - # although in Python it's possible, since the class we are - # currently working is in fact new style. - # So, we fallback to ancestors here. - ancestors = list(base.ancestors(context=context)) - bases_mro.append(ancestors) - - unmerged_mro = ([[self]] + bases_mro + [inferred_bases]) - _verify_duplicates_mro(unmerged_mro, self, context) - return _c3_merge(unmerged_mro, self, context) - - def mro(self, context=None): - """Get the method resolution order, using C3 linearization. - - It returns the list of ancestors sorted by the mro. - This will raise `NotImplementedError` for old-style classes, since - they don't have the concept of MRO. - """ - - if not self.newstyle: - raise NotImplementedError( - "Could not obtain mro for old-style classes.") - - return self._compute_mro(context=context) - - def bool_value(self): - return True - - -# Backwards-compatibility aliases -Class = util.proxy_alias('Class', ClassDef) -Function = util.proxy_alias('Function', FunctionDef) -GenExpr = util.proxy_alias('GenExpr', GeneratorExp) diff --git a/pymode/libs/astroid/test_utils.py b/pymode/libs/astroid/test_utils.py deleted file mode 100644 index e7e64b17..00000000 --- a/pymode/libs/astroid/test_utils.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson -# 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 - -"""Utility functions for test code that uses astroid ASTs as input.""" -import contextlib -import functools -import sys -import warnings - -from astroid import nodes -from astroid import util - - -def require_version(minver=None, maxver=None): - """ Compare version of python interpreter to the given one. Skip the test - if older. - """ - def parse(string, default=None): - string = string or default - try: - return tuple(int(v) for v in string.split('.')) - except ValueError: - util.reraise(ValueError('%s is not a correct version : should be X.Y[.Z].' % string)) - - def check_require_version(f): - current = sys.version_info[:3] - if parse(minver, "0") < current <= parse(maxver, "4"): - return f - - str_version = '.'.join(str(v) for v in sys.version_info) - @functools.wraps(f) - def new_f(self, *args, **kwargs): - if minver is not None: - self.skipTest('Needs Python > %s. Current version is %s.' - % (minver, str_version)) - elif maxver is not None: - self.skipTest('Needs Python <= %s. Current version is %s.' - % (maxver, str_version)) - return new_f - - - return check_require_version - -def get_name_node(start_from, name, index=0): - return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index] - - -@contextlib.contextmanager -def enable_warning(warning): - warnings.simplefilter('always', warning) - try: - yield - finally: - # Reset it to default value, so it will take - # into account the values from the -W flag. - warnings.simplefilter('default', warning) - \ No newline at end of file diff --git a/pymode/libs/astroid/transforms.py b/pymode/libs/astroid/transforms.py deleted file mode 100644 index 852b9854..00000000 --- a/pymode/libs/astroid/transforms.py +++ /dev/null @@ -1,84 +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 - - -import collections -import warnings - - -class TransformVisitor(object): - """A visitor for handling transforms. - - The standard approach of using it is to call - :meth:`~visit` with an *astroid* module and the class - will take care of the rest, walking the tree and running the - transforms for each encountered node. - """ - - def __init__(self): - self.transforms = collections.defaultdict(list) - - def _transform(self, node): - """Call matching transforms for the given node if any and return the - transformed node. - """ - cls = node.__class__ - if cls not in self.transforms: - # no transform registered for this class of node - return node - - transforms = self.transforms[cls] - orig_node = node # copy the reference - for transform_func, predicate in transforms: - if predicate is None or predicate(node): - ret = transform_func(node) - # if the transformation function returns something, it's - # expected to be a replacement for the node - if ret is not None: - if node is not orig_node: - # node has already be modified by some previous - # transformation, warn about it - warnings.warn('node %s substituted multiple times' % node) - node = ret - return node - - def _visit(self, node): - if hasattr(node, '_astroid_fields'): - for field in node._astroid_fields: - value = getattr(node, field) - visited = self._visit_generic(value) - setattr(node, field, visited) - return self._transform(node) - - def _visit_generic(self, node): - if isinstance(node, list): - return [self._visit_generic(child) for child in node] - elif isinstance(node, tuple): - return tuple(self._visit_generic(child) for child in node) - - return self._visit(node) - - def register_transform(self, node_class, transform, predicate=None): - """Register `transform(node)` function to be applied on the given - astroid's `node_class` if `predicate` is None or returns true - when called with the node as argument. - - The transform function may return a value which is then used to - substitute the original node in the tree. - """ - self.transforms[node_class].append((transform, predicate)) - - def unregister_transform(self, node_class, transform, predicate=None): - """Unregister the given transform.""" - self.transforms[node_class].remove((transform, predicate)) - - def visit(self, module): - """Walk the given astroid *tree* and transform each encountered node - - Only the nodes which have transforms registered will actually - be replaced or changed. - """ - module.body = [self._visit(child) for child in module.body] - return self._transform(module) diff --git a/pymode/libs/astroid/util.py b/pymode/libs/astroid/util.py deleted file mode 100644 index 6d2d0177..00000000 --- a/pymode/libs/astroid/util.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# 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 - -import sys -import warnings - -import importlib -import lazy_object_proxy -import six - - -def lazy_descriptor(obj): - class DescriptorProxy(lazy_object_proxy.Proxy): - def __get__(self, instance, owner=None): - return self.__class__.__get__(self, instance) - return DescriptorProxy(obj) - - -def lazy_import(module_name): - return lazy_object_proxy.Proxy( - lambda: importlib.import_module('.' + module_name, 'astroid')) - - -def reraise(exception): - '''Reraises an exception with the traceback from the current exception - block.''' - six.reraise(type(exception), exception, sys.exc_info()[2]) - - -@object.__new__ -class Uninferable(object): - """Special inference object, which is returned when inference fails.""" - def __repr__(self): - return 'Uninferable' - __str__ = __repr__ - - def __getattribute__(self, name): - if name == 'next': - raise AttributeError('next method should not be called') - if name.startswith('__') and name.endswith('__'): - return object.__getattribute__(self, name) - if name == 'accept': - return object.__getattribute__(self, name) - return self - - def __call__(self, *args, **kwargs): - return self - - def __bool__(self): - return False - - __nonzero__ = __bool__ - - def accept(self, visitor): - func = getattr(visitor, "visit_uninferable") - return func(self) - -class BadOperationMessage(object): - """Object which describes a TypeError occurred somewhere in the inference chain - - This is not an exception, but a container object which holds the types and - the error which occurred. - """ - - -class BadUnaryOperationMessage(BadOperationMessage): - """Object which describes operational failures on UnaryOps.""" - - def __init__(self, operand, op, error): - self.operand = operand - self.op = op - self.error = error - - def __str__(self): - operand_type = self.operand.name - msg = "bad operand type for unary {}: {}" - return msg.format(self.op, operand_type) - - -class BadBinaryOperationMessage(BadOperationMessage): - """Object which describes type errors for BinOps.""" - - def __init__(self, left_type, op, right_type): - self.left_type = left_type - self.right_type = right_type - self.op = op - - def __str__(self): - msg = "unsupported operand type(s) for {}: {!r} and {!r}" - return msg.format(self.op, self.left_type.name, self.right_type.name) - - -def _instancecheck(cls, other): - wrapped = cls.__wrapped__ - other_cls = other.__class__ - is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped) - warnings.warn("%r is deprecated and slated for removal in astroid " - "2.0, use %r instead" % (cls.__class__.__name__, - wrapped.__name__), - PendingDeprecationWarning, stacklevel=2) - return is_instance_of - - -def proxy_alias(alias_name, node_type): - """Get a Proxy from the given name to the given node type.""" - proxy = type(alias_name, (lazy_object_proxy.Proxy,), - {'__class__': object.__dict__['__class__'], - '__instancecheck__': _instancecheck}) - return proxy(lambda: node_type) - - -# Backwards-compatibility aliases -YES = Uninferable diff --git a/pymode/libs/easy_install.py b/pymode/libs/easy_install.py deleted file mode 100644 index d87e9840..00000000 --- a/pymode/libs/easy_install.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Run the EasyInstall command""" - -if __name__ == '__main__': - from setuptools.command.easy_install import main - main() 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/__init__.py b/pymode/libs/logilab/common/__init__.py deleted file mode 100644 index fc01e4df..00000000 --- a/pymode/libs/logilab/common/__init__.py +++ /dev/null @@ -1,184 +0,0 @@ -# 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 . -"""Logilab common library (aka Logilab's extension to the standard library). - -:type STD_BLACKLIST: tuple -:var STD_BLACKLIST: directories ignored by default by the functions in - this package which have to recurse into directories - -:type IGNORED_EXTENSIONS: tuple -:var IGNORED_EXTENSIONS: file extensions that may usually be ignored -""" -__docformat__ = "restructuredtext en" - -import sys -import types -import pkg_resources - -__version__ = pkg_resources.get_distribution('logilab-common').version - -# deprecated, but keep compatibility with pylint < 1.4.4 -__pkginfo__ = types.ModuleType('__pkginfo__') -__pkginfo__.__package__ = __name__ -__pkginfo__.version = __version__ -sys.modules['logilab.common.__pkginfo__'] = __pkginfo__ - -STD_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build') - -IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc', '~', '.swp', '.orig') - -# set this to False if you've mx DateTime installed but you don't want your db -# adapter to use it (should be set before you got a connection) -USE_MX_DATETIME = True - - -class attrdict(dict): - """A dictionary for which keys are also accessible as attributes.""" - def __getattr__(self, attr): - try: - return self[attr] - except KeyError: - raise AttributeError(attr) - -class dictattr(dict): - def __init__(self, proxy): - self.__proxy = proxy - - def __getitem__(self, attr): - try: - return getattr(self.__proxy, attr) - except AttributeError: - raise KeyError(attr) - -class nullobject(object): - def __repr__(self): - return '' - def __bool__(self): - return False - __nonzero__ = __bool__ - -class tempattr(object): - def __init__(self, obj, attr, value): - self.obj = obj - self.attr = attr - self.value = value - - def __enter__(self): - self.oldvalue = getattr(self.obj, self.attr) - setattr(self.obj, self.attr, self.value) - return self.obj - - def __exit__(self, exctype, value, traceback): - setattr(self.obj, self.attr, self.oldvalue) - - - -# flatten ----- -# XXX move in a specific module and use yield instead -# do not mix flatten and translate -# -# def iterable(obj): -# try: iter(obj) -# except: return False -# return True -# -# def is_string_like(obj): -# try: obj +'' -# except (TypeError, ValueError): return False -# return True -# -#def is_scalar(obj): -# return is_string_like(obj) or not iterable(obj) -# -#def flatten(seq): -# for item in seq: -# if is_scalar(item): -# yield item -# else: -# for subitem in flatten(item): -# yield subitem - -def flatten(iterable, tr_func=None, results=None): - """Flatten a list of list with any level. - - If tr_func is not None, it should be a one argument function that'll be called - on each final element. - - :rtype: list - - >>> flatten([1, [2, 3]]) - [1, 2, 3] - """ - if results is None: - results = [] - for val in iterable: - if isinstance(val, (list, tuple)): - flatten(val, tr_func, results) - elif tr_func is None: - results.append(val) - else: - results.append(tr_func(val)) - return results - - -# XXX is function below still used ? - -def make_domains(lists): - """ - Given a list of lists, return a list of domain for each list to produce all - combinations of possibles values. - - :rtype: list - - Example: - - >>> make_domains(['a', 'b'], ['c','d', 'e']) - [['a', 'b', 'a', 'b', 'a', 'b'], ['c', 'c', 'd', 'd', 'e', 'e']] - """ - from six.moves import range - domains = [] - for iterable in lists: - new_domain = iterable[:] - for i in range(len(domains)): - domains[i] = domains[i]*len(iterable) - if domains: - missing = (len(domains[0]) - len(iterable)) / len(iterable) - i = 0 - for j in range(len(iterable)): - value = iterable[j] - for dummy in range(missing): - new_domain.insert(i, value) - i += 1 - i += 1 - domains.append(new_domain) - return domains - - -# private stuff ################################################################ - -def _handle_blacklist(blacklist, dirnames, filenames): - """remove files/directories in the black list - - dirnames/filenames are usually from os.walk - """ - for norecurs in blacklist: - if norecurs in dirnames: - dirnames.remove(norecurs) - elif norecurs in filenames: - filenames.remove(norecurs) - diff --git a/pymode/libs/logilab/common/cache.py b/pymode/libs/logilab/common/cache.py deleted file mode 100644 index 11ed1370..00000000 --- a/pymode/libs/logilab/common/cache.py +++ /dev/null @@ -1,114 +0,0 @@ -# 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 . -"""Cache module, with a least recently used algorithm for the management of the -deletion of entries. - - - - -""" -__docformat__ = "restructuredtext en" - -from threading import Lock - -from logilab.common.decorators import locked - -_marker = object() - -class Cache(dict): - """A dictionary like cache. - - inv: - len(self._usage) <= self.size - len(self.data) <= self.size - """ - - def __init__(self, size=100): - """ Warning : Cache.__init__() != dict.__init__(). - Constructor does not take any arguments beside size. - """ - assert size >= 0, 'cache size must be >= 0 (0 meaning no caching)' - self.size = size - self._usage = [] - self._lock = Lock() - super(Cache, self).__init__() - - def _acquire(self): - self._lock.acquire() - - def _release(self): - self._lock.release() - - def _update_usage(self, key): - if not self._usage: - self._usage.append(key) - elif self._usage[-1] != key: - try: - self._usage.remove(key) - except ValueError: - # we are inserting a new key - # check the size of the dictionary - # and remove the oldest item in the cache - if self.size and len(self._usage) >= self.size: - super(Cache, self).__delitem__(self._usage[0]) - del self._usage[0] - self._usage.append(key) - else: - pass # key is already the most recently used key - - def __getitem__(self, key): - value = super(Cache, self).__getitem__(key) - self._update_usage(key) - return value - __getitem__ = locked(_acquire, _release)(__getitem__) - - def __setitem__(self, key, item): - # Just make sure that size > 0 before inserting a new item in the cache - if self.size > 0: - super(Cache, self).__setitem__(key, item) - self._update_usage(key) - __setitem__ = locked(_acquire, _release)(__setitem__) - - def __delitem__(self, key): - super(Cache, self).__delitem__(key) - self._usage.remove(key) - __delitem__ = locked(_acquire, _release)(__delitem__) - - def clear(self): - super(Cache, self).clear() - self._usage = [] - clear = locked(_acquire, _release)(clear) - - def pop(self, key, default=_marker): - if key in self: - self._usage.remove(key) - #if default is _marker: - # return super(Cache, self).pop(key) - return super(Cache, self).pop(key, default) - pop = locked(_acquire, _release)(pop) - - def popitem(self): - raise NotImplementedError() - - def setdefault(self, key, default=None): - raise NotImplementedError() - - def update(self, other): - raise NotImplementedError() - - diff --git a/pymode/libs/logilab/common/changelog.py b/pymode/libs/logilab/common/changelog.py deleted file mode 100644 index 2fff2ed6..00000000 --- a/pymode/libs/logilab/common/changelog.py +++ /dev/null @@ -1,238 +0,0 @@ -# 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 . -"""Manipulation of upstream change log files. - -The upstream change log files format handled is simpler than the one -often used such as those generated by the default Emacs changelog mode. - -Sample ChangeLog format:: - - Change log for project Yoo - ========================== - - -- - * add a new functionality - - 2002-02-01 -- 0.1.1 - * fix bug #435454 - * fix bug #434356 - - 2002-01-01 -- 0.1 - * initial release - - -There is 3 entries in this change log, one for each released version and one -for the next version (i.e. the current entry). -Each entry contains a set of messages corresponding to changes done in this -release. -All the non empty lines before the first entry are considered as the change -log title. -""" - -__docformat__ = "restructuredtext en" - -import sys -from stat import S_IWRITE - -from six import string_types - -BULLET = '*' -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? - parsed = cls.parse(versionstr) - else: - parsed = versionstr - return tuple.__new__(cls, parsed) - - @classmethod - def parse(cls, versionstr): - versionstr = versionstr.strip(' :') - try: - return [int(i) for i in versionstr.split('.')] - except ValueError as 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): - """a change log entry, i.e. a set of messages associated to a version and - its release date - """ - version_class = Version - - def __init__(self, date=None, version=None, **kwargs): - self.__dict__.update(kwargs) - if version: - self.version = self.version_class(version) - else: - self.version = None - self.date = date - self.messages = [] - - def add_message(self, msg): - """add a new message""" - self.messages.append(([msg], [])) - - 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 - self.messages[-1][1][-1].append(msg_suite) - 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)') - if key is None: - self.messages[-1][1].append([sub_msg]) - else: - 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 '')) - for msg, sub_msgs in self.messages: - stream.write('%s%s %s\n' % (INDENT, BULLET, msg[0])) - stream.write(''.join(msg[1:])) - if sub_msgs: - stream.write('\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('\n\n') - -class ChangeLog(object): - """object representation of a whole ChangeLog file""" - - entry_class = ChangeLogEntry - - def __init__(self, changelog_file, title=''): - self.file = changelog_file - self.title = title - self.additional_content = '' - self.entries = [] - self.load() - - def __repr__(self): - return '' % (self.file, id(self), - len(self.entries)) - - def add_entry(self, entry): - """add a new entry to the change log""" - self.entries.append(entry) - - def get_entry(self, version='', create=None): - """ return a given changelog entry - if version is omitted, return the current entry - """ - if not self.entries: - if version or not create: - raise NoEntry() - self.entries.append(self.entry_class()) - if not version: - if self.entries[0].version and create is not None: - self.entries.insert(0, self.entry_class()) - return self.entries[0] - version = self.version_class(version) - for entry in self.entries: - if entry.version == version: - return entry - raise EntryNotFound() - - def add(self, msg, create=None): - """add a new message to the latest opened entry""" - entry = self.get_entry(create=create) - entry.add_message(msg) - - def load(self): - """ read a logilab's ChangeLog from file """ - try: - stream = open(self.file) - except IOError: - return - last = None - expect_sub = False - for line in stream.readlines(): - sline = line.strip() - words = sline.split() - # if new entry - if len(words) == 1 and words[0] == '--': - expect_sub = False - last = self.entry_class() - self.add_entry(last) - # if old entry - elif len(words) == 3 and words[1] == '--': - expect_sub = False - last = self.entry_class(words[0], words[2]) - self.add_entry(last) - # if title - elif sline and last is None: - self.title = '%s%s' % (self.title, line) - # if new entry - elif sline and sline[0] == BULLET: - expect_sub = False - last.add_message(sline[1:].strip()) - # if new sub_entry - elif expect_sub and sline and sline[0] == SUBBULLET: - last.add_sub_message(sline[1:].strip()) - # if new line for current entry - elif sline and last.messages: - last.complete_latest_message(line) - else: - expect_sub = True - self.additional_content += line - stream.close() - - def format_title(self): - return '%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')) - - 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/clcommands.py deleted file mode 100644 index 4778b99b..00000000 --- a/pymode/libs/logilab/common/clcommands.py +++ /dev/null @@ -1,334 +0,0 @@ -# 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 . -"""Helper functions to support command line tools providing more than -one command. - -e.g called as "tool command [options] args..." where and are -command'specific -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import logging -from os.path import basename - -from logilab.common.configuration import Configuration -from logilab.common.logging_ext import init_log, get_threshold -from logilab.common.deprecation import deprecated - - -class BadCommandUsage(Exception): - """Raised when an unknown command is used or when a command is not - correctly used (bad options, too much / missing arguments...). - - Trigger display of command usage. - """ - -class CommandError(Exception): - """Raised when a command can't be processed and we want to display it and - exit, without traceback nor usage displayed. - """ - - -# command line access point #################################################### - -class CommandLine(dict): - """Usage: - - >>> LDI = cli.CommandLine('ldi', doc='Logilab debian installer', - version=version, rcfile=RCFILE) - >>> LDI.register(MyCommandClass) - >>> LDI.register(MyOtherCommandClass) - >>> LDI.run(sys.argv[1:]) - - Arguments: - - * `pgm`, the program name, default to `basename(sys.argv[0])` - - * `doc`, a short description of the command line tool - - * `copyright`, additional doc string that will be appended to the generated - doc - - * `version`, version number of string of the tool. If specified, global - --version option will be available. - - * `rcfile`, path to a configuration file. If specified, global --C/--rc-file - option will be available? self.rcfile = rcfile - - * `logger`, logger to propagate to commands, default to - `logging.getLogger(self.pgm))` - """ - def __init__(self, pgm=None, doc=None, copyright=None, version=None, - rcfile=None, logthreshold=logging.ERROR, - check_duplicated_command=True): - if pgm is None: - pgm = basename(sys.argv[0]) - self.pgm = pgm - self.doc = doc - self.copyright = copyright - self.version = version - self.rcfile = rcfile - self.logger = None - self.logthreshold = logthreshold - self.check_duplicated_command = check_duplicated_command - - def register(self, cls, force=False): - """register the given :class:`Command` subclass""" - assert not self.check_duplicated_command or force or not cls.name in self, \ - 'a command %s is already defined' % cls.name - self[cls.name] = cls - return cls - - def run(self, args): - """main command line access point: - * init logging - * handle global options (-h/--help, --version, -C/--rc-file) - * check command - * run command - - Terminate by :exc:`SystemExit` - """ - init_log(debug=True, # so that we use StreamHandler - logthreshold=self.logthreshold, - logformat='%(levelname)s: %(message)s') - try: - arg = args.pop(0) - except IndexError: - self.usage_and_exit(1) - if arg in ('-h', '--help'): - self.usage_and_exit(0) - if self.version is not None and arg in ('--version'): - print(self.version) - sys.exit(0) - rcfile = self.rcfile - if rcfile is not None and arg in ('-C', '--rc-file'): - try: - rcfile = args.pop(0) - arg = args.pop(0) - except IndexError: - self.usage_and_exit(1) - try: - command = self.get_command(arg) - except KeyError: - print('ERROR: no %s command' % arg) - print() - self.usage_and_exit(1) - try: - sys.exit(command.main_run(args, rcfile)) - except KeyboardInterrupt as exc: - print('Interrupted', end=' ') - if str(exc): - print(': %s' % exc, end=' ') - print() - sys.exit(4) - except BadCommandUsage as err: - print('ERROR:', err) - print() - print(command.help()) - sys.exit(1) - - def create_logger(self, handler, logthreshold=None): - logger = logging.Logger(self.pgm) - logger.handlers = [handler] - if logthreshold is None: - logthreshold = get_threshold(self.logthreshold) - logger.setLevel(logthreshold) - return logger - - def get_command(self, cmd, logger=None): - if logger is None: - logger = self.logger - if logger is None: - logger = self.logger = logging.getLogger(self.pgm) - logger.setLevel(get_threshold(self.logthreshold)) - return self[cmd](logger) - - def usage(self): - """display usage for the main program (i.e. when no command supplied) - and exit - """ - print('usage:', self.pgm, end=' ') - if self.rcfile: - print('[--rc-file=]', end=' ') - print(' [options] ...') - if self.doc: - print('\n%s' % self.doc) - print(''' -Type "%(pgm)s --help" for more information about a specific -command. Available commands are :\n''' % self.__dict__) - max_len = max([len(cmd) for cmd in self]) - padding = ' ' * max_len - for cmdname, cmd in sorted(self.items()): - if not cmd.hidden: - print(' ', (cmdname + padding)[:max_len], cmd.short_description()) - if self.rcfile: - print(''' -Use --rc-file= / -C before the command -to specify a configuration file. Default to %s. -''' % self.rcfile) - print('''%(pgm)s -h/--help - display this usage information and exit''' % self.__dict__) - if self.version: - print('''%(pgm)s -v/--version - display version configuration and exit''' % self.__dict__) - if self.copyright: - print('\n', self.copyright) - - def usage_and_exit(self, status): - self.usage() - sys.exit(status) - - -# base command classes ######################################################### - -class Command(Configuration): - """Base class for command line commands. - - Class attributes: - - * `name`, the name of the command - - * `min_args`, minimum number of arguments, None if unspecified - - * `max_args`, maximum number of arguments, None if unspecified - - * `arguments`, string describing arguments, used in command usage - - * `hidden`, boolean flag telling if the command should be hidden, e.g. does - not appear in help's commands list - - * `options`, options list, as allowed by :mod:configuration - """ - - arguments = '' - name = '' - # hidden from help ? - hidden = False - # max/min args, None meaning unspecified - min_args = None - max_args = None - - @classmethod - def description(cls): - return cls.__doc__.replace(' ', '') - - @classmethod - def short_description(cls): - return cls.description().split('.')[0] - - def __init__(self, logger): - usage = '%%prog %s %s\n\n%s' % (self.name, self.arguments, - self.description()) - Configuration.__init__(self, usage=usage) - self.logger = logger - - def check_args(self, args): - """check command's arguments are provided""" - if self.min_args is not None and len(args) < self.min_args: - raise BadCommandUsage('missing argument') - if self.max_args is not None and len(args) > self.max_args: - raise BadCommandUsage('too many arguments') - - def main_run(self, args, rcfile=None): - """Run the command and return status 0 if everything went fine. - - If :exc:`CommandError` is raised by the underlying command, simply log - the error and return status 2. - - Any other exceptions, including :exc:`BadCommandUsage` will be - propagated. - """ - if rcfile: - self.load_file_configuration(rcfile) - args = self.load_command_line_configuration(args) - try: - self.check_args(args) - self.run(args) - except CommandError as err: - self.logger.error(err) - return 2 - return 0 - - def run(self, args): - """run the command with its specific arguments""" - raise NotImplementedError() - - -class ListCommandsCommand(Command): - """list available commands, useful for bash completion.""" - name = 'listcommands' - arguments = '[command]' - hidden = True - - def run(self, args): - """run the command with its specific arguments""" - if args: - command = args.pop() - cmd = _COMMANDS[command] - for optname, optdict in cmd.options: - print('--help') - print('--' + optname) - else: - commands = sorted(_COMMANDS.keys()) - for command in commands: - cmd = _COMMANDS[command] - if not cmd.hidden: - print(command) - - -# deprecated stuff ############################################################# - -_COMMANDS = CommandLine() - -DEFAULT_COPYRIGHT = '''\ -Copyright (c) 2004-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -http://www.logilab.fr/ -- mailto:contact@logilab.fr''' - -@deprecated('use cls.register(cli)') -def register_commands(commands): - """register existing commands""" - for command_klass in commands: - _COMMANDS.register(command_klass) - -@deprecated('use args.pop(0)') -def main_run(args, doc=None, copyright=None, version=None): - """command line tool: run command specified by argument list (without the - program name). Raise SystemExit with status 0 if everything went fine. - - >>> main_run(sys.argv[1:]) - """ - _COMMANDS.doc = doc - _COMMANDS.copyright = copyright - _COMMANDS.version = version - _COMMANDS.run(args) - -@deprecated('use args.pop(0)') -def pop_arg(args_list, expected_size_after=None, msg="Missing argument"): - """helper function to get and check command line arguments""" - try: - value = args_list.pop(0) - except IndexError: - raise BadCommandUsage(msg) - if expected_size_after is not None and len(args_list) > expected_size_after: - raise BadCommandUsage('too many arguments') - return value - diff --git a/pymode/libs/logilab/common/compat.py b/pymode/libs/logilab/common/compat.py deleted file mode 100644 index f2eb5905..00000000 --- a/pymode/libs/logilab/common/compat.py +++ /dev/null @@ -1,78 +0,0 @@ -# pylint: disable=E0601,W0622,W0611 -# 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 . -"""Wrappers around some builtins introduced in python 2.3, 2.4 and -2.5, making them available in for earlier versions of python. - -See another compatibility snippets from other projects: - - :mod:`lib2to3.fixes` - :mod:`coverage.backward` - :mod:`unittest2.compatibility` -""" - - -__docformat__ = "restructuredtext en" - -import os -import sys -import types -from warnings import warn - -# not used here, but imported to preserve API -from six.moves import builtins - -if sys.version_info < (3, 0): - str_to_bytes = str - def str_encode(string, encoding): - if isinstance(string, unicode): - return string.encode(encoding) - return str(string) -else: - def str_to_bytes(string): - return str.encode(string) - # we have to ignore the encoding in py3k to be able to write a string into a - # TextIOWrapper or like object (which expect an unicode string) - def str_encode(string, encoding): - return str(string) - -# See also http://bugs.python.org/issue11776 -if sys.version_info[0] == 3: - def method_type(callable, instance, klass): - # api change. klass is no more considered - return types.MethodType(callable, instance) -else: - # alias types otherwise - method_type = types.MethodType - -# Pythons 2 and 3 differ on where to get StringIO -if sys.version_info < (3, 0): - from cStringIO import StringIO - FileIO = file - BytesIO = StringIO - reload = reload -else: - from io import FileIO, BytesIO, StringIO - from imp import reload - -from logilab.common.deprecation import deprecated - -# Other projects import these from here, keep providing them for -# backwards compat -any = deprecated('use builtin "any"')(any) -all = deprecated('use builtin "all"')(all) diff --git a/pymode/libs/logilab/common/configuration.py b/pymode/libs/logilab/common/configuration.py deleted file mode 100644 index b2924277..00000000 --- a/pymode/libs/logilab/common/configuration.py +++ /dev/null @@ -1,1105 +0,0 @@ -# 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 . -"""Classes to handle advanced configuration in simple to complex applications. - -Allows to load the configuration from a file or from command line -options, to generate a sample configuration file or to display -program's usage. Fills the gap between optik/optparse and ConfigParser -by adding data types (which are also available as a standalone optik -extension in the `optik_ext` module). - - -Quick start: simplest usage ---------------------------- - -.. python :: - - >>> import sys - >>> from logilab.common.configuration import Configuration - >>> options = [('dothis', {'type':'yn', 'default': True, 'metavar': ''}), - ... ('value', {'type': 'string', 'metavar': ''}), - ... ('multiple', {'type': 'csv', 'default': ('yop',), - ... 'metavar': '', - ... 'help': 'you can also document the option'}), - ... ('number', {'type': 'int', 'default':2, 'metavar':''}), - ... ] - >>> config = Configuration(options=options, name='My config') - >>> print config['dothis'] - True - >>> print config['value'] - None - >>> print config['multiple'] - ('yop',) - >>> print config['number'] - 2 - >>> print config.help() - Usage: [options] - - Options: - -h, --help show this help message and exit - --dothis= - --value= - --multiple= - you can also document the option [current: none] - --number= - - >>> f = open('myconfig.ini', 'w') - >>> f.write('''[MY CONFIG] - ... number = 3 - ... dothis = no - ... multiple = 1,2,3 - ... ''') - >>> f.close() - >>> config.load_file_configuration('myconfig.ini') - >>> print config['dothis'] - False - >>> print config['value'] - None - >>> print config['multiple'] - ['1', '2', '3'] - >>> print config['number'] - 3 - >>> sys.argv = ['mon prog', '--value', 'bacon', '--multiple', '4,5,6', - ... 'nonoptionargument'] - >>> print config.load_command_line_configuration() - ['nonoptionargument'] - >>> print config['value'] - bacon - >>> config.generate_config() - # class for simple configurations which don't need the - # manager / providers model and prefer delegation to inheritance - # - # configuration values are accessible through a dict like interface - # - [MY CONFIG] - - dothis=no - - value=bacon - - # you can also document the option - multiple=4,5,6 - - number=3 - - Note : starting with Python 2.7 ConfigParser is able to take into - account the order of occurrences of the options into a file (by - using an OrderedDict). If you have two options changing some common - state, like a 'disable-all-stuff' and a 'enable-some-stuff-a', their - order of appearance will be significant : the last specified in the - file wins. For earlier version of python and logilab.common newer - than 0.61 the behaviour is unspecified. - -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -__all__ = ('OptionsManagerMixIn', 'OptionsProviderMixIn', - 'ConfigurationMixIn', 'Configuration', - 'OptionsManager2ConfigurationAdapter') - -import os -import sys -import re -from os.path import exists, expanduser -from copy import copy -from warnings import warn - -from six import string_types -from six.moves import range, configparser as cp, input - -from logilab.common.compat import str_encode as _encode -from logilab.common.deprecation import deprecated -from logilab.common.textutils import normalize_text, unquote -from logilab.common import optik_ext - -OptionError = optik_ext.OptionError - -REQUIRED = [] - -class UnsupportedAction(Exception): - """raised by set_option when it doesn't know what to do for an action""" - - -def _get_encoding(encoding, stream): - encoding = encoding or getattr(stream, 'encoding', None) - if not encoding: - import locale - encoding = locale.getpreferredencoding() - return encoding - - -# validation functions ######################################################## - -# validators will return the validated value or raise optparse.OptionValueError -# XXX add to documentation - -def choice_validator(optdict, name, value): - """validate and return a converted value for option of type 'choice' - """ - if not value in optdict['choices']: - msg = "option %s: invalid value: %r, should be in %s" - raise optik_ext.OptionValueError(msg % (name, value, optdict['choices'])) - return value - -def multiple_choice_validator(optdict, name, value): - """validate and return a converted value for option of type 'choice' - """ - choices = optdict['choices'] - values = optik_ext.check_csv(None, name, value) - for value in values: - if not value in choices: - msg = "option %s: invalid value: %r, should be in %s" - raise optik_ext.OptionValueError(msg % (name, value, choices)) - return values - -def csv_validator(optdict, name, value): - """validate and return a converted value for option of type 'csv' - """ - return optik_ext.check_csv(None, name, value) - -def yn_validator(optdict, name, value): - """validate and return a converted value for option of type 'yn' - """ - return optik_ext.check_yn(None, name, value) - -def named_validator(optdict, name, value): - """validate and return a converted value for option of type 'named' - """ - return optik_ext.check_named(None, name, value) - -def file_validator(optdict, name, value): - """validate and return a filepath for option of type 'file'""" - return optik_ext.check_file(None, name, value) - -def color_validator(optdict, name, value): - """validate and return a valid color for option of type 'color'""" - return optik_ext.check_color(None, name, value) - -def password_validator(optdict, name, value): - """validate and return a string for option of type 'password'""" - return optik_ext.check_password(None, name, value) - -def date_validator(optdict, name, value): - """validate and return a mx DateTime object for option of type 'date'""" - return optik_ext.check_date(None, name, value) - -def time_validator(optdict, name, value): - """validate and return a time object for option of type 'time'""" - return optik_ext.check_time(None, name, value) - -def bytes_validator(optdict, name, value): - """validate and return an integer for option of type 'bytes'""" - return optik_ext.check_bytes(None, name, value) - - -VALIDATORS = {'string': unquote, - 'int': int, - 'float': float, - 'file': file_validator, - 'font': unquote, - 'color': color_validator, - 'regexp': re.compile, - 'csv': csv_validator, - 'yn': yn_validator, - 'bool': yn_validator, - 'named': named_validator, - 'password': password_validator, - 'date': date_validator, - 'time': time_validator, - 'bytes': bytes_validator, - 'choice': choice_validator, - 'multiple_choice': multiple_choice_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 optik_ext.OptionValueError: - raise - except: - raise optik_ext.OptionValueError('%s value (%r) should be of type %s' % - (option, value, opttype)) - -# user input functions ######################################################## - -# user input functions will ask the user for input on stdin then validate -# the result and return the validated value or raise optparse.OptionValueError -# XXX add to documentation - -def input_password(optdict, question='password:'): - from getpass import getpass - while True: - value = getpass(question) - value2 = getpass('confirm: ') - if value == value2: - return value - print('password mismatch, try again') - -def input_string(optdict, question): - value = input(question).strip() - return value or None - -def _make_input_function(opttype): - def input_validator(optdict, question): - while True: - value = input(question) - if not value.strip(): - return None - try: - return _call_validator(opttype, optdict, None, value) - except optik_ext.OptionValueError as ex: - msg = str(ex).split(':', 1)[-1].strip() - print('bad value: %s' % msg) - return input_validator - -INPUT_FUNCTIONS = { - 'string': input_string, - 'password': input_password, - } - -for opttype in VALIDATORS.keys(): - INPUT_FUNCTIONS.setdefault(opttype, _make_input_function(opttype)) - -# utility functions ############################################################ - -def expand_default(self, option): - """monkey patch OptionParser.expand_default since we have a particular - way to 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 = format_option_value(optdict, value) - if value is optik_ext.NO_DEFAULT or not value: - value = self.NO_DEFAULT_VALUE - return option.help.replace(self.default_tag, str(value)) - - -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) -convert = deprecated('[0.60] convert() was renamed _validate()')(_validate) - -# format and output functions ################################################## - -def comment(string): - """return string as a comment""" - lines = [line.strip() for line in string.splitlines()] - return '# ' + ('%s# ' % os.linesep).join(lines) - -def format_time(value): - if not value: - return '0' - if value != int(value): - return '%.2fs' % value - value = int(value) - nbmin, nbsec = divmod(value, 60) - if nbsec: - return '%ss' % value - nbhour, nbmin_ = divmod(nbmin, 60) - if nbmin_: - return '%smin' % nbmin - nbday, nbhour_ = divmod(nbhour, 24) - if nbhour_: - return '%sh' % nbhour - return '%sd' % nbday - -def format_bytes(value): - if not value: - return '0' - if value != int(value): - return '%.2fB' % value - value = int(value) - prevunit = 'B' - for unit in ('KB', 'MB', 'GB', 'TB'): - next, remain = divmod(value, 1024) - if remain: - return '%s%s' % (value, prevunit) - prevunit = unit - value = next - return '%s%s' % (value, unit) - -def format_option_value(optdict, value): - """return the user input's value from a 'compiled' value""" - if isinstance(value, (list, tuple)): - value = ','.join(value) - elif isinstance(value, dict): - value = ','.join(['%s:%s' % (k, v) for k, v in value.items()]) - elif hasattr(value, 'match'): # optdict.get('type') == 'regexp' - # compiled regexp - value = value.pattern - elif optdict.get('type') == 'yn': - 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)): - value = format_time(value) - elif optdict.get('type') == 'bytes' and hasattr(value, '__int__'): - value = format_bytes(value) - return value - -def ini_format_section(stream, section, options, encoding=None, doc=None): - """format an options section using the INI format""" - encoding = _get_encoding(encoding, stream) - if doc: - print(_encode(comment(doc), encoding), file=stream) - print('[%s]' % section, file=stream) - ini_format(stream, options, encoding) - -def ini_format(stream, options, encoding): - """format options using the INI format""" - for optname, optdict, value in options: - value = format_option_value(optdict, value) - help = optdict.get('help') - if help: - help = normalize_text(help, line_len=79, indent='# ') - print(file=stream) - print(_encode(help, encoding), file=stream) - else: - print(file=stream) - if value is None: - print('#%s=' % optname, file=stream) - else: - value = _encode(value, encoding).strip() - print('%s=%s' % (optname, value), file=stream) - -format_section = ini_format_section - -def rest_format_section(stream, section, options, encoding=None, doc=None): - """format an options section using as ReST formatted output""" - encoding = _get_encoding(encoding, stream) - if section: - print('%s\n%s' % (section, "'"*len(section)), file=stream) - if doc: - print(_encode(normalize_text(doc, line_len=79, indent=''), encoding), file=stream) - print(file=stream) - for optname, optdict, value in options: - help = optdict.get('help') - print(':%s:' % optname, file=stream) - if help: - help = normalize_text(help, line_len=79, indent=' ') - print(_encode(help, encoding), file=stream) - if value: - value = _encode(format_option_value(optdict, value), encoding) - print(file=stream) - print(' Default: ``%s``' % value.replace("`` ", "```` ``"), file=stream) - -# Options Manager ############################################################## - -class OptionsManagerMixIn(object): - """MixIn to handle a 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 = {} - self._short_options = {} - self._nocallback_options = {} - self._mygroups = dict() - # verbosity - self.quiet = quiet - self._maxlevel = 0 - - def reset_parsers(self, usage='', version=None): - # configuration file parser - self.cfgfile_parser = cp.ConfigParser() - # command line parser - self.cmdline_parser = optik_ext.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, doc, options, provider): - """add an option group including the listed options - """ - assert options - # add option group to the command line parser - if group_name in self._mygroups: - group = self._mygroups[group_name] - else: - group = optik_ext.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": - 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): - if 'inputlevel' in optdict: - warn('[0.50] "inputlevel" in option dictionary for %s is deprecated,' - ' use "level"' % opt, DeprecationWarning) - optdict['level'] = optdict.pop('inputlevel') - 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(optdict) - others = {} - 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 not optdict['action'] 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 not key 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] - if not options: - continue - if not section in sections: - sections.append(section) - alloptions = options_by_section.setdefault(section, []) - alloptions += options - stream = stream or sys.stdout - encoding = _get_encoding(encoding, stream) - printed = False - for section in sections: - if printed: - print('\n', file=stream) - format_section(stream, section.upper(), options_by_section[section], - encoding) - printed = True - - def generate_manpage(self, pkginfo, section=1, stream=None): - """write a man page for the current configuration into the given - stream or stdout - """ - self._monkeypatch_expand_default() - try: - optik_ext.generate_manpage(self.cmdline_parser, pkginfo, - section, stream=stream or sys.stdout, - level=self._maxlevel) - finally: - self._unmonkeypatch_expand_default() - - # initialization methods ################################################## - - def load_provider_defaults(self): - """initialize configuration using default values""" - for provider in self.options_providers: - provider.load_defaults() - - def load_file_configuration(self, config_file=None): - """load the configuration from file""" - self.read_config_file(config_file) - self.load_config_file() - - 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 - 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 = expanduser(config_file) - if config_file and exists(config_file): - parser = self.cfgfile_parser - parser.read([config_file]) - # normalize sections'title - for sect, values in 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 input_config(self, onlysection=None, inputlevel=0, stream=None): - """interactively get configuration values by asking to the user and generate - a configuration file - """ - if onlysection is not None: - onlysection = onlysection.upper() - for provider in self.options_providers: - for section, option, optdict in provider.all_options(): - if onlysection is not None and section != onlysection: - continue - if not 'type' in optdict: - # ignore action without type (callback, store_true...) - continue - provider.input_option(option, optdict, inputlevel) - # now we can generate the configuration file - if stream is not None: - self.generate_config(stream) - - 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, OptionError): - # TODO handle here undeclared options appearing in the config file - continue - - def load_configuration(self, **kwargs): - """override configuration according to given parameters - """ - for opt, opt_value in kwargs.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 - """ - self._monkeypatch_expand_default() - try: - 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.keys(): - 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 - finally: - self._unmonkeypatch_expand_default() - - - # help methods ############################################################ - - def add_help_section(self, title, description, level=0): - """add a dummy option section for help purpose """ - group = optik_ext.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 _monkeypatch_expand_default(self): - # monkey patch optik_ext to deal with our default values - try: - self.__expand_default_backup = optik_ext.HelpFormatter.expand_default - optik_ext.HelpFormatter.expand_default = expand_default - except AttributeError: - # python < 2.4: nothing to be done - pass - def _unmonkeypatch_expand_default(self): - # remove monkey patch - if hasattr(optik_ext.HelpFormatter, 'expand_default'): - # unpatch optik_ext to avoid side effects - optik_ext.HelpFormatter.expand_default = self.__expand_default_backup - - def help(self, level=0): - """return the usage string for available options """ - self.cmdline_parser.formatter.output_level = level - self._monkeypatch_expand_default() - try: - return self.cmdline_parser.format_help() - finally: - self._unmonkeypatch_expand_default() - - -class Method(object): - """used to ease late binding of default method (so you can define options - on the class using default methods on the configuration instance) - """ - def __init__(self, methname): - self.method = methname - self._inst = None - - def bind(self, instance): - """bind the method to its instance""" - if self._inst is None: - self._inst = instance - - def __call__(self, *args, **kwargs): - assert self._inst, 'unbound method' - return getattr(self._inst, self.method)(*args, **kwargs) - -# Options Provider ############################################################# - -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 = optik_ext.Values() - for option in self.options: - try: - option, optdict = option - except ValueError: - raise Exception('Bad option: %r' % option) - if isinstance(optdict.get('default'), Method): - optdict['default'].bind(self) - elif isinstance(optdict.get('callback'), Method): - optdict['callback'].bind(self) - 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 - default = self.option_default(opt, optdict) - if default is REQUIRED: - continue - self.set_option(opt, default, action, optdict) - - def option_default(self, opt, optdict=None): - """return the default value for an option""" - if optdict is None: - optdict = self.get_option_def(opt) - default = optdict.get('default') - if callable(default): - default = default() - return default - - 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('-', '_')) - option_name = deprecated('[0.60] OptionsProviderMixIn.option_name() was renamed to option_attrname()')(option_attrname) - - 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, opt, 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(opt) - if value is not None: - value = _validate(value, optdict, opt) - if action is None: - action = optdict.get('action', 'store') - if optdict.get('type') == 'named': # XXX need specific handling - optname = self.option_attrname(opt, optdict) - currentvalue = getattr(self.config, optname, None) - if currentvalue: - currentvalue.update(value) - value = currentvalue - if action == 'store': - setattr(self.config, self.option_attrname(opt, optdict), value) - elif action in ('store_true', 'count'): - setattr(self.config, self.option_attrname(opt, optdict), 0) - elif action == 'store_false': - setattr(self.config, self.option_attrname(opt, optdict), 1) - elif action == 'append': - opt = self.option_attrname(opt, optdict) - _list = getattr(self.config, opt, None) - if _list is None: - if isinstance(value, (list, tuple)): - _list = value - elif value is not None: - _list = [] - _list.append(value) - setattr(self.config, opt, _list) - elif isinstance(_list, tuple): - setattr(self.config, opt, _list + (value,)) - else: - _list.append(value) - elif action == 'callback': - optdict['callback'](None, opt, value, None) - else: - raise UnsupportedAction(action) - - def input_option(self, option, optdict, inputlevel=99): - default = self.option_default(option, optdict) - if default is REQUIRED: - defaultstr = '(required): ' - elif optdict.get('level', 0) > inputlevel: - return - elif optdict['type'] == 'password' or default is None: - defaultstr = ': ' - else: - defaultstr = '(default: %s): ' % format_option_value(optdict, default) - print(':%s:' % option) - print(optdict.get('help') or option) - inputfunc = INPUT_FUNCTIONS[optdict['type']] - value = inputfunc(optdict, defaultstr) - while default is REQUIRED and not value: - print('please specify a value') - value = inputfunc(optdict, '%s: ' % option) - if value is None and default is not None: - value = default - self.set_option(option, value, optdict=optdict) - - def get_option_def(self, opt): - """return the dictionary defining an option given it's name""" - assert self.options - for option in self.options: - if option[0] == opt: - return option[1] - raise OptionError('no such option %s in section %r' - % (opt, self.name), opt) - - - def all_options(self): - """return an iterator on available options for this provider - option are actually described by a 3-uple: - (section, option name, option dictionary) - """ - for section, options in self.options_by_section(): - if section is None: - if self.name is None: - continue - section = self.name.upper() - for option, optiondict, value in options: - yield section, option, optiondict - - 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 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)) - -# configuration ################################################################ - -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 option, optdict in self.options: - try: - gdef = (optdict['group'].upper(), '') - except KeyError: - continue - if not gdef in self.option_groups: - self.option_groups.append(gdef) - self.register_options_provider(self, own_group=False) - - def register_options(self, options): - """add some options to the configuration""" - 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) - self.options += tuple(options) - - def load_defaults(self): - OptionsProviderMixIn.load_defaults(self) - - def __iter__(self): - return iter(self.config.__dict__.iteritems()) - - def __getitem__(self, key): - try: - return getattr(self.config, self.option_attrname(key)) - except (optik_ext.OptionValueError, AttributeError): - raise KeyError(key) - - def __setitem__(self, key, value): - self.set_option(key, value) - - def get(self, key, default=None): - try: - return getattr(self.config, self.option_attrname(key)) - except (OptionError, AttributeError): - return default - - -class Configuration(ConfigurationMixIn): - """class for simple configurations which don't need the - manager / providers model and prefer delegation to inheritance - - configuration values are accessible through a dict like interface - """ - - def __init__(self, config_file=None, options=None, name=None, - usage=None, doc=None, version=None): - if options is not None: - self.options = options - if name is not None: - self.name = name - if doc is not None: - self.__doc__ = doc - super(Configuration, self).__init__(config_file=config_file, usage=usage, version=version) - - -class OptionsManager2ConfigurationAdapter(object): - """Adapt an option manager to behave like a - `logilab.common.configuration.Configuration` instance - """ - def __init__(self, provider): - self.config = provider - - def __getattr__(self, key): - return getattr(self.config, key) - - def __getitem__(self, key): - provider = self.config._all_options[key] - try: - return getattr(provider.config, provider.option_attrname(key)) - except AttributeError: - raise KeyError(key) - - def __setitem__(self, key, value): - self.config.global_set_option(self.config.option_attrname(key), value) - - def get(self, key, default=None): - provider = self.config._all_options[key] - try: - return getattr(provider.config, provider.option_attrname(key)) - except AttributeError: - return default - -# other functions ############################################################## - -def read_old_config(newconfig, changes, configfile): - """initialize newconfig from a deprecated configuration file - - possible changes: - * ('renamed', oldname, newname) - * ('moved', option, oldgroup, newgroup) - * ('typechanged', option, oldtype, newvalue) - """ - # build an index of changes - changesindex = {} - for action in changes: - if action[0] == 'moved': - option, oldgroup, newgroup = action[1:] - changesindex.setdefault(option, []).append((action[0], oldgroup, newgroup)) - continue - if action[0] == 'renamed': - oldname, newname = action[1:] - changesindex.setdefault(newname, []).append((action[0], oldname)) - continue - if action[0] == 'typechanged': - option, oldtype, newvalue = action[1:] - changesindex.setdefault(option, []).append((action[0], oldtype, newvalue)) - continue - if action[1] 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 - options = [] - for optname, optdef in newconfig.options: - for action in changesindex.pop(optname, ()): - if action[0] == 'moved': - oldgroup, newgroup = action[1:] - optdef = optdef.copy() - optdef['group'] = oldgroup - elif action[0] == 'renamed': - optname = action[1] - elif action[0] == 'typechanged': - oldtype = action[1] - optdef = optdef.copy() - optdef['type'] = oldtype - options.append((optname, optdef)) - if changesindex: - raise Exception('unapplied changes: %s' % changesindex) - oldconfig = Configuration(options=options, name=newconfig.name) - # read the old config - oldconfig.load_file_configuration(configfile) - # apply values reverting changes - changes.reverse() - done = set() - for action in changes: - if action[0] == 'renamed': - oldname, newname = action[1:] - newconfig[newname] = oldconfig[oldname] - done.add(newname) - elif action[0] == 'typechanged': - optname, oldtype, newvalue = action[1:] - newconfig[optname] = newvalue - done.add(optname) - for optname, optdef in newconfig.options: - if optdef.get('type') and not optname in done: - newconfig.set_option(optname, oldconfig[optname], optdict=optdef) - - -def merge_options(options, optgroup=None): - """preprocess a list of options and remove duplicates, returning a new list - (tuple actually) of options. - - Options dictionaries are copied to avoid later side-effect. Also, if - `otpgroup` argument is specified, ensure all options are in the given group. - """ - alloptions = {} - options = list(options) - for i in range(len(options)-1, -1, -1): - optname, optdict = options[i] - if optname in alloptions: - options.pop(i) - alloptions[optname].update(optdict) - else: - optdict = optdict.copy() - options[i] = (optname, optdict) - alloptions[optname] = optdict - if optgroup is not None: - alloptions[optname]['group'] = optgroup - return tuple(options) diff --git a/pymode/libs/logilab/common/daemon.py b/pymode/libs/logilab/common/daemon.py deleted file mode 100644 index 40319a43..00000000 --- a/pymode/libs/logilab/common/daemon.py +++ /dev/null @@ -1,101 +0,0 @@ -# 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 . -"""A daemonize function (for Unices)""" - -__docformat__ = "restructuredtext en" - -import os -import errno -import signal -import sys -import time -import warnings - -from six.moves import range - -def setugid(user): - """Change process user and group ID - - Argument is a numeric user id or a user name""" - try: - from pwd import getpwuid - passwd = getpwuid(int(user)) - except ValueError: - from pwd import getpwnam - passwd = getpwnam(user) - - if hasattr(os, 'initgroups'): # python >= 2.7 - os.initgroups(passwd.pw_name, passwd.pw_gid) - else: - import ctypes - if ctypes.CDLL(None).initgroups(passwd.pw_name, passwd.pw_gid) < 0: - err = ctypes.c_int.in_dll(ctypes.pythonapi,"errno").value - raise OSError(err, os.strerror(err), 'initgroups') - os.setgid(passwd.pw_gid) - os.setuid(passwd.pw_uid) - os.environ['HOME'] = passwd.pw_dir - - -def daemonize(pidfile=None, uid=None, umask=0o77): - """daemonize a Unix process. Set paranoid umask by default. - - Return 1 in the original process, 2 in the first fork, and None for the - second fork (eg daemon process). - """ - # http://www.faqs.org/faqs/unix-faq/programmer/faq/ - # - # fork so the parent can exit - if os.fork(): # launch child and... - return 1 - # disconnect from tty and create a new session - os.setsid() - # fork again so the parent, (the session group leader), can exit. - # as a non-session group leader, we can never regain a controlling - # terminal. - if os.fork(): # launch child again. - return 2 - # move to the root to avoit mount pb - os.chdir('/') - # redirect standard descriptors - null = os.open('/dev/null', os.O_RDWR) - for i in range(3): - try: - os.dup2(null, i) - except OSError as e: - if e.errno != errno.EBADF: - raise - os.close(null) - # filter warnings - warnings.filterwarnings('ignore') - # write pid in a file - if pidfile: - # ensure the directory where the pid-file should be set exists (for - # instance /var/run/cubicweb may be deleted on computer restart) - piddir = os.path.dirname(pidfile) - if not os.path.exists(piddir): - os.makedirs(piddir) - f = file(pidfile, 'w') - f.write(str(os.getpid())) - f.close() - # set umask if specified - if umask is not None: - os.umask(umask) - # change process uid - if uid: - setugid(uid) - return None diff --git a/pymode/libs/logilab/common/date.py b/pymode/libs/logilab/common/date.py deleted file mode 100644 index a093a8a9..00000000 --- a/pymode/libs/logilab/common/date.py +++ /dev/null @@ -1,335 +0,0 @@ -# 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 . -"""Date manipulation helper functions.""" -from __future__ import division - -__docformat__ = "restructuredtext en" - -import math -import re -import sys -from locale import getlocale, LC_TIME -from datetime import date, time, datetime, timedelta -from time import strptime as time_strptime -from calendar import monthrange, timegm - -from six.moves import range - -try: - from mx.DateTime import RelativeDateTime, Date, DateTimeType -except ImportError: - endOfMonth = None - DateTimeType = datetime -else: - endOfMonth = RelativeDateTime(months=1, day=-1) - -# NOTE: should we implement a compatibility layer between date representations -# as we have in lgc.db ? - -FRENCH_FIXED_HOLIDAYS = { - 'jour_an': '%s-01-01', - 'fete_travail': '%s-05-01', - 'armistice1945': '%s-05-08', - 'fete_nat': '%s-07-14', - 'assomption': '%s-08-15', - 'toussaint': '%s-11-01', - 'armistice1918': '%s-11-11', - 'noel': '%s-12-25', - } - -FRENCH_MOBILE_HOLIDAYS = { - 'paques2004': '2004-04-12', - 'ascension2004': '2004-05-20', - 'pentecote2004': '2004-05-31', - - 'paques2005': '2005-03-28', - 'ascension2005': '2005-05-05', - 'pentecote2005': '2005-05-16', - - 'paques2006': '2006-04-17', - 'ascension2006': '2006-05-25', - 'pentecote2006': '2006-06-05', - - 'paques2007': '2007-04-09', - 'ascension2007': '2007-05-17', - 'pentecote2007': '2007-05-28', - - 'paques2008': '2008-03-24', - 'ascension2008': '2008-05-01', - 'pentecote2008': '2008-05-12', - - 'paques2009': '2009-04-13', - 'ascension2009': '2009-05-21', - 'pentecote2009': '2009-06-01', - - 'paques2010': '2010-04-05', - 'ascension2010': '2010-05-13', - 'pentecote2010': '2010-05-24', - - 'paques2011': '2011-04-25', - 'ascension2011': '2011-06-02', - 'pentecote2011': '2011-06-13', - - 'paques2012': '2012-04-09', - 'ascension2012': '2012-05-17', - 'pentecote2012': '2012-05-28', - } - -# XXX this implementation cries for multimethod dispatching - -def get_step(dateobj, nbdays=1): - # assume date is either a python datetime or a mx.DateTime object - if isinstance(dateobj, date): - return ONEDAY * nbdays - return nbdays # mx.DateTime is ok with integers - -def datefactory(year, month, day, sampledate): - # assume date is either a python datetime or a mx.DateTime object - if isinstance(sampledate, datetime): - return datetime(year, month, day) - if isinstance(sampledate, date): - return date(year, month, day) - return Date(year, month, day) - -def weekday(dateobj): - # assume date is either a python datetime or a mx.DateTime object - if isinstance(dateobj, date): - return dateobj.weekday() - return dateobj.day_of_week - -def str2date(datestr, sampledate): - # NOTE: datetime.strptime is not an option until we drop py2.4 compat - year, month, day = [int(chunk) for chunk in datestr.split('-')] - return datefactory(year, month, day, sampledate) - -def days_between(start, end): - if isinstance(start, date): - delta = end - start - # datetime.timedelta.days is always an integer (floored) - if delta.seconds: - return delta.days + 1 - return delta.days - else: - return int(math.ceil((end - start).days)) - -def get_national_holidays(begin, end): - """return french national days off between begin and end""" - begin = datefactory(begin.year, begin.month, begin.day, begin) - end = datefactory(end.year, end.month, end.day, end) - holidays = [str2date(datestr, begin) - for datestr in FRENCH_MOBILE_HOLIDAYS.values()] - for year in range(begin.year, end.year+1): - for datestr in FRENCH_FIXED_HOLIDAYS.values(): - date = str2date(datestr % year, begin) - if date not in holidays: - holidays.append(date) - return [day for day in holidays if begin <= day < end] - -def add_days_worked(start, days): - """adds date but try to only take days worked into account""" - step = get_step(start) - weeks, plus = divmod(days, 5) - end = start + ((weeks * 7) + plus) * step - if weekday(end) >= 5: # saturday or sunday - end += (2 * step) - end += len([x for x in get_national_holidays(start, end + step) - if weekday(x) < 5]) * step - if weekday(end) >= 5: # saturday or sunday - end += (2 * step) - return end - -def nb_open_days(start, end): - assert start <= end - step = get_step(start) - days = days_between(start, end) - weeks, plus = divmod(days, 7) - if weekday(start) > weekday(end): - plus -= 2 - elif weekday(end) == 6: - plus -= 1 - open_days = weeks * 5 + plus - nb_week_holidays = len([x for x in get_national_holidays(start, end+step) - if weekday(x) < 5 and x < end]) - open_days -= nb_week_holidays - if open_days < 0: - return 0 - return open_days - -def date_range(begin, end, incday=None, incmonth=None): - """yields each date between begin and end - - :param begin: the start date - :param end: the end date - :param incr: the step to use to iterate over dates. Default is - one day. - :param include: None (means no exclusion) or a function taking a - date as parameter, and returning True if the date - should be included. - - When using mx datetime, you should *NOT* use incmonth argument, use instead - oneDay, oneHour, oneMinute, oneSecond, oneWeek or endOfMonth (to enumerate - months) as `incday` argument - """ - assert not (incday and incmonth) - begin = todate(begin) - end = todate(end) - if incmonth: - while begin < end: - yield begin - begin = next_month(begin, incmonth) - else: - incr = get_step(begin, incday or 1) - while begin < end: - yield begin - begin += incr - -# makes py datetime usable ##################################################### - -ONEDAY = timedelta(days=1) -ONEWEEK = timedelta(days=7) - -try: - strptime = datetime.strptime -except AttributeError: # py < 2.5 - from time import strptime as time_strptime - def strptime(value, format): - return datetime(*time_strptime(value, format)[:6]) - -def strptime_time(value, format='%H:%M'): - return time(*time_strptime(value, format)[3:6]) - -def todate(somedate): - """return a date from a date (leaving unchanged) or a datetime""" - if isinstance(somedate, datetime): - return date(somedate.year, somedate.month, somedate.day) - assert isinstance(somedate, (date, DateTimeType)), repr(somedate) - return somedate - -def totime(somedate): - """return a time from a time (leaving unchanged), date or datetime""" - # XXX mx compat - if not isinstance(somedate, time): - return time(somedate.hour, somedate.minute, somedate.second) - assert isinstance(somedate, (time)), repr(somedate) - return somedate - -def todatetime(somedate): - """return a date from a date (leaving unchanged) or a datetime""" - # take care, datetime is a subclass of date - if isinstance(somedate, datetime): - return somedate - assert isinstance(somedate, (date, DateTimeType)), repr(somedate) - return datetime(somedate.year, somedate.month, somedate.day) - -def datetime2ticks(somedate): - return timegm(somedate.timetuple()) * 1000 - -def ticks2datetime(ticks): - miliseconds, microseconds = divmod(ticks, 1000) - try: - return datetime.fromtimestamp(miliseconds) - except (ValueError, OverflowError): - epoch = datetime.fromtimestamp(0) - nb_days, seconds = divmod(int(miliseconds), 86400) - delta = timedelta(nb_days, seconds=seconds, microseconds=microseconds) - try: - return epoch + delta - except (ValueError, OverflowError): - raise - -def days_in_month(somedate): - return monthrange(somedate.year, somedate.month)[1] - -def days_in_year(somedate): - feb = date(somedate.year, 2, 1) - if days_in_month(feb) == 29: - return 366 - else: - return 365 - -def previous_month(somedate, nbmonth=1): - while nbmonth: - somedate = first_day(somedate) - ONEDAY - nbmonth -= 1 - return somedate - -def next_month(somedate, nbmonth=1): - while nbmonth: - somedate = last_day(somedate) + ONEDAY - nbmonth -= 1 - return somedate - -def first_day(somedate): - return date(somedate.year, somedate.month, 1) - -def last_day(somedate): - return date(somedate.year, somedate.month, days_in_month(somedate)) - -def ustrftime(somedate, fmt='%Y-%m-%d'): - """like strftime, but returns a unicode string instead of an encoded - string which may be problematic with localized date. - """ - if sys.version_info >= (3, 3): - # datetime.date.strftime() supports dates since year 1 in Python >=3.3. - return somedate.strftime(fmt) - else: - try: - if sys.version_info < (3, 0): - encoding = getlocale(LC_TIME)[1] or 'ascii' - return unicode(somedate.strftime(str(fmt)), encoding) - else: - return somedate.strftime(fmt) - except ValueError: - if somedate.year >= 1900: - raise - # datetime is not happy with dates before 1900 - # we try to work around this, assuming a simple - # format string - fields = {'Y': somedate.year, - 'm': somedate.month, - 'd': somedate.day, - } - if isinstance(somedate, datetime): - fields.update({'H': somedate.hour, - 'M': somedate.minute, - 'S': somedate.second}) - fmt = re.sub('%([YmdHMS])', r'%(\1)02d', fmt) - return unicode(fmt) % fields - -def utcdatetime(dt): - if dt.tzinfo is None: - return dt - return (dt.replace(tzinfo=None) - dt.utcoffset()) - -def utctime(dt): - if dt.tzinfo is None: - return dt - return (dt + dt.utcoffset() + dt.dst()).replace(tzinfo=None) - -def datetime_to_seconds(date): - """return the number of seconds since the begining of the day for that date - """ - return date.second+60*date.minute + 3600*date.hour - -def timedelta_to_days(delta): - """return the time delta as a number of seconds""" - return delta.days + delta.seconds / (3600*24) - -def timedelta_to_seconds(delta): - """return the time delta as a fraction of days""" - return delta.days*(3600*24) + delta.seconds diff --git a/pymode/libs/logilab/common/debugger.py b/pymode/libs/logilab/common/debugger.py deleted file mode 100644 index 1f540a18..00000000 --- a/pymode/libs/logilab/common/debugger.py +++ /dev/null @@ -1,214 +0,0 @@ -# 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 . -"""Customized version of pdb's default debugger. - -- sets up a history file -- uses ipython if available to colorize lines of code -- overrides list command to search for current block instead - of using 5 lines of context - - - - -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -try: - import readline -except ImportError: - readline = None -import os -import os.path as osp -import sys -from pdb import Pdb -import inspect - -from logilab.common.compat import StringIO - -try: - from IPython import PyColorize -except ImportError: - def colorize(source, *args): - """fallback colorize function""" - return source - def colorize_source(source, *args): - return source -else: - def colorize(source, start_lineno, curlineno): - """colorize and annotate source with linenos - (as in pdb's list command) - """ - parser = PyColorize.Parser() - output = StringIO() - parser.format(source, output) - annotated = [] - for index, line in enumerate(output.getvalue().splitlines()): - lineno = index + start_lineno - if lineno == curlineno: - annotated.append('%4s\t->\t%s' % (lineno, line)) - else: - annotated.append('%4s\t\t%s' % (lineno, line)) - return '\n'.join(annotated) - - def colorize_source(source): - """colorize given source""" - parser = PyColorize.Parser() - output = StringIO() - parser.format(source, output) - return output.getvalue() - - -def getsource(obj): - """Return the text of the source code for an object. - - The argument may be a module, class, method, function, traceback, frame, - or code object. The source code is returned as a single string. An - IOError is raised if the source code cannot be retrieved.""" - lines, lnum = inspect.getsourcelines(obj) - return ''.join(lines), lnum - - -################################################################ -class Debugger(Pdb): - """custom debugger - - - sets up a history file - - uses ipython if available to colorize lines of code - - overrides list command to search for current block instead - of using 5 lines of context - """ - def __init__(self, tcbk=None): - Pdb.__init__(self) - self.reset() - if tcbk: - while tcbk.tb_next is not None: - tcbk = tcbk.tb_next - self._tcbk = tcbk - self._histfile = os.path.expanduser("~/.pdbhist") - - def setup_history_file(self): - """if readline is available, read pdb history file - """ - if readline is not None: - try: - # XXX try..except shouldn't be necessary - # read_history_file() can accept None - readline.read_history_file(self._histfile) - except IOError: - pass - - def start(self): - """starts the interactive mode""" - self.interaction(self._tcbk.tb_frame, self._tcbk) - - def setup(self, frame, tcbk): - """setup hook: set up history file""" - self.setup_history_file() - Pdb.setup(self, frame, tcbk) - - def set_quit(self): - """quit hook: save commands in the history file""" - if readline is not None: - readline.write_history_file(self._histfile) - Pdb.set_quit(self) - - def complete_p(self, text, line, begin_idx, end_idx): - """provide variable names completion for the ``p`` command""" - namespace = dict(self.curframe.f_globals) - namespace.update(self.curframe.f_locals) - if '.' in text: - return self.attr_matches(text, namespace) - return [varname for varname in namespace if varname.startswith(text)] - - - def attr_matches(self, text, namespace): - """implementation coming from rlcompleter.Completer.attr_matches - Compute matches when text contains a dot. - - Assuming the text is of the form NAME.NAME....[NAME], and is - evaluatable in self.namespace, it will be evaluated and its attributes - (as revealed by dir()) are used as possible completions. (For class - instances, class members are also considered.) - - WARNING: this can still invoke arbitrary C code, if an object - with a __getattr__ hook is evaluated. - - """ - import re - m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) - if not m: - return - expr, attr = m.group(1, 3) - object = eval(expr, namespace) - words = dir(object) - if hasattr(object, '__class__'): - words.append('__class__') - words = words + self.get_class_members(object.__class__) - matches = [] - n = len(attr) - for word in words: - if word[:n] == attr and word != "__builtins__": - matches.append("%s.%s" % (expr, word)) - return matches - - def get_class_members(self, klass): - """implementation coming from rlcompleter.get_class_members""" - ret = dir(klass) - if hasattr(klass, '__bases__'): - for base in klass.__bases__: - ret = ret + self.get_class_members(base) - return ret - - ## specific / overridden commands - def do_list(self, arg): - """overrides default list command to display the surrounding block - instead of 5 lines of context - """ - self.lastcmd = 'list' - if not arg: - try: - source, start_lineno = getsource(self.curframe) - print(colorize(''.join(source), start_lineno, - self.curframe.f_lineno)) - except KeyboardInterrupt: - pass - except IOError: - Pdb.do_list(self, arg) - else: - Pdb.do_list(self, arg) - do_l = do_list - - def do_open(self, arg): - """opens source file corresponding to the current stack level""" - filename = self.curframe.f_code.co_filename - lineno = self.curframe.f_lineno - cmd = 'emacsclient --no-wait +%s %s' % (lineno, filename) - os.system(cmd) - - do_o = do_open - -def pm(): - """use our custom debugger""" - dbg = Debugger(sys.last_traceback) - dbg.start() - -def set_trace(): - Debugger().set_trace(sys._getframe().f_back) diff --git a/pymode/libs/logilab/common/decorators.py b/pymode/libs/logilab/common/decorators.py deleted file mode 100644 index beafa202..00000000 --- a/pymode/libs/logilab/common/decorators.py +++ /dev/null @@ -1,281 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of 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 . -""" A few useful function/method decorators. """ - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import types -from time import clock, time -from inspect import isgeneratorfunction, getargspec - -from logilab.common.compat import method_type - -# XXX rewrite so we can use the decorator syntax when keyarg has to be specified - -class cached_decorator(object): - def __init__(self, cacheattr=None, keyarg=None): - self.cacheattr = cacheattr - self.keyarg = keyarg - def __call__(self, callableobj=None): - assert not isgeneratorfunction(callableobj), \ - 'cannot cache generator function: %s' % callableobj - if len(getargspec(callableobj).args) == 1 or self.keyarg == 0: - cache = _SingleValueCache(callableobj, self.cacheattr) - elif self.keyarg: - cache = _MultiValuesKeyArgCache(callableobj, self.keyarg, self.cacheattr) - else: - cache = _MultiValuesCache(callableobj, self.cacheattr) - return cache.closure() - -class _SingleValueCache(object): - def __init__(self, callableobj, cacheattr=None): - self.callable = callableobj - if cacheattr is None: - self.cacheattr = '_%s_cache_' % callableobj.__name__ - else: - assert cacheattr != callableobj.__name__ - self.cacheattr = cacheattr - - def __call__(__me, self, *args): - try: - return self.__dict__[__me.cacheattr] - except KeyError: - value = __me.callable(self, *args) - setattr(self, __me.cacheattr, value) - return value - - def closure(self): - def wrapped(*args, **kwargs): - return self.__call__(*args, **kwargs) - wrapped.cache_obj = self - try: - wrapped.__doc__ = self.callable.__doc__ - wrapped.__name__ = self.callable.__name__ - except: - pass - return wrapped - - def clear(self, holder): - holder.__dict__.pop(self.cacheattr, None) - - -class _MultiValuesCache(_SingleValueCache): - def _get_cache(self, holder): - try: - _cache = holder.__dict__[self.cacheattr] - except KeyError: - _cache = {} - setattr(holder, self.cacheattr, _cache) - return _cache - - def __call__(__me, self, *args, **kwargs): - _cache = __me._get_cache(self) - try: - return _cache[args] - except KeyError: - _cache[args] = __me.callable(self, *args) - return _cache[args] - -class _MultiValuesKeyArgCache(_MultiValuesCache): - def __init__(self, callableobj, keyarg, cacheattr=None): - super(_MultiValuesKeyArgCache, self).__init__(callableobj, cacheattr) - self.keyarg = keyarg - - def __call__(__me, self, *args, **kwargs): - _cache = __me._get_cache(self) - key = args[__me.keyarg-1] - try: - return _cache[key] - except KeyError: - _cache[key] = __me.callable(self, *args, **kwargs) - return _cache[key] - - -def cached(callableobj=None, keyarg=None, **kwargs): - """Simple decorator to cache result of method call.""" - kwargs['keyarg'] = keyarg - decorator = cached_decorator(**kwargs) - if callableobj is None: - return decorator - else: - return decorator(callableobj) - - -class cachedproperty(object): - """ Provides a cached property equivalent to the stacking of - @cached and @property, but more efficient. - - After first usage, the becomes part of the object's - __dict__. Doing: - - del obj. empties the cache. - - Idea taken from the pyramid_ framework and the mercurial_ project. - - .. _pyramid: http://pypi.python.org/pypi/pyramid - .. _mercurial: http://pypi.python.org/pypi/Mercurial - """ - __slots__ = ('wrapped',) - - def __init__(self, wrapped): - try: - wrapped.__name__ - except AttributeError: - raise TypeError('%s must have a __name__ attribute' % - wrapped) - self.wrapped = wrapped - - @property - def __doc__(self): - doc = getattr(self.wrapped, '__doc__', None) - return ('%s' - % ('\n%s' % doc if doc else '')) - - def __get__(self, inst, objtype=None): - if inst is None: - return self - val = self.wrapped(inst) - setattr(inst, self.wrapped.__name__, val) - return val - - -def get_cache_impl(obj, funcname): - cls = obj.__class__ - member = getattr(cls, funcname) - if isinstance(member, property): - member = member.fget - return member.cache_obj - -def clear_cache(obj, funcname): - """Clear a cache handled by the :func:`cached` decorator. If 'x' class has - @cached on its method `foo`, type - - >>> clear_cache(x, 'foo') - - to purge this method's cache on the instance. - """ - get_cache_impl(obj, funcname).clear(obj) - -def copy_cache(obj, funcname, cacheobj): - """Copy cache for from cacheobj to obj.""" - cacheattr = get_cache_impl(obj, funcname).cacheattr - try: - setattr(obj, cacheattr, cacheobj.__dict__[cacheattr]) - except KeyError: - pass - - -class wproperty(object): - """Simple descriptor expecting to take a modifier function as first argument - and looking for a _ to retrieve the attribute. - """ - def __init__(self, setfunc): - self.setfunc = setfunc - self.attrname = '_%s' % setfunc.__name__ - - def __set__(self, obj, value): - self.setfunc(obj, value) - - def __get__(self, obj, cls): - assert obj is not None - return getattr(obj, self.attrname) - - -class classproperty(object): - """this is a simple property-like class but for class attributes. - """ - def __init__(self, get): - self.get = get - def __get__(self, inst, cls): - return self.get(cls) - - -class iclassmethod(object): - '''Descriptor for method which should be available as class method if called - on the class or instance method if called on an instance. - ''' - def __init__(self, func): - self.func = func - def __get__(self, instance, objtype): - if instance is None: - return method_type(self.func, objtype, objtype.__class__) - return method_type(self.func, instance, objtype) - def __set__(self, instance, value): - raise AttributeError("can't set attribute") - - -def timed(f): - def wrap(*args, **kwargs): - t = time() - c = clock() - res = f(*args, **kwargs) - print('%s clock: %.9f / time: %.9f' % (f.__name__, - clock() - c, time() - t)) - return res - return wrap - - -def locked(acquire, release): - """Decorator taking two methods to acquire/release a lock as argument, - returning a decorator function which will call the inner method after - having called acquire(self) et will call release(self) afterwards. - """ - def decorator(f): - def wrapper(self, *args, **kwargs): - acquire(self) - try: - return f(self, *args, **kwargs) - finally: - release(self) - return wrapper - return decorator - - -def monkeypatch(klass, methodname=None): - """Decorator extending class with the decorated callable. This is basically - a syntactic sugar vs class assignment. - - >>> class A: - ... pass - >>> @monkeypatch(A) - ... def meth(self): - ... return 12 - ... - >>> a = A() - >>> a.meth() - 12 - >>> @monkeypatch(A, 'foo') - ... def meth(self): - ... return 12 - ... - >>> a.foo() - 12 - """ - def decorator(func): - try: - name = methodname or func.__name__ - except AttributeError: - raise AttributeError('%s has no __name__ attribute: ' - 'you should provide an explicit `methodname`' - % func) - setattr(klass, name, func) - return func - return decorator diff --git a/pymode/libs/logilab/common/deprecation.py b/pymode/libs/logilab/common/deprecation.py deleted file mode 100644 index 1c81b638..00000000 --- a/pymode/libs/logilab/common/deprecation.py +++ /dev/null @@ -1,189 +0,0 @@ -# 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 . -"""Deprecation utilities.""" - -__docformat__ = "restructuredtext en" - -import sys -from warnings import warn - -from logilab.common.changelog import Version - - -class DeprecationWrapper(object): - """proxy to print a warning on access to any attribute of the wrapped object - """ - def __init__(self, proxied, msg=None): - self._proxied = proxied - self._msg = msg - - def __getattr__(self, attr): - warn(self._msg, DeprecationWarning, stacklevel=2) - return getattr(self._proxied, attr) - - def __setattr__(self, attr, value): - if attr in ('_proxied', '_msg'): - self.__dict__[attr] = value - else: - warn(self._msg, DeprecationWarning, stacklevel=2) - setattr(self._proxied, attr, value) - - -class DeprecationManager(object): - """Manage the deprecation message handling. Messages are dropped for - versions more recent than the 'compatible' version. Example:: - - deprecator = deprecation.DeprecationManager("module_name") - deprecator.compatibility('1.3') - - deprecator.warn('1.2', "message.") - - @deprecator.deprecated('1.2', 'Message') - def any_func(): - pass - - class AnyClass(object): - __metaclass__ = deprecator.class_deprecated('1.2') - """ - def __init__(self, module_name=None): - """ - """ - self.module_name = module_name - self.compatible_version = None - - def compatibility(self, compatible_version): - """Set the compatible version. - """ - self.compatible_version = Version(compatible_version) - - def deprecated(self, version=None, reason=None, stacklevel=2, name=None, doc=None): - """Display a deprecation message only if the version is older than the - compatible version. - """ - def decorator(func): - message = reason or 'The function "%s" is deprecated' - if '%s' in message: - message %= func.__name__ - def wrapped(*args, **kwargs): - self.warn(version, message, stacklevel+1) - return func(*args, **kwargs) - return wrapped - return decorator - - def class_deprecated(self, version=None): - class metaclass(type): - """metaclass to print a warning on instantiation of a deprecated class""" - - def __call__(cls, *args, **kwargs): - msg = getattr(cls, "__deprecation_warning__", - "%(cls)s is deprecated") % {'cls': cls.__name__} - self.warn(version, msg, stacklevel=3) - return type.__call__(cls, *args, **kwargs) - return metaclass - - def moved(self, version, modpath, objname): - """use to tell that a callable has been moved to a new module. - - It returns a callable wrapper, so that when its called a warning is printed - telling where the object can be found, import is done (and not before) and - the actual object is called. - - NOTE: the usage is somewhat limited on classes since it will fail if the - wrapper is use in a class ancestors list, use the `class_moved` function - instead (which has no lazy import feature though). - """ - def callnew(*args, **kwargs): - from logilab.common.modutils import load_module_from_name - message = "object %s has been moved to module %s" % (objname, modpath) - self.warn(version, message) - m = load_module_from_name(modpath) - return getattr(m, objname)(*args, **kwargs) - return callnew - - def class_renamed(self, version, old_name, new_class, message=None): - clsdict = {} - if message is None: - message = '%s is deprecated, use %s' % (old_name, new_class.__name__) - clsdict['__deprecation_warning__'] = message - try: - # new-style class - return self.class_deprecated(version)(old_name, (new_class,), clsdict) - except (NameError, TypeError): - # old-style class - warn = self.warn - class DeprecatedClass(new_class): - """FIXME: There might be a better way to handle old/new-style class - """ - def __init__(self, *args, **kwargs): - warn(version, message, stacklevel=3) - new_class.__init__(self, *args, **kwargs) - return DeprecatedClass - - def class_moved(self, version, new_class, old_name=None, message=None): - """nice wrapper around class_renamed when a class has been moved into - another module - """ - if old_name is None: - old_name = new_class.__name__ - if message is None: - message = 'class %s is now available as %s.%s' % ( - old_name, new_class.__module__, new_class.__name__) - return self.class_renamed(version, old_name, new_class, message) - - def warn(self, version=None, reason="", stacklevel=2): - """Display a deprecation message only if the version is older than the - compatible version. - """ - if (self.compatible_version is None - or version is None - or Version(version) < self.compatible_version): - if self.module_name and version: - reason = '[%s %s] %s' % (self.module_name, version, reason) - elif self.module_name: - reason = '[%s] %s' % (self.module_name, reason) - elif version: - reason = '[%s] %s' % (version, reason) - warn(reason, DeprecationWarning, stacklevel=stacklevel) - -_defaultdeprecator = DeprecationManager() - -def deprecated(reason=None, stacklevel=2, name=None, doc=None): - return _defaultdeprecator.deprecated(None, reason, stacklevel, name, doc) - -class_deprecated = _defaultdeprecator.class_deprecated() - -def moved(modpath, objname): - return _defaultdeprecator.moved(None, modpath, objname) -moved.__doc__ = _defaultdeprecator.moved.__doc__ - -def class_renamed(old_name, new_class, message=None): - """automatically creates a class which fires a DeprecationWarning - when instantiated. - - >>> Set = class_renamed('Set', set, 'Set is now replaced by set') - >>> s = Set() - sample.py:57: DeprecationWarning: Set is now replaced by set - s = Set() - >>> - """ - return _defaultdeprecator.class_renamed(None, old_name, new_class, message) - -def class_moved(new_class, old_name=None, message=None): - return _defaultdeprecator.class_moved(None, new_class, old_name, message) -class_moved.__doc__ = _defaultdeprecator.class_moved.__doc__ - diff --git a/pymode/libs/logilab/common/fileutils.py b/pymode/libs/logilab/common/fileutils.py deleted file mode 100644 index b30cf5f8..00000000 --- a/pymode/libs/logilab/common/fileutils.py +++ /dev/null @@ -1,404 +0,0 @@ -# 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 . -"""File and file-path manipulation utilities. - -:group path manipulation: first_level_directory, relative_path, is_binary,\ -get_by_ext, remove_dead_links -:group file manipulation: norm_read, norm_open, lines, stream_lines, lines,\ -write_open_mode, ensure_fs_mode, export -:sort: path manipulation, file manipulation -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import shutil -import mimetypes -from os.path import isabs, isdir, islink, split, exists, normpath, join -from os.path import abspath -from os import sep, mkdir, remove, listdir, stat, chmod, walk -from stat import ST_MODE, S_IWRITE - -from logilab.common import STD_BLACKLIST as BASE_BLACKLIST, IGNORED_EXTENSIONS -from logilab.common.shellutils import find -from logilab.common.deprecation import deprecated -from logilab.common.compat import FileIO - -def first_level_directory(path): - """Return the first level directory of a path. - - >>> first_level_directory('home/syt/work') - 'home' - >>> first_level_directory('/home/syt/work') - '/' - >>> first_level_directory('work') - 'work' - >>> - - :type path: str - :param path: the path for which we want the first level directory - - :rtype: str - :return: the first level directory appearing in `path` - """ - head, tail = split(path) - while head and tail: - head, tail = split(head) - if tail: - return tail - # path was absolute, head is the fs root - 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'] - """ - path = abspath(path) - return [join(path, filename) for filename in listdir(path)] - - -def is_binary(filename): - """Return true if filename may be a binary file, according to it's - extension. - - :type filename: str - :param filename: the name of the file - - :rtype: bool - :return: - true if the file is a binary file (actually if it's mime type - isn't beginning by text/) - """ - try: - return not mimetypes.guess_type(filename)[0].startswith('text') - except AttributeError: - return 1 - - -def write_open_mode(filename): - """Return the write mode that should used to open file. - - :type filename: str - :param filename: the name of the file - - :rtype: str - :return: the mode that should be use to open the file ('w' or 'wb') - """ - if is_binary(filename): - return 'wb' - return 'w' - - -def ensure_fs_mode(filepath, desired_mode=S_IWRITE): - """Check that the given file has the given mode(s) set, else try to - set it. - - :type filepath: str - :param filepath: path of the file - - :type desired_mode: int - :param desired_mode: - ORed flags describing the desired mode. Use constants from the - `stat` module for file permission's modes - """ - mode = stat(filepath)[ST_MODE] - if not mode & desired_mode: - chmod(filepath, mode | desired_mode) - - -# XXX (syt) unused? kill? -class ProtectedFile(FileIO): - """A special file-object class that automatically does a 'chmod +w' when - needed. - - XXX: for now, the way it is done allows 'normal file-objects' to be - created during the ProtectedFile object lifetime. - One way to circumvent this would be to chmod / unchmod on each - write operation. - - One other way would be to : - - - catch the IOError in the __init__ - - - if IOError, then create a StringIO object - - - each write operation writes in this StringIO object - - - on close()/del(), write/append the StringIO content to the file and - do the chmod only once - """ - def __init__(self, filepath, mode): - self.original_mode = stat(filepath)[ST_MODE] - self.mode_changed = False - if mode in ('w', 'a', 'wb', 'ab'): - if not self.original_mode & S_IWRITE: - chmod(filepath, self.original_mode | S_IWRITE) - self.mode_changed = True - FileIO.__init__(self, filepath, mode) - - def _restore_mode(self): - """restores the original mode if needed""" - if self.mode_changed: - chmod(self.name, self.original_mode) - # Don't re-chmod in case of several restore - self.mode_changed = False - - def close(self): - """restore mode before closing""" - self._restore_mode() - FileIO.close(self) - - def __del__(self): - if not self.closed: - self.close() - - -class UnresolvableError(Exception): - """Exception raised by relative path when it's unable to compute relative - path between two paths. - """ - -def relative_path(from_file, to_file): - """Try to get a relative path from `from_file` to `to_file` - (path will be absolute if to_file is an absolute file). This function - is useful to create link in `from_file` to `to_file`. This typical use - case is used in this function description. - - If both files are relative, they're expected to be relative to the same - directory. - - >>> relative_path( from_file='toto/index.html', to_file='index.html') - '../index.html' - >>> relative_path( from_file='index.html', to_file='toto/index.html') - 'toto/index.html' - >>> relative_path( from_file='tutu/index.html', to_file='toto/index.html') - '../toto/index.html' - >>> relative_path( from_file='toto/index.html', to_file='/index.html') - '/index.html' - >>> relative_path( from_file='/toto/index.html', to_file='/index.html') - '../index.html' - >>> relative_path( from_file='/toto/index.html', to_file='/toto/summary.html') - 'summary.html' - >>> relative_path( from_file='index.html', to_file='index.html') - '' - >>> relative_path( from_file='/index.html', to_file='toto/index.html') - Traceback (most recent call last): - File "", line 1, in ? - File "", line 37, in relative_path - UnresolvableError - >>> relative_path( from_file='/index.html', to_file='/index.html') - '' - >>> - - :type from_file: str - :param from_file: source file (where links will be inserted) - - :type to_file: str - :param to_file: target file (on which links point) - - :raise UnresolvableError: if it has been unable to guess a correct path - - :rtype: str - :return: the relative path of `to_file` from `from_file` - """ - from_file = normpath(from_file) - to_file = normpath(to_file) - if from_file == to_file: - return '' - if isabs(to_file): - if not isabs(from_file): - return to_file - elif isabs(from_file): - raise UnresolvableError() - from_parts = from_file.split(sep) - to_parts = to_file.split(sep) - idem = 1 - result = [] - while len(from_parts) > 1: - dirname = from_parts.pop(0) - if idem and len(to_parts) > 1 and dirname == to_parts[0]: - to_parts.pop(0) - else: - idem = 0 - result.append('..') - result += to_parts - return sep.join(result) - - -def norm_read(path): - """Return the content of the file with normalized line feeds. - - :type path: str - :param path: path to the file to read - - :rtype: str - :return: the content of the file with normalized line feeds - """ - return open(path, 'U').read() -norm_read = deprecated("use \"open(path, 'U').read()\"")(norm_read) - -def norm_open(path): - """Return a stream for a file with content with normalized line feeds. - - :type path: str - :param path: path to the file to open - - :rtype: file or StringIO - :return: the opened file with normalized line feeds - """ - return open(path, 'U') -norm_open = deprecated("use \"open(path, 'U')\"")(norm_open) - -def lines(path, comments=None): - """Return a list of non empty lines in the file located at `path`. - - :type path: str - :param path: path to the file - - :type comments: str or None - :param comments: - optional string which can be used to comment a line in the file - (i.e. lines starting with this string won't be returned) - - :rtype: list - :return: - a list of stripped line in the file, without empty and commented - lines - - :warning: at some point this function will probably return an iterator - """ - stream = open(path, 'U') - result = stream_lines(stream, comments) - stream.close() - return result - - -def stream_lines(stream, comments=None): - """Return a list of non empty lines in the given `stream`. - - :type stream: object implementing 'xreadlines' or 'readlines' - :param stream: file like object - - :type comments: str or None - :param comments: - optional string which can be used to comment a line in the file - (i.e. lines starting with this string won't be returned) - - :rtype: list - :return: - a list of stripped line in the file, without empty and commented - lines - - :warning: at some point this function will probably return an iterator - """ - try: - readlines = stream.xreadlines - except AttributeError: - readlines = stream.readlines - result = [] - for line in readlines(): - line = line.strip() - if line and (comments is None or not line.startswith(comments)): - result.append(line) - return result - - -def export(from_dir, to_dir, - blacklist=BASE_BLACKLIST, ignore_ext=IGNORED_EXTENSIONS, - verbose=0): - """Make a mirror of `from_dir` in `to_dir`, omitting directories and - files listed in the black list or ending with one of the given - extensions. - - :type from_dir: str - :param from_dir: directory to export - - :type to_dir: str - :param to_dir: destination directory - - :type blacklist: list or tuple - :param blacklist: - list of files or directories to ignore, default to the content of - `BASE_BLACKLIST` - - :type ignore_ext: list or tuple - :param ignore_ext: - list of extensions to ignore, default to the content of - `IGNORED_EXTENSIONS` - - :type verbose: bool - :param verbose: - flag indicating whether information about exported files should be - printed to stderr, default to False - """ - try: - mkdir(to_dir) - except OSError: - pass # FIXME we should use "exists" if the point is about existing dir - # else (permission problems?) shouldn't return / raise ? - for directory, dirnames, filenames in walk(from_dir): - for norecurs in blacklist: - try: - dirnames.remove(norecurs) - except ValueError: - continue - for dirname in dirnames: - src = join(directory, dirname) - dest = to_dir + src[len(from_dir):] - if isdir(src): - if not exists(dest): - mkdir(dest) - for filename in filenames: - # don't include binary files - # endswith does not accept tuple in 2.4 - if any([filename.endswith(ext) for ext in ignore_ext]): - continue - src = join(directory, filename) - dest = to_dir + src[len(from_dir):] - if verbose: - print(src, '->', dest, file=sys.stderr) - if exists(dest): - remove(dest) - shutil.copy2(src, dest) - - -def remove_dead_links(directory, verbose=0): - """Recursively traverse directory and remove all dead links. - - :type directory: str - :param directory: directory to cleanup - - :type verbose: bool - :param verbose: - flag indicating whether information about deleted links should be - printed to stderr, default to False - """ - for dirpath, dirname, filenames in walk(directory): - for filename in dirnames + filenames: - src = join(dirpath, filename) - if islink(src) and not exists(src): - if verbose: - print('remove dead link', src) - remove(src) - diff --git a/pymode/libs/logilab/common/graph.py b/pymode/libs/logilab/common/graph.py deleted file mode 100644 index cef1c984..00000000 --- a/pymode/libs/logilab/common/graph.py +++ /dev/null @@ -1,282 +0,0 @@ -# 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 . -"""Graph manipulation utilities. - -(dot generation adapted from pypy/translator/tool/make_dot.py) -""" - -__docformat__ = "restructuredtext en" - -__metaclass__ = type - -import os.path as osp -import os -import sys -import tempfile -import codecs -import errno - -def escape(value): - """Make usable in a dot file.""" - lines = [line.replace('"', '\\"') for line in value.split('\n')] - data = '\\l'.join(lines) - return '\\n' + data - -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: - """Dot File backend.""" - def __init__(self, graphname, rankdir=None, size=None, ratio=None, - charset='utf-8', renderer='dot', additionnal_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 sorted(additionnal_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 outputfile: filename and path [defaults to graphname.png] - :param dotfile: filename and path [defaults to graphname.dot] - - :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, basename, 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': - if sys.platform == 'win32': - use_shell = True - else: - use_shell = False - try: - 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) - except OSError as e: - if e.errno == errno.ENOENT: - e.strerror = 'File not found: {0}'.format(self.renderer) - raise - 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 - -class GraphGenerator: - def __init__(self, backend): - # the backend is responsible to output the graph in a particular format - self.backend = backend - - # XXX doesn't like space in outpufile / mapfile - def generate(self, visitor, propshdlr, outputfile=None, mapfile=None): - # the visitor - # the property handler is used to get node and edge properties - # according to the graph and to the backend - self.propshdlr = propshdlr - for nodeid, node in visitor.nodes(): - props = propshdlr.node_properties(node) - self.backend.emit_node(nodeid, **props) - for subjnode, objnode, edge in visitor.edges(): - props = propshdlr.edge_properties(edge, subjnode, objnode) - self.backend.emit_edge(subjnode, objnode, **props) - return self.backend.generate(outputfile=outputfile, mapfile=mapfile) - - -class UnorderableGraph(Exception): - pass - -def ordered_nodes(graph): - """takes a dependency graph dict as arguments and return an ordered tuple of - nodes starting with nodes without dependencies and up to the outermost node. - - If there is some cycle in the graph, :exc:`UnorderableGraph` will be raised. - - Also the given graph dict will be emptied. - """ - # check graph consistency - cycles = get_cycles(graph) - if cycles: - cycles = '\n'.join([' -> '.join(cycle) for cycle in cycles]) - raise UnorderableGraph('cycles in graph: %s' % cycles) - vertices = set(graph) - to_vertices = set() - for edges in graph.values(): - to_vertices |= set(edges) - missing_vertices = to_vertices - vertices - if missing_vertices: - raise UnorderableGraph('missing vertices: %s' % ', '.join(missing_vertices)) - # order vertices - order = [] - order_set = set() - old_len = None - while graph: - if old_len == len(graph): - raise UnorderableGraph('unknown problem with %s' % graph) - old_len = len(graph) - deps_ok = [] - for node, node_deps in graph.items(): - for dep in node_deps: - if dep not in order_set: - break - else: - deps_ok.append(node) - order.append(deps_ok) - order_set |= set(deps_ok) - for node in deps_ok: - del graph[node] - result = [] - for grp in reversed(order): - result.extend(sorted(grp)) - return tuple(result) - - -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 not cycle 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() - -def has_path(graph_dict, fromnode, tonode, path=None): - """generic function taking a simple graph definition as a dictionary, with - node has key associated to a list of nodes directly reachable from it. - - Return None if no path exists to go from `fromnode` to `tonode`, else the - first path found (as a list including the destination node at last) - """ - if path is None: - path = [] - elif fromnode in path: - return None - path.append(fromnode) - for destnode in graph_dict[fromnode]: - if destnode == tonode or has_path(graph_dict, destnode, tonode, path): - return path[1:] + [tonode] - path.pop() - return None - diff --git a/pymode/libs/logilab/common/interface.py b/pymode/libs/logilab/common/interface.py deleted file mode 100644 index 3ea4ab7e..00000000 --- a/pymode/libs/logilab/common/interface.py +++ /dev/null @@ -1,71 +0,0 @@ -# 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 . -"""Bases class for interfaces to provide 'light' interface handling. - - TODO: - _ implements a check method which check that an object implements the - interface - _ Attribute objects - - This module requires at least python 2.2 -""" -__docformat__ = "restructuredtext en" - - -class Interface(object): - """Base class for interfaces.""" - def is_implemented_by(cls, instance): - return implements(instance, cls) - is_implemented_by = classmethod(is_implemented_by) - - -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 - - -def extend(klass, interface, _recurs=False): - """Add interface to klass'__implements__ if not already implemented in. - - If klass is subclassed, ensure subclasses __implements__ it as well. - - NOTE: klass should be e new class. - """ - if not implements(klass, interface): - try: - kimplements = klass.__implements__ - kimplementsklass = type(kimplements) - kimplements = list(kimplements) - except AttributeError: - kimplementsklass = tuple - kimplements = [] - kimplements.append(interface) - klass.__implements__ = kimplementsklass(kimplements) - for subklass in klass.__subclasses__(): - extend(subklass, interface, _recurs=True) - elif _recurs: - for subklass in klass.__subclasses__(): - extend(subklass, interface, _recurs=True) diff --git a/pymode/libs/logilab/common/logging_ext.py b/pymode/libs/logilab/common/logging_ext.py deleted file mode 100644 index 3b6a580a..00000000 --- a/pymode/libs/logilab/common/logging_ext.py +++ /dev/null @@ -1,195 +0,0 @@ -# -*- 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 . -"""Extends the logging module from the standard library.""" - -__docformat__ = "restructuredtext en" - -import os -import sys -import logging - -from six import string_types - -from logilab.common.textutils import colorize_ansi - - -def set_log_methods(cls, logger): - """bind standard logger's methods as methods on the class""" - cls.__logger = logger - for attr in ('debug', 'info', 'warning', 'error', 'critical', 'exception'): - setattr(cls, attr, getattr(logger, attr)) - - -def xxx_cyan(record): - if 'XXX' in record.message: - return 'cyan' - -class ColorFormatter(logging.Formatter): - """ - A color Formatter for the logging standard module. - - By default, colorize CRITICAL and ERROR in red, WARNING in orange, INFO in - green and DEBUG in yellow. - - self.colors is customizable via the 'color' constructor argument (dictionary). - - self.colorfilters is a list of functions that get the LogRecord - and return a color name or None. - """ - - def __init__(self, fmt=None, datefmt=None, colors=None): - logging.Formatter.__init__(self, fmt, datefmt) - self.colorfilters = [] - self.colors = {'CRITICAL': 'red', - 'ERROR': 'red', - 'WARNING': 'magenta', - 'INFO': 'green', - 'DEBUG': 'yellow', - } - if colors is not None: - assert isinstance(colors, dict) - self.colors.update(colors) - - def format(self, record): - msg = logging.Formatter.format(self, record) - if record.levelname in self.colors: - color = self.colors[record.levelname] - return colorize_ansi(msg, color) - else: - for cf in self.colorfilters: - color = cf(record) - if color: - return colorize_ansi(msg, color) - return msg - -def set_color_formatter(logger=None, **kw): - """ - Install a color formatter on the 'logger'. If not given, it will - defaults to the default logger. - - Any additional keyword will be passed as-is to the ColorFormatter - constructor. - """ - if logger is None: - logger = logging.getLogger() - if not logger.handlers: - logging.basicConfig() - format_msg = logger.handlers[0].formatter._fmt - fmt = ColorFormatter(format_msg, **kw) - fmt.colorfilters.append(xxx_cyan) - logger.handlers[0].setFormatter(fmt) - - -LOG_FORMAT = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s' -LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' - -def get_handler(debug=False, syslog=False, logfile=None, rotation_parameters=None): - """get an apropriate handler according to given parameters""" - if os.environ.get('APYCOT_ROOT'): - handler = logging.StreamHandler(sys.stdout) - if debug: - handler = logging.StreamHandler() - elif logfile is None: - if syslog: - from logging import handlers - handler = handlers.SysLogHandler() - else: - handler = logging.StreamHandler() - else: - try: - if rotation_parameters is None: - if os.name == 'posix' and sys.version_info >= (2, 6): - from logging.handlers import WatchedFileHandler - handler = WatchedFileHandler(logfile) - else: - handler = logging.FileHandler(logfile) - else: - from logging.handlers import TimedRotatingFileHandler - handler = TimedRotatingFileHandler( - logfile, **rotation_parameters) - except IOError: - handler = logging.StreamHandler() - return handler - -def get_threshold(debug=False, logthreshold=None): - if logthreshold is None: - if debug: - logthreshold = logging.DEBUG - else: - logthreshold = logging.ERROR - elif isinstance(logthreshold, string_types): - logthreshold = getattr(logging, THRESHOLD_MAP.get(logthreshold, - logthreshold)) - return logthreshold - -def _colorable_terminal(): - isatty = hasattr(sys.__stdout__, 'isatty') and sys.__stdout__.isatty() - if not isatty: - return False - if os.name == 'nt': - try: - from colorama import init as init_win32_colors - except ImportError: - return False - init_win32_colors() - return True - -def get_formatter(logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT): - if _colorable_terminal(): - fmt = ColorFormatter(logformat, logdateformat) - def col_fact(record): - if 'XXX' in record.message: - return 'cyan' - if 'kick' in record.message: - return 'red' - fmt.colorfilters.append(col_fact) - else: - fmt = logging.Formatter(logformat, logdateformat) - return fmt - -def init_log(debug=False, syslog=False, logthreshold=None, logfile=None, - logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT, fmt=None, - rotation_parameters=None, handler=None): - """init the log service""" - logger = logging.getLogger() - if handler is None: - handler = get_handler(debug, syslog, logfile, rotation_parameters) - # only addHandler and removeHandler method while I would like a setHandler - # method, so do it this way :$ - logger.handlers = [handler] - logthreshold = get_threshold(debug, logthreshold) - logger.setLevel(logthreshold) - if fmt is None: - if debug: - fmt = get_formatter(logformat=logformat, logdateformat=logdateformat) - else: - fmt = logging.Formatter(logformat, logdateformat) - handler.setFormatter(fmt) - return handler - -# map logilab.common.logger thresholds to logging thresholds -THRESHOLD_MAP = {'LOG_DEBUG': 'DEBUG', - 'LOG_INFO': 'INFO', - 'LOG_NOTICE': 'INFO', - 'LOG_WARN': 'WARNING', - 'LOG_WARNING': 'WARNING', - 'LOG_ERR': 'ERROR', - 'LOG_ERROR': 'ERROR', - 'LOG_CRIT': 'CRITICAL', - } diff --git a/pymode/libs/logilab/common/modutils.py b/pymode/libs/logilab/common/modutils.py deleted file mode 100644 index dd725d24..00000000 --- a/pymode/libs/logilab/common/modutils.py +++ /dev/null @@ -1,713 +0,0 @@ -# -*- coding: utf-8 -*- -# 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 . -"""Python modules manipulation utility functions. - -:type PY_SOURCE_EXTS: tuple(str) -:var PY_SOURCE_EXTS: list of possible python source file extension - -:type STD_LIB_DIR: str -:var STD_LIB_DIR: directory where standard modules are located - -:type BUILTIN_MODULES: dict -:var BUILTIN_MODULES: dictionary with builtin module names as key -""" - -__docformat__ = "restructuredtext en" - -import sys -import os -from os.path import splitext, join, abspath, isdir, dirname, exists, basename -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 - -try: - import zipimport -except ImportError: - zipimport = None - -ZIPFILE = object() - -from logilab.common import STD_BLACKLIST, _handle_blacklist - -# Notes about STD_LIB_DIR -# Consider arch-specific installation for STD_LIB_DIR definition -# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on -# -# :see: `Problems with /usr/lib64 builds `_ -# :see: `FHS `_ -if sys.platform.startswith('win'): - PY_SOURCE_EXTS = ('py', 'pyw') - PY_COMPILED_EXTS = ('dll', 'pyd') -else: - PY_SOURCE_EXTS = ('py',) - PY_COMPILED_EXTS = ('so',) - -try: - STD_LIB_DIR = get_python_lib(standard_lib=True) -# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to -# non-valid path, see https://bugs.pypy.org/issue1164 -except DistutilsPlatformError: - STD_LIB_DIR = '//' - -EXT_LIB_DIR = get_python_lib() - -BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - -class LazyObject(object): - def __init__(self, module, obj): - self.module = module - self.obj = obj - self._imported = None - - def _getobj(self): - if self._imported is None: - self._imported = getattr(load_module_from_name(self.module), - self.obj) - return self._imported - - def __getattribute__(self, attr): - try: - return super(LazyObject, self).__getattribute__(attr) - except AttributeError as ex: - return getattr(self._getobj(), attr) - - def __call__(self, *args, **kwargs): - return self._getobj()(*args, **kwargs) - - -def load_module_from_name(dotted_name, path=None, use_sys=True): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=True): - """Load a python module from its splitted name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package splitted on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = find_module(part, path) - module = load_module(curname, mp_file, mp_filename, mp_desc) - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, '__file__', '') - prevmodule = module - if not _file and _is_namespace(curname): - continue - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):]) ) - path = [dirname( _file )] - return module - - -def load_module_from_file(filepath, path=None, use_sys=True, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def _check_init(path, mod_path): - """check there are some __init__.py all along the way""" - modpath = [] - for part in mod_path: - modpath.append(part) - path = join(path, part) - if not _is_namespace('.'.join(modpath)) and not _has_init(path): - return False - return True - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding splitted module's name - (i.e name of a module or package splitted on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package splitted in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding splitted module's name - """ - base = splitext(abspath(filename))[0] - if extrapath is not None: - for path_ in extrapath: - path = abspath(path_) - if path and base[:len(path)] == path: - submodpath = [pkg for pkg in base[len(path):].split(os.sep) - if pkg] - if _check_init(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - for path in sys.path: - path = abspath(path) - if path and base.startswith(path): - modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] - if _check_init(path, modpath[:-1]): - return modpath - raise ImportError('Unable to find module for %s in %s' % ( - filename, ', \n'.join(sys.path))) - - - -def file_from_modpath(modpath, path=None, context_file=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file, giving priority to source file over precompiled - file if it exists - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.') - (this means explicit relative imports that start with dots have - empty strings in this list!) - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the path to the module's file or None if it's an integrated - builtin module such as 'sys' - """ - if context_file is not None: - context = dirname(context_file) - else: - context = context_file - if modpath[0] == 'xml': - # handle _xmlplus - try: - return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context) - except ImportError: - return _file_from_modpath(modpath, path, context) - elif modpath == ['os', 'path']: - # FIXME: currently ignoring search_path... - return os.path.__file__ - return _file_from_modpath(modpath, path, context) - - - -def get_module_part(dotted_name, context_file=None): - """given a dotted name return the module part of the name : - - >>> get_module_part('logilab.common.modutils.get_module_part') - 'logilab.common.modutils' - - :type dotted_name: str - :param dotted_name: full name of the identifier we are interested in - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the module part of the name or None if we have not been able at - all to import the given name - - XXX: deprecated, since it doesn't handle package precedence over module - (see #10066) - """ - # os.path trick - if dotted_name.startswith('os.path'): - return 'os.path' - parts = dotted_name.split('.') - if context_file is not None: - # first check for builtin module which won't be considered latter - # in that case (path != None) - if parts[0] in BUILTIN_MODULES: - if len(parts) > 2: - raise ImportError(dotted_name) - return parts[0] - # don't use += or insert, we want a new list to be created ! - path = None - starti = 0 - if parts[0] == '': - assert context_file is not None, \ - 'explicit relative import, but no context_file?' - path = [] # prevent resolving the import non-relatively - starti = 1 - while parts[starti] == '': # for all further dots: change context - starti += 1 - context_file = dirname(context_file) - for i in range(starti, len(parts)): - try: - file_from_modpath(parts[starti:i+1], - path=path, context_file=context_file) - except ImportError: - if not i >= max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_modules(package, src_directory, blacklist=STD_BLACKLIST): - """given a package directory return a list of all available python - modules in the package and its subpackages - - :type package: str - :param package: the python name for the package - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to - the value of `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all available python modules in the package and its - subpackages - """ - modules = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not '__init__.py' in filenames: - dirnames[:] = () - continue - if directory != src_directory: - dir_package = directory[len(src_directory):].replace(os.sep, '.') - modules.append(package + dir_package) - for filename in filenames: - if _is_python_file(filename) and filename != '__init__.py': - src = join(directory, filename) - module = package + src[len(src_directory):-3] - modules.append(module.replace(os.sep, '.')) - return modules - - - -def get_module_files(src_directory, blacklist=STD_BLACKLIST): - """given a package directory return a list of all available python - module's files in the package and its subpackages - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all available python module's files in the package and - its subpackages - """ - files = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not '__init__.py' in filenames: - dirnames[:] = () - continue - for filename in filenames: - if _is_python_file(filename): - src = join(directory, filename) - files.append(src) - return files - - -def get_source_file(filename, include_no_ext=False): - """given a python module's file name return the matching source file - name (the filename will be returned identically if it's a already an - absolute path to a python source file...) - - :type filename: str - :param filename: python module's file name - - - :raise NoSourceFile: if no source file exists on the file system - - :rtype: str - :return: the absolute path of the source file if it exists - """ - base, orig_ext = splitext(abspath(filename)) - for ext in PY_SOURCE_EXTS: - source_path = '%s.%s' % (base, ext) - if exists(source_path): - return source_path - if include_no_ext and not orig_ext and exists(base): - return base - raise NoSourceFile(filename) - - -def cleanup_sys_modules(directories): - """remove submodules of `directories` from `sys.modules`""" - cleaned = [] - for modname, module in list(sys.modules.items()): - modfile = getattr(module, '__file__', None) - if modfile: - for directory in directories: - if modfile.startswith(directory): - cleaned.append(modname) - del sys.modules[modname] - break - return cleaned - - -def is_python_source(filename): - """ - rtype: bool - return: True if the filename is a python source file - """ - return splitext(filename)[1][1:] in PY_SOURCE_EXTS - - -def is_standard_module(modname, std_path=(STD_LIB_DIR,)): - """try to guess if a module is a standard python module (by default, - see `std_path` parameter's description) - - :type modname: str - :param modname: name of the module we are interested in - - :type std_path: list(str) or tuple(str) - :param std_path: list of path considered as standard - - - :rtype: bool - :return: - true if the module: - - is located on the path listed in one of the directory in `std_path` - - is a built-in module - - Note: this function is known to return wrong values when inside virtualenv. - See https://www.logilab.org/ticket/294756. - """ - modname = modname.split('.')[0] - try: - filename = file_from_modpath([modname]) - except ImportError as ex: - # import failed, i'm probably not so wrong by supposing it's - # not standard... - return False - # modules which are not living in a file are considered standard - # (sys and __builtin__ for instance) - if filename is None: - # we assume there are no namespaces in stdlib - return not _is_namespace(modname) - filename = abspath(filename) - if filename.startswith(EXT_LIB_DIR): - return False - for path in std_path: - if filename.startswith(abspath(path)): - return True - return False - - - -def is_relative(modname, from_file): - """return true if the given module name is relative to the given - file name - - :type modname: str - :param modname: name of the module we are interested in - - :type from_file: str - :param from_file: - path of the module from which modname has been imported - - :rtype: bool - :return: - true if the module has been imported relatively to `from_file` - """ - if not isdir(from_file): - from_file = dirname(from_file) - if from_file in sys.path: - return False - try: - find_module(modname.split('.')[0], [from_file]) - return True - except ImportError: - return False - - -# internal only functions ##################################################### - -def _file_from_modpath(modpath, path=None, context=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file - - this function is used internally, see `file_from_modpath`'s - documentation for more information - """ - assert len(modpath) > 0 - if context is not None: - try: - mtype, mp_filename = _module_file(modpath, [context]) - except ImportError: - mtype, mp_filename = _module_file(modpath, path) - else: - mtype, mp_filename = _module_file(modpath, path) - if mtype == PY_COMPILED: - try: - return get_source_file(mp_filename) - except NoSourceFile: - return mp_filename - elif mtype == C_BUILTIN: - # integrated builtin module - return None - elif mtype == PKG_DIRECTORY: - mp_filename = _has_init(mp_filename) - return mp_filename - -def _search_zip(modpath, pic): - for filepath, importer in pic.items(): - if importer is not None: - if importer.find_module(modpath[0]): - if not importer.find_module('/'.join(modpath)): - raise ImportError('No module named %s in %s/%s' % ( - '.'.join(modpath[1:]), filepath, modpath)) - return ZIPFILE, abspath(filepath) + '/' + '/'.join(modpath), filepath - raise ImportError('No module named %s' % '.'.join(modpath)) - -try: - import pkg_resources -except ImportError: - pkg_resources = None - - -def _is_namespace(modname): - return (pkg_resources is not None - and modname in pkg_resources._namespace_packages) - - -def _module_file(modpath, path=None): - """get a module type / file path - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.'), with leading empty strings for explicit relative import - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - - :rtype: tuple(int, str) - :return: the module type flag and the file path for a module - """ - # egg support compat - try: - pic = sys.path_importer_cache - _path = (path is None and sys.path or path) - for __path in _path: - if not __path in pic: - try: - pic[__path] = zipimport.zipimporter(__path) - except zipimport.ZipImportError: - pic[__path] = None - checkeggs = True - except AttributeError: - checkeggs = False - # pkg_resources support (aka setuptools namespace packages) - if (_is_namespace(modpath[0]) and modpath[0] in sys.modules): - # 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__ - if not modpath: - return C_BUILTIN, None - imported = [] - while modpath: - modname = modpath[0] - # take care to changes in find_module implementation wrt builtin modules - # - # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) - # >>> imp.find_module('posix') - # (None, 'posix', ('', '', 6)) - # - # Python 3.3.1 (default, Apr 26 2013, 12:08:46) - # >>> imp.find_module('posix') - # (None, None, ('', '', 6)) - try: - _, mp_filename, mp_desc = find_module(modname, path) - except ImportError: - if checkeggs: - return _search_zip(modpath, pic)[:2] - raise - else: - if checkeggs and mp_filename: - fullabspath = [abspath(x) for x in _path] - try: - pathindex = fullabspath.index(dirname(abspath(mp_filename))) - emtype, emp_filename, zippath = _search_zip(modpath, pic) - if pathindex > _path.index(zippath): - # an egg takes priority - return emtype, emp_filename - except ValueError: - # XXX not in _path - pass - except ImportError: - pass - checkeggs = False - imported.append(modpath.pop(0)) - mtype = mp_desc[2] - if modpath: - if mtype != PKG_DIRECTORY: - raise ImportError('No module %s in %s' % ('.'.join(modpath), - '.'.join(imported))) - # XXX guess if package is using pkgutil.extend_path by looking for - # those keywords in the first four Kbytes - try: - with open(join(mp_filename, '__init__.py')) as stream: - data = stream.read(4096) - except IOError: - path = [mp_filename] - else: - if 'pkgutil' in data and 'extend_path' in data: - # extend_path is called, search sys.path for module/packages - # of this name see pkgutil.extend_path documentation - path = [join(p, *imported) for p in sys.path - if isdir(join(p, *imported))] - else: - path = [mp_filename] - return mtype, mp_filename - -def _is_python_file(filename): - """return true if the given filename should be considered as a python file - - .pyc and .pyo are ignored - """ - for ext in ('.py', '.so', '.pyd', '.pyw'): - if filename.endswith(ext): - return True - return False - - -def _has_init(directory): - """if the given directory has a valid __init__ file, return its path, - else return None - """ - mod_or_pack = join(directory, '__init__') - for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'): - if exists(mod_or_pack + '.' + ext): - return mod_or_pack + '.' + ext - return None diff --git a/pymode/libs/logilab/common/optik_ext.py b/pymode/libs/logilab/common/optik_ext.py deleted file mode 100644 index 1fd2a7f8..00000000 --- a/pymode/libs/logilab/common/optik_ext.py +++ /dev/null @@ -1,392 +0,0 @@ -# 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 . -"""Add an abstraction level to transparently import optik classes from optparse -(python >= 2.3) or the optik package. - -It also defines three new types for optik/optparse command line parser : - - * regexp - argument of this type will be converted using re.compile - * csv - argument of this type will be converted using split(',') - * yn - argument of this type will be true if 'y' or 'yes', false if 'n' or 'no' - * named - argument of this type are in the form = or : - * password - argument of this type wont be converted but this is used by other tools - such as interactive prompt for configuration to double check value and - use an invisible field - * multiple_choice - same as default "choice" type but multiple choices allowed - * file - argument of this type wont be converted but checked that the given file exists - * color - argument of this type wont be converted but checked its either a - named color or a color specified using hexadecimal notation (preceded by a #) - * time - argument of this type will be converted to a float value in seconds - according to time units (ms, s, min, h, d) - * bytes - argument of this type will be converted to a float value in bytes - according to byte units (b, kb, mb, gb, tb) -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import re -import sys -import time -from copy import copy -from os.path import exists - -# python >= 2.3 -from optparse import OptionParser as BaseParser, Option as BaseOption, \ - OptionGroup, OptionContainer, OptionValueError, OptionError, \ - Values, HelpFormatter, NO_DEFAULT, SUPPRESS_HELP - -try: - from mx import DateTime - HAS_MX_DATETIME = True -except ImportError: - HAS_MX_DATETIME = False - -from logilab.common.textutils import splitstrip, TIME_UNITS, BYTE_UNITS, \ - apply_units - - -def check_regexp(option, opt, value): - """check a regexp value by trying to compile it - return the compiled regexp - """ - if hasattr(value, 'pattern'): - return value - try: - return re.compile(value) - except ValueError: - raise OptionValueError( - "option %s: invalid regexp value: %r" % (opt, value)) - -def check_csv(option, opt, value): - """check a csv value by trying to split it - return the list of separated values - """ - if isinstance(value, (list, tuple)): - return value - try: - return splitstrip(value) - except ValueError: - raise OptionValueError( - "option %s: invalid csv value: %r" % (opt, value)) - -def check_yn(option, opt, value): - """check a yn value - return true for yes and false for no - """ - 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 OptionValueError(msg % (opt, value)) - -def check_named(option, opt, value): - """check a named value - return a dictionary containing (name, value) associations - """ - if isinstance(value, dict): - return value - values = [] - for value in check_csv(option, opt, value): - if value.find('=') != -1: - values.append(value.split('=', 1)) - elif value.find(':') != -1: - values.append(value.split(':', 1)) - if values: - return dict(values) - msg = "option %s: invalid named value %r, should be = or \ -:" - raise OptionValueError(msg % (opt, value)) - -def check_password(option, opt, value): - """check a password value (can't be empty) - """ - # no actual checking, monkey patch if you want more - return value - -def check_file(option, opt, value): - """check a file value - return the filepath - """ - if exists(value): - return value - msg = "option %s: file %r does not exist" - raise OptionValueError(msg % (opt, value)) - -# XXX use python datetime -def check_date(option, opt, value): - """check a file value - return the filepath - """ - try: - return DateTime.strptime(value, "%Y/%m/%d") - except DateTime.Error : - raise OptionValueError( - "expected format of %s is yyyy/mm/dd" % opt) - -def check_color(option, opt, value): - """check a color value and returns it - /!\ does *not* check color labels (like 'red', 'green'), only - checks hexadecimal forms - """ - # Case (1) : color label, we trust the end-user - if re.match('[a-z0-9 ]+$', value, re.I): - return value - # Case (2) : only accepts hexadecimal forms - if re.match('#[a-f0-9]{6}', value, re.I): - return value - # Else : not a color label neither a valid hexadecimal form => error - msg = "option %s: invalid color : %r, should be either hexadecimal \ - value or predefined color" - raise OptionValueError(msg % (opt, value)) - -def check_time(option, opt, value): - if isinstance(value, (int, long, 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) - - -class Option(BaseOption): - """override optik.Option to add some new option types - """ - TYPES = BaseOption.TYPES + ('regexp', 'csv', 'yn', 'named', 'password', - 'multiple_choice', 'file', 'color', - 'time', 'bytes') - ATTRS = BaseOption.ATTRS + ['hide', 'level'] - TYPE_CHECKER = copy(BaseOption.TYPE_CHECKER) - TYPE_CHECKER['regexp'] = check_regexp - TYPE_CHECKER['csv'] = check_csv - TYPE_CHECKER['yn'] = check_yn - TYPE_CHECKER['named'] = check_named - TYPE_CHECKER['multiple_choice'] = check_csv - TYPE_CHECKER['file'] = check_file - TYPE_CHECKER['color'] = check_color - TYPE_CHECKER['password'] = check_password - TYPE_CHECKER['time'] = check_time - TYPE_CHECKER['bytes'] = check_bytes - if HAS_MX_DATETIME: - TYPES += ('date',) - TYPE_CHECKER['date'] = check_date - - def __init__(self, *opts, **attrs): - BaseOption.__init__(self, *opts, **attrs) - if hasattr(self, "hide") and self.hide: - self.help = SUPPRESS_HELP - - def _check_choice(self): - """FIXME: need to override this due to optik misdesign""" - if self.type in ("choice", "multiple_choice"): - if self.choices is None: - raise OptionError( - "must supply a list of choices for type 'choice'", self) - elif not isinstance(self.choices, (tuple, list)): - raise OptionError( - "choices must be a list of strings ('%s' supplied)" - % str(type(self.choices)).split("'")[1], self) - elif self.choices is not None: - raise OptionError( - "must not supply choices for type %r" % self.type, self) - BaseOption.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': - existant = getattr(values, self.dest) - if existant: - existant.update(value) - value = existant - # 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(BaseParser): - """override optik.OptionParser to use our Option class - """ - def __init__(self, option_class=Option, *args, **kwargs): - BaseParser.__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(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]) - - -OptionGroup.level = 0 - -def level_options(group, outputlevel): - return [option for option in group.option_list - if (getattr(option, 'level', 0) or 0) <= outputlevel - and not option.help is SUPPRESS_HELP] - -def format_option_help(self, formatter): - result = [] - outputlevel = getattr(formatter, 'output_level', 0) or 0 - for option in level_options(self, outputlevel): - result.append(formatter.format_option(option)) - return "".join(result) -OptionContainer.format_option_help = format_option_help - - -class ManHelpFormatter(HelpFormatter): - """Format help using man pages ROFF format""" - - def __init__ (self, - indent_increment=0, - max_help_position=24, - width=79, - short_first=0): - 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) - - def format_title(self, pgm, section): - date = '-'.join([str(num) for num in time.localtime()[:3]]) - return '.TH %s %s "%s" %s' % (pgm, section, date, pgm) - - def format_short_description(self, pgm, short_desc): - return '''.SH NAME -.B %s -\- %s -''' % (pgm, short_desc.strip()) - - def format_synopsis(self, pgm): - return '''.SH SYNOPSIS -.B %s -[ -.I OPTIONS -] [ -.I -] -''' % pgm - - def format_long_description(self, 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()) - - def format_tail(self, 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 - -def generate_manpage(optparser, pkginfo, section=1, stream=sys.stdout, level=0): - """generate a man page from an optik parser""" - 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) - - -__all__ = ('OptionParser', 'Option', 'OptionGroup', 'OptionValueError', - 'Values') diff --git a/pymode/libs/logilab/common/optparser.py b/pymode/libs/logilab/common/optparser.py deleted file mode 100644 index aa17750e..00000000 --- a/pymode/libs/logilab/common/optparser.py +++ /dev/null @@ -1,92 +0,0 @@ -# -*- 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 . -"""Extend OptionParser with commands. - -Example: - ->>> parser = OptionParser() ->>> parser.usage = '%prog COMMAND [options] ...' ->>> parser.add_command('build', 'mymod.build') ->>> parser.add_command('clean', run_clean, add_opt_clean) ->>> run, options, args = parser.parse_command(sys.argv[1:]) ->>> return run(options, args[1:]) - -With mymod.build that defines two functions run and add_options -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from warnings import warn -warn('lgc.optparser module is deprecated, use lgc.clcommands instead', DeprecationWarning, - stacklevel=2) - -import sys -import optparse - -class OptionParser(optparse.OptionParser): - - def __init__(self, *args, **kwargs): - optparse.OptionParser.__init__(self, *args, **kwargs) - self._commands = {} - self.min_args, self.max_args = 0, 1 - - def add_command(self, name, mod_or_funcs, help=''): - """name of the command, name of module or tuple of functions - (run, add_options) - """ - assert isinstance(mod_or_funcs, str) or isinstance(mod_or_funcs, tuple), \ - "mod_or_funcs has to be a module name or a tuple of functions" - self._commands[name] = (mod_or_funcs, help) - - def print_main_help(self): - optparse.OptionParser.print_help(self) - print('\ncommands:') - for cmdname, (_, help) in self._commands.items(): - print('% 10s - %s' % (cmdname, help)) - - def parse_command(self, args): - if len(args) == 0: - self.print_main_help() - sys.exit(1) - cmd = args[0] - args = args[1:] - if cmd not in self._commands: - if cmd in ('-h', '--help'): - self.print_main_help() - sys.exit(0) - elif self.version is not None and cmd == "--version": - self.print_version() - sys.exit(0) - self.error('unknown command') - self.prog = '%s %s' % (self.prog, cmd) - mod_or_f, help = self._commands[cmd] - # optparse inserts self.description between usage and options help - self.description = help - if isinstance(mod_or_f, str): - exec('from %s import run, add_options' % mod_or_f) - else: - run, add_options = mod_or_f - add_options(self) - (options, args) = self.parse_args(args) - if not (self.min_args <= len(args) <= self.max_args): - self.error('incorrect number of arguments') - return run, options, args - - diff --git a/pymode/libs/logilab/common/proc.py b/pymode/libs/logilab/common/proc.py deleted file mode 100644 index c27356c6..00000000 --- a/pymode/libs/logilab/common/proc.py +++ /dev/null @@ -1,277 +0,0 @@ -# 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 . -"""module providing: -* process information (linux specific: rely on /proc) -* a class for resource control (memory / time / cpu time) - -This module doesn't work on windows platforms (only tested on linux) - -:organization: Logilab - - - -""" -__docformat__ = "restructuredtext en" - -import os -import stat -from resource import getrlimit, setrlimit, RLIMIT_CPU, RLIMIT_AS -from signal import signal, SIGXCPU, SIGKILL, SIGUSR2, SIGUSR1 -from threading import Timer, currentThread, Thread, Event -from time import time - -from logilab.common.tree import Node - -class NoSuchProcess(Exception): pass - -def proc_exists(pid): - """check the a pid is registered in /proc - raise NoSuchProcess exception if not - """ - if not os.path.exists('/proc/%s' % pid): - raise NoSuchProcess() - -PPID = 3 -UTIME = 13 -STIME = 14 -CUTIME = 15 -CSTIME = 16 -VSIZE = 22 - -class ProcInfo(Node): - """provide access to process information found in /proc""" - - def __init__(self, pid): - self.pid = int(pid) - Node.__init__(self, self.pid) - proc_exists(self.pid) - self.file = '/proc/%s/stat' % self.pid - self.ppid = int(self.status()[PPID]) - - def memory_usage(self): - """return the memory usage of the process in Ko""" - try : - return int(self.status()[VSIZE]) - except IOError: - return 0 - - def lineage_memory_usage(self): - return self.memory_usage() + sum([child.lineage_memory_usage() - for child in self.children]) - - def time(self, children=0): - """return the number of jiffies that this process has been scheduled - in user and kernel mode""" - status = self.status() - time = int(status[UTIME]) + int(status[STIME]) - if children: - time += int(status[CUTIME]) + int(status[CSTIME]) - return time - - def status(self): - """return the list of fields found in /proc//stat""" - return open(self.file).read().split() - - def name(self): - """return the process name found in /proc//stat - """ - return self.status()[1].strip('()') - - def age(self): - """return the age of the process - """ - return os.stat(self.file)[stat.ST_MTIME] - -class ProcInfoLoader: - """manage process information""" - - def __init__(self): - self._loaded = {} - - def list_pids(self): - """return a list of existent process ids""" - for subdir in os.listdir('/proc'): - if subdir.isdigit(): - yield int(subdir) - - def load(self, pid): - """get a ProcInfo object for a given pid""" - pid = int(pid) - try: - return self._loaded[pid] - except KeyError: - procinfo = ProcInfo(pid) - procinfo.manager = self - self._loaded[pid] = procinfo - return procinfo - - - def load_all(self): - """load all processes information""" - for pid in self.list_pids(): - try: - procinfo = self.load(pid) - if procinfo.parent is None and procinfo.ppid: - pprocinfo = self.load(procinfo.ppid) - pprocinfo.append(procinfo) - except NoSuchProcess: - pass - - -try: - class ResourceError(BaseException): - """Error raise when resource limit is reached""" - limit = "Unknown Resource Limit" -except NameError: - class ResourceError(Exception): - """Error raise when resource limit is reached""" - limit = "Unknown Resource Limit" - - -class XCPUError(ResourceError): - """Error raised when CPU Time limit is reached""" - limit = "CPU Time" - -class LineageMemoryError(ResourceError): - """Error raised when the total amount of memory used by a process and - it's child is reached""" - limit = "Lineage total Memory" - -class TimeoutError(ResourceError): - """Error raised when the process is running for to much time""" - limit = "Real Time" - -# Can't use subclass because the StandardError MemoryError raised -RESOURCE_LIMIT_EXCEPTION = (ResourceError, MemoryError) - - -class MemorySentinel(Thread): - """A class checking a process don't use too much memory in a separated - daemonic thread - """ - def __init__(self, interval, memory_limit, gpid=os.getpid()): - Thread.__init__(self, target=self._run, name="Test.Sentinel") - self.memory_limit = memory_limit - self._stop = Event() - self.interval = interval - self.setDaemon(True) - self.gpid = gpid - - def stop(self): - """stop ap""" - self._stop.set() - - def _run(self): - pil = ProcInfoLoader() - while not self._stop.isSet(): - if self.memory_limit <= pil.load(self.gpid).lineage_memory_usage(): - os.killpg(self.gpid, SIGUSR1) - self._stop.wait(self.interval) - - -class ResourceController: - - def __init__(self, max_cpu_time=None, max_time=None, max_memory=None, - max_reprieve=60): - if SIGXCPU == -1: - raise RuntimeError("Unsupported platform") - self.max_time = max_time - self.max_memory = max_memory - self.max_cpu_time = max_cpu_time - self._reprieve = max_reprieve - self._timer = None - self._msentinel = None - self._old_max_memory = None - self._old_usr1_hdlr = None - self._old_max_cpu_time = None - self._old_usr2_hdlr = None - self._old_sigxcpu_hdlr = None - self._limit_set = 0 - self._abort_try = 0 - self._start_time = None - self._elapse_time = 0 - - def _hangle_sig_timeout(self, sig, frame): - raise TimeoutError() - - def _hangle_sig_memory(self, sig, frame): - if self._abort_try < self._reprieve: - self._abort_try += 1 - raise LineageMemoryError("Memory limit reached") - else: - os.killpg(os.getpid(), SIGKILL) - - def _handle_sigxcpu(self, sig, frame): - if self._abort_try < self._reprieve: - self._abort_try += 1 - raise XCPUError("Soft CPU time limit reached") - else: - os.killpg(os.getpid(), SIGKILL) - - def _time_out(self): - if self._abort_try < self._reprieve: - self._abort_try += 1 - os.killpg(os.getpid(), SIGUSR2) - if self._limit_set > 0: - self._timer = Timer(1, self._time_out) - self._timer.start() - else: - os.killpg(os.getpid(), SIGKILL) - - def setup_limit(self): - """set up the process limit""" - assert currentThread().getName() == 'MainThread' - os.setpgrp() - if self._limit_set <= 0: - if self.max_time is not None: - self._old_usr2_hdlr = signal(SIGUSR2, self._hangle_sig_timeout) - self._timer = Timer(max(1, int(self.max_time) - self._elapse_time), - self._time_out) - self._start_time = int(time()) - self._timer.start() - if self.max_cpu_time is not None: - self._old_max_cpu_time = getrlimit(RLIMIT_CPU) - cpu_limit = (int(self.max_cpu_time), self._old_max_cpu_time[1]) - self._old_sigxcpu_hdlr = signal(SIGXCPU, self._handle_sigxcpu) - setrlimit(RLIMIT_CPU, cpu_limit) - if self.max_memory is not None: - self._msentinel = MemorySentinel(1, int(self.max_memory) ) - self._old_max_memory = getrlimit(RLIMIT_AS) - self._old_usr1_hdlr = signal(SIGUSR1, self._hangle_sig_memory) - as_limit = (int(self.max_memory), self._old_max_memory[1]) - setrlimit(RLIMIT_AS, as_limit) - self._msentinel.start() - self._limit_set += 1 - - def clean_limit(self): - """reinstall the old process limit""" - if self._limit_set > 0: - if self.max_time is not None: - self._timer.cancel() - self._elapse_time += int(time())-self._start_time - self._timer = None - signal(SIGUSR2, self._old_usr2_hdlr) - if self.max_cpu_time is not None: - setrlimit(RLIMIT_CPU, self._old_max_cpu_time) - signal(SIGXCPU, self._old_sigxcpu_hdlr) - if self.max_memory is not None: - self._msentinel.stop() - self._msentinel = None - setrlimit(RLIMIT_AS, self._old_max_memory) - signal(SIGUSR1, self._old_usr1_hdlr) - self._limit_set -= 1 diff --git a/pymode/libs/logilab/common/pytest.py b/pymode/libs/logilab/common/pytest.py deleted file mode 100644 index 3d8aca34..00000000 --- a/pymode/libs/logilab/common/pytest.py +++ /dev/null @@ -1,1202 +0,0 @@ -# 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 . -"""pytest is a tool that eases test running and debugging. - -To be able to use 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 -change the way tests are run. - -To add command line options, you must define a ``update_parser`` function in -your ``pytestconf.py`` file. The function must accept a single parameter -that will be the OptionParser's instance to customize. - -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. - -For instance, if you wish to add a custom -l option to specify a loglevel, you -could define the following ``pytestconf.py`` file :: - - import logging - from logilab.common.pytest import PyTester - - def update_parser(parser): - parser.add_option('-l', '--loglevel', dest='loglevel', action='store', - choices=('debug', 'info', 'warning', 'error', 'critical'), - default='critical', help="the default log level possible choices are " - "('debug', 'info', 'warning', 'error', 'critical')") - return parser - - - class CustomPyTester(PyTester): - def __init__(self, cvg, options): - super(CustomPyTester, self).__init__(cvg, options) - loglevel = options.loglevel.upper() - logger = logging.getLogger('erudi') - logger.setLevel(logging.getLevelName(loglevel)) - - -In your TestCase class you can then get the value of a specific option with -the ``optval`` method:: - - class MyTestCase(TestCase): - def test_foo(self): - loglevel = self.optval('loglevel') - # ... - - -You can also tag your tag your test for fine filtering - -With those tag:: - - from logilab.common.testlib import tag, TestCase - - class Exemple(TestCase): - - @tag('rouge', 'carre') - def toto(self): - pass - - @tag('carre', 'vert') - def tata(self): - pass - - @tag('rouge') - def titi(test): - pass - -you can filter the function with a simple python expression - - * ``toto`` and ``titi`` match ``rouge`` - * ``toto``, ``tata`` and ``titi``, match ``rouge or carre`` - * ``tata`` and ``titi`` match``rouge ^ carre`` - * ``titi`` match ``rouge and not carre`` -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -PYTEST_DOC = """%prog [OPTIONS] [testfile [testpattern]] - -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' - -pytest one (will run both test_thisone and test_thatone) -pytest path/to/mytests.py -s not (will skip test_notthisone) -""" - -ENABLE_DBC = False -FILE_RESTART = ".pytest.restart" - -import os, sys, re -import os.path as osp -from time import time, clock -import warnings -import types -from inspect import isgeneratorfunction, isclass -from contextlib import contextmanager -from random import shuffle - -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 -import doctest - -import unittest as unittest_legacy -if not getattr(unittest_legacy, "__package__", None): - try: - import unittest2.suite as unittest_suite - except ImportError: - sys.exit("You have to install python-unittest2 to use this module") -else: - import unittest.suite as unittest_suite - -try: - import django - from logilab.common.modutils import modpath_from_file, load_module_from_modpath - DJANGO_FOUND = True -except ImportError: - DJANGO_FOUND = False - -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""" - return TESTFILE_RE.match(osp.basename(filename)) - -TESTDIR_RE = re.compile("^(unit)?tests?$") -def this_is_a_testdir(dirpath): - """returns True if `filename` seems to be a test directory""" - return TESTDIR_RE.match(osp.basename(dirpath)) - - -def load_pytest_conf(path, parser): - """loads a ``pytestconf.py`` file and update default parser - and / or tester. - """ - namespace = {} - exec(open(path, 'rb').read(), namespace) - if 'update_parser' in namespace: - namespace['update_parser'](parser) - return namespace.get('CustomPyTester', PyTester) - - -def project_root(parser, projdir=os.getcwd()): - """try to find project's root and add it to sys.path""" - previousdir = curdir = osp.abspath(projdir) - testercls = PyTester - conf_file_path = osp.join(curdir, CONF_FILE) - if osp.isfile(conf_file_path): - testercls = load_pytest_conf(conf_file_path, parser) - while this_is_a_testdir(curdir) or \ - osp.isfile(osp.join(curdir, '__init__.py')): - newdir = osp.normpath(osp.join(curdir, os.pardir)) - if newdir == curdir: - break - previousdir = curdir - curdir = newdir - conf_file_path = osp.join(curdir, CONF_FILE) - if osp.isfile(conf_file_path): - testercls = load_pytest_conf(conf_file_path, parser) - return previousdir, testercls - - -class GlobalTestReport(object): - """this class holds global test statistics""" - def __init__(self): - self.ran = 0 - self.skipped = 0 - self.failures = 0 - self.errors = 0 - self.ttime = 0 - self.ctime = 0 - self.modulescount = 0 - self.errmodules = [] - - def feed(self, filename, testresult, ttime, ctime): - """integrates new test information into internal statistics""" - ran = testresult.testsRun - self.ran += ran - self.skipped += len(getattr(testresult, 'skipped', ())) - self.failures += len(testresult.failures) - self.errors += len(testresult.errors) - self.ttime += ttime - self.ctime += ctime - self.modulescount += 1 - if not testresult.wasSuccessful(): - problems = len(testresult.failures) + len(testresult.errors) - self.errmodules.append((filename[:-3], problems, ran)) - - def failed_to_test_module(self, filename): - """called when the test module could not be imported by unittest - """ - self.errors += 1 - self.modulescount += 1 - self.ran += 1 - self.errmodules.append((filename[:-3], 1, 1)) - - def skip_module(self, filename): - self.modulescount += 1 - self.ran += 1 - self.errmodules.append((filename[:-3], 0, 0)) - - def __str__(self): - """this is just presentation stuff""" - line1 = ['Ran %s test cases in %.2fs (%.2fs CPU)' - % (self.ran, self.ttime, self.ctime)] - if self.errors: - line1.append('%s errors' % self.errors) - if self.failures: - line1.append('%s failures' % self.failures) - if self.skipped: - line1.append('%s skipped' % self.skipped) - modulesok = self.modulescount - len(self.errmodules) - if self.errors or self.failures: - line2 = '%s modules OK (%s failed)' % (modulesok, - len(self.errmodules)) - descr = ', '.join(['%s [%s/%s]' % info for info in self.errmodules]) - line3 = '\nfailures: %s' % descr - elif modulesok: - line2 = 'All %s modules OK' % modulesok - line3 = '' - else: - return '' - return '%s\n%s%s' % (', '.join(line1), line2, line3) - - - -def remove_local_modules_from_sys(testdir): - """remove all modules from cache that come from `testdir` - - This is used to avoid strange side-effects when using the - testall() mode of pytest. - For instance, if we run pytest on this tree:: - - A/test/test_utils.py - B/test/test_utils.py - - we **have** to clean sys.modules to make sure the correct test_utils - module is ran in B - """ - for modname, mod in list(sys.modules.items()): - if mod is None: - continue - if not hasattr(mod, '__file__'): - # this is the case of some built-in modules like sys, imp, marshal - continue - modfile = mod.__file__ - # if modfile is not an absolute path, it was probably loaded locally - # during the tests - if not osp.isabs(modfile) or modfile.startswith(testdir): - del sys.modules[modname] - - - -class PyTester(object): - """encapsulates testrun logic""" - - def __init__(self, cvg, options): - self.report = GlobalTestReport() - self.cvg = cvg - self.options = options - self.firstwrite = True - self._errcode = None - - def show_report(self): - """prints the report and returns appropriate exitcode""" - # everything has been ran, print report - print("*" * 79) - print(self.report) - - def get_errcode(self): - # errcode set explicitly - if self._errcode is not None: - return self._errcode - return self.report.failures + self.report.errors - - def set_errcode(self, errcode): - self._errcode = errcode - errcode = property(get_errcode, set_errcode) - - def testall(self, exitfirst=False): - """walks through current working directory, finds something - which can be considered as a testdir and runs every test there - """ - here = os.getcwd() - for dirname, dirs, _ in os.walk(here): - for skipped in STD_BLACKLIST: - if skipped in dirs: - dirs.remove(skipped) - basename = osp.basename(dirname) - if this_is_a_testdir(basename): - print("going into", dirname) - # we found a testdir, let's explore it ! - if not self.testonedir(dirname, exitfirst): - break - dirs[:] = [] - if self.report.ran == 0: - print("no test dir found testing here:", here) - # if no test was found during the visit, consider - # the local directory as a test directory even if - # it doesn't have a traditional test directory name - self.testonedir(here) - - def testonedir(self, testdir, exitfirst=False): - """finds each testfile in the `testdir` and runs it - - return true when all tests has been executed, false if exitfirst and - some test has failed. - """ - files = abspath_listdir(testdir) - shuffle(files) - for filename in files: - if this_is_a_testfile(filename): - if self.options.exitfirst and not self.options.restart: - # overwrite restart file - try: - restartfile = open(FILE_RESTART, "w") - restartfile.close() - except Exception: - print("Error while overwriting succeeded test file :", - osp.join(os.getcwd(), FILE_RESTART), - file=sys.__stderr__) - raise - # run test and collect information - prog = self.testfile(filename, batchmode=True) - if exitfirst and (prog is None or not prog.result.wasSuccessful()): - return False - self.firstwrite = True - # clean local modules - remove_local_modules_from_sys(testdir) - return True - - def testfile(self, filename, batchmode=False): - """runs every test in `filename` - - :param filename: an absolute path pointing to a unittest file - """ - here = os.getcwd() - dirname = osp.dirname(filename) - if dirname: - os.chdir(dirname) - # overwrite restart file if it has not been done already - if self.options.exitfirst and not self.options.restart and self.firstwrite: - try: - restartfile = open(FILE_RESTART, "w") - restartfile.close() - except Exception: - print("Error while overwriting succeeded test file :", - osp.join(os.getcwd(), FILE_RESTART), file=sys.__stderr__) - raise - modname = osp.basename(filename)[:-3] - print((' %s ' % osp.basename(filename)).center(70, '='), - file=sys.__stderr__) - try: - tstart, cstart = time(), clock() - try: - testprog = SkipAwareTestProgram(modname, batchmode=batchmode, cvg=self.cvg, - options=self.options, outstream=sys.stderr) - except KeyboardInterrupt: - raise - except SystemExit as exc: - self.errcode = exc.code - raise - except testlib.SkipTest: - print("Module skipped:", filename) - self.report.skip_module(filename) - return None - except Exception: - self.report.failed_to_test_module(filename) - print('unhandled exception occurred while testing', modname, - file=sys.stderr) - import traceback - traceback.print_exc(file=sys.stderr) - return None - - tend, cend = time(), clock() - ttime, ctime = (tend - tstart), (cend - cstart) - self.report.feed(filename, testprog.result, ttime, ctime) - return testprog - finally: - if dirname: - os.chdir(here) - - - -class DjangoTester(PyTester): - - def load_django_settings(self, dirname): - """try to find project's setting and load it""" - curdir = osp.abspath(dirname) - previousdir = curdir - while not osp.isfile(osp.join(curdir, 'settings.py')) and \ - osp.isfile(osp.join(curdir, '__init__.py')): - newdir = osp.normpath(osp.join(curdir, os.pardir)) - if newdir == curdir: - raise AssertionError('could not find settings.py') - previousdir = curdir - curdir = newdir - # late django initialization - settings = load_module_from_modpath(modpath_from_file(osp.join(curdir, 'settings.py'))) - from django.core.management import setup_environ - setup_environ(settings) - settings.DEBUG = False - self.settings = settings - # add settings dir to pythonpath since it's the project's root - if curdir not in sys.path: - sys.path.insert(1, curdir) - - def before_testfile(self): - # Those imports must be done **after** setup_environ was called - from django.test.utils import setup_test_environment - from django.test.utils import create_test_db - setup_test_environment() - create_test_db(verbosity=0) - self.dbname = self.settings.TEST_DATABASE_NAME - - def after_testfile(self): - # Those imports must be done **after** setup_environ was called - from django.test.utils import teardown_test_environment - from django.test.utils import destroy_test_db - teardown_test_environment() - print('destroying', self.dbname) - destroy_test_db(self.dbname, verbosity=0) - - def testall(self, exitfirst=False): - """walks through current working directory, finds something - which can be considered as a testdir and runs every test there - """ - for dirname, dirs, files in os.walk(os.getcwd()): - for skipped in ('CVS', '.svn', '.hg'): - if skipped in dirs: - dirs.remove(skipped) - if 'tests.py' in files: - if not self.testonedir(dirname, exitfirst): - break - dirs[:] = [] - else: - basename = osp.basename(dirname) - if basename in ('test', 'tests'): - print("going into", dirname) - # we found a testdir, let's explore it ! - if not self.testonedir(dirname, exitfirst): - break - dirs[:] = [] - - def testonedir(self, testdir, exitfirst=False): - """finds each testfile in the `testdir` and runs it - - return true when all tests has been executed, false if exitfirst and - some test has failed. - """ - # special django behaviour : if tests are splitted in several files, - # remove the main tests.py file and tests each test file separately - testfiles = [fpath for fpath in abspath_listdir(testdir) - if this_is_a_testfile(fpath)] - if len(testfiles) > 1: - try: - testfiles.remove(osp.join(testdir, 'tests.py')) - except ValueError: - pass - for filename in testfiles: - # run test and collect information - prog = self.testfile(filename, batchmode=True) - if exitfirst and (prog is None or not prog.result.wasSuccessful()): - return False - # clean local modules - remove_local_modules_from_sys(testdir) - return True - - def testfile(self, filename, batchmode=False): - """runs every test in `filename` - - :param filename: an absolute path pointing to a unittest file - """ - here = os.getcwd() - dirname = osp.dirname(filename) - if dirname: - os.chdir(dirname) - self.load_django_settings(dirname) - modname = osp.basename(filename)[:-3] - print((' %s ' % osp.basename(filename)).center(70, '='), - file=sys.stderr) - try: - try: - tstart, cstart = time(), clock() - self.before_testfile() - testprog = SkipAwareTestProgram(modname, batchmode=batchmode, cvg=self.cvg) - tend, cend = time(), clock() - ttime, ctime = (tend - tstart), (cend - cstart) - self.report.feed(filename, testprog.result, ttime, ctime) - return testprog - except SystemExit: - raise - except Exception as exc: - import traceback - traceback.print_exc() - self.report.failed_to_test_module(filename) - print('unhandled exception occurred while testing', modname) - print('error: %s' % exc) - return None - finally: - self.after_testfile() - if dirname: - os.chdir(here) - - -def make_parser(): - """creates the OptionParser instance - """ - from optparse import OptionParser - parser = OptionParser(usage=PYTEST_DOC) - - parser.newargs = [] - def rebuild_cmdline(option, opt, value, parser): - """carry the option to unittest_main""" - parser.newargs.append(opt) - - def rebuild_and_store(option, opt, value, parser): - """carry the option to unittest_main and store - the value on current parser - """ - parser.newargs.append(opt) - setattr(parser.values, option.dest, True) - - def capture_and_rebuild(option, opt, value, parser): - warnings.simplefilter('ignore', DeprecationWarning) - rebuild_cmdline(option, opt, value, parser) - - # 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 - parser.add_option('-v', '--verbose', callback=rebuild_cmdline, - action="callback", help="Verbose output") - parser.add_option('-i', '--pdb', callback=rebuild_and_store, - dest="pdb", action="callback", - help="Enable test failure inspection") - 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)") - parser.add_option('-R', '--restart', callback=rebuild_and_store, - dest="restart", default=False, - action="callback", - help="Restart tests from where it failed (implies exitfirst) " - "(only make sense if tests previously ran with exitfirst only)") - parser.add_option('--color', callback=rebuild_cmdline, - action="callback", - help="colorize tracebacks") - parser.add_option('-s', '--skip', - # XXX: I wish I could use the callback action but it - # doesn't seem to be able to get the value - # associated to the option - action="store", dest="skipped", default=None, - help="test names matching this name will be skipped " - "to skip several patterns, use commas") - parser.add_option('-q', '--quiet', callback=rebuild_cmdline, - action="callback", help="Minimal output") - parser.add_option('-P', '--profile', default=None, dest='profile', - help="Profile execution and store data in the given file") - parser.add_option('-m', '--match', default=None, dest='tags_pattern', - help="only execute test whose tag match the current pattern") - - if DJANGO_FOUND: - parser.add_option('-J', '--django', dest='django', default=False, - action="store_true", - help='use pytest for django test cases') - return parser - - -def parseargs(parser): - """Parse the command line and return (options processed), (options to pass to - unittest_main()), (explicitfile or None). - """ - # parse the command line - options, args = parser.parse_args() - filenames = [arg for arg in args if arg.endswith('.py')] - if filenames: - if len(filenames) > 1: - parser.error("only one filename is acceptable") - explicitfile = filenames[0] - args.remove(explicitfile) - else: - explicitfile = None - # someone wants DBC - testlib.ENABLE_DBC = options.dbc - newargs = parser.newargs - if options.skipped: - newargs.extend(['--skip', options.skipped]) - # restart implies exitfirst - if options.restart: - options.exitfirst = True - # append additional args to the new sys.argv and let unittest_main - # do the rest - newargs += args - return options, explicitfile - - - -def run(): - parser = make_parser() - rootdir, testercls = project_root(parser) - options, explicitfile = parseargs(parser) - # mock a new command line - sys.argv[1:] = parser.newargs - cvg = None - if not '' in sys.path: - sys.path.insert(0, '') - if DJANGO_FOUND and options.django: - tester = DjangoTester(cvg, options) - else: - tester = testercls(cvg, options) - if explicitfile: - cmd, args = tester.testfile, (explicitfile,) - elif options.testdir: - cmd, args = tester.testonedir, (options.testdir, options.exitfirst) - else: - cmd, args = tester.testall, (options.exitfirst,) - try: - try: - if options.profile: - import hotshot - prof = hotshot.Profile(options.profile) - prof.runcall(cmd, *args) - prof.close() - print('profile data saved in', options.profile) - else: - cmd(*args) - except SystemExit: - raise - except: - import traceback - traceback.print_exc() - finally: - tester.show_report() - sys.exit(tester.errcode) - -class SkipAwareTestProgram(unittest.TestProgram): - # XXX: don't try to stay close to unittest.py, use optparse - USAGE = """\ -Usage: %(progName)s [options] [test] [...] - -Options: - -h, --help Show this message - -v, --verbose Verbose output - -i, --pdb Enable test failure inspection - -x, --exitfirst Exit on first failure - -s, --skip skip test matching this pattern (no regexp for now) - -q, --quiet Minimal output - --color colorize tracebacks - - -m, --match Run only test whose tag match this pattern - - -P, --profile FILE: Run the tests using cProfile and saving results - in FILE - -Examples: - %(progName)s - run default set of tests - %(progName)s MyTestSuite - run suite 'MyTestSuite' - %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething - %(progName)s MyTestCase - run all 'test*' test methods - in MyTestCase -""" - def __init__(self, module='__main__', defaultTest=None, batchmode=False, - cvg=None, options=None, outstream=sys.stderr): - self.batchmode = batchmode - self.cvg = cvg - self.options = options - self.outstream = outstream - super(SkipAwareTestProgram, self).__init__( - module=module, defaultTest=defaultTest, - testLoader=NonStrictTestLoader()) - - def parseArgs(self, argv): - self.pdbmode = False - self.exitfirst = False - self.skipped_patterns = [] - self.test_pattern = None - self.tags_pattern = None - self.colorize = False - self.profile_name = None - import getopt - try: - options, args = getopt.getopt(argv[1:], 'hHvixrqcp:s:m:P:', - ['help', 'verbose', 'quiet', 'pdb', - 'exitfirst', 'restart', - 'skip=', 'color', 'match=', 'profile=']) - for opt, value in options: - if opt in ('-h', '-H', '--help'): - self.usageExit() - if opt in ('-i', '--pdb'): - self.pdbmode = True - if opt in ('-x', '--exitfirst'): - self.exitfirst = True - if opt in ('-r', '--restart'): - self.restart = True - self.exitfirst = True - if opt in ('-q', '--quiet'): - self.verbosity = 0 - if opt in ('-v', '--verbose'): - self.verbosity = 2 - if opt in ('-s', '--skip'): - self.skipped_patterns = [pat.strip() for pat in - value.split(', ')] - if opt == '--color': - self.colorize = True - if opt in ('-m', '--match'): - #self.tags_pattern = value - self.options["tag_pattern"] = value - if opt in ('-P', '--profile'): - self.profile_name = value - self.testLoader.skipped_patterns = self.skipped_patterns - if len(args) == 0 and self.defaultTest is None: - suitefunc = getattr(self.module, 'suite', None) - if isinstance(suitefunc, (types.FunctionType, - types.MethodType)): - self.test = self.module.suite() - else: - self.test = self.testLoader.loadTestsFromModule(self.module) - return - if len(args) > 0: - self.test_pattern = args[0] - self.testNames = args - else: - self.testNames = (self.defaultTest, ) - self.createTests() - except getopt.error as msg: - self.usageExit(msg) - - def runTests(self): - if self.profile_name: - import cProfile - cProfile.runctx('self._runTests()', globals(), locals(), self.profile_name ) - else: - return self._runTests() - - def _runTests(self): - self.testRunner = SkipAwareTextTestRunner(verbosity=self.verbosity, - stream=self.outstream, - exitfirst=self.exitfirst, - pdbmode=self.pdbmode, - cvg=self.cvg, - test_pattern=self.test_pattern, - skipped_patterns=self.skipped_patterns, - colorize=self.colorize, - batchmode=self.batchmode, - options=self.options) - - def removeSucceededTests(obj, succTests): - """ Recursive function that removes succTests from - a TestSuite or TestCase - """ - if isinstance(obj, unittest.TestSuite): - removeSucceededTests(obj._tests, succTests) - if isinstance(obj, list): - for el in obj[:]: - if isinstance(el, unittest.TestSuite): - removeSucceededTests(el, succTests) - elif isinstance(el, unittest.TestCase): - descr = '.'.join((el.__class__.__module__, - el.__class__.__name__, - el._testMethodName)) - if descr in succTests: - obj.remove(el) - # take care, self.options may be None - if getattr(self.options, 'restart', False): - # retrieve succeeded tests from FILE_RESTART - try: - restartfile = open(FILE_RESTART, 'r') - try: - succeededtests = list(elem.rstrip('\n\r') for elem in - restartfile.readlines()) - removeSucceededTests(self.test, succeededtests) - finally: - restartfile.close() - except Exception as ex: - raise Exception("Error while reading succeeded tests into %s: %s" - % (osp.join(os.getcwd(), FILE_RESTART), ex)) - - result = self.testRunner.run(self.test) - # help garbage collection: we want TestSuite, which hold refs to every - # executed TestCase, to be gc'ed - del self.test - if getattr(result, "debuggers", None) and \ - getattr(self, "pdbmode", None): - start_interactive_mode(result) - if not getattr(self, "batchmode", None): - sys.exit(not result.wasSuccessful()) - self.result = result - - -class SkipAwareTextTestRunner(unittest.TextTestRunner): - - def __init__(self, stream=sys.stderr, verbosity=1, - exitfirst=False, pdbmode=False, cvg=None, test_pattern=None, - skipped_patterns=(), colorize=False, batchmode=False, - options=None): - super(SkipAwareTextTestRunner, self).__init__(stream=stream, - verbosity=verbosity) - self.exitfirst = exitfirst - self.pdbmode = pdbmode - self.cvg = cvg - self.test_pattern = test_pattern - self.skipped_patterns = skipped_patterns - self.colorize = colorize - self.batchmode = batchmode - self.options = options - - def _this_is_skipped(self, testedname): - return any([(pat in testedname) for pat in self.skipped_patterns]) - - def _runcondition(self, test, skipgenerator=True): - if isinstance(test, testlib.InnerTest): - testname = test.name - else: - if isinstance(test, testlib.TestCase): - meth = test._get_test_method() - testname = '%s.%s' % (test.__name__, meth.__name__) - elif isinstance(test, types.FunctionType): - func = test - testname = func.__name__ - elif isinstance(test, types.MethodType): - cls = test.__self__.__class__ - testname = '%s.%s' % (cls.__name__, test.__name__) - else: - return True # Not sure when this happens - if isgeneratorfunction(test) and skipgenerator: - return self.does_match_tags(test) # Let inner tests decide at run time - if self._this_is_skipped(testname): - return False # this was explicitly skipped - if self.test_pattern is not None: - try: - classpattern, testpattern = self.test_pattern.split('.') - klass, name = testname.split('.') - if classpattern not in klass or testpattern not in name: - return False - except ValueError: - if self.test_pattern not in testname: - return False - - return self.does_match_tags(test) - - def does_match_tags(self, test): - if self.options is not None: - tags_pattern = getattr(self.options, 'tags_pattern', None) - if tags_pattern is not None: - tags = getattr(test, 'tags', testlib.Tags()) - if tags.inherit and isinstance(test, types.MethodType): - tags = tags | getattr(test.__self__.__class__, 'tags', testlib.Tags()) - return tags.match(tags_pattern) - return True # no pattern - - def _makeResult(self): - return testlib.SkipAwareTestResult(self.stream, self.descriptions, - self.verbosity, self.exitfirst, - self.pdbmode, self.cvg, self.colorize) - - def run(self, test): - "Run the given test case or test suite." - result = self._makeResult() - startTime = time() - test(result, runcondition=self._runcondition, options=self.options) - stopTime = time() - timeTaken = stopTime - startTime - result.printErrors() - if not self.batchmode: - self.stream.writeln(result.separator2) - run = result.testsRun - self.stream.writeln("Ran %d test%s in %.3fs" % - (run, run != 1 and "s" or "", timeTaken)) - self.stream.writeln() - if not result.wasSuccessful(): - if self.colorize: - self.stream.write(textutils.colorize_ansi("FAILED", color='red')) - else: - self.stream.write("FAILED") - else: - if self.colorize: - self.stream.write(textutils.colorize_ansi("OK", color='green')) - else: - self.stream.write("OK") - failed, errored, skipped = map(len, (result.failures, - result.errors, - result.skipped)) - - det_results = [] - for name, value in (("failures", result.failures), - ("errors",result.errors), - ("skipped", result.skipped)): - if value: - det_results.append("%s=%i" % (name, len(value))) - if det_results: - self.stream.write(" (") - self.stream.write(', '.join(det_results)) - self.stream.write(")") - self.stream.writeln("") - return result - -class NonStrictTestLoader(unittest.TestLoader): - """ - Overrides default testloader to be able to omit classname when - specifying tests to run on command line. - - For example, if the file test_foo.py contains :: - - class FooTC(TestCase): - def test_foo1(self): # ... - def test_foo2(self): # ... - def test_bar1(self): # ... - - class BarTC(TestCase): - def test_bar2(self): # ... - - 'python test_foo.py' will run the 3 tests in FooTC - 'python test_foo.py FooTC' will run the 3 tests in FooTC - 'python test_foo.py test_foo' will run test_foo1 and test_foo2 - 'python test_foo.py test_foo1' will run test_foo1 - 'python test_foo.py test_bar' will run FooTC.test_bar1 and BarTC.test_bar2 - """ - - def __init__(self): - self.skipped_patterns = () - - # some magic here to accept empty list by extending - # and to provide callable capability - def loadTestsFromNames(self, names, module=None): - suites = [] - for name in names: - suites.extend(self.loadTestsFromName(name, module)) - return self.suiteClass(suites) - - def _collect_tests(self, module): - tests = {} - for obj in vars(module).values(): - if isclass(obj) and issubclass(obj, unittest.TestCase): - classname = obj.__name__ - if classname[0] == '_' or self._this_is_skipped(classname): - continue - methodnames = [] - # obj is a TestCase class - for attrname in dir(obj): - if attrname.startswith(self.testMethodPrefix): - attr = getattr(obj, attrname) - if callable(attr): - methodnames.append(attrname) - # keep track of class (obj) for convenience - tests[classname] = (obj, methodnames) - return tests - - def loadTestsFromSuite(self, module, suitename): - try: - suite = getattr(module, suitename)() - except AttributeError: - return [] - assert hasattr(suite, '_tests'), \ - "%s.%s is not a valid TestSuite" % (module.__name__, suitename) - # python2.3 does not implement __iter__ on suites, we need to return - # _tests explicitly - return suite._tests - - def loadTestsFromName(self, name, module=None): - parts = name.split('.') - if module is None or len(parts) > 2: - # let the base class do its job here - return [super(NonStrictTestLoader, self).loadTestsFromName(name)] - tests = self._collect_tests(module) - collected = [] - if len(parts) == 1: - pattern = parts[0] - if callable(getattr(module, pattern, None) - ) and pattern not in tests: - # consider it as a suite - return self.loadTestsFromSuite(module, pattern) - if pattern in tests: - # case python unittest_foo.py MyTestTC - klass, methodnames = tests[pattern] - for methodname in methodnames: - collected = [klass(methodname) - for methodname in methodnames] - else: - # case python unittest_foo.py something - for klass, methodnames in tests.values(): - # skip methodname if matched by skipped_patterns - for skip_pattern in self.skipped_patterns: - methodnames = [methodname - for methodname in methodnames - if skip_pattern not in methodname] - collected += [klass(methodname) - for methodname in methodnames - if pattern in methodname] - elif len(parts) == 2: - # case "MyClass.test_1" - classname, pattern = parts - klass, methodnames = tests.get(classname, (None, [])) - for methodname in methodnames: - collected = [klass(methodname) for methodname in methodnames - if pattern in methodname] - return collected - - def _this_is_skipped(self, testedname): - return any([(pat in testedname) for pat in self.skipped_patterns]) - - def getTestCaseNames(self, testCaseClass): - """Return a sorted sequence of method names found within testCaseClass - """ - is_skipped = self._this_is_skipped - classname = testCaseClass.__name__ - if classname[0] == '_' or is_skipped(classname): - return [] - testnames = super(NonStrictTestLoader, self).getTestCaseNames( - testCaseClass) - return [testname for testname in testnames if not is_skipped(testname)] - - -# The 2 functions below are modified versions of the TestSuite.run method -# that is provided with unittest2 for python 2.6, in unittest2/suite.py -# It is used to monkeypatch the original implementation to support -# extra runcondition and options arguments (see in testlib.py) - -def _ts_run(self, result, runcondition=None, options=None): - self._wrapped_run(result, runcondition=runcondition, options=options) - self._tearDownPreviousClass(None, result) - self._handleModuleTearDown(result) - return result - -def _ts_wrapped_run(self, result, debug=False, runcondition=None, options=None): - for test in self: - if result.shouldStop: - break - if unittest_suite._isnotsuite(test): - self._tearDownPreviousClass(test, result) - self._handleModuleFixture(test, result) - self._handleClassSetUp(test, result) - result._previousTestClass = test.__class__ - if (getattr(test.__class__, '_classSetupFailed', False) or - getattr(result, '_moduleSetUpFailed', False)): - continue - - # --- modifications to deal with _wrapped_run --- - # original code is: - # - # if not debug: - # test(result) - # else: - # test.debug() - if hasattr(test, '_wrapped_run'): - try: - test._wrapped_run(result, debug, runcondition=runcondition, options=options) - except TypeError: - test._wrapped_run(result, debug) - elif not debug: - try: - test(result, runcondition, options) - except TypeError: - test(result) - else: - test.debug() - # --- end of modifications to deal with _wrapped_run --- - return result - -if sys.version_info >= (2, 7): - # The function below implements a modified version of the - # TestSuite.run method that is provided with python 2.7, in - # unittest/suite.py - def _ts_run(self, result, debug=False, runcondition=None, options=None): - topLevel = False - if getattr(result, '_testRunEntered', False) is False: - result._testRunEntered = topLevel = True - - self._wrapped_run(result, debug, runcondition, options) - - if topLevel: - self._tearDownPreviousClass(None, result) - self._handleModuleTearDown(result) - result._testRunEntered = False - return result - - -def enable_dbc(*args): - """ - Without arguments, return True if contracts can be enabled and should be - enabled (see option -d), return False otherwise. - - With arguments, return False if contracts can't or shouldn't be enabled, - otherwise weave ContractAspect with items passed as arguments. - """ - if not ENABLE_DBC: - return False - try: - from logilab.aspects.weaver import weaver - from logilab.aspects.lib.contracts import ContractAspect - except ImportError: - sys.stderr.write( - 'Warning: logilab.aspects is not available. Contracts disabled.') - return False - for arg in args: - weaver.weave_module(arg, ContractAspect) - return True - - -# monkeypatch unittest and doctest (ouch !) -unittest._TextTestResult = testlib.SkipAwareTestResult -unittest.TextTestRunner = SkipAwareTextTestRunner -unittest.TestLoader = NonStrictTestLoader -unittest.TestProgram = SkipAwareTestProgram - -if sys.version_info >= (2, 4): - doctest.DocTestCase.__bases__ = (testlib.TestCase,) - # XXX check python2.6 compatibility - #doctest.DocTestCase._cleanups = [] - #doctest.DocTestCase._out = [] -else: - unittest.FunctionTestCase.__bases__ = (testlib.TestCase,) -unittest.TestSuite.run = _ts_run -unittest.TestSuite._wrapped_run = _ts_wrapped_run diff --git a/pymode/libs/logilab/common/registry.py b/pymode/libs/logilab/common/registry.py deleted file mode 100644 index 86a85f94..00000000 --- a/pymode/libs/logilab/common/registry.py +++ /dev/null @@ -1,1125 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of 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 . -"""This module provides bases for predicates dispatching (the pattern in use -here is similar to what's refered as multi-dispatch or predicate-dispatch in the -literature, though a bit different since the idea is to select across different -implementation 'e.g. classes), not to dispatch a message to a function or -method. It contains the following classes: - -* :class:`RegistryStore`, the top level object which loads implementation - objects and stores them into registries. You'll usually use it to access - registries and their contained objects; - -* :class:`Registry`, the base class which contains objects semantically grouped - (for instance, sharing a same API, hence the 'implementation' name). You'll - use it to select the proper implementation according to a context. Notice you - may use registries on their own without using the store. - -.. Note:: - - implementation objects are usually designed to be accessed through the - registry and not by direct instantiation, besides to use it as base classe. - -The selection procedure is delegated to a selector, which is responsible for -scoring the object according to some context. At the end of the selection, if an -implementation has been found, an instance of this class is returned. A selector -is built from one or more predicates combined together using AND, OR, NOT -operators (actually `&`, `|` and `~`). You'll thus find some base classes to -build predicates: - -* :class:`Predicate`, the abstract base predicate class - -* :class:`AndPredicate`, :class:`OrPredicate`, :class:`NotPredicate`, which you - shouldn't have to use directly. You'll use `&`, `|` and '~' operators between - predicates directly - -* :func:`objectify_predicate` - -You'll eventually find one concrete predicate: :class:`yes` - -.. autoclass:: RegistryStore -.. autoclass:: Registry - -Predicates ----------- -.. autoclass:: Predicate -.. autofunc:: objectify_predicate -.. autoclass:: yes - -Debugging ---------- -.. autoclass:: traced_selection - -Exceptions ----------- -.. autoclass:: RegistryException -.. autoclass:: RegistryNotFound -.. autoclass:: ObjectNotFound -.. autoclass:: NoSelectableObject -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import types -import weakref -import traceback as tb -from os import listdir, stat -from os.path import join, isdir, exists -from logging import getLogger -from warnings import warn - -from six import string_types, add_metaclass - -from logilab.common.modutils import modpath_from_file -from logilab.common.logging_ext import set_log_methods -from logilab.common.decorators import classproperty - - -class RegistryException(Exception): - """Base class for registry exception.""" - -class RegistryNotFound(RegistryException): - """Raised when an unknown registry is requested. - - This is usually a programming/typo error. - """ - -class ObjectNotFound(RegistryException): - """Raised when an unregistered object is requested. - - This may be a programming/typo or a misconfiguration error. - """ - -class NoSelectableObject(RegistryException): - """Raised when no object is selectable for a given context.""" - def __init__(self, args, kwargs, objects): - self.args = args - self.kwargs = kwargs - self.objects = objects - - def __str__(self): - return ('args: %s, kwargs: %s\ncandidates: %s' - % (self.args, self.kwargs.keys(), self.objects)) - -class SelectAmbiguity(RegistryException): - """Raised when several objects compete at selection time with an equal - score. - - """ - - -def _modname_from_path(path, extrapath=None): - modpath = modpath_from_file(path, extrapath) - # omit '__init__' from package's name to avoid loading that module - # once for each name when it is imported by some other object - # module. This supposes import in modules are done as:: - # - # from package import something - # - # not:: - # - # from package.__init__ import something - # - # which seems quite correct. - if modpath[-1] == '__init__': - modpath.pop() - return '.'.join(modpath) - - -def _toload_info(path, extrapath, _toload=None): - """Return a dictionary of : and an ordered list of - (file, module name) to load - """ - if _toload is None: - assert isinstance(path, list) - _toload = {}, [] - for fileordir in path: - if isdir(fileordir) and exists(join(fileordir, '__init__.py')): - subfiles = [join(fileordir, fname) for fname in listdir(fileordir)] - _toload_info(subfiles, extrapath, _toload) - elif fileordir[-3:] == '.py': - modname = _modname_from_path(fileordir, extrapath) - _toload[0][modname] = fileordir - _toload[1].append((fileordir, modname)) - return _toload - - -class RegistrableObject(object): - """This is the base class for registrable objects which are selected - according to a context. - - :attr:`__registry__` - name of the registry for this object (string like 'views', - 'templates'...). You may want to define `__registries__` directly if your - object should be registered in several registries. - - :attr:`__regid__` - object's identifier in the registry (string like 'main', - 'primary', 'folder_box') - - :attr:`__select__` - class'selector - - Moreover, the `__abstract__` attribute may be set to True to indicate that a - class is abstract and should not be registered. - - You don't have to inherit from this class to put it in a registry (having - `__regid__` and `__select__` is enough), though this is needed for classes - that should be automatically registered. - """ - - __registry__ = None - __regid__ = None - __select__ = None - __abstract__ = True # see doc snipppets below (in Registry class) - - @classproperty - def __registries__(cls): - if cls.__registry__ is None: - return () - return (cls.__registry__,) - - -class RegistrableInstance(RegistrableObject): - """Inherit this class if you want instances of the classes to be - automatically registered. - """ - - def __new__(cls, *args, **kwargs): - """Add a __module__ attribute telling the module where the instance was - created, for automatic registration. - """ - 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) - return obj - - -class Registry(dict): - """The registry store a set of implementations associated to identifier: - - * to each identifier are associated a list of implementations - - * to select an implementation of a given identifier, you should use one of the - :meth:`select` or :meth:`select_or_none` method - - * to select a list of implementations for a context, you should use the - :meth:`possible_objects` method - - * dictionary like access to an identifier will return the bare list of - implementations for this identifier. - - To be usable in a registry, the only requirement is to have a `__select__` - attribute. - - At the end of the registration process, the :meth:`__registered__` - method is called on each registered object which have them, given the - registry in which it's registered as argument. - - Registration methods: - - .. automethod: register - .. automethod: unregister - - Selection methods: - - .. automethod: select - .. automethod: select_or_none - .. automethod: possible_objects - .. automethod: object_by_id - """ - def __init__(self, debugmode): - super(Registry, self).__init__() - self.debugmode = debugmode - - def __getitem__(self, name): - """return the registry (list of implementation objects) associated to - this name - """ - try: - return super(Registry, self).__getitem__(name) - except KeyError: - exc = ObjectNotFound(name) - exc.__traceback__ = sys.exc_info()[-1] - raise exc - - @classmethod - def objid(cls, obj): - """returns a unique identifier for an object stored in the registry""" - return '%s.%s' % (obj.__module__, cls.objname(obj)) - - @classmethod - def objname(cls, obj): - """returns a readable name for an object stored in the registry""" - return getattr(obj, '__name__', id(obj)) - - def initialization_completed(self): - """call method __registered__() on registered objects when the callback - is defined""" - for objects in self.values(): - for objectcls in objects: - registered = getattr(objectcls, '__registered__', None) - if registered: - registered(self) - if self.debugmode: - wrap_predicates(_lltrace) - - def register(self, obj, oid=None, clear=False): - """base method to add an object in the registry""" - assert not '__abstract__' in obj.__dict__, obj - assert obj.__select__, obj - oid = oid or obj.__regid__ - assert oid, ('no explicit name supplied to register object %s, ' - 'which has no __regid__ set' % obj) - if clear: - objects = self[oid] = [] - else: - objects = self.setdefault(oid, []) - assert not obj in objects, 'object %s is already registered' % obj - objects.append(obj) - - def register_and_replace(self, obj, replaced): - """remove and register """ - # XXXFIXME this is a duplication of unregister() - # remove register_and_replace in favor of unregister + register - # or simplify by calling unregister then register here - if not isinstance(replaced, string_types): - replaced = self.objid(replaced) - # prevent from misspelling - assert obj is not replaced, 'replacing an object by itself: %s' % obj - registered_objs = self.get(obj.__regid__, ()) - for index, registered in enumerate(registered_objs): - if self.objid(registered) == replaced: - del registered_objs[index] - break - else: - self.warning('trying to replace %s that is not registered with %s', - replaced, obj) - self.register(obj) - - def unregister(self, obj): - """remove object from this registry""" - objid = self.objid(obj) - oid = obj.__regid__ - for registered in self.get(oid, ()): - # use self.objid() to compare objects because vreg will probably - # have its own version of the object, loaded through execfile - if self.objid(registered) == objid: - self[oid].remove(registered) - break - else: - self.warning('can\'t remove %s, no id %s in the registry', - objid, oid) - - def all_objects(self): - """return a list containing all objects in this registry. - """ - result = [] - for objs in self.values(): - result += objs - return result - - # dynamic selection methods ################################################ - - def object_by_id(self, oid, *args, **kwargs): - """return object with the `oid` identifier. Only one object is expected - to be found. - - raise :exc:`ObjectNotFound` if there are no object with id `oid` in this - registry - - raise :exc:`AssertionError` if there is more than one object there - """ - objects = self[oid] - assert len(objects) == 1, objects - return objects[0](*args, **kwargs) - - def select(self, __oid, *args, **kwargs): - """return the most specific object among those with the given oid - according to the given context. - - raise :exc:`ObjectNotFound` if there are no object with id `oid` in this - registry - - raise :exc:`NoSelectableObject` if no object can be selected - """ - obj = self._select_best(self[__oid], *args, **kwargs) - if obj is None: - raise NoSelectableObject(args, kwargs, self[__oid] ) - return obj - - def select_or_none(self, __oid, *args, **kwargs): - """return the most specific object among those with the given oid - according to the given context, or None if no object applies. - """ - try: - return self._select_best(self[__oid], *args, **kwargs) - except ObjectNotFound: - return None - - def possible_objects(self, *args, **kwargs): - """return an iterator on possible objects in this registry for the given - context - """ - for objects in self.values(): - obj = self._select_best(objects, *args, **kwargs) - if obj is None: - continue - yield obj - - def _select_best(self, objects, *args, **kwargs): - """return an instance of the most specific object according - to parameters - - return None if not object apply (don't raise `NoSelectableObject` since - it's costly when searching objects using `possible_objects` - (e.g. searching for hooks). - """ - score, winners = 0, None - for obj in objects: - objectscore = obj.__select__(obj, *args, **kwargs) - if objectscore > score: - score, winners = objectscore, [obj] - elif objectscore > 0 and objectscore == score: - winners.append(obj) - if winners is None: - return None - if len(winners) > 1: - # log in production environement / test, error while debugging - msg = 'select ambiguity: %s\n(args: %s, kwargs: %s)' - if self.debugmode: - # raise bare exception in debug mode - raise SelectAmbiguity(msg % (winners, args, kwargs.keys())) - self.error(msg, winners, args, kwargs.keys()) - # return the result of calling the object - return self.selected(winners[0], args, kwargs) - - def selected(self, winner, args, kwargs): - """override here if for instance you don't want "instanciation" - """ - return winner(*args, **kwargs) - - # these are overridden by set_log_methods below - # only defining here to prevent pylint from complaining - info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None - - -def obj_registries(cls, registryname=None): - """return a tuple of registry names (see __registries__)""" - if registryname: - return (registryname,) - return cls.__registries__ - - -class RegistryStore(dict): - """This class is responsible for loading objects and storing them - in their registry which is created on the fly as needed. - - It handles dynamic registration of objects and provides a - convenient api to access them. To be recognized as an object that - should be stored into one of the store's registry - (:class:`Registry`), an object must provide the following - attributes, used control how they interact with the registry: - - :attr:`__registries__` - list of registry names (string like 'views', 'templates'...) into which - the object should be registered - - :attr:`__regid__` - object identifier in the registry (string like 'main', - 'primary', 'folder_box') - - :attr:`__select__` - the object predicate selectors - - Moreover, the :attr:`__abstract__` attribute may be set to `True` - to indicate that an object is abstract and should not be registered - (such inherited attributes not considered). - - .. Note:: - - When using the store to load objects dynamically, you *always* have - to use **super()** to get the methods and attributes of the - superclasses, and not use the class identifier. If not, you'll get into - trouble at reload time. - - For example, instead of writing:: - - class Thing(Parent): - __regid__ = 'athing' - __select__ = yes() - - def f(self, arg1): - Parent.f(self, arg1) - - You must write:: - - class Thing(Parent): - __regid__ = 'athing' - __select__ = yes() - - def f(self, arg1): - super(Thing, self).f(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. - - .. automethod: register_objects - - For each module, by default, all compatible objects are registered - automatically. However if some objects come as replacement of - other objects, or have to be included only if some condition is - met, you'll have to define a `registration_callback(vreg)` - function in the module and explicitly register **all objects** in - this module, using the api defined below. - - - .. automethod:: RegistryStore.register_all - .. automethod:: RegistryStore.register_and_replace - .. automethod:: RegistryStore.register - .. automethod:: RegistryStore.unregister - - .. Note:: - Once the function `registration_callback(vreg)` is implemented in a - module, all the objects from this module have to be explicitly - registered as it disables the automatic object registration. - - - Examples: - - .. sourcecode:: python - - def registration_callback(store): - # register everything in the module except BabarClass - store.register_all(globals().values(), __name__, (BabarClass,)) - - # conditionally register BabarClass - if 'babar_relation' in store.schema: - store.register(BabarClass) - - In this example, we register all application object classes defined in the module - except `BabarClass`. This class is then registered only if the 'babar_relation' - relation type is defined in the instance schema. - - .. sourcecode:: python - - def registration_callback(store): - store.register(Elephant) - # replace Babar by Celeste - store.register_and_replace(Celeste, Babar) - - In this example, we explicitly register classes one by one: - - * the `Elephant` class - * the `Celeste` to replace `Babar` - - If at some point we register a new appobject class in this module, it won't be - registered at all without modification to the `registration_callback` - implementation. The first example will register it though, thanks to the call - to the `register_all` method. - - Controlling registry instantiation - ---------------------------------- - - The `REGISTRY_FACTORY` class dictionary allows to specify which class should - be instantiated for a given registry name. The class associated to `None` - key will be the class used when there is no specific class for a name. - """ - - def __init__(self, debugmode=False): - super(RegistryStore, self).__init__() - self.debugmode = debugmode - - def reset(self): - """clear all registries managed by this store""" - # don't use self.clear, we want to keep existing subdictionaries - for subdict in self.values(): - subdict.clear() - self._lastmodifs = {} - - def __getitem__(self, name): - """return the registry (dictionary of class objects) associated to - this name - """ - try: - return super(RegistryStore, self).__getitem__(name) - except KeyError: - exc = RegistryNotFound(name) - exc.__traceback__ = sys.exc_info()[-1] - raise exc - - # methods for explicit (un)registration ################################### - - # default class, when no specific class set - REGISTRY_FACTORY = {None: Registry} - - def registry_class(self, regid): - """return existing registry named regid or use factory to create one and - return it""" - try: - return self.REGISTRY_FACTORY[regid] - except KeyError: - return self.REGISTRY_FACTORY[None] - - def setdefault(self, regid): - try: - return self[regid] - except RegistryNotFound: - self[regid] = self.registry_class(regid)(self.debugmode) - return self[regid] - - def register_all(self, objects, modname, butclasses=()): - """register registrable objects into `objects`. - - Registrable objects are properly configured subclasses of - :class:`RegistrableObject`. Objects which are not defined in the module - `modname` or which are in `butclasses` won't be registered. - - Typical usage is: - - .. sourcecode:: python - - store.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,)) - - So you get partially automatic registration, keeping manual registration - for some object (to use - :meth:`~logilab.common.registry.RegistryStore.register_and_replace` for - instance). - """ - assert isinstance(modname, string_types), \ - 'modname expected to be a module name (ie string), got %r' % modname - for obj in objects: - if self.is_registrable(obj) and obj.__module__ == modname and not obj in butclasses: - if isinstance(obj, type): - self._load_ancestors_then_object(modname, obj, butclasses) - else: - self.register(obj) - - def register(self, obj, registryname=None, oid=None, clear=False): - """register `obj` implementation into `registryname` or - `obj.__registries__` if not specified, with identifier `oid` or - `obj.__regid__` if not specified. - - If `clear` is true, all objects with the same identifier will be - previously unregistered. - """ - assert not obj.__dict__.get('__abstract__'), obj - for registryname in obj_registries(obj, registryname): - registry = self.setdefault(registryname) - registry.register(obj, oid=oid, clear=clear) - self.debug("register %s in %s['%s']", - registry.objname(obj), registryname, oid or obj.__regid__) - self._loadedmods.setdefault(obj.__module__, {})[registry.objid(obj)] = obj - - def unregister(self, obj, registryname=None): - """unregister `obj` object from the registry `registryname` or - `obj.__registries__` if not specified. - """ - for registryname in obj_registries(obj, registryname): - registry = self[registryname] - registry.unregister(obj) - self.debug("unregister %s from %s['%s']", - registry.objname(obj), registryname, obj.__regid__) - - def register_and_replace(self, obj, replaced, registryname=None): - """register `obj` object into `registryname` or - `obj.__registries__` if not specified. If found, the `replaced` object - will be unregistered first (else a warning will be issued as it is - generally unexpected). - """ - for registryname in obj_registries(obj, registryname): - registry = self[registryname] - registry.register_and_replace(obj, replaced) - self.debug("register %s in %s['%s'] instead of %s", - registry.objname(obj), registryname, obj.__regid__, - registry.objname(replaced)) - - # initialization methods ################################################### - - def init_registration(self, path, extrapath=None): - """reset registry and walk down path to return list of (path, name) - file modules to be loaded""" - # XXX make this private by renaming it to _init_registration ? - self.reset() - # compute list of all modules that have to be loaded - self._toloadmods, filemods = _toload_info(path, extrapath) - # XXX is _loadedmods still necessary ? It seems like it's useful - # to avoid loading same module twice, especially with the - # _load_ancestors_then_object logic but this needs to be checked - self._loadedmods = {} - return filemods - - def register_objects(self, path, extrapath=None): - """register all objects found walking down """ - # load views from each directory in the instance's path - # XXX inline init_registration ? - filemods = self.init_registration(path, extrapath) - for filepath, modname in filemods: - self.load_file(filepath, modname) - self.initialization_completed() - - def initialization_completed(self): - """call initialization_completed() on all known registries""" - for reg in self.values(): - reg.initialization_completed() - - def _mdate(self, filepath): - """ return the modification date of a file path """ - try: - return stat(filepath)[-2] - except OSError: - # this typically happens on emacs backup files (.#foo.py) - self.warning('Unable to load %s. It is likely to be a backup file', - filepath) - return None - - def is_reload_needed(self, path): - """return True if something module changed and the registry should be - reloaded - """ - lastmodifs = self._lastmodifs - for fileordir in path: - if isdir(fileordir) and exists(join(fileordir, '__init__.py')): - if self.is_reload_needed([join(fileordir, fname) - for fname in listdir(fileordir)]): - return True - elif fileordir[-3:] == '.py': - mdate = self._mdate(fileordir) - if mdate is None: - continue # backup file, see _mdate implementation - elif "flymake" in fileordir: - # flymake + pylint in use, don't consider these they will corrupt the registry - continue - if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate: - self.info('File %s changed since last visit', fileordir) - return True - return False - - 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] = {} - mdate = self._mdate(filepath) - if mdate is None: - return # backup file, see _mdate implementation - elif "flymake" in filepath: - # flymake + pylint in use, don't consider these they will corrupt the registry - return - # set update time before module loading, else we get some reloading - # weirdness in case of syntax error or other error while importing the - # module - self._lastmodifs[filepath] = mdate - # load the module - module = load_module_from_name(modname) - self.load_module(module) - - def load_module(self, module): - """Automatically handle module objects registration. - - Instances are registered as soon as they are hashable and have the - following attributes: - - * __regid__ (a string) - * __select__ (a callable) - * __registries__ (a tuple/list of string) - - For classes this is a bit more complicated : - - - first ensure parent classes are already registered - - - class with __abstract__ == True in their local dictionary are skipped - - - object class needs to have registries and identifier properly set to a - non empty string to be registered. - """ - self.info('loading %s from %s', module.__name__, module.__file__) - if hasattr(module, 'registration_callback'): - module.registration_callback(self) - else: - self.register_all(vars(module).values(), module.__name__) - - def _load_ancestors_then_object(self, modname, objectcls, butclasses=()): - """handle class registration according to rules defined in - :meth:`load_module` - """ - # backward compat, we used to allow whatever else than classes - if not isinstance(objectcls, type): - if self.is_registrable(objectcls) and objectcls.__module__ == modname: - self.register(objectcls) - return - # imported classes - objmodname = objectcls.__module__ - if objmodname != modname: - # The module of the object is not the same as the currently - # worked on module, or this is actually an instance, which - # has no module at all - if objmodname in self._toloadmods: - # if this is still scheduled for loading, let's proceed immediately, - # but using the object module - self.load_file(self._toloadmods[objmodname], objmodname) - return - # ensure object hasn't been already processed - clsid = '%s.%s' % (modname, objectcls.__name__) - if clsid in self._loadedmods[modname]: - return - self._loadedmods[modname][clsid] = objectcls - # ensure ancestors are registered - for parent in objectcls.__bases__: - self._load_ancestors_then_object(modname, parent, butclasses) - # ensure object is registrable - if objectcls in butclasses or not self.is_registrable(objectcls): - return - # backward compat - reg = self.setdefault(obj_registries(objectcls)[0]) - if reg.objname(objectcls)[0] == '_': - warn("[lgc 0.59] object whose name start with '_' won't be " - "skipped anymore at some point, use __abstract__ = True " - "instead (%s)" % objectcls, DeprecationWarning) - return - # register, finally - self.register(objectcls) - - @classmethod - def is_registrable(cls, obj): - """ensure `obj` should be registered - - as arbitrary stuff may be registered, do a lot of check and warn about - weird cases (think to dumb proxy objects) - """ - if isinstance(obj, type): - if not issubclass(obj, RegistrableObject): - # ducktyping backward compat - if not (getattr(obj, '__registries__', None) - and getattr(obj, '__regid__', None) - and getattr(obj, '__select__', None)): - return False - elif issubclass(obj, RegistrableInstance): - return False - elif not isinstance(obj, RegistrableInstance): - return False - if not obj.__regid__: - return False # no regid - registries = obj.__registries__ - if not registries: - return False # no registries - selector = obj.__select__ - if not selector: - return False # no selector - if obj.__dict__.get('__abstract__', False): - return False - # then detect potential problems that should be warned - if not isinstance(registries, (tuple, list)): - cls.warning('%s has __registries__ which is not a list or tuple', obj) - return False - if not callable(selector): - cls.warning('%s has not callable __select__', obj) - return False - return True - - # these are overridden by set_log_methods below - # only defining here to prevent pylint from complaining - info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None - - -# init logging -set_log_methods(RegistryStore, getLogger('registry.store')) -set_log_methods(Registry, getLogger('registry')) - - -# helpers for debugging selectors -TRACED_OIDS = None - -def _trace_selector(cls, selector, args, ret): - vobj = args[0] - if TRACED_OIDS == 'all' or vobj.__regid__ in TRACED_OIDS: - print('%s -> %s for %s(%s)' % (cls, ret, vobj, vobj.__regid__)) - -def _lltrace(selector): - """use this decorator on your predicates so they become traceable with - :class:`traced_selection` - """ - def traced(cls, *args, **kwargs): - ret = selector(cls, *args, **kwargs) - if TRACED_OIDS is not None: - _trace_selector(cls, selector, args, ret) - return ret - traced.__name__ = selector.__name__ - traced.__doc__ = selector.__doc__ - return traced - -class traced_selection(object): # pylint: disable=C0103 - """ - Typical usage is : - - .. sourcecode:: python - - >>> from logilab.common.registry import traced_selection - >>> with traced_selection(): - ... # some code in which you want to debug selectors - ... # for all objects - - This will yield lines like this in the logs:: - - selector one_line_rset returned 0 for - - You can also give to :class:`traced_selection` the identifiers of objects on - which you want to debug selection ('oid1' and 'oid2' in the example above). - - .. sourcecode:: python - - >>> with traced_selection( ('regid1', 'regid2') ): - ... # some code in which you want to debug selectors - ... # for objects with __regid__ 'regid1' and 'regid2' - - A potentially useful point to set up such a tracing function is - the `logilab.common.registry.Registry.select` method body. - """ - - def __init__(self, traced='all'): - self.traced = traced - - def __enter__(self): - global TRACED_OIDS - TRACED_OIDS = self.traced - - def __exit__(self, exctype, exc, traceback): - global TRACED_OIDS - TRACED_OIDS = None - return traceback is None - -# selector base classes and operations ######################################## - -def objectify_predicate(selector_func): - """Most of the time, a simple score function is enough to build a selector. - The :func:`objectify_predicate` decorator turn it into a proper selector - class:: - - @objectify_predicate - def one(cls, req, rset=None, **kwargs): - return 1 - - class MyView(View): - __select__ = View.__select__ & one() - - """ - return type(selector_func.__name__, (Predicate,), - {'__doc__': selector_func.__doc__, - '__call__': lambda self, *a, **kw: selector_func(*a, **kw)}) - - -_PREDICATES = {} - -def wrap_predicates(decorator): - for predicate in _PREDICATES.values(): - if not '_decorators' in predicate.__dict__: - predicate._decorators = set() - if decorator in predicate._decorators: - continue - predicate._decorators.add(decorator) - predicate.__call__ = decorator(predicate.__call__) - -class PredicateMetaClass(type): - def __new__(mcs, *args, **kwargs): - # use __new__ so subclasses doesn't have to call Predicate.__init__ - inst = type.__new__(mcs, *args, **kwargs) - proxy = weakref.proxy(inst, lambda p: _PREDICATES.pop(id(p))) - _PREDICATES[id(proxy)] = proxy - return inst - - -@add_metaclass(PredicateMetaClass) -class Predicate(object): - """base class for selector classes providing implementation - for operators ``&``, ``|`` and ``~`` - - This class is only here to give access to binary operators, the selector - logic itself should be implemented in the :meth:`__call__` method. Notice it - should usually accept any arbitrary arguments (the context), though that may - vary depending on your usage of the registry. - - a selector is called to help choosing the correct object for a - particular context by returning a score (`int`) telling how well - the implementation given as first argument fit to the given context. - - 0 score means that the class doesn't apply. - """ - - @property - def func_name(self): - # backward compatibility - return self.__class__.__name__ - - def search_selector(self, selector): - """search for the given selector, selector instance or tuple of - selectors in the selectors tree. Return None if not found. - """ - if self is selector: - return self - if (isinstance(selector, type) or isinstance(selector, tuple)) and \ - isinstance(self, selector): - return self - return None - - def __str__(self): - return self.__class__.__name__ - - def __and__(self, other): - return AndPredicate(self, other) - def __rand__(self, other): - return AndPredicate(other, self) - def __iand__(self, other): - return AndPredicate(self, other) - def __or__(self, other): - return OrPredicate(self, other) - def __ror__(self, other): - return OrPredicate(other, self) - def __ior__(self, other): - return OrPredicate(self, other) - - def __invert__(self): - return NotPredicate(self) - - # XXX (function | function) or (function & function) not managed yet - - def __call__(self, cls, *args, **kwargs): - return NotImplementedError("selector %s must implement its logic " - "in its __call__ method" % self.__class__) - - def __repr__(self): - return u'' % (self.__class__.__name__, id(self)) - - -class MultiPredicate(Predicate): - """base class for compound selector classes""" - - def __init__(self, *selectors): - self.selectors = self.merge_selectors(selectors) - - def __str__(self): - return '%s(%s)' % (self.__class__.__name__, - ','.join(str(s) for s in self.selectors)) - - @classmethod - def merge_selectors(cls, selectors): - """deal with selector instanciation when necessary and merge - multi-selectors if possible: - - AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4)) - ==> AndPredicate(sel1, sel2, sel3, sel4) - """ - merged_selectors = [] - for selector in selectors: - # XXX do we really want magic-transformations below? - # if so, wanna warn about them? - if isinstance(selector, types.FunctionType): - selector = objectify_predicate(selector)() - if isinstance(selector, type) and issubclass(selector, Predicate): - selector = selector() - assert isinstance(selector, Predicate), selector - if isinstance(selector, cls): - merged_selectors += selector.selectors - else: - merged_selectors.append(selector) - return merged_selectors - - def search_selector(self, selector): - """search for the given selector or selector instance (or tuple of - selectors) in the selectors tree. Return None if not found - """ - for childselector in self.selectors: - if childselector is selector: - return childselector - found = childselector.search_selector(selector) - if found is not None: - return found - # if not found in children, maybe we are looking for self? - return super(MultiPredicate, self).search_selector(selector) - - -class AndPredicate(MultiPredicate): - """and-chained selectors""" - def __call__(self, cls, *args, **kwargs): - score = 0 - for selector in self.selectors: - partscore = selector(cls, *args, **kwargs) - if not partscore: - return 0 - score += partscore - return score - - -class OrPredicate(MultiPredicate): - """or-chained selectors""" - def __call__(self, cls, *args, **kwargs): - for selector in self.selectors: - partscore = selector(cls, *args, **kwargs) - if partscore: - return partscore - return 0 - -class NotPredicate(Predicate): - """negation selector""" - def __init__(self, selector): - self.selector = selector - - def __call__(self, cls, *args, **kwargs): - score = self.selector(cls, *args, **kwargs) - return int(not score) - - def __str__(self): - return 'NOT(%s)' % self.selector - - -class yes(Predicate): # pylint: disable=C0103 - """Return the score given as parameter, with a default score of 0.5 so any - other selector take precedence. - - Usually used for objects which can be selected whatever the context, or - also sometimes to add arbitrary points to a score. - - Take care, `yes(0)` could be named 'no'... - """ - def __init__(self, score=0.5): - self.score = score - - def __call__(self, *args, **kwargs): - return self.score - - -# 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__) - -@deprecated('[lgc 0.59] use obj_registries function instead') -def class_registries(cls, registryname): - return obj_registries(cls, registryname) - diff --git a/pymode/libs/logilab/common/shellutils.py b/pymode/libs/logilab/common/shellutils.py deleted file mode 100644 index 4e689560..00000000 --- a/pymode/libs/logilab/common/shellutils.py +++ /dev/null @@ -1,462 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of 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 . -"""shell/term utilities, useful to write some python scripts instead of shell -scripts. -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import os -import glob -import shutil -import stat -import sys -import tempfile -import time -import fnmatch -import errno -import string -import random -import subprocess -from os.path import exists, isdir, islink, basename, join - -from six import string_types -from six.moves import range, input as raw_input - -from logilab.common import STD_BLACKLIST, _handle_blacklist -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): - - def __enter__(self): - self.path = tempfile.mkdtemp() - return self.path - - def __exit__(self, exctype, value, traceback): - # rmtree in all cases - shutil.rmtree(self.path) - return traceback is None - - -class pushd(object): - def __init__(self, directory): - self.directory = directory - - def __enter__(self): - self.cwd = os.getcwd() - os.chdir(self.directory) - return self.directory - - def __exit__(self, exctype, value, traceback): - os.chdir(self.cwd) - - -def chown(path, login=None, group=None): - """Same as `os.chown` function but accepting user login or group name as - argument. If login or group is omitted, it's left unchanged. - - Note: you must own the file to chown it (or be root). Otherwise OSError is raised. - """ - if login is None: - uid = -1 - else: - try: - uid = int(login) - except ValueError: - import pwd # Platforms: Unix - uid = pwd.getpwnam(login).pw_uid - if group is None: - gid = -1 - else: - try: - gid = int(group) - except ValueError: - import grp - gid = grp.getgrnam(group).gr_gid - os.chown(path, uid, gid) - -def mv(source, destination, _action=shutil.move): - """A shell-like mv, supporting wildcards. - """ - sources = glob.glob(source) - if len(sources) > 1: - assert isdir(destination) - for filename in sources: - _action(filename, join(destination, basename(filename))) - else: - try: - source = sources[0] - except IndexError: - raise OSError('No file matching %s' % source) - if isdir(destination) and exists(destination): - destination = join(destination, basename(source)) - try: - _action(source, destination) - except OSError as ex: - raise OSError('Unable to move %r to %r (%s)' % ( - source, destination, ex)) - -def rm(*files): - """A shell-like rm, supporting wildcards. - """ - for wfile in files: - for filename in glob.glob(wfile): - if islink(filename): - os.remove(filename) - elif isdir(filename): - shutil.rmtree(filename) - else: - os.remove(filename) - -def cp(source, destination): - """A shell-like cp, supporting wildcards. - """ - mv(source, destination, _action=shutil.copy) - -def find(directory, exts, exclude=False, blacklist=STD_BLACKLIST): - """Recursively find files ending with the given extensions from the directory. - - :type directory: str - :param directory: - directory where the search should start - - :type exts: basestring or list or tuple - :param exts: - extensions or lists or extensions to search - - :type exclude: boolean - :param exts: - if this argument is True, returning files NOT ending with the given - extensions - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all matching files - """ - if isinstance(exts, string_types): - exts = (exts,) - if exclude: - def match(filename, exts): - for ext in exts: - if filename.endswith(ext): - return False - return True - else: - def match(filename, exts): - for ext in exts: - if filename.endswith(ext): - return True - return False - files = [] - for dirpath, dirnames, filenames in os.walk(directory): - _handle_blacklist(blacklist, dirnames, filenames) - # don't append files if the directory is blacklisted - dirname = basename(dirpath) - if dirname in blacklist: - continue - files.extend([join(dirpath, f) for f in filenames if match(f, exts)]) - return files - - -def globfind(directory, pattern, blacklist=STD_BLACKLIST): - """Recursively finds files matching glob `pattern` under `directory`. - - This is an alternative to `logilab.common.shellutils.find`. - - :type directory: str - :param directory: - directory where the search should start - - :type pattern: basestring - :param pattern: - the glob pattern (e.g *.py, foo*.py, etc.) - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: iterator - :return: - iterator over the list of all matching files - """ - for curdir, dirnames, filenames in os.walk(directory): - _handle_blacklist(blacklist, dirnames, filenames) - for fname in fnmatch.filter(filenames, pattern): - yield join(curdir, fname) - -def unzip(archive, destdir): - import zipfile - if not exists(destdir): - os.mkdir(destdir) - zfobj = zipfile.ZipFile(archive) - for name in zfobj.namelist(): - if name.endswith('/'): - os.mkdir(join(destdir, name)) - else: - outfile = open(join(destdir, name), 'wb') - outfile.write(zfobj.read(name)) - outfile.close() - - -class Execute: - """This is a deadlock safe version of popen2 (no stdin), that returns - an object with errorlevel, out and err. - """ - - def __init__(self, command): - cmd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - self.out, self.err = cmd.communicate() - self.status = os.WEXITSTATUS(cmd.returncode) - -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.""" - - def __init__(self, nbops, size=20, stream=sys.stdout, title=''): - if title: - self._fstr = '\r%s [%%-%ss]' % (title, int(size)) - else: - self._fstr = '\r[%%-%ss]' % int(size) - self._stream = stream - self._total = nbops - self._size = size - self._current = 0 - self._progress = 0 - self._current_text = None - self._last_text_write_size = 0 - - def _get_text(self): - return self._current_text - - def _set_text(self, text=None): - if text != self._current_text: - self._current_text = text - self.refresh() - - def _del_text(self): - self.text = None - - text = property(_get_text, _set_text, _del_text) - - def update(self, offset=1, exact=False): - """Move FORWARD to new cursor position (cursor will never go backward). - - :offset: fraction of ``size`` - - :exact: - - - False: offset relative to current cursor position if True - - True: offset as an asbsolute position - - """ - if exact: - self._current = offset - else: - self._current += offset - - progress = int((float(self._current)/float(self._total))*self._size) - if progress > self._progress: - self._progress = progress - self.refresh() - - def refresh(self): - """Refresh the progression bar display.""" - self._stream.write(self._fstr % ('=' * min(self._progress, self._size)) ) - if self._last_text_write_size or self._current_text: - template = ' %%-%is' % (self._last_text_write_size) - text = self._current_text - if text is None: - text = '' - self._stream.write(template % text) - self._last_text_write_size = len(text.rstrip()) - self._stream.flush() - - def finish(self): - self._stream.write('\n') - self._stream.flush() - - -class DummyProgressBar(object): - __slot__ = ('text',) - - def refresh(self): - pass - def update(self): - pass - def finish(self): - pass - - -_MARKER = object() -class progress(object): - - def __init__(self, nbops=_MARKER, size=_MARKER, stream=_MARKER, title=_MARKER, enabled=True): - self.nbops = nbops - self.size = size - self.stream = stream - self.title = title - self.enabled = enabled - - def __enter__(self): - if self.enabled: - kwargs = {} - for attr in ('nbops', 'size', 'stream', 'title'): - value = getattr(self, attr) - if value is not _MARKER: - kwargs[attr] = value - self.pb = ProgressBar(**kwargs) - else: - self.pb = DummyProgressBar() - return self.pb - - def __exit__(self, exc_type, exc_val, exc_tb): - self.pb.finish() - -class RawInput(object): - - def __init__(self, input=None, printer=None): - self._input = input or raw_input - self._print = printer - - def ask(self, question, options, default): - assert default in options - choices = [] - for option in options: - if option == default: - label = option[0].upper() - else: - label = option[0].lower() - if len(option) > 1: - label += '(%s)' % option[1:].lower() - choices.append((option, label)) - prompt = "%s [%s]: " % (question, - '/'.join([opt[1] for opt in choices])) - tries = 3 - while tries > 0: - answer = self._input(prompt).strip().lower() - if not answer: - return default - possible = [option for option, label in choices - if option.lower().startswith(answer)] - if len(possible) == 1: - return possible[0] - elif len(possible) == 0: - msg = '%s is not an option.' % answer - else: - msg = ('%s is an ambiguous answer, do you mean %s ?' % ( - answer, ' or '.join(possible))) - if self._print: - self._print(msg) - else: - print(msg) - tries -= 1 - raise Exception('unable to get a sensible answer') - - def confirm(self, question, default_is_yes=True): - default = default_is_yes and 'y' or 'n' - answer = self.ask(question, ('y', 'n'), default) - return answer == 'y' - -ASK = RawInput() - - -def getlogin(): - """avoid using os.getlogin() because of strange tty / stdin problems - (man 3 getlogin) - Another solution would be to use $LOGNAME, $USER or $USERNAME - """ - if sys.platform != 'win32': - import pwd # Platforms: Unix - return pwd.getpwuid(os.getuid())[0] - else: - return os.environ['USERNAME'] - -def generate_password(length=8, vocab=string.ascii_letters + string.digits): - """dumb password generation function""" - pwd = '' - for i in range(length): - pwd += random.choice(vocab) - return pwd diff --git a/pymode/libs/logilab/common/sphinx_ext.py b/pymode/libs/logilab/common/sphinx_ext.py deleted file mode 100644 index a24608ce..00000000 --- a/pymode/libs/logilab/common/sphinx_ext.py +++ /dev/null @@ -1,87 +0,0 @@ -# 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.decorators import monkeypatch - -from sphinx.ext import autodoc - -class DocstringOnlyModuleDocumenter(autodoc.ModuleDocumenter): - objtype = 'docstring' - def format_signature(self): - pass - def add_directive_header(self, sig): - pass - def document_members(self, all_members=False): - pass - - def resolve_name(self, modname, parents, path, base): - if modname is not None: - return modname, parents + [base] - return (path or '') + base, [] - - -#autodoc.add_documenter(DocstringOnlyModuleDocumenter) - -def setup(app): - app.add_autodocumenter(DocstringOnlyModuleDocumenter) - - - -from sphinx.ext.autodoc import (ViewList, Options, AutodocReporter, nodes, - assemble_option_dict, nested_parse_with_titles) - -@monkeypatch(autodoc.AutoDirective) -def run(self): - self.filename_set = set() # a set of dependent filenames - self.reporter = self.state.document.reporter - self.env = self.state.document.settings.env - self.warnings = [] - self.result = ViewList() - - # find out what documenter to call - objtype = self.name[4:] - doc_class = self._registry[objtype] - # process the options with the selected documenter's option_spec - self.genopt = Options(assemble_option_dict( - self.options.items(), doc_class.option_spec)) - # generate the output - documenter = doc_class(self, self.arguments[0]) - documenter.generate(more_content=self.content) - if not self.result: - return self.warnings - - # record all filenames as dependencies -- this will at least - # partially make automatic invalidation possible - for fn in self.filename_set: - self.env.note_dependency(fn) - - # use a custom reporter that correctly assigns lines to source - # filename/description and lineno - old_reporter = self.state.memo.reporter - self.state.memo.reporter = AutodocReporter(self.result, - self.state.memo.reporter) - if self.name in ('automodule', 'autodocstring'): - node = nodes.section() - # necessary so that the child nodes get the right source/line set - node.document = self.state.document - nested_parse_with_titles(self.state, self.result, node) - else: - node = nodes.paragraph() - node.document = self.state.document - self.state.nested_parse(self.result, 0, node) - self.state.memo.reporter = old_reporter - return self.warnings + node.children diff --git a/pymode/libs/logilab/common/sphinxutils.py b/pymode/libs/logilab/common/sphinxutils.py deleted file mode 100644 index ab6e8a18..00000000 --- a/pymode/libs/logilab/common/sphinxutils.py +++ /dev/null @@ -1,122 +0,0 @@ -# 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 . -"""Sphinx utils - -ModuleGenerator: Generate a file that lists all the modules of a list of -packages in order to pull all the docstring. -This should not be used in a makefile to systematically generate sphinx -documentation! - -Typical usage: - ->>> from logilab.common.sphinxutils import ModuleGenerator ->>> mgen = ModuleGenerator('logilab common', '/home/adim/src/logilab/common') ->>> mgen.generate('api_logilab_common.rst', exclude_dirs=('test',)) -""" - -import os, sys -import os.path as osp -import inspect - -from logilab.common import STD_BLACKLIST -from logilab.common.shellutils import globfind -from logilab.common.modutils import load_module_from_file, modpath_from_file - -def module_members(module): - members = [] - for name, value in inspect.getmembers(module): - if getattr(value, '__module__', None) == module.__name__: - members.append( (name, value) ) - return sorted(members) - - -def class_members(klass): - return sorted([name for name in vars(klass) - if name not in ('__doc__', '__module__', - '__dict__', '__weakref__')]) - -class ModuleGenerator: - file_header = """.. -*- coding: utf-8 -*-\n\n%s\n""" - module_def = """ -:mod:`%s` -=======%s - -.. automodule:: %s - :members: %s -""" - class_def = """ - -.. autoclass:: %s - :members: %s - -""" - - def __init__(self, project_title, code_dir): - self.title = project_title - self.code_dir = osp.abspath(code_dir) - - def generate(self, dest_file, exclude_dirs=STD_BLACKLIST): - """make the module file""" - self.fn = open(dest_file, 'w') - num = len(self.title) + 6 - title = "=" * num + "\n %s API\n" % self.title + "=" * num - self.fn.write(self.file_header % title) - self.gen_modules(exclude_dirs=exclude_dirs) - self.fn.close() - - def gen_modules(self, exclude_dirs): - """generate all modules""" - for module in self.find_modules(exclude_dirs): - modname = module.__name__ - classes = [] - modmembers = [] - for objname, obj in module_members(module): - if inspect.isclass(obj): - classmembers = class_members(obj) - classes.append( (objname, classmembers) ) - else: - modmembers.append(objname) - self.fn.write(self.module_def % (modname, '=' * len(modname), - modname, - ', '.join(modmembers))) - for klass, members in classes: - self.fn.write(self.class_def % (klass, ', '.join(members))) - - def find_modules(self, exclude_dirs): - basepath = osp.dirname(self.code_dir) - basedir = osp.basename(basepath) + osp.sep - if basedir not in sys.path: - sys.path.insert(1, basedir) - for filepath in globfind(self.code_dir, '*.py', exclude_dirs): - if osp.basename(filepath) in ('setup.py', '__pkginfo__.py'): - continue - try: - module = load_module_from_file(filepath) - except: # module might be broken or magic - dotted_path = modpath_from_file(filepath) - module = type('.'.join(dotted_path), (), {}) # mock it - yield module - - -if __name__ == '__main__': - # example : - title, code_dir, outfile = sys.argv[1:] - generator = ModuleGenerator(title, code_dir) - # XXX modnames = ['logilab'] - generator.generate(outfile, ('test', 'tests', 'examples', - 'data', 'doc', '.hg', 'migration')) diff --git a/pymode/libs/logilab/common/table.py b/pymode/libs/logilab/common/table.py deleted file mode 100644 index 2f3df694..00000000 --- a/pymode/libs/logilab/common/table.py +++ /dev/null @@ -1,929 +0,0 @@ -# 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 . -"""Table management module.""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from six.moves import range - -class Table(object): - """Table defines a data table with column and row names. - inv: - len(self.data) <= len(self.row_names) - forall(self.data, lambda x: len(x) <= len(self.col_names)) - """ - - def __init__(self, default_value=0, col_names=None, row_names=None): - self.col_names = [] - self.row_names = [] - self.data = [] - self.default_value = default_value - if col_names: - self.create_columns(col_names) - if row_names: - self.create_rows(row_names) - - def _next_row_name(self): - return 'row%s' % (len(self.row_names)+1) - - def __iter__(self): - return iter(self.data) - - def __eq__(self, other): - if other is None: - return False - else: - return list(self) == list(other) - - __hash__ = object.__hash__ - - def __ne__(self, other): - return not self == other - - def __len__(self): - return len(self.row_names) - - ## Rows / Columns creation ################################################# - def create_rows(self, row_names): - """Appends row_names to the list of existing rows - """ - self.row_names.extend(row_names) - for row_name in row_names: - self.data.append([self.default_value]*len(self.col_names)) - - def create_columns(self, col_names): - """Appends col_names to the list of existing columns - """ - for col_name in col_names: - self.create_column(col_name) - - def create_row(self, row_name=None): - """Creates a rowname to the row_names list - """ - row_name = row_name or self._next_row_name() - self.row_names.append(row_name) - self.data.append([self.default_value]*len(self.col_names)) - - - def create_column(self, col_name): - """Creates a colname to the col_names list - """ - self.col_names.append(col_name) - for row in self.data: - row.append(self.default_value) - - ## Sort by column ########################################################## - def sort_by_column_id(self, col_id, method = 'asc'): - """Sorts the table (in-place) according to data stored in col_id - """ - try: - col_index = self.col_names.index(col_id) - self.sort_by_column_index(col_index, method) - except ValueError: - raise KeyError("Col (%s) not found in table" % (col_id)) - - - def sort_by_column_index(self, col_index, method = 'asc'): - """Sorts the table 'in-place' according to data stored in col_index - - method should be in ('asc', 'desc') - """ - sort_list = sorted([(row[col_index], row, row_name) - for row, row_name in zip(self.data, self.row_names)]) - # Sorting sort_list will sort according to col_index - # If we want reverse sort, then reverse list - if method.lower() == 'desc': - sort_list.reverse() - - # Rebuild data / row names - self.data = [] - self.row_names = [] - for val, row, row_name in sort_list: - self.data.append(row) - self.row_names.append(row_name) - - def groupby(self, colname, *others): - """builds indexes of data - :returns: nested dictionaries pointing to actual rows - """ - groups = {} - colnames = (colname,) + others - col_indexes = [self.col_names.index(col_id) for col_id in colnames] - for row in self.data: - ptr = groups - for col_index in col_indexes[:-1]: - ptr = ptr.setdefault(row[col_index], {}) - ptr = ptr.setdefault(row[col_indexes[-1]], - Table(default_value=self.default_value, - col_names=self.col_names)) - ptr.append_row(tuple(row)) - return groups - - def select(self, colname, value): - grouped = self.groupby(colname) - try: - return grouped[value] - except KeyError: - return [] - - def remove(self, colname, value): - col_index = self.col_names.index(colname) - for row in self.data[:]: - if row[col_index] == value: - self.data.remove(row) - - - ## The 'setter' part ####################################################### - def set_cell(self, row_index, col_index, data): - """sets value of cell 'row_indew', 'col_index' to data - """ - self.data[row_index][col_index] = data - - - def set_cell_by_ids(self, row_id, col_id, data): - """sets value of cell mapped by row_id and col_id to data - Raises a KeyError if row_id or col_id are not found in the table - """ - try: - row_index = self.row_names.index(row_id) - except ValueError: - raise KeyError("Row (%s) not found in table" % (row_id)) - else: - try: - col_index = self.col_names.index(col_id) - self.data[row_index][col_index] = data - except ValueError: - raise KeyError("Column (%s) not found in table" % (col_id)) - - - def set_row(self, row_index, row_data): - """sets the 'row_index' row - pre: - type(row_data) == types.ListType - len(row_data) == len(self.col_names) - """ - self.data[row_index] = row_data - - - def set_row_by_id(self, row_id, row_data): - """sets the 'row_id' column - pre: - type(row_data) == types.ListType - len(row_data) == len(self.row_names) - Raises a KeyError if row_id is not found - """ - try: - row_index = self.row_names.index(row_id) - self.set_row(row_index, row_data) - except ValueError: - raise KeyError('Row (%s) not found in table' % (row_id)) - - - def append_row(self, row_data, row_name=None): - """Appends a row to the table - pre: - type(row_data) == types.ListType - len(row_data) == len(self.col_names) - """ - row_name = row_name or self._next_row_name() - self.row_names.append(row_name) - self.data.append(row_data) - return len(self.data) - 1 - - def insert_row(self, index, row_data, row_name=None): - """Appends row_data before 'index' in the table. To make 'insert' - behave like 'list.insert', inserting in an out of range index will - insert row_data to the end of the list - pre: - type(row_data) == types.ListType - len(row_data) == len(self.col_names) - """ - row_name = row_name or self._next_row_name() - self.row_names.insert(index, row_name) - self.data.insert(index, row_data) - - - def delete_row(self, index): - """Deletes the 'index' row in the table, and returns it. - Raises an IndexError if index is out of range - """ - self.row_names.pop(index) - return self.data.pop(index) - - - def delete_row_by_id(self, row_id): - """Deletes the 'row_id' row in the table. - Raises a KeyError if row_id was not found. - """ - try: - row_index = self.row_names.index(row_id) - self.delete_row(row_index) - except ValueError: - raise KeyError('Row (%s) not found in table' % (row_id)) - - - def set_column(self, col_index, col_data): - """sets the 'col_index' column - pre: - type(col_data) == types.ListType - len(col_data) == len(self.row_names) - """ - - for row_index, cell_data in enumerate(col_data): - self.data[row_index][col_index] = cell_data - - - def set_column_by_id(self, col_id, col_data): - """sets the 'col_id' column - pre: - type(col_data) == types.ListType - len(col_data) == len(self.col_names) - Raises a KeyError if col_id is not found - """ - try: - col_index = self.col_names.index(col_id) - self.set_column(col_index, col_data) - except ValueError: - raise KeyError('Column (%s) not found in table' % (col_id)) - - - def append_column(self, col_data, col_name): - """Appends the 'col_index' column - pre: - type(col_data) == types.ListType - len(col_data) == len(self.row_names) - """ - self.col_names.append(col_name) - for row_index, cell_data in enumerate(col_data): - self.data[row_index].append(cell_data) - - - def insert_column(self, index, col_data, col_name): - """Appends col_data before 'index' in the table. To make 'insert' - behave like 'list.insert', inserting in an out of range index will - insert col_data to the end of the list - pre: - type(col_data) == types.ListType - len(col_data) == len(self.row_names) - """ - self.col_names.insert(index, col_name) - for row_index, cell_data in enumerate(col_data): - self.data[row_index].insert(index, cell_data) - - - def delete_column(self, index): - """Deletes the 'index' column in the table, and returns it. - Raises an IndexError if index is out of range - """ - self.col_names.pop(index) - return [row.pop(index) for row in self.data] - - - def delete_column_by_id(self, col_id): - """Deletes the 'col_id' col in the table. - Raises a KeyError if col_id was not found. - """ - try: - col_index = self.col_names.index(col_id) - self.delete_column(col_index) - except ValueError: - raise KeyError('Column (%s) not found in table' % (col_id)) - - - ## The 'getter' part ####################################################### - - def get_shape(self): - """Returns a tuple which represents the table's shape - """ - return len(self.row_names), len(self.col_names) - shape = property(get_shape) - - def __getitem__(self, indices): - """provided for convenience""" - rows, multirows = None, False - cols, multicols = None, False - if isinstance(indices, tuple): - rows = indices[0] - if len(indices) > 1: - cols = indices[1] - else: - rows = indices - # define row slice - if isinstance(rows, str): - try: - rows = self.row_names.index(rows) - except ValueError: - raise KeyError("Row (%s) not found in table" % (rows)) - if isinstance(rows, int): - rows = slice(rows, rows+1) - multirows = False - else: - rows = slice(None) - multirows = True - # define col slice - if isinstance(cols, str): - try: - cols = self.col_names.index(cols) - except ValueError: - raise KeyError("Column (%s) not found in table" % (cols)) - if isinstance(cols, int): - cols = slice(cols, cols+1) - multicols = False - else: - cols = slice(None) - multicols = True - # get sub-table - tab = Table() - tab.default_value = self.default_value - tab.create_rows(self.row_names[rows]) - tab.create_columns(self.col_names[cols]) - for idx, row in enumerate(self.data[rows]): - tab.set_row(idx, row[cols]) - if multirows : - if multicols: - return tab - else: - return [item[0] for item in tab.data] - else: - if multicols: - return tab.data[0] - else: - return tab.data[0][0] - - def get_cell_by_ids(self, row_id, col_id): - """Returns the element at [row_id][col_id] - """ - try: - row_index = self.row_names.index(row_id) - except ValueError: - raise KeyError("Row (%s) not found in table" % (row_id)) - else: - try: - col_index = self.col_names.index(col_id) - except ValueError: - raise KeyError("Column (%s) not found in table" % (col_id)) - return self.data[row_index][col_index] - - def get_row_by_id(self, row_id): - """Returns the 'row_id' row - """ - try: - row_index = self.row_names.index(row_id) - except ValueError: - raise KeyError("Row (%s) not found in table" % (row_id)) - return self.data[row_index] - - def get_column_by_id(self, col_id, distinct=False): - """Returns the 'col_id' col - """ - try: - col_index = self.col_names.index(col_id) - except ValueError: - raise KeyError("Column (%s) not found in table" % (col_id)) - return self.get_column(col_index, distinct) - - def get_columns(self): - """Returns all the columns in the table - """ - return [self[:, index] for index in range(len(self.col_names))] - - def get_column(self, col_index, distinct=False): - """get a column by index""" - col = [row[col_index] for row in self.data] - if distinct: - col = list(set(col)) - return col - - def apply_stylesheet(self, stylesheet): - """Applies the stylesheet to this table - """ - for instruction in stylesheet.instructions: - eval(instruction) - - - def transpose(self): - """Keeps the self object intact, and returns the transposed (rotated) - table. - """ - transposed = Table() - transposed.create_rows(self.col_names) - transposed.create_columns(self.row_names) - for col_index, column in enumerate(self.get_columns()): - transposed.set_row(col_index, column) - return transposed - - - def pprint(self): - """returns a string representing the table in a pretty - printed 'text' format. - """ - # The maximum row name (to know the start_index of the first col) - max_row_name = 0 - for row_name in self.row_names: - if len(row_name) > max_row_name: - max_row_name = len(row_name) - col_start = max_row_name + 5 - - lines = [] - # Build the 'first' line <=> the col_names one - # The first cell <=> an empty one - col_names_line = [' '*col_start] - for col_name in self.col_names: - col_names_line.append(col_name + ' '*5) - lines.append('|' + '|'.join(col_names_line) + '|') - max_line_length = len(lines[0]) - - # Build the table - for row_index, row in enumerate(self.data): - line = [] - # First, build the row_name's cell - row_name = self.row_names[row_index] - line.append(row_name + ' '*(col_start-len(row_name))) - - # Then, build all the table's cell for this line. - for col_index, cell in enumerate(row): - col_name_length = len(self.col_names[col_index]) + 5 - data = str(cell) - line.append(data + ' '*(col_name_length - len(data))) - lines.append('|' + '|'.join(line) + '|') - if len(lines[-1]) > max_line_length: - max_line_length = len(lines[-1]) - - # Wrap the table with '-' to make a frame - lines.insert(0, '-'*max_line_length) - lines.append('-'*max_line_length) - return '\n'.join(lines) - - - def __repr__(self): - return repr(self.data) - - def as_text(self): - data = [] - # We must convert cells into strings before joining them - for row in self.data: - data.append([str(cell) for cell in row]) - lines = ['\t'.join(row) for row in data] - return '\n'.join(lines) - - - -class TableStyle: - """Defines a table's style - """ - - def __init__(self, table): - - self._table = table - self.size = dict([(col_name, '1*') for col_name in table.col_names]) - # __row_column__ is a special key to define the first column which - # actually has no name (<=> left most column <=> row names column) - self.size['__row_column__'] = '1*' - self.alignment = dict([(col_name, 'right') - for col_name in table.col_names]) - self.alignment['__row_column__'] = 'right' - - # We shouldn't have to create an entry for - # the 1st col (the row_column one) - self.units = dict([(col_name, '') for col_name in table.col_names]) - self.units['__row_column__'] = '' - - # XXX FIXME : params order should be reversed for all set() methods - def set_size(self, value, col_id): - """sets the size of the specified col_id to value - """ - self.size[col_id] = value - - def set_size_by_index(self, value, col_index): - """Allows to set the size according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - self.size[col_id] = value - - - def set_alignment(self, value, col_id): - """sets the alignment of the specified col_id to value - """ - self.alignment[col_id] = value - - - def set_alignment_by_index(self, value, col_index): - """Allows to set the alignment according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - self.alignment[col_id] = value - - - def set_unit(self, value, col_id): - """sets the unit of the specified col_id to value - """ - self.units[col_id] = value - - - def set_unit_by_index(self, value, col_index): - """Allows to set the unit according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - (Note that in the 'unit' case, you shouldn't have to set a unit - for the 1st column (the __row__column__ one)) - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - self.units[col_id] = value - - - def get_size(self, col_id): - """Returns the size of the specified col_id - """ - return self.size[col_id] - - - def get_size_by_index(self, col_index): - """Allows to get the size according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - return self.size[col_id] - - - def get_alignment(self, col_id): - """Returns the alignment of the specified col_id - """ - return self.alignment[col_id] - - - def get_alignment_by_index(self, col_index): - """Allors to get the alignment according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - return self.alignment[col_id] - - - def get_unit(self, col_id): - """Returns the unit of the specified col_id - """ - return self.units[col_id] - - - def get_unit_by_index(self, col_index): - """Allors to get the unit according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - return self.units[col_id] - - -import re -CELL_PROG = re.compile("([0-9]+)_([0-9]+)") - -class TableStyleSheet: - """A simple Table stylesheet - Rules are expressions where cells are defined by the row_index - and col_index separated by an underscore ('_'). - For example, suppose you want to say that the (2,5) cell must be - the sum of its two preceding cells in the row, you would create - the following rule : - 2_5 = 2_3 + 2_4 - You can also use all the math.* operations you want. For example: - 2_5 = sqrt(2_3**2 + 2_4**2) - """ - - def __init__(self, rules = None): - rules = rules or [] - self.rules = [] - self.instructions = [] - for rule in rules: - self.add_rule(rule) - - - def add_rule(self, rule): - """Adds a rule to the stylesheet rules - """ - try: - source_code = ['from math import *'] - source_code.append(CELL_PROG.sub(r'self.data[\1][\2]', rule)) - self.instructions.append(compile('\n'.join(source_code), - 'table.py', 'exec')) - self.rules.append(rule) - except SyntaxError: - print("Bad Stylesheet Rule : %s [skipped]" % rule) - - - def add_rowsum_rule(self, dest_cell, row_index, start_col, end_col): - """Creates and adds a rule to sum over the row at row_index from - start_col to end_col. - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_col >= 0 - end_col > start_col - """ - cell_list = ['%d_%d'%(row_index, index) for index in range(start_col, - end_col + 1)] - rule = '%d_%d=' % dest_cell + '+'.join(cell_list) - self.add_rule(rule) - - - def add_rowavg_rule(self, dest_cell, row_index, start_col, end_col): - """Creates and adds a rule to make the row average (from start_col - to end_col) - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_col >= 0 - end_col > start_col - """ - cell_list = ['%d_%d'%(row_index, index) for index in range(start_col, - end_col + 1)] - num = (end_col - start_col + 1) - rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num - self.add_rule(rule) - - - def add_colsum_rule(self, dest_cell, col_index, start_row, end_row): - """Creates and adds a rule to sum over the col at col_index from - start_row to end_row. - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_row >= 0 - end_row > start_row - """ - cell_list = ['%d_%d'%(index, col_index) for index in range(start_row, - end_row + 1)] - rule = '%d_%d=' % dest_cell + '+'.join(cell_list) - self.add_rule(rule) - - - def add_colavg_rule(self, dest_cell, col_index, start_row, end_row): - """Creates and adds a rule to make the col average (from start_row - to end_row) - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_row >= 0 - end_row > start_row - """ - cell_list = ['%d_%d'%(index, col_index) for index in range(start_row, - end_row + 1)] - num = (end_row - start_row + 1) - rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num - self.add_rule(rule) - - - -class TableCellRenderer: - """Defines a simple text renderer - """ - - def __init__(self, **properties): - """keywords should be properties with an associated boolean as value. - For example : - renderer = TableCellRenderer(units = True, alignment = False) - An unspecified property will have a 'False' value by default. - Possible properties are : - alignment, unit - """ - self.properties = properties - - - def render_cell(self, cell_coord, table, table_style): - """Renders the cell at 'cell_coord' in the table, using table_style - """ - row_index, col_index = cell_coord - cell_value = table.data[row_index][col_index] - final_content = self._make_cell_content(cell_value, - table_style, col_index +1) - return self._render_cell_content(final_content, - table_style, col_index + 1) - - - def render_row_cell(self, row_name, table, table_style): - """Renders the cell for 'row_id' row - """ - cell_value = row_name - return self._render_cell_content(cell_value, table_style, 0) - - - def render_col_cell(self, col_name, table, table_style): - """Renders the cell for 'col_id' row - """ - cell_value = col_name - col_index = table.col_names.index(col_name) - return self._render_cell_content(cell_value, table_style, col_index +1) - - - - def _render_cell_content(self, content, table_style, col_index): - """Makes the appropriate rendering for this cell content. - Rendering properties will be searched using the - *table_style.get_xxx_by_index(col_index)' methods - - **This method should be overridden in the derived renderer classes.** - """ - return content - - - def _make_cell_content(self, cell_content, table_style, col_index): - """Makes the cell content (adds decoration data, like units for - example) - """ - final_content = cell_content - if 'skip_zero' in self.properties: - replacement_char = self.properties['skip_zero'] - else: - replacement_char = 0 - if replacement_char and final_content == 0: - return replacement_char - - try: - units_on = self.properties['units'] - if units_on: - final_content = self._add_unit( - cell_content, table_style, col_index) - except KeyError: - pass - - return final_content - - - def _add_unit(self, cell_content, table_style, col_index): - """Adds unit to the cell_content if needed - """ - unit = table_style.get_unit_by_index(col_index) - return str(cell_content) + " " + unit - - - -class DocbookRenderer(TableCellRenderer): - """Defines how to render a cell for a docboook table - """ - - def define_col_header(self, col_index, table_style): - """Computes the colspec element according to the style - """ - size = table_style.get_size_by_index(col_index) - return '\n' % \ - (col_index, size) - - - def _render_cell_content(self, cell_content, table_style, col_index): - """Makes the appropriate rendering for this cell content. - Rendering properties will be searched using the - table_style.get_xxx_by_index(col_index)' methods. - """ - try: - align_on = self.properties['alignment'] - alignment = table_style.get_alignment_by_index(col_index) - if align_on: - return "%s\n" % \ - (alignment, cell_content) - except KeyError: - # KeyError <=> Default alignment - return "%s\n" % cell_content - - -class TableWriter: - """A class to write tables - """ - - def __init__(self, stream, table, style, **properties): - self._stream = stream - self.style = style or TableStyle(table) - self._table = table - self.properties = properties - self.renderer = None - - - def set_style(self, style): - """sets the table's associated style - """ - self.style = style - - - def set_renderer(self, renderer): - """sets the way to render cell - """ - self.renderer = renderer - - - def update_properties(self, **properties): - """Updates writer's properties (for cell rendering) - """ - self.properties.update(properties) - - - def write_table(self, title = ""): - """Writes the table - """ - raise NotImplementedError("write_table must be implemented !") - - - -class DocbookTableWriter(TableWriter): - """Defines an implementation of TableWriter to write a table in Docbook - """ - - def _write_headers(self): - """Writes col headers - """ - # Define col_headers (colstpec elements) - for col_index in range(len(self._table.col_names)+1): - self._stream.write(self.renderer.define_col_header(col_index, - self.style)) - - self._stream.write("\n\n") - # XXX FIXME : write an empty entry <=> the first (__row_column) column - self._stream.write('\n') - for col_name in self._table.col_names: - self._stream.write(self.renderer.render_col_cell( - col_name, self._table, - self.style)) - - self._stream.write("\n\n") - - - def _write_body(self): - """Writes the table body - """ - self._stream.write('\n') - - for row_index, row in enumerate(self._table.data): - self._stream.write('\n') - row_name = self._table.row_names[row_index] - # Write the first entry (row_name) - self._stream.write(self.renderer.render_row_cell(row_name, - self._table, - self.style)) - - for col_index, cell in enumerate(row): - self._stream.write(self.renderer.render_cell( - (row_index, col_index), - self._table, self.style)) - - self._stream.write('\n') - - self._stream.write('\n') - - - def write_table(self, title = ""): - """Writes the table - """ - self._stream.write('\nCodestin Search App\n'%(title)) - self._stream.write( - '\n'% - (len(self._table.col_names)+1)) - self._write_headers() - self._write_body() - - self._stream.write('\n
\n') - - diff --git a/pymode/libs/logilab/common/tasksqueue.py b/pymode/libs/logilab/common/tasksqueue.py deleted file mode 100644 index ed74cf5a..00000000 --- a/pymode/libs/logilab/common/tasksqueue.py +++ /dev/null @@ -1,101 +0,0 @@ -# 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 . -"""Prioritized tasks queue""" - -__docformat__ = "restructuredtext en" - -from bisect import insort_left - -from six.moves import queue - -LOW = 0 -MEDIUM = 10 -HIGH = 100 - -PRIORITY = { - 'LOW': LOW, - 'MEDIUM': MEDIUM, - 'HIGH': HIGH, - } -REVERSE_PRIORITY = dict((values, key) for key, values in PRIORITY.items()) - - - -class PrioritizedTasksQueue(queue.Queue): - - def _init(self, maxsize): - """Initialize the queue representation""" - self.maxsize = maxsize - # ordered list of task, from the lowest to the highest priority - self.queue = [] - - def _put(self, item): - """Put a new item in the queue""" - for i, task in enumerate(self.queue): - # equivalent task - if task == item: - # if new task has a higher priority, remove the one already - # queued so the new priority will be considered - if task < item: - item.merge(task) - del self.queue[i] - break - # else keep it so current order is kept - task.merge(item) - return - insort_left(self.queue, item) - - def _get(self): - """Get an item from the queue""" - return self.queue.pop() - - def __iter__(self): - return iter(self.queue) - - def remove(self, tid): - """remove a specific task from the queue""" - # XXX acquire lock - for i, task in enumerate(self): - if task.id == tid: - self.queue.pop(i) - return - raise ValueError('not task of id %s in queue' % tid) - -class Task(object): - def __init__(self, tid, priority=LOW): - # task id - self.id = tid - # task priority - self.priority = priority - - def __repr__(self): - return '' % (self.id, id(self)) - - def __cmp__(self, other): - return cmp(self.priority, other.priority) - - def __lt__(self, other): - return self.priority < other.priority - - def __eq__(self, other): - return self.id == other.id - - __hash__ = object.__hash__ - - def merge(self, other): - pass 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/textutils.py b/pymode/libs/logilab/common/textutils.py deleted file mode 100644 index 9046f975..00000000 --- a/pymode/libs/logilab/common/textutils.py +++ /dev/null @@ -1,537 +0,0 @@ -# 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 . -"""Some text manipulation utility functions. - - -:group text formatting: normalize_text, normalize_paragraph, pretty_match,\ -unquote, colorize_ansi -:group text manipulation: searchall, splitstrip -:sort: text formatting, text manipulation - -:type ANSI_STYLES: dict(str) -:var ANSI_STYLES: dictionary mapping style identifier to ANSI terminal code - -:type ANSI_COLORS: dict(str) -:var ANSI_COLORS: dictionary mapping color identifier to ANSI terminal code - -:type ANSI_PREFIX: str -:var ANSI_PREFIX: - ANSI terminal code notifying the start of an ANSI escape sequence - -:type ANSI_END: str -:var ANSI_END: - ANSI terminal code notifying the end of an ANSI escape sequence - -:type ANSI_RESET: str -:var ANSI_RESET: - ANSI terminal code resetting format defined by a previous ANSI escape sequence -""" -__docformat__ = "restructuredtext en" - -import sys -import re -import os.path as osp -from warnings import warn -from unicodedata import normalize as _uninormalize -try: - from os import linesep -except ImportError: - linesep = '\n' # gae - -from logilab.common.deprecation import deprecated - -MANUAL_UNICODE_MAP = { - u'\xa1': u'!', # INVERTED EXCLAMATION MARK - u'\u0142': u'l', # LATIN SMALL LETTER L WITH STROKE - u'\u2044': u'/', # FRACTION SLASH - u'\xc6': u'AE', # LATIN CAPITAL LETTER AE - u'\xa9': u'(c)', # COPYRIGHT SIGN - u'\xab': u'"', # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK - u'\xe6': u'ae', # LATIN SMALL LETTER AE - u'\xae': u'(r)', # REGISTERED SIGN - u'\u0153': u'oe', # LATIN SMALL LIGATURE OE - u'\u0152': u'OE', # LATIN CAPITAL LIGATURE OE - u'\xd8': u'O', # LATIN CAPITAL LETTER O WITH STROKE - 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 - } - -def unormalize(ustring, ignorenonascii=None, substitute=None): - """replace diacritical characters with their corresponding ascii characters - - Convert the unicode string to its long normalized form (unicode character - will be transform into several characters) and keep the first one only. - The normal form KD (NFKD) will apply the compatibility decomposition, i.e. - replace all compatibility characters with their equivalents. - - :type substitute: str - :param substitute: replacement character to use if decomposition fails - - :see: Another project about ASCII transliterations of Unicode text - http://pypi.python.org/pypi/Unidecode - """ - # backward compatibility, ignorenonascii was a boolean - if ignorenonascii is not None: - warn("ignorenonascii is deprecated, use substitute named parameter instead", - DeprecationWarning, stacklevel=2) - if ignorenonascii: - substitute = '' - res = [] - for letter in ustring[:]: - try: - replacement = MANUAL_UNICODE_MAP[letter] - except KeyError: - replacement = _uninormalize('NFKD', letter)[0] - if ord(replacement) >= 2 ** 7: - if substitute is None: - raise ValueError("can't deal with non-ascii based characters") - replacement = substitute - res.append(replacement) - return u''.join(res) - -def unquote(string): - """remove optional quotes (simple or double) from the string - - :type string: str or unicode - :param string: an optionally quoted string - - :rtype: str or unicode - :return: the unquoted string (or the input string if it wasn't quoted) - """ - if not string: - return string - if string[0] in '"\'': - string = string[1:] - if string[-1] in '"\'': - string = string[:-1] - return string - - -_BLANKLINES_RGX = re.compile('\r?\n\r?\n') -_NORM_SPACES_RGX = re.compile('\s+') - -def normalize_text(text, line_len=80, indent='', rest=False): - """normalize a text to display it with a maximum line size and - optionally arbitrary indentation. Line jumps are normalized but blank - lines are kept. The indentation string may be used to insert a - comment (#) or a quoting (>) mark for instance. - - :type text: str or unicode - :param text: the input text to normalize - - :type line_len: int - :param line_len: expected maximum line's length, default to 80 - - :type indent: str or unicode - :param indent: optional string to use as indentation - - :rtype: str or unicode - :return: - the input text normalized to fit on lines with a maximized size - inferior to `line_len`, and optionally prefixed by an - indentation string - """ - if rest: - normp = normalize_rest_paragraph - else: - normp = normalize_paragraph - result = [] - for text in _BLANKLINES_RGX.split(text): - result.append(normp(text, line_len, indent)) - return ('%s%s%s' % (linesep, indent, linesep)).join(result) - - -def normalize_paragraph(text, line_len=80, indent=''): - """normalize a text to display it with a maximum line size and - optionally arbitrary indentation. Line jumps are normalized. The - indentation string may be used top insert a comment mark for - instance. - - :type text: str or unicode - :param text: the input text to normalize - - :type line_len: int - :param line_len: expected maximum line's length, default to 80 - - :type indent: str or unicode - :param indent: optional string to use as indentation - - :rtype: str or unicode - :return: - the input text normalized to fit on lines with a maximized size - inferior to `line_len`, and optionally prefixed by an - indentation string - """ - text = _NORM_SPACES_RGX.sub(' ', text) - line_len = line_len - len(indent) - lines = [] - while text: - aline, text = splittext(text.strip(), line_len) - lines.append(indent + aline) - return linesep.join(lines) - -def normalize_rest_paragraph(text, line_len=80, indent=''): - """normalize a ReST text to display it with a maximum line size and - optionally arbitrary indentation. Line jumps are normalized. The - indentation string may be used top insert a comment mark for - instance. - - :type text: str or unicode - :param text: the input text to normalize - - :type line_len: int - :param line_len: expected maximum line's length, default to 80 - - :type indent: str or unicode - :param indent: optional string to use as indentation - - :rtype: str or unicode - :return: - the input text normalized to fit on lines with a maximized size - inferior to `line_len`, and optionally prefixed by an - indentation string - """ - toreport = '' - lines = [] - line_len = line_len - len(indent) - for line in text.splitlines(): - line = toreport + _NORM_SPACES_RGX.sub(' ', line.strip()) - toreport = '' - while len(line) > line_len: - # too long line, need split - line, toreport = splittext(line, line_len) - lines.append(indent + line) - if toreport: - line = toreport + ' ' - toreport = '' - else: - line = '' - if line: - lines.append(indent + line.strip()) - return linesep.join(lines) - - -def splittext(text, line_len): - """split the given text on space according to the given max line size - - return a 2-uple: - * a line <= line_len if possible - * the rest of the text which has to be reported on another line - """ - if len(text) <= line_len: - return text, '' - pos = min(len(text)-1, line_len) - while pos > 0 and text[pos] != ' ': - pos -= 1 - if pos == 0: - pos = min(len(text), line_len) - while len(text) > pos and text[pos] != ' ': - pos += 1 - return text[:pos], text[pos+1:].strip() - - -def splitstrip(string, sep=','): - """return a list of stripped string by splitting the string given as - argument on `sep` (',' by default). Empty string are discarded. - - >>> splitstrip('a, b, c , 4,,') - ['a', 'b', 'c', '4'] - >>> splitstrip('a') - ['a'] - >>> - - :type string: str or unicode - :param string: a csv line - - :type sep: str or unicode - :param sep: field separator, default to the comma (',') - - :rtype: str or unicode - :return: the unquoted string (or the input string if it wasn't quoted) - """ - return [word.strip() for word in string.split(sep) if word.strip()] - -get_csv = deprecated('get_csv is deprecated, use splitstrip')(splitstrip) - - -def split_url_or_path(url_or_path): - """return the latest component of a string containing either an url of the - form :// or a local file system path - """ - if '://' in url_or_path: - return url_or_path.rstrip('/').rsplit('/', 1) - return osp.split(url_or_path.rstrip(osp.sep)) - - -def text_to_dict(text): - """parse multilines text containing simple 'key=value' lines and return a - dict of {'key': 'value'}. When the same key is encountered multiple time, - value is turned into a list containing all values. - - >>> d = text_to_dict('''multiple=1 - ... multiple= 2 - ... single =3 - ... ''') - >>> d['single'] - '3' - >>> d['multiple'] - ['1', '2'] - - """ - res = {} - if not text: - return res - for line in text.splitlines(): - line = line.strip() - if line and not line.startswith('#'): - key, value = [w.strip() for w in line.split('=', 1)] - if key in res: - try: - res[key].append(value) - except AttributeError: - res[key] = [res[key], value] - else: - res[key] = value - return res - - -_BLANK_URE = r'(\s|,)+' -_BLANK_RE = re.compile(_BLANK_URE) -__VALUE_URE = r'-?(([0-9]+\.[0-9]*)|((0x?)?[0-9]+))' -__UNITS_URE = r'[a-zA-Z]+' -_VALUE_RE = re.compile(r'(?P%s)(?P%s)?'%(__VALUE_URE, __UNITS_URE)) -_VALIDATION_RE = re.compile(r'^((%s)(%s))*(%s)?$' % (__VALUE_URE, __UNITS_URE, - __VALUE_URE)) - -BYTE_UNITS = { - "b": 1, - "kb": 1024, - "mb": 1024 ** 2, - "gb": 1024 ** 3, - "tb": 1024 ** 4, -} - -TIME_UNITS = { - "ms": 0.0001, - "s": 1, - "min": 60, - "h": 60 * 60, - "d": 60 * 60 *24, -} - -def apply_units(string, units, inter=None, final=float, blank_reg=_BLANK_RE, - value_reg=_VALUE_RE): - """Parse the string applying the units defined in units - (e.g.: "1.5m",{'m',60} -> 80). - - :type string: str or unicode - :param string: the string to parse - - :type units: dict (or any object with __getitem__ using basestring key) - :param units: a dict mapping a unit string repr to its value - - :type inter: type - :param inter: used to parse every intermediate value (need __sum__) - - :type blank_reg: regexp - :param blank_reg: should match every blank char to ignore. - - :type value_reg: regexp with "value" and optional "unit" group - :param value_reg: match a value and it's unit into the - """ - if inter is None: - inter = final - fstring = _BLANK_RE.sub('', string) - if not (fstring and _VALIDATION_RE.match(fstring)): - raise ValueError("Invalid unit string: %r." % string) - values = [] - for match in value_reg.finditer(fstring): - dic = match.groupdict() - lit, unit = dic["value"], dic.get("unit") - value = inter(lit) - if unit is not None: - try: - value *= units[unit.lower()] - except KeyError: - raise KeyError('invalid unit %s. valid units are %s' % - (unit, units.keys())) - values.append(value) - return final(sum(values)) - - -_LINE_RGX = re.compile('\r\n|\r+|\n') - -def pretty_match(match, string, underline_char='^'): - """return a string with the match location underlined: - - >>> import re - >>> print(pretty_match(re.search('mange', 'il mange du bacon'), 'il mange du bacon')) - il mange du bacon - ^^^^^ - >>> - - :type match: _sre.SRE_match - :param match: object returned by re.match, re.search or re.finditer - - :type string: str or unicode - :param string: - the string on which the regular expression has been applied to - obtain the `match` object - - :type underline_char: str or unicode - :param underline_char: - character to use to underline the matched section, default to the - carret '^' - - :rtype: str or unicode - :return: - the original string with an inserted line to underline the match - location - """ - start = match.start() - end = match.end() - string = _LINE_RGX.sub(linesep, string) - start_line_pos = string.rfind(linesep, 0, start) - if start_line_pos == -1: - start_line_pos = 0 - result = [] - else: - result = [string[:start_line_pos]] - start_line_pos += len(linesep) - offset = start - start_line_pos - underline = ' ' * offset + underline_char * (end - start) - end_line_pos = string.find(linesep, end) - if end_line_pos == -1: - string = string[start_line_pos:] - result.append(string) - result.append(underline) - else: - end = string[end_line_pos + len(linesep):] - string = string[start_line_pos:end_line_pos] - result.append(string) - result.append(underline) - result.append(end) - return linesep.join(result).rstrip() - - -# Ansi colorization ########################################################### - -ANSI_PREFIX = '\033[' -ANSI_END = 'm' -ANSI_RESET = '\033[0m' -ANSI_STYLES = { - 'reset': "0", - 'bold': "1", - 'italic': "3", - 'underline': "4", - 'blink': "5", - 'inverse': "7", - 'strike': "9", -} -ANSI_COLORS = { - 'reset': "0", - 'black': "30", - 'red': "31", - 'green': "32", - 'yellow': "33", - 'blue': "34", - 'magenta': "35", - 'cyan': "36", - 'white': "37", -} - -def _get_ansi_code(color=None, style=None): - """return ansi escape code corresponding to color and style - - :type color: str or None - :param color: - the color name (see `ANSI_COLORS` for available values) - or the color number when 256 colors are available - - :type style: str or None - :param style: - style string (see `ANSI_COLORS` for available values). To get - several style effects at the same time, use a coma as separator. - - :raise KeyError: if an unexistent color or style identifier is given - - :rtype: str - :return: the built escape code - """ - ansi_code = [] - if style: - style_attrs = splitstrip(style) - for effect in style_attrs: - ansi_code.append(ANSI_STYLES[effect]) - if color: - if color.isdigit(): - ansi_code.extend(['38', '5']) - ansi_code.append(color) - else: - ansi_code.append(ANSI_COLORS[color]) - if ansi_code: - return ANSI_PREFIX + ';'.join(ansi_code) + ANSI_END - return '' - -def colorize_ansi(msg, color=None, style=None): - """colorize message by wrapping it with ansi escape codes - - :type msg: str or unicode - :param msg: the message string to colorize - - :type color: str or None - :param color: - the color identifier (see `ANSI_COLORS` for available values) - - :type style: str or None - :param style: - style string (see `ANSI_COLORS` for available values). To get - several style effects at the same time, use a coma as separator. - - :raise KeyError: if an unexistent color or style identifier is given - - :rtype: str or unicode - :return: the ansi escaped string - """ - # If both color and style are not defined, then leave the text as is - if color is None and style is None: - return msg - escape_code = _get_ansi_code(color, style) - # If invalid (or unknown) color, don't wrap msg with ansi codes - if escape_code: - return '%s%s%s' % (escape_code, msg, ANSI_RESET) - return msg - -DIFF_STYLE = {'separator': 'cyan', 'remove': 'red', 'add': 'green'} - -def diff_colorize_ansi(lines, out=sys.stdout, style=DIFF_STYLE): - for line in lines: - if line[:4] in ('--- ', '+++ '): - out.write(colorize_ansi(line, style['separator'])) - elif line[0] == '-': - out.write(colorize_ansi(line, style['remove'])) - elif line[0] == '+': - out.write(colorize_ansi(line, style['add'])) - elif line[:4] == '--- ': - out.write(colorize_ansi(line, style['separator'])) - elif line[:4] == '+++ ': - out.write(colorize_ansi(line, style['separator'])) - else: - out.write(line) - diff --git a/pymode/libs/logilab/common/tree.py b/pymode/libs/logilab/common/tree.py deleted file mode 100644 index 885eb0fa..00000000 --- a/pymode/libs/logilab/common/tree.py +++ /dev/null @@ -1,369 +0,0 @@ -# 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 . -"""Base class to represent a tree structure. - - - - -""" -__docformat__ = "restructuredtext en" - -import sys - -from logilab.common import flatten -from logilab.common.visitor import VisitedMixIn, FilteredIterator, no_filter - -## Exceptions ################################################################# - -class NodeNotFound(Exception): - """raised when a node has not been found""" - -EX_SIBLING_NOT_FOUND = "No such sibling as '%s'" -EX_CHILD_NOT_FOUND = "No such child as '%s'" -EX_NODE_NOT_FOUND = "No such node as '%s'" - - -# Base node ################################################################### - -class Node(object): - """a basic tree node, characterized by an id""" - - def __init__(self, nid=None) : - self.id = nid - # navigation - self.parent = None - self.children = [] - - def __iter__(self): - return iter(self.children) - - def __str__(self, indent=0): - s = ['%s%s %s' % (' '*indent, self.__class__.__name__, self.id)] - indent += 2 - for child in self.children: - try: - s.append(child.__str__(indent)) - except TypeError: - s.append(child.__str__()) - return '\n'.join(s) - - def is_leaf(self): - return not self.children - - def append(self, child): - """add a node to children""" - self.children.append(child) - child.parent = self - - def remove(self, child): - """remove a child node""" - self.children.remove(child) - child.parent = None - - def insert(self, index, child): - """insert a child node""" - self.children.insert(index, child) - child.parent = self - - def replace(self, old_child, new_child): - """replace a child node with another""" - i = self.children.index(old_child) - self.children.pop(i) - self.children.insert(i, new_child) - new_child.parent = self - - def get_sibling(self, nid): - """return the sibling node that has given id""" - try: - return self.parent.get_child_by_id(nid) - except NodeNotFound : - raise NodeNotFound(EX_SIBLING_NOT_FOUND % nid) - - def next_sibling(self): - """ - return the next sibling for this node if any - """ - parent = self.parent - if parent is None: - # root node has no sibling - return None - index = parent.children.index(self) - try: - return parent.children[index+1] - except IndexError: - return None - - def previous_sibling(self): - """ - return the previous sibling for this node if any - """ - parent = self.parent - if parent is None: - # root node has no sibling - return None - index = parent.children.index(self) - if index > 0: - return parent.children[index-1] - return None - - def get_node_by_id(self, nid): - """ - return node in whole hierarchy that has given id - """ - root = self.root() - try: - return root.get_child_by_id(nid, 1) - except NodeNotFound : - raise NodeNotFound(EX_NODE_NOT_FOUND % nid) - - def get_child_by_id(self, nid, recurse=None): - """ - return child of given id - """ - if self.id == nid: - return self - for c in self.children : - if recurse: - try: - return c.get_child_by_id(nid, 1) - except NodeNotFound : - continue - if c.id == nid : - return c - raise NodeNotFound(EX_CHILD_NOT_FOUND % nid) - - def get_child_by_path(self, path): - """ - return child of given path (path is a list of ids) - """ - if len(path) > 0 and path[0] == self.id: - if len(path) == 1 : - return self - else : - for c in self.children : - try: - return c.get_child_by_path(path[1:]) - except NodeNotFound : - pass - raise NodeNotFound(EX_CHILD_NOT_FOUND % path) - - def depth(self): - """ - return depth of this node in the tree - """ - if self.parent is not None: - return 1 + self.parent.depth() - else : - return 0 - - def depth_down(self): - """ - return depth of the tree from this node - """ - if self.children: - return 1 + max([c.depth_down() for c in self.children]) - return 1 - - def width(self): - """ - return the width of the tree from this node - """ - return len(self.leaves()) - - def root(self): - """ - return the root node of the tree - """ - if self.parent is not None: - return self.parent.root() - return self - - def leaves(self): - """ - return a list with all the leaves nodes descendant from this node - """ - leaves = [] - if self.children: - for child in self.children: - leaves += child.leaves() - return leaves - else: - return [self] - - def flatten(self, _list=None): - """ - return a list with all the nodes descendant from this node - """ - if _list is None: - _list = [] - _list.append(self) - for c in self.children: - c.flatten(_list) - return _list - - def lineage(self): - """ - return list of parents up to root node - """ - lst = [self] - if self.parent is not None: - lst.extend(self.parent.lineage()) - return lst - -class VNode(Node, VisitedMixIn): - """a visitable node - """ - pass - - -class BinaryNode(VNode): - """a binary node (i.e. only two children - """ - def __init__(self, lhs=None, rhs=None) : - VNode.__init__(self) - if lhs is not None or rhs is not None: - assert lhs and rhs - self.append(lhs) - self.append(rhs) - - def remove(self, child): - """remove the child and replace this node with the other child - """ - self.children.remove(child) - self.parent.replace(self, self.children[0]) - - def get_parts(self): - """ - return the left hand side and the right hand side of this node - """ - return self.children[0], self.children[1] - - - -if sys.version_info[0:2] >= (2, 2): - list_class = list -else: - from UserList import UserList - list_class = UserList - -class ListNode(VNode, list_class): - """Used to manipulate Nodes as Lists - """ - def __init__(self): - list_class.__init__(self) - VNode.__init__(self) - self.children = self - - def __str__(self, indent=0): - return '%s%s %s' % (indent*' ', self.__class__.__name__, - ', '.join([str(v) for v in self])) - - def append(self, child): - """add a node to children""" - list_class.append(self, child) - child.parent = self - - def insert(self, index, child): - """add a node to children""" - list_class.insert(self, index, child) - child.parent = self - - def remove(self, child): - """add a node to children""" - list_class.remove(self, child) - child.parent = None - - def pop(self, index): - """add a node to children""" - child = list_class.pop(self, index) - child.parent = None - - def __iter__(self): - return list_class.__iter__(self) - -# construct list from tree #################################################### - -def post_order_list(node, filter_func=no_filter): - """ - create a list with tree nodes for which the function returned true - in a post order fashion - """ - l, stack = [], [] - poped, index = 0, 0 - while node: - if filter_func(node): - if node.children and not poped: - stack.append((node, index)) - index = 0 - node = node.children[0] - else: - l.append(node) - index += 1 - try: - node = stack[-1][0].children[index] - except IndexError: - node = None - else: - node = None - poped = 0 - if node is None and stack: - node, index = stack.pop() - poped = 1 - return l - -def pre_order_list(node, filter_func=no_filter): - """ - create a list with tree nodes for which the function returned true - in a pre order fashion - """ - l, stack = [], [] - poped, index = 0, 0 - while node: - if filter_func(node): - if not poped: - l.append(node) - if node.children and not poped: - stack.append((node, index)) - index = 0 - node = node.children[0] - else: - index += 1 - try: - node = stack[-1][0].children[index] - except IndexError: - node = None - else: - node = None - poped = 0 - if node is None and len(stack) > 1: - node, index = stack.pop() - poped = 1 - return l - -class PostfixedDepthFirstIterator(FilteredIterator): - """a postfixed depth first iterator, designed to be used with visitors - """ - def __init__(self, node, filter_func=None): - FilteredIterator.__init__(self, node, post_order_list, filter_func) - -class PrefixedDepthFirstIterator(FilteredIterator): - """a prefixed depth first iterator, designed to be used with visitors - """ - def __init__(self, node, filter_func=None): - FilteredIterator.__init__(self, node, pre_order_list, filter_func) - diff --git a/pymode/libs/logilab/common/umessage.py b/pymode/libs/logilab/common/umessage.py deleted file mode 100644 index a5e47995..00000000 --- a/pymode/libs/logilab/common/umessage.py +++ /dev/null @@ -1,194 +0,0 @@ -# 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 . -"""Unicode email support (extends email from stdlib)""" - -__docformat__ = "restructuredtext en" - -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 datetime import datetime - -try: - from mx.DateTime import DateTime -except ImportError: - DateTime = datetime - -import logilab.common as lgc - - -def decode_QP(string): - parts = [] - for decoded, charset in decode_header(string): - if not charset : - charset = 'iso-8859-15' - parts.append(decoded.decode(charset, 'replace')) - - if sys.version_info < (3, 3): - # decoding was non-RFC compliant wrt to whitespace handling - # see http://bugs.python.org/issue1079 - return u' '.join(parts) - return u''.join(parts) - -def message_from_file(fd): - try: - return UMessage(email.message_from_file(fd)) - except email.Errors.MessageParseError: - return '' - -def message_from_string(string): - try: - return UMessage(email.message_from_string(string)) - except email.Errors.MessageParseError: - return '' - -class UMessage: - """Encapsulates an email.Message instance and returns only unicode objects. - """ - - def __init__(self, message): - self.message = message - - # email.Message interface ################################################# - - def get(self, header, default=None): - value = self.message.get(header, default) - if value: - return decode_QP(value) - return value - - def __getitem__(self, header): - return self.get(header) - - def get_all(self, header, default=()): - return [decode_QP(val) for val in self.message.get_all(header, default) - if val is not None] - - def is_multipart(self): - return self.message.is_multipart() - - def get_boundary(self): - return self.message.get_boundary() - - 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] - 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) - - # other convenience methods ############################################### - - def headers(self): - """return an unicode string containing all the message's headers""" - values = [] - for header in self.message.keys(): - values.append(u'%s: %s' % (header, self.get(header))) - return '\n'.join(values) - - def multi_addrs(self, header): - """return a list of 2-uple (name, address) for the given address (which - is expected to be an header containing address such as from, to, cc...) - """ - persons = [] - for person in self.get_all(header, ()): - name, mail = parseaddr(person) - persons.append((name, mail)) - return persons - - def date(self, alternative_source=False, return_str=False): - """return a datetime object for the email's date or None if no date is - set or if it can't be parsed - """ - value = self.get('date') - if value is None and alternative_source: - unix_from = self.message.get_unixfrom() - if unix_from is not None: - try: - value = unix_from.split(" ", 2)[2] - except IndexError: - pass - if value is not None: - datetuple = parsedate(value) - if datetuple: - if lgc.USE_MX_DATETIME: - return DateTime(*datetuple[:6]) - return datetime(*datetuple[:6]) - elif not return_str: - return None - return value diff --git a/pymode/libs/logilab/common/ureports/__init__.py b/pymode/libs/logilab/common/ureports/__init__.py deleted file mode 100644 index d76ebe52..00000000 --- a/pymode/libs/logilab/common/ureports/__init__.py +++ /dev/null @@ -1,172 +0,0 @@ -# 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 . -"""Universal report objects and some formatting drivers. - -A way to create simple reports using python objects, primarily designed to be -formatted as text and html. -""" -__docformat__ = "restructuredtext en" - -import sys - -from logilab.common.compat import StringIO -from logilab.common.textutils import linesep - - -def get_nodes(node, klass): - """return an iterator on all children node of the given klass""" - for child in node.children: - if isinstance(child, klass): - yield child - # recurse (FIXME: recursion controled by an option) - for grandchild in get_nodes(child, klass): - yield grandchild - -def layout_title(layout): - """try to return the layout's title as string, return None if not found - """ - for child in layout.children: - if isinstance(child, Title): - return u' '.join([node.data for node in get_nodes(child, Text)]) - -def build_summary(layout, level=1): - """make a summary for the report, including X level""" - assert level > 0 - level -= 1 - summary = List(klass=u'summary') - for child in layout.children: - if not isinstance(child, Section): - continue - label = layout_title(child) - if not label and not child.id: - continue - if not child.id: - child.id = label.replace(' ', '-') - node = Link(u'#'+child.id, label=label or child.id) - # FIXME: Three following lines produce not very compliant - # docbook: there are some useless . They might be - # replaced by the three commented lines but this then produces - # a bug in html display... - if level and [n for n in child.children if isinstance(n, Section)]: - node = Paragraph([node, build_summary(child, level)]) - summary.append(node) -# summary.append(node) -# if level and [n for n in child.children if isinstance(n, Section)]: -# summary.append(build_summary(child, level)) - return summary - - -class BaseWriter(object): - """base class for ureport writers""" - - def format(self, layout, stream=None, encoding=None): - """format and write the given layout into the stream object - - unicode policy: unicode strings may be found in the layout; - try to call stream.write with it, but give it back encoded using - the given encoding if it fails - """ - if stream is None: - stream = sys.stdout - if not encoding: - encoding = getattr(stream, 'encoding', 'UTF-8') - self.encoding = encoding or 'UTF-8' - self.__compute_funcs = [] - self.out = stream - self.begin_format(layout) - layout.accept(self) - self.end_format(layout) - - def format_children(self, layout): - """recurse on the layout children and call their accept method - (see the Visitor pattern) - """ - for child in getattr(layout, 'children', ()): - child.accept(self) - - def writeln(self, string=u''): - """write a line in the output buffer""" - self.write(string + linesep) - - def write(self, string): - """write a string in the output buffer""" - try: - self.out.write(string) - except UnicodeEncodeError: - self.out.write(string.encode(self.encoding)) - - def begin_format(self, layout): - """begin to format a layout""" - self.section = 0 - - def end_format(self, layout): - """finished to format a layout""" - - def get_table_content(self, table): - """trick to get table content without actually writing it - - return an aligned list of lists containing table cells values as string - """ - result = [[]] - cols = table.cols - for cell in self.compute_content(table): - if cols == 0: - result.append([]) - cols = table.cols - cols -= 1 - result[-1].append(cell) - # fill missing cells - while len(result[-1]) < cols: - result[-1].append(u'') - return result - - def compute_content(self, layout): - """trick to compute the formatting of children layout before actually - writing it - - return an iterator on strings (one for each child element) - """ - # use cells ! - def write(data): - try: - stream.write(data) - except UnicodeEncodeError: - stream.write(data.encode(self.encoding)) - def writeln(data=u''): - try: - stream.write(data+linesep) - except UnicodeEncodeError: - stream.write(data.encode(self.encoding)+linesep) - self.write = write - self.writeln = writeln - self.__compute_funcs.append((write, writeln)) - for child in layout.children: - stream = StringIO() - child.accept(self) - yield stream.getvalue() - self.__compute_funcs.pop() - try: - self.write, self.writeln = self.__compute_funcs[-1] - except IndexError: - del self.write - del self.writeln - - -from logilab.common.ureports.nodes import * -from logilab.common.ureports.text_writer import TextWriter -from logilab.common.ureports.html_writer import HTMLWriter diff --git a/pymode/libs/logilab/common/ureports/docbook_writer.py b/pymode/libs/logilab/common/ureports/docbook_writer.py deleted file mode 100644 index 857068c8..00000000 --- a/pymode/libs/logilab/common/ureports/docbook_writer.py +++ /dev/null @@ -1,140 +0,0 @@ -# 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 . -"""HTML formatting drivers for ureports""" -__docformat__ = "restructuredtext en" - -from six.moves import range - -from logilab.common.ureports import HTMLWriter - -class DocbookWriter(HTMLWriter): - """format layouts as HTML""" - - def begin_format(self, layout): - """begin to format a layout""" - super(HTMLWriter, self).begin_format(layout) - if self.snippet is None: - self.writeln('') - self.writeln(""" - -""") - - def end_format(self, layout): - """finished to format a layout""" - if self.snippet is None: - self.writeln('') - - def visit_section(self, layout): - """display a section (using (level 0) or
)""" - if self.section == 0: - tag = "chapter" - else: - tag = "section" - self.section += 1 - self.writeln(self._indent('<%s%s>' % (tag, self.handle_attrs(layout)))) - self.format_children(layout) - self.writeln(self._indent(''% tag)) - self.section -= 1 - - def visit_title(self, layout): - """display a title using Codestin Search App') - - def visit_table(self, layout): - """display a table as html""" - self.writeln(self._indent(' Codestin Search App' \ - % (self.handle_attrs(layout), layout.title))) - self.writeln(self._indent(' '% layout.cols)) - for i in range(layout.cols): - self.writeln(self._indent(' ' % i)) - - table_content = self.get_table_content(layout) - # write headers - if layout.cheaders: - self.writeln(self._indent(' ')) - self._write_row(table_content[0]) - self.writeln(self._indent(' ')) - table_content = table_content[1:] - elif layout.rcheaders: - self.writeln(self._indent(' ')) - self._write_row(table_content[-1]) - self.writeln(self._indent(' ')) - table_content = table_content[:-1] - # write body - self.writeln(self._indent(' ')) - for i in range(len(table_content)): - row = table_content[i] - self.writeln(self._indent(' ')) - for j in range(len(row)): - cell = row[j] or ' ' - self.writeln(self._indent(' %s' % cell)) - self.writeln(self._indent(' ')) - self.writeln(self._indent(' ')) - self.writeln(self._indent(' ')) - self.writeln(self._indent(' ')) - - def _write_row(self, row): - """write content of row (using )""" - self.writeln(' ') - for j in range(len(row)): - cell = row[j] or ' ' - self.writeln(' %s' % cell) - self.writeln(self._indent(' ')) - - def visit_list(self, layout): - """display a list (using )""" - self.writeln(self._indent(' ' % self.handle_attrs(layout))) - for row in list(self.compute_content(layout)): - self.writeln(' %s' % row) - self.writeln(self._indent(' ')) - - def visit_paragraph(self, layout): - """display links (using )""" - self.write(self._indent(' ')) - self.format_children(layout) - self.writeln('') - - def visit_span(self, layout): - """display links (using

)""" - #TODO: translate in docbook - self.write('' % self.handle_attrs(layout)) - self.format_children(layout) - self.write('') - - def visit_link(self, layout): - """display links (using )""" - self.write('%s' % (layout.url, - self.handle_attrs(layout), - layout.label)) - - def visit_verbatimtext(self, layout): - """display verbatim text (using )""" - self.writeln(self._indent(' ')) - self.write(layout.data.replace('&', '&').replace('<', '<')) - self.writeln(self._indent(' ')) - - def visit_text(self, layout): - """add some text""" - self.write(layout.data.replace('&', '&').replace('<', '<')) - - def _indent(self, string): - """correctly indent string according to section""" - return ' ' * 2*(self.section) + string diff --git a/pymode/libs/logilab/common/ureports/html_writer.py b/pymode/libs/logilab/common/ureports/html_writer.py deleted file mode 100644 index eba34ea4..00000000 --- a/pymode/libs/logilab/common/ureports/html_writer.py +++ /dev/null @@ -1,133 +0,0 @@ -# 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 . -"""HTML formatting drivers for ureports""" -__docformat__ = "restructuredtext en" - -from cgi import escape - -from six.moves import range - -from logilab.common.ureports import BaseWriter - - -class HTMLWriter(BaseWriter): - """format layouts as HTML""" - - def __init__(self, snippet=None): - super(HTMLWriter, self).__init__() - self.snippet = snippet - - def handle_attrs(self, layout): - """get an attribute string from layout member attributes""" - attrs = u'' - klass = getattr(layout, 'klass', None) - if klass: - attrs += u' class="%s"' % klass - nid = getattr(layout, 'id', None) - if nid: - attrs += u' id="%s"' % nid - return attrs - - def begin_format(self, layout): - """begin to format a layout""" - super(HTMLWriter, self).begin_format(layout) - if self.snippet is None: - self.writeln(u'') - self.writeln(u'') - - def end_format(self, layout): - """finished to format a layout""" - if self.snippet is None: - self.writeln(u'') - self.writeln(u'') - - - def visit_section(self, layout): - """display a section as html, using div + h[section level]""" - self.section += 1 - self.writeln(u'' % self.handle_attrs(layout)) - self.format_children(layout) - self.writeln(u'') - self.section -= 1 - - def visit_title(self, layout): - """display a title using """ - self.write(u'' % (self.section, self.handle_attrs(layout))) - self.format_children(layout) - self.writeln(u'' % self.section) - - def visit_table(self, layout): - """display a table as html""" - self.writeln(u'' % self.handle_attrs(layout)) - table_content = self.get_table_content(layout) - for i in range(len(table_content)): - row = table_content[i] - if i == 0 and layout.rheaders: - self.writeln(u'') - elif i+1 == len(table_content) and layout.rrheaders: - self.writeln(u'') - else: - self.writeln(u'' % (i%2 and 'even' or 'odd')) - for j in range(len(row)): - cell = row[j] or u' ' - if (layout.rheaders and i == 0) or \ - (layout.cheaders and j == 0) or \ - (layout.rrheaders and i+1 == len(table_content)) or \ - (layout.rcheaders and j+1 == len(row)): - self.writeln(u'%s' % cell) - else: - self.writeln(u'%s' % cell) - self.writeln(u'') - self.writeln(u'') - - def visit_list(self, layout): - """display a list as html""" - self.writeln(u'' % self.handle_attrs(layout)) - for row in list(self.compute_content(layout)): - self.writeln(u'

  • %s
  • ' % row) - self.writeln(u'') - - def visit_paragraph(self, layout): - """display links (using

    )""" - self.write(u'

    ') - self.format_children(layout) - self.write(u'

    ') - - def visit_span(self, layout): - """display links (using

    )""" - self.write(u'' % self.handle_attrs(layout)) - self.format_children(layout) - self.write(u'') - - def visit_link(self, layout): - """display links (using )""" - self.write(u' %s' % (layout.url, - self.handle_attrs(layout), - layout.label)) - def visit_verbatimtext(self, layout): - """display verbatim text (using

    )"""
    -        self.write(u'
    ')
    -        self.write(layout.data.replace(u'&', u'&').replace(u'<', u'<'))
    -        self.write(u'
    ') - - def visit_text(self, layout): - """add some text""" - data = layout.data - if layout.escaped: - data = data.replace(u'&', u'&').replace(u'<', u'<') - self.write(data) diff --git a/pymode/libs/logilab/common/ureports/nodes.py b/pymode/libs/logilab/common/ureports/nodes.py deleted file mode 100644 index a9585b30..00000000 --- a/pymode/libs/logilab/common/ureports/nodes.py +++ /dev/null @@ -1,203 +0,0 @@ -# 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 . -"""Micro reports objects. - -A micro report is a tree of layout and content objects. -""" -__docformat__ = "restructuredtext en" - -from logilab.common.tree import VNode - -from six import string_types - -class BaseComponent(VNode): - """base report component - - attributes - * id : the component's optional id - * klass : the component's optional klass - """ - def __init__(self, id=None, klass=None): - VNode.__init__(self, id) - self.klass = klass - -class BaseLayout(BaseComponent): - """base container node - - attributes - * BaseComponent attributes - * children : components in this table (i.e. the table's cells) - """ - def __init__(self, children=(), **kwargs): - super(BaseLayout, self).__init__(**kwargs) - for child in children: - if isinstance(child, BaseComponent): - self.append(child) - else: - self.add_text(child) - - def append(self, child): - """overridden to detect problems easily""" - assert child not in self.parents() - VNode.append(self, child) - - def parents(self): - """return the ancestor nodes""" - assert self.parent is not self - if self.parent is None: - return [] - return [self.parent] + self.parent.parents() - - def add_text(self, text): - """shortcut to add text data""" - self.children.append(Text(text)) - - -# non container nodes ######################################################### - -class Text(BaseComponent): - """a text portion - - attributes : - * BaseComponent attributes - * data : the text value as an encoded or unicode string - """ - def __init__(self, data, escaped=True, **kwargs): - super(Text, self).__init__(**kwargs) - #if isinstance(data, unicode): - # data = data.encode('ascii') - assert isinstance(data, string_types), data.__class__ - self.escaped = escaped - self.data = data - -class VerbatimText(Text): - """a verbatim text, display the raw data - - attributes : - * BaseComponent attributes - * data : the text value as an encoded or unicode string - """ - -class Link(BaseComponent): - """a labelled link - - attributes : - * BaseComponent attributes - * url : the link's target (REQUIRED) - * label : the link's label as a string (use the url by default) - """ - def __init__(self, url, label=None, **kwargs): - super(Link, self).__init__(**kwargs) - assert url - self.url = url - self.label = label or url - - -class Image(BaseComponent): - """an embedded or a single image - - attributes : - * BaseComponent attributes - * filename : the image's filename (REQUIRED) - * stream : the stream object containing the image data (REQUIRED) - * title : the image's optional title - """ - def __init__(self, filename, stream, title=None, **kwargs): - super(Image, self).__init__(**kwargs) - assert filename - assert stream - self.filename = filename - self.stream = stream - self.title = title - - -# container nodes ############################################################# - -class Section(BaseLayout): - """a section - - attributes : - * BaseLayout attributes - - a title may also be given to the constructor, it'll be added - as a first element - a description may also be given to the constructor, it'll be added - as a first paragraph - """ - def __init__(self, title=None, description=None, **kwargs): - super(Section, self).__init__(**kwargs) - if description: - self.insert(0, Paragraph([Text(description)])) - if title: - self.insert(0, Title(children=(title,))) - -class Title(BaseLayout): - """a title - - attributes : - * BaseLayout attributes - - A title must not contains a section nor a paragraph! - """ - -class Span(BaseLayout): - """a title - - attributes : - * BaseLayout attributes - - A span should only contains Text and Link nodes (in-line elements) - """ - -class Paragraph(BaseLayout): - """a simple text paragraph - - attributes : - * BaseLayout attributes - - A paragraph must not contains a section ! - """ - -class Table(BaseLayout): - """some tabular data - - attributes : - * BaseLayout attributes - * cols : the number of columns of the table (REQUIRED) - * rheaders : the first row's elements are table's header - * cheaders : the first col's elements are table's header - * title : the table's optional title - """ - def __init__(self, cols, title=None, - rheaders=0, cheaders=0, rrheaders=0, rcheaders=0, - **kwargs): - super(Table, self).__init__(**kwargs) - assert isinstance(cols, int) - self.cols = cols - self.title = title - self.rheaders = rheaders - self.cheaders = cheaders - self.rrheaders = rrheaders - self.rcheaders = rcheaders - -class List(BaseLayout): - """some list data - - attributes : - * BaseLayout attributes - """ diff --git a/pymode/libs/logilab/common/ureports/text_writer.py b/pymode/libs/logilab/common/ureports/text_writer.py deleted file mode 100644 index c87613c9..00000000 --- a/pymode/libs/logilab/common/ureports/text_writer.py +++ /dev/null @@ -1,145 +0,0 @@ -# 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 . -"""Text formatting drivers for ureports""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from six.moves import range - -from logilab.common.textutils import linesep -from logilab.common.ureports import BaseWriter - - -TITLE_UNDERLINES = [u'', u'=', u'-', u'`', u'.', u'~', u'^'] -BULLETS = [u'*', u'-'] - -class TextWriter(BaseWriter): - """format layouts as text - (ReStructured inspiration but not totally handled yet) - """ - def begin_format(self, layout): - super(TextWriter, self).begin_format(layout) - self.list_level = 0 - self.pending_urls = [] - - def visit_section(self, layout): - """display a section as text - """ - self.section += 1 - self.writeln() - self.format_children(layout) - if self.pending_urls: - self.writeln() - for label, url in self.pending_urls: - self.writeln(u'.. _`%s`: %s' % (label, url)) - self.pending_urls = [] - self.section -= 1 - self.writeln() - - def visit_title(self, layout): - title = u''.join(list(self.compute_content(layout))) - self.writeln(title) - try: - self.writeln(TITLE_UNDERLINES[self.section] * len(title)) - except IndexError: - print("FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT") - - def visit_paragraph(self, layout): - """enter a paragraph""" - self.format_children(layout) - self.writeln() - - def visit_span(self, layout): - """enter a span""" - self.format_children(layout) - - def visit_table(self, layout): - """display a table as text""" - table_content = self.get_table_content(layout) - # get columns width - cols_width = [0]*len(table_content[0]) - for row in table_content: - for index in range(len(row)): - col = row[index] - cols_width[index] = max(cols_width[index], len(col)) - if layout.klass == 'field': - self.field_table(layout, table_content, cols_width) - else: - self.default_table(layout, table_content, cols_width) - self.writeln() - - def default_table(self, layout, table_content, cols_width): - """format a table""" - cols_width = [size+1 for size in cols_width] - format_strings = u' '.join([u'%%-%ss'] * len(cols_width)) - format_strings = format_strings % tuple(cols_width) - format_strings = format_strings.split(' ') - table_linesep = u'\n+' + u'+'.join([u'-'*w for w in cols_width]) + u'+\n' - headsep = u'\n+' + u'+'.join([u'='*w for w in cols_width]) + u'+\n' - # FIXME: layout.cheaders - self.write(table_linesep) - for i in range(len(table_content)): - self.write(u'|') - line = table_content[i] - for j in range(len(line)): - self.write(format_strings[j] % line[j]) - self.write(u'|') - if i == 0 and layout.rheaders: - self.write(headsep) - else: - self.write(table_linesep) - - def field_table(self, layout, table_content, cols_width): - """special case for field table""" - assert layout.cols == 2 - format_string = u'%s%%-%ss: %%s' % (linesep, cols_width[0]) - for field, value in table_content: - self.write(format_string % (field, value)) - - - def visit_list(self, layout): - """display a list layout as text""" - bullet = BULLETS[self.list_level % len(BULLETS)] - indent = ' ' * self.list_level - self.list_level += 1 - for child in layout.children: - self.write(u'%s%s%s ' % (linesep, indent, bullet)) - child.accept(self) - self.list_level -= 1 - - def visit_link(self, layout): - """add a hyperlink""" - if layout.label != layout.url: - self.write(u'`%s`_' % layout.label) - self.pending_urls.append( (layout.label, layout.url) ) - else: - self.write(layout.url) - - def visit_verbatimtext(self, layout): - """display a verbatim layout as text (so difficult ;) - """ - self.writeln(u'::\n') - for line in layout.data.splitlines(): - self.writeln(u' ' + line) - self.writeln() - - def visit_text(self, layout): - """add some text""" - self.write(u'%s' % layout.data) diff --git a/pymode/libs/logilab/common/urllib2ext.py b/pymode/libs/logilab/common/urllib2ext.py deleted file mode 100644 index 339aec06..00000000 --- a/pymode/libs/logilab/common/urllib2ext.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import print_function - -import logging -import urllib2 - -import kerberos as krb - -class GssapiAuthError(Exception): - """raised on error during authentication process""" - -import re -RGX = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I) - -def get_negociate_value(headers): - for authreq in headers.getheaders('www-authenticate'): - match = RGX.search(authreq) - if match: - return match.group(1) - -class HTTPGssapiAuthHandler(urllib2.BaseHandler): - """Negotiate HTTP authentication using context from GSSAPI""" - - handler_order = 400 # before Digest Auth - - def __init__(self): - self._reset() - - def _reset(self): - self._retried = 0 - self._context = None - - def clean_context(self): - if self._context is not None: - krb.authGSSClientClean(self._context) - - def http_error_401(self, req, fp, code, msg, headers): - try: - if self._retried > 5: - raise urllib2.HTTPError(req.get_full_url(), 401, - "negotiate auth failed", headers, None) - self._retried += 1 - logging.debug('gssapi handler, try %s' % self._retried) - negotiate = get_negociate_value(headers) - if negotiate is None: - logging.debug('no negociate found in a www-authenticate header') - return None - logging.debug('HTTPGssapiAuthHandler: negotiate 1 is %r' % negotiate) - result, self._context = krb.authGSSClientInit("HTTP@%s" % req.get_host()) - if result < 1: - raise GssapiAuthError("HTTPGssapiAuthHandler: init failed with %d" % result) - result = krb.authGSSClientStep(self._context, negotiate) - if result < 0: - raise GssapiAuthError("HTTPGssapiAuthHandler: step 1 failed with %d" % result) - client_response = krb.authGSSClientResponse(self._context) - logging.debug('HTTPGssapiAuthHandler: client response is %s...' % client_response[:10]) - req.add_unredirected_header('Authorization', "Negotiate %s" % client_response) - server_response = self.parent.open(req) - negotiate = get_negociate_value(server_response.info()) - if negotiate is None: - logging.warning('HTTPGssapiAuthHandler: failed to authenticate server') - else: - logging.debug('HTTPGssapiAuthHandler negotiate 2: %s' % negotiate) - result = krb.authGSSClientStep(self._context, negotiate) - if result < 1: - raise GssapiAuthError("HTTPGssapiAuthHandler: step 2 failed with %d" % result) - return server_response - except GssapiAuthError as exc: - logging.error(repr(exc)) - finally: - self.clean_context() - self._reset() - -if __name__ == '__main__': - import sys - # debug - import httplib - httplib.HTTPConnection.debuglevel = 1 - httplib.HTTPSConnection.debuglevel = 1 - # debug - import logging - logging.basicConfig(level=logging.DEBUG) - # handle cookies - import cookielib - cj = cookielib.CookieJar() - ch = urllib2.HTTPCookieProcessor(cj) - # test with url sys.argv[1] - h = HTTPGssapiAuthHandler() - response = urllib2.build_opener(h, ch).open(sys.argv[1]) - print('\nresponse: %s\n--------------\n' % response.code, response.info()) diff --git a/pymode/libs/logilab/common/vcgutils.py b/pymode/libs/logilab/common/vcgutils.py deleted file mode 100644 index 9cd2acda..00000000 --- a/pymode/libs/logilab/common/vcgutils.py +++ /dev/null @@ -1,216 +0,0 @@ -# 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 . -"""Functions to generate files readable with Georg Sander's vcg -(Visualization of Compiler Graphs). - -You can download vcg at http://rw4.cs.uni-sb.de/~sander/html/gshome.html -Note that vcg exists as a debian package. - -See vcg's documentation for explanation about the different values that -maybe used for the functions parameters. - - - - -""" -__docformat__ = "restructuredtext en" - -import string - -ATTRS_VAL = { - 'algos': ('dfs', 'tree', 'minbackward', - 'left_to_right', 'right_to_left', - 'top_to_bottom', 'bottom_to_top', - 'maxdepth', 'maxdepthslow', 'mindepth', 'mindepthslow', - 'mindegree', 'minindegree', 'minoutdegree', - 'maxdegree', 'maxindegree', 'maxoutdegree'), - 'booleans': ('yes', 'no'), - 'colors': ('black', 'white', 'blue', 'red', 'green', 'yellow', - 'magenta', 'lightgrey', - 'cyan', 'darkgrey', 'darkblue', 'darkred', 'darkgreen', - 'darkyellow', 'darkmagenta', 'darkcyan', 'gold', - 'lightblue', 'lightred', 'lightgreen', 'lightyellow', - 'lightmagenta', 'lightcyan', 'lilac', 'turquoise', - 'aquamarine', 'khaki', 'purple', 'yellowgreen', 'pink', - 'orange', 'orchid'), - 'shapes': ('box', 'ellipse', 'rhomb', 'triangle'), - 'textmodes': ('center', 'left_justify', 'right_justify'), - 'arrowstyles': ('solid', 'line', 'none'), - 'linestyles': ('continuous', 'dashed', 'dotted', 'invisible'), - } - -# meaning of possible values: -# O -> string -# 1 -> int -# list -> value in list -GRAPH_ATTRS = { - 'title': 0, - 'label': 0, - 'color': ATTRS_VAL['colors'], - 'textcolor': ATTRS_VAL['colors'], - 'bordercolor': ATTRS_VAL['colors'], - 'width': 1, - 'height': 1, - 'borderwidth': 1, - 'textmode': ATTRS_VAL['textmodes'], - 'shape': ATTRS_VAL['shapes'], - 'shrink': 1, - 'stretch': 1, - 'orientation': ATTRS_VAL['algos'], - 'vertical_order': 1, - 'horizontal_order': 1, - 'xspace': 1, - 'yspace': 1, - 'layoutalgorithm': ATTRS_VAL['algos'], - 'late_edge_labels': ATTRS_VAL['booleans'], - 'display_edge_labels': ATTRS_VAL['booleans'], - 'dirty_edge_labels': ATTRS_VAL['booleans'], - 'finetuning': ATTRS_VAL['booleans'], - 'manhattan_edges': ATTRS_VAL['booleans'], - 'smanhattan_edges': ATTRS_VAL['booleans'], - 'port_sharing': ATTRS_VAL['booleans'], - 'edges': ATTRS_VAL['booleans'], - 'nodes': ATTRS_VAL['booleans'], - 'splines': ATTRS_VAL['booleans'], - } -NODE_ATTRS = { - 'title': 0, - 'label': 0, - 'color': ATTRS_VAL['colors'], - 'textcolor': ATTRS_VAL['colors'], - 'bordercolor': ATTRS_VAL['colors'], - 'width': 1, - 'height': 1, - 'borderwidth': 1, - 'textmode': ATTRS_VAL['textmodes'], - 'shape': ATTRS_VAL['shapes'], - 'shrink': 1, - 'stretch': 1, - 'vertical_order': 1, - 'horizontal_order': 1, - } -EDGE_ATTRS = { - 'sourcename': 0, - 'targetname': 0, - 'label': 0, - 'linestyle': ATTRS_VAL['linestyles'], - 'class': 1, - 'thickness': 0, - 'color': ATTRS_VAL['colors'], - 'textcolor': ATTRS_VAL['colors'], - 'arrowcolor': ATTRS_VAL['colors'], - 'backarrowcolor': ATTRS_VAL['colors'], - 'arrowsize': 1, - 'backarrowsize': 1, - 'arrowstyle': ATTRS_VAL['arrowstyles'], - 'backarrowstyle': ATTRS_VAL['arrowstyles'], - 'textmode': ATTRS_VAL['textmodes'], - 'priority': 1, - 'anchor': 1, - 'horizontal_order': 1, - } - - -# Misc utilities ############################################################### - -def latin_to_vcg(st): - """Convert latin characters using vcg escape sequence. - """ - for char in st: - if char not in string.ascii_letters: - try: - num = ord(char) - if num >= 192: - st = st.replace(char, r'\fi%d'%ord(char)) - except: - pass - return st - - -class VCGPrinter: - """A vcg graph writer. - """ - - def __init__(self, output_stream): - self._stream = output_stream - self._indent = '' - - def open_graph(self, **args): - """open a vcg graph - """ - self._stream.write('%sgraph:{\n'%self._indent) - self._inc_indent() - self._write_attributes(GRAPH_ATTRS, **args) - - def close_graph(self): - """close a vcg graph - """ - self._dec_indent() - self._stream.write('%s}\n'%self._indent) - - - def node(self, title, **args): - """draw a node - """ - self._stream.write('%snode: {title:"%s"' % (self._indent, title)) - self._write_attributes(NODE_ATTRS, **args) - self._stream.write('}\n') - - - def edge(self, from_node, to_node, edge_type='', **args): - """draw an edge from a node to another. - """ - self._stream.write( - '%s%sedge: {sourcename:"%s" targetname:"%s"' % ( - self._indent, edge_type, from_node, to_node)) - self._write_attributes(EDGE_ATTRS, **args) - self._stream.write('}\n') - - - # private ################################################################## - - def _write_attributes(self, attributes_dict, **args): - """write graph, node or edge attributes - """ - for key, value in args.items(): - try: - _type = attributes_dict[key] - except KeyError: - raise Exception('''no such attribute %s -possible attributes are %s''' % (key, attributes_dict.keys())) - - if not _type: - self._stream.write('%s%s:"%s"\n' % (self._indent, key, value)) - elif _type == 1: - self._stream.write('%s%s:%s\n' % (self._indent, key, - int(value))) - elif value in _type: - self._stream.write('%s%s:%s\n' % (self._indent, key, value)) - else: - raise Exception('''value %s isn\'t correct for attribute %s -correct values are %s''' % (value, key, _type)) - - def _inc_indent(self): - """increment indentation - """ - self._indent = ' %s' % self._indent - - def _dec_indent(self): - """decrement indentation - """ - self._indent = self._indent[:-2] diff --git a/pymode/libs/logilab/common/visitor.py b/pymode/libs/logilab/common/visitor.py deleted file mode 100644 index ed2b70f9..00000000 --- a/pymode/libs/logilab/common/visitor.py +++ /dev/null @@ -1,109 +0,0 @@ -# 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 . -"""A generic visitor abstract implementation. - - - - -""" -__docformat__ = "restructuredtext en" - -def no_filter(_): - return 1 - -# Iterators ################################################################### -class FilteredIterator(object): - - def __init__(self, node, list_func, filter_func=None): - self._next = [(node, 0)] - if filter_func is None: - filter_func = no_filter - self._list = list_func(node, filter_func) - - def __next__(self): - try: - return self._list.pop(0) - except : - return None - - next = __next__ - -# Base Visitor ################################################################ -class Visitor(object): - - def __init__(self, iterator_class, filter_func=None): - self._iter_class = iterator_class - self.filter = filter_func - - def visit(self, node, *args, **kargs): - """ - launch the visit on a given node - - call 'open_visit' before the beginning of the visit, with extra args - given - when all nodes have been visited, call the 'close_visit' method - """ - self.open_visit(node, *args, **kargs) - return self.close_visit(self._visit(node)) - - def _visit(self, node): - iterator = self._get_iterator(node) - n = next(iterator) - while n: - result = n.accept(self) - n = next(iterator) - return result - - def _get_iterator(self, node): - return self._iter_class(node, self.filter) - - def open_visit(self, *args, **kargs): - """ - method called at the beginning of the visit - """ - pass - - def close_visit(self, result): - """ - method called at the end of the visit - """ - return result - -# standard visited mixin ###################################################### -class VisitedMixIn(object): - """ - Visited interface allow node visitors to use the node - """ - def get_visit_name(self): - """ - return the visit name for the mixed class. When calling 'accept', the - method <'visit_' + name returned by this method> will be called on the - visitor - """ - try: - return self.TYPE.replace('-', '_') - except: - return self.__class__.__name__.lower() - - def accept(self, visitor, *args, **kwargs): - func = getattr(visitor, 'visit_%s' % self.get_visit_name()) - return func(self, *args, **kwargs) - - def leave(self, visitor, *args, **kwargs): - func = getattr(visitor, 'leave_%s' % self.get_visit_name()) - return func(self, *args, **kwargs) diff --git a/pymode/libs/logilab/common/xmlutils.py b/pymode/libs/logilab/common/xmlutils.py deleted file mode 100644 index d383b9d5..00000000 --- a/pymode/libs/logilab/common/xmlutils.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- 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 . -"""XML utilities. - -This module contains useful functions for parsing and using XML data. For the -moment, there is only one function that can parse the data inside a processing -instruction and return a Python dictionary. - - - - -""" -__docformat__ = "restructuredtext en" - -import re - -RE_DOUBLE_QUOTE = re.compile('([\w\-\.]+)="([^"]+)"') -RE_SIMPLE_QUOTE = re.compile("([\w\-\.]+)='([^']+)'") - -def parse_pi_data(pi_data): - """ - Utility function that parses the data contained in an XML - processing instruction and returns a dictionary of keywords and their - associated values (most of the time, the processing instructions contain - data like ``keyword="value"``, if a keyword is not associated to a value, - for example ``keyword``, it will be associated to ``None``). - - :param pi_data: data contained in an XML processing instruction. - :type pi_data: unicode - - :returns: Dictionary of the keywords (Unicode strings) associated to - their values (Unicode strings) as they were defined in the - data. - :rtype: dict - """ - results = {} - for elt in pi_data.split(): - if RE_DOUBLE_QUOTE.match(elt): - kwd, val = RE_DOUBLE_QUOTE.match(elt).groups() - elif RE_SIMPLE_QUOTE.match(elt): - kwd, val = RE_SIMPLE_QUOTE.match(elt).groups() - else: - kwd, val = elt, None - results[kwd] = val - return results 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/DESCRIPTION.rst b/pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst deleted file mode 100644 index 6b483af3..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,153 +0,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/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/pkg_resources/__init__.py b/pymode/libs/pkg_resources/__init__.py deleted file mode 100644 index 42ddcf7c..00000000 --- a/pymode/libs/pkg_resources/__init__.py +++ /dev/null @@ -1,3113 +0,0 @@ -""" -Package resource API --------------------- - -A resource is a logical file contained within a package, or a logical -subdirectory thereof. The package resource API expects resource names -to have their path parts separated with ``/``, *not* whatever the local -path separator is. Do not use os.path operations to manipulate resource -names being passed into the API. - -The package resource API is designed to work with normal filesystem packages, -.egg files, and unpacked .egg files. It can also work in a limited way with -.zip files and with custom PEP 302 loaders that support the ``get_data()`` -method. -""" - -from __future__ import absolute_import - -import sys -import os -import io -import time -import re -import types -import zipfile -import zipimport -import warnings -import stat -import functools -import pkgutil -import token -import symbol -import operator -import platform -import collections -import plistlib -import email.parser -import tempfile -import textwrap -from pkgutil import get_importer - -try: - import _imp -except ImportError: - # Python 3.2 compatibility - import imp as _imp - -PY3 = sys.version_info > (3,) -PY2 = not PY3 - -if PY3: - from urllib.parse import urlparse, urlunparse - -if PY2: - from urlparse import urlparse, urlunparse - -if PY3: - string_types = str, -else: - string_types = str, eval('unicode') - -iteritems = (lambda i: i.items()) if PY3 else lambda i: i.iteritems() - -# capture these to bypass sandboxing -from os import utime -try: - from os import mkdir, rename, unlink - WRITE_SUPPORT = True -except ImportError: - # no write support, probably under GAE - WRITE_SUPPORT = False - -from os import open as os_open -from os.path import isdir, split - -# Avoid try/except due to potential problems with delayed import mechanisms. -if sys.version_info >= (3, 3) and sys.implementation.name == "cpython": - import importlib.machinery as importlib_machinery -else: - importlib_machinery = None - -try: - import parser -except ImportError: - pass - -try: - import pkg_resources._vendor.packaging.version - import pkg_resources._vendor.packaging.specifiers - packaging = pkg_resources._vendor.packaging -except ImportError: - # fallback to naturally-installed version; allows system packagers to - # omit vendored packages. - import packaging.version - import packaging.specifiers - - -# declare some globals that will be defined later to -# satisfy the linters. -require = None -working_set = None - - -class PEP440Warning(RuntimeWarning): - """ - Used when there is an issue with a version or specifier not complying with - PEP 440. - """ - - -class _SetuptoolsVersionMixin(object): - - def __hash__(self): - return super(_SetuptoolsVersionMixin, self).__hash__() - - def __lt__(self, other): - if isinstance(other, tuple): - return tuple(self) < other - else: - return super(_SetuptoolsVersionMixin, self).__lt__(other) - - def __le__(self, other): - if isinstance(other, tuple): - return tuple(self) <= other - else: - return super(_SetuptoolsVersionMixin, self).__le__(other) - - def __eq__(self, other): - if isinstance(other, tuple): - return tuple(self) == other - else: - return super(_SetuptoolsVersionMixin, self).__eq__(other) - - def __ge__(self, other): - if isinstance(other, tuple): - return tuple(self) >= other - else: - return super(_SetuptoolsVersionMixin, self).__ge__(other) - - def __gt__(self, other): - if isinstance(other, tuple): - return tuple(self) > other - else: - return super(_SetuptoolsVersionMixin, self).__gt__(other) - - def __ne__(self, other): - if isinstance(other, tuple): - return tuple(self) != other - else: - return super(_SetuptoolsVersionMixin, self).__ne__(other) - - def __getitem__(self, key): - return tuple(self)[key] - - def __iter__(self): - component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) - replace = { - 'pre': 'c', - 'preview': 'c', - '-': 'final-', - 'rc': 'c', - 'dev': '@', - }.get - - def _parse_version_parts(s): - for part in component_re.split(s): - part = replace(part, part) - if not part or part == '.': - continue - if part[:1] in '0123456789': - # pad for numeric comparison - yield part.zfill(8) - else: - yield '*'+part - - # ensure that alpha/beta/candidate are before final - yield '*final' - - def old_parse_version(s): - parts = [] - for part in _parse_version_parts(s.lower()): - if part.startswith('*'): - # remove '-' before a prerelease tag - if part < '*final': - while parts and parts[-1] == '*final-': - parts.pop() - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == '00000000': - parts.pop() - parts.append(part) - return tuple(parts) - - # Warn for use of this function - warnings.warn( - "You have iterated over the result of " - "pkg_resources.parse_version. This is a legacy behavior which is " - "inconsistent with the new version class introduced in setuptools " - "8.0. In most cases, conversion to a tuple is unnecessary. For " - "comparison of versions, sort the Version instances directly. If " - "you have another use case requiring the tuple, please file a " - "bug with the setuptools project describing that need.", - RuntimeWarning, - stacklevel=1, - ) - - for part in old_parse_version(str(self)): - yield part - - -class SetuptoolsVersion(_SetuptoolsVersionMixin, packaging.version.Version): - pass - - -class SetuptoolsLegacyVersion(_SetuptoolsVersionMixin, - packaging.version.LegacyVersion): - pass - - -def parse_version(v): - try: - return SetuptoolsVersion(v) - except packaging.version.InvalidVersion: - return SetuptoolsLegacyVersion(v) - - -_state_vars = {} - -def _declare_state(vartype, **kw): - globals().update(kw) - _state_vars.update(dict.fromkeys(kw, vartype)) - -def __getstate__(): - state = {} - g = globals() - for k, v in _state_vars.items(): - state[k] = g['_sget_'+v](g[k]) - return state - -def __setstate__(state): - g = globals() - for k, v in state.items(): - g['_sset_'+_state_vars[k]](k, g[k], v) - return state - -def _sget_dict(val): - return val.copy() - -def _sset_dict(key, ob, state): - ob.clear() - ob.update(state) - -def _sget_object(val): - return val.__getstate__() - -def _sset_object(key, ob, state): - ob.__setstate__(state) - -_sget_none = _sset_none = lambda *args: None - - -def get_supported_platform(): - """Return this platform's maximum compatible version. - - distutils.util.get_platform() normally reports the minimum version - of Mac OS X that would be required to *use* extensions produced by - distutils. But what we want when checking compatibility is to know the - version of Mac OS X that we are *running*. To allow usage of packages that - explicitly require a newer version of Mac OS X, we must also know the - current version of the OS. - - If this condition occurs for any other platform with a version in its - platform strings, this function should be extended accordingly. - """ - plat = get_build_platform() - m = macosVersionString.match(plat) - if m is not None and sys.platform == "darwin": - try: - plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) - except ValueError: - # not Mac OS X - pass - return plat - -__all__ = [ - # Basic resource access and distribution/entry point discovery - 'require', 'run_script', 'get_provider', 'get_distribution', - 'load_entry_point', 'get_entry_map', 'get_entry_info', - 'iter_entry_points', - 'resource_string', 'resource_stream', 'resource_filename', - 'resource_listdir', 'resource_exists', 'resource_isdir', - - # Environmental control - 'declare_namespace', 'working_set', 'add_activation_listener', - 'find_distributions', 'set_extraction_path', 'cleanup_resources', - 'get_default_cache', - - # Primary implementation classes - 'Environment', 'WorkingSet', 'ResourceManager', - 'Distribution', 'Requirement', 'EntryPoint', - - # Exceptions - 'ResolutionError', 'VersionConflict', 'DistributionNotFound', - 'UnknownExtra', 'ExtractionError', - - # Warnings - 'PEP440Warning', - - # Parsing functions and string utilities - 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', - 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', - 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', - - # filesystem utilities - 'ensure_directory', 'normalize_path', - - # Distribution "precedence" constants - 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', - - # "Provider" interfaces, implementations, and registration/lookup APIs - 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', - 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', - 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', - 'register_finder', 'register_namespace_handler', 'register_loader_type', - 'fixup_namespace_packages', 'get_importer', - - # Deprecated/backward compatibility only - 'run_main', 'AvailableDistributions', -] - -class ResolutionError(Exception): - """Abstract base for dependency resolution errors""" - def __repr__(self): - return self.__class__.__name__+repr(self.args) - - -class VersionConflict(ResolutionError): - """ - An already-installed version conflicts with the requested version. - - Should be initialized with the installed Distribution and the requested - Requirement. - """ - - _template = "{self.dist} is installed but {self.req} is required" - - @property - def dist(self): - return self.args[0] - - @property - def req(self): - return self.args[1] - - def report(self): - return self._template.format(**locals()) - - def with_context(self, required_by): - """ - If required_by is non-empty, return a version of self that is a - ContextualVersionConflict. - """ - if not required_by: - return self - args = self.args + (required_by,) - return ContextualVersionConflict(*args) - - -class ContextualVersionConflict(VersionConflict): - """ - A VersionConflict that accepts a third parameter, the set of the - requirements that required the installed Distribution. - """ - - _template = VersionConflict._template + ' by {self.required_by}' - - @property - def required_by(self): - return self.args[2] - - -class DistributionNotFound(ResolutionError): - """A requested distribution was not found""" - - _template = ("The '{self.req}' distribution was not found " - "and is required by {self.requirers_str}") - - @property - def req(self): - return self.args[0] - - @property - def requirers(self): - return self.args[1] - - @property - def requirers_str(self): - if not self.requirers: - return 'the application' - return ', '.join(self.requirers) - - def report(self): - return self._template.format(**locals()) - - def __str__(self): - return self.report() - - -class UnknownExtra(ResolutionError): - """Distribution doesn't have an "extra feature" of the given name""" -_provider_factories = {} - -PY_MAJOR = sys.version[:3] -EGG_DIST = 3 -BINARY_DIST = 2 -SOURCE_DIST = 1 -CHECKOUT_DIST = 0 -DEVELOP_DIST = -1 - -def register_loader_type(loader_type, provider_factory): - """Register `provider_factory` to make providers for `loader_type` - - `loader_type` is the type or class of a PEP 302 ``module.__loader__``, - and `provider_factory` is a function that, passed a *module* object, - returns an ``IResourceProvider`` for that module. - """ - _provider_factories[loader_type] = provider_factory - -def get_provider(moduleOrReq): - """Return an IResourceProvider for the named module or requirement""" - if isinstance(moduleOrReq, Requirement): - return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] - try: - module = sys.modules[moduleOrReq] - except KeyError: - __import__(moduleOrReq) - module = sys.modules[moduleOrReq] - loader = getattr(module, '__loader__', None) - return _find_adapter(_provider_factories, loader)(module) - -def _macosx_vers(_cache=[]): - if not _cache: - version = platform.mac_ver()[0] - # fallback for MacPorts - if version == '': - plist = '/System/Library/CoreServices/SystemVersion.plist' - if os.path.exists(plist): - if hasattr(plistlib, 'readPlist'): - plist_content = plistlib.readPlist(plist) - if 'ProductVersion' in plist_content: - version = plist_content['ProductVersion'] - - _cache.append(version.split('.')) - return _cache[0] - -def _macosx_arch(machine): - return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) - -def get_build_platform(): - """Return this platform's string for platform-specific distributions - - XXX Currently this is the same as ``distutils.util.get_platform()``, but it - needs some hacks for Linux and Mac OS X. - """ - try: - # Python 2.7 or >=3.2 - from sysconfig import get_platform - except ImportError: - from distutils.util import get_platform - - plat = get_platform() - if sys.platform == "darwin" and not plat.startswith('macosx-'): - try: - version = _macosx_vers() - machine = os.uname()[4].replace(" ", "_") - return "macosx-%d.%d-%s" % (int(version[0]), int(version[1]), - _macosx_arch(machine)) - except ValueError: - # if someone is running a non-Mac darwin system, this will fall - # through to the default implementation - pass - return plat - -macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") -darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") -# XXX backward compat -get_platform = get_build_platform - - -def compatible_platforms(provided, required): - """Can code for the `provided` platform run on the `required` platform? - - Returns true if either platform is ``None``, or the platforms are equal. - - XXX Needs compatibility checks for Linux and other unixy OSes. - """ - if provided is None or required is None or provided==required: - # easy case - return True - - # Mac OS X special cases - reqMac = macosVersionString.match(required) - if reqMac: - provMac = macosVersionString.match(provided) - - # is this a Mac package? - if not provMac: - # this is backwards compatibility for packages built before - # setuptools 0.6. All packages built after this point will - # use the new macosx designation. - provDarwin = darwinVersionString.match(provided) - if provDarwin: - dversion = int(provDarwin.group(1)) - macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) - if dversion == 7 and macosversion >= "10.3" or \ - dversion == 8 and macosversion >= "10.4": - return True - # egg isn't macosx or legacy darwin - return False - - # are they the same major version and machine type? - if provMac.group(1) != reqMac.group(1) or \ - provMac.group(3) != reqMac.group(3): - return False - - # is the required OS major update >= the provided one? - if int(provMac.group(2)) > int(reqMac.group(2)): - return False - - return True - - # XXX Linux and other platforms' special cases should go here - return False - - -def run_script(dist_spec, script_name): - """Locate distribution `dist_spec` and run its `script_name` script""" - ns = sys._getframe(1).f_globals - name = ns['__name__'] - ns.clear() - ns['__name__'] = name - require(dist_spec)[0].run_script(script_name, ns) - -# backward compatibility -run_main = run_script - -def get_distribution(dist): - """Return a current distribution object for a Requirement or string""" - if isinstance(dist, string_types): - dist = Requirement.parse(dist) - if isinstance(dist, Requirement): - dist = get_provider(dist) - if not isinstance(dist, Distribution): - raise TypeError("Expected string, Requirement, or Distribution", dist) - return dist - -def load_entry_point(dist, group, name): - """Return `name` entry point of `group` for `dist` or raise ImportError""" - return get_distribution(dist).load_entry_point(group, name) - -def get_entry_map(dist, group=None): - """Return the entry point map for `group`, or the full entry map""" - return get_distribution(dist).get_entry_map(group) - -def get_entry_info(dist, group, name): - """Return the EntryPoint object for `group`+`name`, or ``None``""" - return get_distribution(dist).get_entry_info(group, name) - - -class IMetadataProvider: - - def has_metadata(name): - """Does the package's distribution contain the named metadata?""" - - def get_metadata(name): - """The named metadata resource as a string""" - - def get_metadata_lines(name): - """Yield named metadata resource as list of non-blank non-comment lines - - Leading and trailing whitespace is stripped from each line, and lines - with ``#`` as the first non-blank character are omitted.""" - - def metadata_isdir(name): - """Is the named metadata a directory? (like ``os.path.isdir()``)""" - - def metadata_listdir(name): - """List of metadata names in the directory (like ``os.listdir()``)""" - - def run_script(script_name, namespace): - """Execute the named script in the supplied namespace dictionary""" - - -class IResourceProvider(IMetadataProvider): - """An object that provides access to package resources""" - - def get_resource_filename(manager, resource_name): - """Return a true filesystem path for `resource_name` - - `manager` must be an ``IResourceManager``""" - - def get_resource_stream(manager, resource_name): - """Return a readable file-like object for `resource_name` - - `manager` must be an ``IResourceManager``""" - - def get_resource_string(manager, resource_name): - """Return a string containing the contents of `resource_name` - - `manager` must be an ``IResourceManager``""" - - def has_resource(resource_name): - """Does the package contain the named resource?""" - - def resource_isdir(resource_name): - """Is the named resource a directory? (like ``os.path.isdir()``)""" - - def resource_listdir(resource_name): - """List of resource names in the directory (like ``os.listdir()``)""" - - -class WorkingSet(object): - """A collection of active distributions on sys.path (or a similar list)""" - - def __init__(self, entries=None): - """Create working set from list of path entries (default=sys.path)""" - self.entries = [] - self.entry_keys = {} - self.by_key = {} - self.callbacks = [] - - if entries is None: - entries = sys.path - - for entry in entries: - self.add_entry(entry) - - @classmethod - def _build_master(cls): - """ - Prepare the master working set. - """ - ws = cls() - try: - from __main__ import __requires__ - except ImportError: - # The main program does not list any requirements - return ws - - # ensure the requirements are met - try: - ws.require(__requires__) - except VersionConflict: - return cls._build_from_requirements(__requires__) - - return ws - - @classmethod - def _build_from_requirements(cls, req_spec): - """ - Build a working set from a requirement spec. Rewrites sys.path. - """ - # try it without defaults already on sys.path - # by starting with an empty path - ws = cls([]) - reqs = parse_requirements(req_spec) - dists = ws.resolve(reqs, Environment()) - for dist in dists: - ws.add(dist) - - # add any missing entries from sys.path - for entry in sys.path: - if entry not in ws.entries: - ws.add_entry(entry) - - # then copy back to sys.path - sys.path[:] = ws.entries - return ws - - def add_entry(self, entry): - """Add a path item to ``.entries``, finding any distributions on it - - ``find_distributions(entry, True)`` is used to find distributions - corresponding to the path entry, and they are added. `entry` is - always appended to ``.entries``, even if it is already present. - (This is because ``sys.path`` can contain the same value more than - once, and the ``.entries`` of the ``sys.path`` WorkingSet should always - equal ``sys.path``.) - """ - self.entry_keys.setdefault(entry, []) - self.entries.append(entry) - for dist in find_distributions(entry, True): - self.add(dist, entry, False) - - def __contains__(self, dist): - """True if `dist` is the active distribution for its project""" - return self.by_key.get(dist.key) == dist - - def find(self, req): - """Find a distribution matching requirement `req` - - If there is an active distribution for the requested project, this - returns it as long as it meets the version requirement specified by - `req`. But, if there is an active distribution for the project and it - does *not* meet the `req` requirement, ``VersionConflict`` is raised. - If there is no active distribution for the requested project, ``None`` - is returned. - """ - dist = self.by_key.get(req.key) - if dist is not None and dist not in req: - # XXX add more info - raise VersionConflict(dist, req) - return dist - - def iter_entry_points(self, group, name=None): - """Yield entry point objects from `group` matching `name` - - If `name` is None, yields all entry points in `group` from all - distributions in the working set, otherwise only ones matching - both `group` and `name` are yielded (in distribution order). - """ - for dist in self: - entries = dist.get_entry_map(group) - if name is None: - for ep in entries.values(): - yield ep - elif name in entries: - yield entries[name] - - def run_script(self, requires, script_name): - """Locate distribution for `requires` and run `script_name` script""" - ns = sys._getframe(1).f_globals - name = ns['__name__'] - ns.clear() - ns['__name__'] = name - self.require(requires)[0].run_script(script_name, ns) - - def __iter__(self): - """Yield distributions for non-duplicate projects in the working set - - The yield order is the order in which the items' path entries were - added to the working set. - """ - seen = {} - for item in self.entries: - if item not in self.entry_keys: - # workaround a cache issue - continue - - for key in self.entry_keys[item]: - if key not in seen: - seen[key]=1 - yield self.by_key[key] - - def add(self, dist, entry=None, insert=True, replace=False): - """Add `dist` to working set, associated with `entry` - - If `entry` is unspecified, it defaults to the ``.location`` of `dist`. - On exit from this routine, `entry` is added to the end of the working - set's ``.entries`` (if it wasn't already present). - - `dist` is only added to the working set if it's for a project that - doesn't already have a distribution in the set, unless `replace=True`. - If it's added, any callbacks registered with the ``subscribe()`` method - will be called. - """ - if insert: - dist.insert_on(self.entries, entry) - - if entry is None: - entry = dist.location - keys = self.entry_keys.setdefault(entry,[]) - keys2 = self.entry_keys.setdefault(dist.location,[]) - if not replace and dist.key in self.by_key: - # ignore hidden distros - return - - self.by_key[dist.key] = dist - if dist.key not in keys: - keys.append(dist.key) - if dist.key not in keys2: - keys2.append(dist.key) - self._added_new(dist) - - def resolve(self, requirements, env=None, installer=None, - replace_conflicting=False): - """List all distributions needed to (recursively) meet `requirements` - - `requirements` must be a sequence of ``Requirement`` objects. `env`, - if supplied, should be an ``Environment`` instance. If - not supplied, it defaults to all distributions available within any - entry or distribution in the working set. `installer`, if supplied, - will be invoked with each requirement that cannot be met by an - already-installed distribution; it should return a ``Distribution`` or - ``None``. - - Unless `replace_conflicting=True`, raises a VersionConflict exception if - any requirements are found on the path that have the correct name but - the wrong version. Otherwise, if an `installer` is supplied it will be - invoked to obtain the correct version of the requirement and activate - it. - """ - - # set up the stack - requirements = list(requirements)[::-1] - # set of processed requirements - processed = {} - # key -> dist - best = {} - to_activate = [] - - # Mapping of requirement to set of distributions that required it; - # useful for reporting info about conflicts. - required_by = collections.defaultdict(set) - - while requirements: - # process dependencies breadth-first - req = requirements.pop(0) - if req in processed: - # Ignore cyclic or redundant dependencies - continue - dist = best.get(req.key) - if dist is None: - # Find the best distribution and add it to the map - dist = self.by_key.get(req.key) - if dist is None or (dist not in req and replace_conflicting): - ws = self - if env is None: - if dist is None: - env = Environment(self.entries) - else: - # Use an empty environment and workingset to avoid - # any further conflicts with the conflicting - # distribution - env = Environment([]) - ws = WorkingSet([]) - dist = best[req.key] = env.best_match(req, ws, installer) - if dist is None: - requirers = required_by.get(req, None) - raise DistributionNotFound(req, requirers) - to_activate.append(dist) - if dist not in req: - # Oops, the "best" so far conflicts with a dependency - dependent_req = required_by[req] - raise VersionConflict(dist, req).with_context(dependent_req) - - # push the new requirements onto the stack - new_requirements = dist.requires(req.extras)[::-1] - requirements.extend(new_requirements) - - # Register the new requirements needed by req - for new_requirement in new_requirements: - required_by[new_requirement].add(req.project_name) - - processed[req] = True - - # return list of distros to activate - return to_activate - - def find_plugins(self, plugin_env, full_env=None, installer=None, - fallback=True): - """Find all activatable distributions in `plugin_env` - - Example usage:: - - distributions, errors = working_set.find_plugins( - Environment(plugin_dirlist) - ) - # add plugins+libs to sys.path - map(working_set.add, distributions) - # display errors - print('Could not load', errors) - - The `plugin_env` should be an ``Environment`` instance that contains - only distributions that are in the project's "plugin directory" or - directories. The `full_env`, if supplied, should be an ``Environment`` - contains all currently-available distributions. If `full_env` is not - supplied, one is created automatically from the ``WorkingSet`` this - method is called on, which will typically mean that every directory on - ``sys.path`` will be scanned for distributions. - - `installer` is a standard installer callback as used by the - ``resolve()`` method. The `fallback` flag indicates whether we should - attempt to resolve older versions of a plugin if the newest version - cannot be resolved. - - This method returns a 2-tuple: (`distributions`, `error_info`), where - `distributions` is a list of the distributions found in `plugin_env` - that were loadable, along with any other distributions that are needed - to resolve their dependencies. `error_info` is a dictionary mapping - unloadable plugin distributions to an exception instance describing the - error that occurred. Usually this will be a ``DistributionNotFound`` or - ``VersionConflict`` instance. - """ - - plugin_projects = list(plugin_env) - # scan project names in alphabetic order - plugin_projects.sort() - - error_info = {} - distributions = {} - - if full_env is None: - env = Environment(self.entries) - env += plugin_env - else: - env = full_env + plugin_env - - shadow_set = self.__class__([]) - # put all our entries in shadow_set - list(map(shadow_set.add, self)) - - for project_name in plugin_projects: - - for dist in plugin_env[project_name]: - - req = [dist.as_requirement()] - - try: - resolvees = shadow_set.resolve(req, env, installer) - - except ResolutionError as v: - # save error info - error_info[dist] = v - if fallback: - # try the next older version of project - continue - else: - # give up on this project, keep going - break - - else: - list(map(shadow_set.add, resolvees)) - distributions.update(dict.fromkeys(resolvees)) - - # success, no need to try any more versions of this project - break - - distributions = list(distributions) - distributions.sort() - - return distributions, error_info - - def require(self, *requirements): - """Ensure that distributions matching `requirements` are activated - - `requirements` must be a string or a (possibly-nested) sequence - thereof, specifying the distributions and versions required. The - return value is a sequence of the distributions that needed to be - activated to fulfill the requirements; all relevant distributions are - included, even if they were already activated in this working set. - """ - needed = self.resolve(parse_requirements(requirements)) - - for dist in needed: - self.add(dist) - - return needed - - def subscribe(self, callback): - """Invoke `callback` for all distributions (including existing ones)""" - if callback in self.callbacks: - return - self.callbacks.append(callback) - for dist in self: - callback(dist) - - def _added_new(self, dist): - for callback in self.callbacks: - callback(dist) - - def __getstate__(self): - return ( - self.entries[:], self.entry_keys.copy(), self.by_key.copy(), - self.callbacks[:] - ) - - def __setstate__(self, e_k_b_c): - entries, keys, by_key, callbacks = e_k_b_c - self.entries = entries[:] - self.entry_keys = keys.copy() - self.by_key = by_key.copy() - self.callbacks = callbacks[:] - - -class Environment(object): - """Searchable snapshot of distributions on a search path""" - - def __init__(self, search_path=None, platform=get_supported_platform(), - python=PY_MAJOR): - """Snapshot distributions available on a search path - - Any distributions found on `search_path` are added to the environment. - `search_path` should be a sequence of ``sys.path`` items. If not - supplied, ``sys.path`` is used. - - `platform` is an optional string specifying the name of the platform - that platform-specific distributions must be compatible with. If - unspecified, it defaults to the current platform. `python` is an - optional string naming the desired version of Python (e.g. ``'3.3'``); - it defaults to the current version. - - You may explicitly set `platform` (and/or `python`) to ``None`` if you - wish to map *all* distributions, not just those compatible with the - running platform or Python version. - """ - self._distmap = {} - self.platform = platform - self.python = python - self.scan(search_path) - - def can_add(self, dist): - """Is distribution `dist` acceptable for this environment? - - The distribution must match the platform and python version - requirements specified when this environment was created, or False - is returned. - """ - return (self.python is None or dist.py_version is None - or dist.py_version==self.python) \ - and compatible_platforms(dist.platform, self.platform) - - def remove(self, dist): - """Remove `dist` from the environment""" - self._distmap[dist.key].remove(dist) - - def scan(self, search_path=None): - """Scan `search_path` for distributions usable in this environment - - Any distributions found are added to the environment. - `search_path` should be a sequence of ``sys.path`` items. If not - supplied, ``sys.path`` is used. Only distributions conforming to - the platform/python version defined at initialization are added. - """ - if search_path is None: - search_path = sys.path - - for item in search_path: - for dist in find_distributions(item): - self.add(dist) - - def __getitem__(self, project_name): - """Return a newest-to-oldest list of distributions for `project_name` - - Uses case-insensitive `project_name` comparison, assuming all the - project's distributions use their project's name converted to all - lowercase as their key. - - """ - distribution_key = project_name.lower() - return self._distmap.get(distribution_key, []) - - def add(self, dist): - """Add `dist` if we ``can_add()`` it and it has not already been added - """ - if self.can_add(dist) and dist.has_version(): - dists = self._distmap.setdefault(dist.key, []) - if dist not in dists: - dists.append(dist) - dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) - - def best_match(self, req, working_set, installer=None): - """Find distribution best matching `req` and usable on `working_set` - - This calls the ``find(req)`` method of the `working_set` to see if a - suitable distribution is already active. (This may raise - ``VersionConflict`` if an unsuitable version of the project is already - active in the specified `working_set`.) If a suitable distribution - isn't active, this method returns the newest distribution in the - environment that meets the ``Requirement`` in `req`. If no suitable - distribution is found, and `installer` is supplied, then the result of - calling the environment's ``obtain(req, installer)`` method will be - returned. - """ - dist = working_set.find(req) - if dist is not None: - return dist - for dist in self[req.key]: - if dist in req: - return dist - # try to download/install - return self.obtain(req, installer) - - def obtain(self, requirement, installer=None): - """Obtain a distribution matching `requirement` (e.g. via download) - - Obtain a distro that matches requirement (e.g. via download). In the - base ``Environment`` class, this routine just returns - ``installer(requirement)``, unless `installer` is None, in which case - None is returned instead. This method is a hook that allows subclasses - to attempt other ways of obtaining a distribution before falling back - to the `installer` argument.""" - if installer is not None: - return installer(requirement) - - def __iter__(self): - """Yield the unique project names of the available distributions""" - for key in self._distmap.keys(): - if self[key]: - yield key - - def __iadd__(self, other): - """In-place addition of a distribution or environment""" - if isinstance(other, Distribution): - self.add(other) - elif isinstance(other, Environment): - for project in other: - for dist in other[project]: - self.add(dist) - else: - raise TypeError("Can't add %r to environment" % (other,)) - return self - - def __add__(self, other): - """Add an environment or distribution to an environment""" - new = self.__class__([], platform=None, python=None) - for env in self, other: - new += env - return new - - -# XXX backward compatibility -AvailableDistributions = Environment - - -class ExtractionError(RuntimeError): - """An error occurred extracting a resource - - The following attributes are available from instances of this exception: - - manager - The resource manager that raised this exception - - cache_path - The base directory for resource extraction - - original_error - The exception instance that caused extraction to fail - """ - - -class ResourceManager: - """Manage resource extraction and packages""" - extraction_path = None - - def __init__(self): - self.cached_files = {} - - def resource_exists(self, package_or_requirement, resource_name): - """Does the named resource exist?""" - return get_provider(package_or_requirement).has_resource(resource_name) - - def resource_isdir(self, package_or_requirement, resource_name): - """Is the named resource an existing directory?""" - return get_provider(package_or_requirement).resource_isdir( - resource_name - ) - - def resource_filename(self, package_or_requirement, resource_name): - """Return a true filesystem path for specified resource""" - return get_provider(package_or_requirement).get_resource_filename( - self, resource_name - ) - - def resource_stream(self, package_or_requirement, resource_name): - """Return a readable file-like object for specified resource""" - return get_provider(package_or_requirement).get_resource_stream( - self, resource_name - ) - - def resource_string(self, package_or_requirement, resource_name): - """Return specified resource as a string""" - return get_provider(package_or_requirement).get_resource_string( - self, resource_name - ) - - def resource_listdir(self, package_or_requirement, resource_name): - """List the contents of the named resource directory""" - return get_provider(package_or_requirement).resource_listdir( - resource_name - ) - - def extraction_error(self): - """Give an error message for problems extracting file(s)""" - - old_exc = sys.exc_info()[1] - cache_path = self.extraction_path or get_default_cache() - - err = ExtractionError("""Can't extract file(s) to egg cache - -The following error occurred while trying to extract file(s) to the Python egg -cache: - - %s - -The Python egg cache directory is currently set to: - - %s - -Perhaps your account does not have write access to this directory? You can -change the cache directory by setting the PYTHON_EGG_CACHE environment -variable to point to an accessible directory. -""" % (old_exc, cache_path) - ) - err.manager = self - err.cache_path = cache_path - err.original_error = old_exc - raise err - - def get_cache_path(self, archive_name, names=()): - """Return absolute location in cache for `archive_name` and `names` - - The parent directory of the resulting path will be created if it does - not already exist. `archive_name` should be the base filename of the - enclosing egg (which may not be the name of the enclosing zipfile!), - including its ".egg" extension. `names`, if provided, should be a - sequence of path name parts "under" the egg's extraction location. - - This method should only be called by resource providers that need to - obtain an extraction location, and only for names they intend to - extract, as it tracks the generated names for possible cleanup later. - """ - extract_path = self.extraction_path or get_default_cache() - target_path = os.path.join(extract_path, archive_name+'-tmp', *names) - try: - _bypass_ensure_directory(target_path) - except: - self.extraction_error() - - self._warn_unsafe_extraction_path(extract_path) - - self.cached_files[target_path] = 1 - return target_path - - @staticmethod - def _warn_unsafe_extraction_path(path): - """ - If the default extraction path is overridden and set to an insecure - location, such as /tmp, it opens up an opportunity for an attacker to - replace an extracted file with an unauthorized payload. Warn the user - if a known insecure location is used. - - See Distribute #375 for more details. - """ - if os.name == 'nt' and not path.startswith(os.environ['windir']): - # On Windows, permissions are generally restrictive by default - # and temp directories are not writable by other users, so - # bypass the warning. - return - mode = os.stat(path).st_mode - if mode & stat.S_IWOTH or mode & stat.S_IWGRP: - msg = ("%s is writable by group/others and vulnerable to attack " - "when " - "used with get_resource_filename. Consider a more secure " - "location (set with .set_extraction_path or the " - "PYTHON_EGG_CACHE environment variable)." % path) - warnings.warn(msg, UserWarning) - - def postprocess(self, tempname, filename): - """Perform any platform-specific postprocessing of `tempname` - - This is where Mac header rewrites should be done; other platforms don't - have anything special they should do. - - Resource providers should call this method ONLY after successfully - extracting a compressed resource. They must NOT call it on resources - that are already in the filesystem. - - `tempname` is the current (temporary) name of the file, and `filename` - is the name it will be renamed to by the caller after this routine - returns. - """ - - if os.name == 'posix': - # Make the resource executable - mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 - os.chmod(tempname, mode) - - def set_extraction_path(self, path): - """Set the base path where resources will be extracted to, if needed. - - If you do not call this routine before any extractions take place, the - path defaults to the return value of ``get_default_cache()``. (Which - is based on the ``PYTHON_EGG_CACHE`` environment variable, with various - platform-specific fallbacks. See that routine's documentation for more - details.) - - Resources are extracted to subdirectories of this path based upon - information given by the ``IResourceProvider``. You may set this to a - temporary directory, but then you must call ``cleanup_resources()`` to - delete the extracted files when done. There is no guarantee that - ``cleanup_resources()`` will be able to remove all extracted files. - - (Note: you may not change the extraction path for a given resource - manager once resources have been extracted, unless you first call - ``cleanup_resources()``.) - """ - if self.cached_files: - raise ValueError( - "Can't change extraction path, files already extracted" - ) - - self.extraction_path = path - - def cleanup_resources(self, force=False): - """ - Delete all extracted resource files and directories, returning a list - of the file and directory names that could not be successfully removed. - This function does not have any concurrency protection, so it should - generally only be called when the extraction path is a temporary - directory exclusive to a single process. This method is not - automatically called; you must call it explicitly or register it as an - ``atexit`` function if you wish to ensure cleanup of a temporary - directory used for extractions. - """ - # XXX - -def get_default_cache(): - """Determine the default cache location - - This returns the ``PYTHON_EGG_CACHE`` environment variable, if set. - Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of the - "Application Data" directory. On all other systems, it's "~/.python-eggs". - """ - try: - return os.environ['PYTHON_EGG_CACHE'] - except KeyError: - pass - - if os.name!='nt': - return os.path.expanduser('~/.python-eggs') - - # XXX this may be locale-specific! - app_data = 'Application Data' - app_homes = [ - # best option, should be locale-safe - (('APPDATA',), None), - (('USERPROFILE',), app_data), - (('HOMEDRIVE','HOMEPATH'), app_data), - (('HOMEPATH',), app_data), - (('HOME',), None), - # 95/98/ME - (('WINDIR',), app_data), - ] - - for keys, subdir in app_homes: - dirname = '' - for key in keys: - if key in os.environ: - dirname = os.path.join(dirname, os.environ[key]) - else: - break - else: - if subdir: - dirname = os.path.join(dirname, subdir) - return os.path.join(dirname, 'Python-Eggs') - else: - raise RuntimeError( - "Please set the PYTHON_EGG_CACHE enviroment variable" - ) - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """ - Convert an arbitrary string to a standard version string - """ - try: - # normalize the version - return str(packaging.version.Version(version)) - except packaging.version.InvalidVersion: - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def safe_extra(extra): - """Convert an arbitrary string to a standard 'extra' name - - Any runs of non-alphanumeric characters are replaced with a single '_', - and the result is always lowercased. - """ - return re.sub('[^A-Za-z0-9.]+', '_', extra).lower() - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') - - -class MarkerEvaluation(object): - values = { - 'os_name': lambda: os.name, - 'sys_platform': lambda: sys.platform, - 'python_full_version': platform.python_version, - 'python_version': lambda: platform.python_version()[:3], - 'platform_version': platform.version, - 'platform_machine': platform.machine, - 'python_implementation': platform.python_implementation, - } - - @classmethod - def is_invalid_marker(cls, text): - """ - Validate text as a PEP 426 environment marker; return an exception - if invalid or False otherwise. - """ - try: - cls.evaluate_marker(text) - except SyntaxError as e: - return cls.normalize_exception(e) - return False - - @staticmethod - def normalize_exception(exc): - """ - Given a SyntaxError from a marker evaluation, normalize the error - message: - - Remove indications of filename and line number. - - Replace platform-specific error messages with standard error - messages. - """ - subs = { - 'unexpected EOF while parsing': 'invalid syntax', - 'parenthesis is never closed': 'invalid syntax', - } - exc.filename = None - exc.lineno = None - exc.msg = subs.get(exc.msg, exc.msg) - return exc - - @classmethod - def and_test(cls, nodelist): - # MUST NOT short-circuit evaluation, or invalid syntax can be skipped! - items = [ - cls.interpret(nodelist[i]) - for i in range(1, len(nodelist), 2) - ] - return functools.reduce(operator.and_, items) - - @classmethod - def test(cls, nodelist): - # MUST NOT short-circuit evaluation, or invalid syntax can be skipped! - items = [ - cls.interpret(nodelist[i]) - for i in range(1, len(nodelist), 2) - ] - return functools.reduce(operator.or_, items) - - @classmethod - def atom(cls, nodelist): - t = nodelist[1][0] - if t == token.LPAR: - if nodelist[2][0] == token.RPAR: - raise SyntaxError("Empty parentheses") - return cls.interpret(nodelist[2]) - msg = "Language feature not supported in environment markers" - raise SyntaxError(msg) - - @classmethod - def comparison(cls, nodelist): - if len(nodelist) > 4: - msg = "Chained comparison not allowed in environment markers" - raise SyntaxError(msg) - comp = nodelist[2][1] - cop = comp[1] - if comp[0] == token.NAME: - if len(nodelist[2]) == 3: - if cop == 'not': - cop = 'not in' - else: - cop = 'is not' - try: - cop = cls.get_op(cop) - except KeyError: - msg = repr(cop) + " operator not allowed in environment markers" - raise SyntaxError(msg) - return cop(cls.evaluate(nodelist[1]), cls.evaluate(nodelist[3])) - - @classmethod - def get_op(cls, op): - ops = { - symbol.test: cls.test, - symbol.and_test: cls.and_test, - symbol.atom: cls.atom, - symbol.comparison: cls.comparison, - 'not in': lambda x, y: x not in y, - 'in': lambda x, y: x in y, - '==': operator.eq, - '!=': operator.ne, - '<': operator.lt, - '>': operator.gt, - '<=': operator.le, - '>=': operator.ge, - } - if hasattr(symbol, 'or_test'): - ops[symbol.or_test] = cls.test - return ops[op] - - @classmethod - def evaluate_marker(cls, text, extra=None): - """ - Evaluate a PEP 426 environment marker on CPython 2.4+. - Return a boolean indicating the marker result in this environment. - Raise SyntaxError if marker is invalid. - - This implementation uses the 'parser' module, which is not implemented - on - Jython and has been superseded by the 'ast' module in Python 2.6 and - later. - """ - return cls.interpret(parser.expr(text).totuple(1)[1]) - - @classmethod - def _markerlib_evaluate(cls, text): - """ - Evaluate a PEP 426 environment marker using markerlib. - Return a boolean indicating the marker result in this environment. - Raise SyntaxError if marker is invalid. - """ - import _markerlib - # markerlib implements Metadata 1.2 (PEP 345) environment markers. - # Translate the variables to Metadata 2.0 (PEP 426). - env = _markerlib.default_environment() - for key in env.keys(): - new_key = key.replace('.', '_') - env[new_key] = env.pop(key) - try: - result = _markerlib.interpret(text, env) - except NameError as e: - raise SyntaxError(e.args[0]) - return result - - if 'parser' not in globals(): - # Fall back to less-complete _markerlib implementation if 'parser' module - # is not available. - evaluate_marker = _markerlib_evaluate - - @classmethod - def interpret(cls, nodelist): - while len(nodelist)==2: nodelist = nodelist[1] - try: - op = cls.get_op(nodelist[0]) - except KeyError: - raise SyntaxError("Comparison or logical expression expected") - return op(nodelist) - - @classmethod - def evaluate(cls, nodelist): - while len(nodelist)==2: nodelist = nodelist[1] - kind = nodelist[0] - name = nodelist[1] - if kind==token.NAME: - try: - op = cls.values[name] - except KeyError: - raise SyntaxError("Unknown name %r" % name) - return op() - if kind==token.STRING: - s = nodelist[1] - if not cls._safe_string(s): - raise SyntaxError( - "Only plain strings allowed in environment markers") - return s[1:-1] - msg = "Language feature not supported in environment markers" - raise SyntaxError(msg) - - @staticmethod - def _safe_string(cand): - return ( - cand[:1] in "'\"" and - not cand.startswith('"""') and - not cand.startswith("'''") and - '\\' not in cand - ) - -invalid_marker = MarkerEvaluation.is_invalid_marker -evaluate_marker = MarkerEvaluation.evaluate_marker - -class NullProvider: - """Try to implement resources and metadata for arbitrary PEP 302 loaders""" - - egg_name = None - egg_info = None - loader = None - - def __init__(self, module): - self.loader = getattr(module, '__loader__', None) - self.module_path = os.path.dirname(getattr(module, '__file__', '')) - - def get_resource_filename(self, manager, resource_name): - return self._fn(self.module_path, resource_name) - - def get_resource_stream(self, manager, resource_name): - return io.BytesIO(self.get_resource_string(manager, resource_name)) - - def get_resource_string(self, manager, resource_name): - return self._get(self._fn(self.module_path, resource_name)) - - def has_resource(self, resource_name): - return self._has(self._fn(self.module_path, resource_name)) - - def has_metadata(self, name): - return self.egg_info and self._has(self._fn(self.egg_info, name)) - - if sys.version_info <= (3,): - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info, name)) - else: - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info, name)).decode("utf-8") - - def get_metadata_lines(self, name): - return yield_lines(self.get_metadata(name)) - - def resource_isdir(self, resource_name): - return self._isdir(self._fn(self.module_path, resource_name)) - - def metadata_isdir(self, name): - return self.egg_info and self._isdir(self._fn(self.egg_info, name)) - - def resource_listdir(self, resource_name): - return self._listdir(self._fn(self.module_path, resource_name)) - - def metadata_listdir(self, name): - if self.egg_info: - return self._listdir(self._fn(self.egg_info, name)) - return [] - - def run_script(self, script_name, namespace): - script = 'scripts/'+script_name - if not self.has_metadata(script): - raise ResolutionError("No script named %r" % script_name) - script_text = self.get_metadata(script).replace('\r\n', '\n') - script_text = script_text.replace('\r', '\n') - script_filename = self._fn(self.egg_info, script) - namespace['__file__'] = script_filename - if os.path.exists(script_filename): - source = open(script_filename).read() - code = compile(source, script_filename, 'exec') - exec(code, namespace, namespace) - else: - from linecache import cache - cache[script_filename] = ( - len(script_text), 0, script_text.split('\n'), script_filename - ) - script_code = compile(script_text, script_filename,'exec') - exec(script_code, namespace, namespace) - - def _has(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _isdir(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _listdir(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _fn(self, base, resource_name): - if resource_name: - return os.path.join(base, *resource_name.split('/')) - return base - - def _get(self, path): - if hasattr(self.loader, 'get_data'): - return self.loader.get_data(path) - raise NotImplementedError( - "Can't perform this operation for loaders without 'get_data()'" - ) - -register_loader_type(object, NullProvider) - - -class EggProvider(NullProvider): - """Provider based on a virtual filesystem""" - - def __init__(self, module): - NullProvider.__init__(self, module) - self._setup_prefix() - - def _setup_prefix(self): - # we assume here that our metadata may be nested inside a "basket" - # of multiple eggs; that's why we use module_path instead of .archive - path = self.module_path - old = None - while path!=old: - if path.lower().endswith('.egg'): - self.egg_name = os.path.basename(path) - self.egg_info = os.path.join(path, 'EGG-INFO') - self.egg_root = path - break - old = path - path, base = os.path.split(path) - -class DefaultProvider(EggProvider): - """Provides access to package resources in the filesystem""" - - def _has(self, path): - return os.path.exists(path) - - def _isdir(self, path): - return os.path.isdir(path) - - def _listdir(self, path): - return os.listdir(path) - - def get_resource_stream(self, manager, resource_name): - return open(self._fn(self.module_path, resource_name), 'rb') - - def _get(self, path): - with open(path, 'rb') as stream: - return stream.read() - -register_loader_type(type(None), DefaultProvider) - -if importlib_machinery is not None: - register_loader_type(importlib_machinery.SourceFileLoader, DefaultProvider) - - -class EmptyProvider(NullProvider): - """Provider that returns nothing for all requests""" - - _isdir = _has = lambda self, path: False - _get = lambda self, path: '' - _listdir = lambda self, path: [] - module_path = None - - def __init__(self): - pass - -empty_provider = EmptyProvider() - - -class ZipManifests(dict): - """ - zip manifest builder - """ - - @classmethod - def build(cls, path): - """ - Build a dictionary similar to the zipimport directory - caches, except instead of tuples, store ZipInfo objects. - - Use a platform-specific path separator (os.sep) for the path keys - for compatibility with pypy on Windows. - """ - with ContextualZipFile(path) as zfile: - items = ( - ( - name.replace('/', os.sep), - zfile.getinfo(name), - ) - for name in zfile.namelist() - ) - return dict(items) - - load = build - - -class MemoizedZipManifests(ZipManifests): - """ - Memoized zipfile manifests. - """ - manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') - - def load(self, path): - """ - Load a manifest at path or return a suitable manifest already loaded. - """ - path = os.path.normpath(path) - mtime = os.stat(path).st_mtime - - if path not in self or self[path].mtime != mtime: - manifest = self.build(path) - self[path] = self.manifest_mod(manifest, mtime) - - return self[path].manifest - - -class ContextualZipFile(zipfile.ZipFile): - """ - Supplement ZipFile class to support context manager for Python 2.6 - """ - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.close() - - def __new__(cls, *args, **kwargs): - """ - Construct a ZipFile or ContextualZipFile as appropriate - """ - if hasattr(zipfile.ZipFile, '__exit__'): - return zipfile.ZipFile(*args, **kwargs) - return super(ContextualZipFile, cls).__new__(cls) - - -class ZipProvider(EggProvider): - """Resource support for zips and eggs""" - - eagers = None - _zip_manifests = MemoizedZipManifests() - - def __init__(self, module): - EggProvider.__init__(self, module) - self.zip_pre = self.loader.archive+os.sep - - def _zipinfo_name(self, fspath): - # Convert a virtual filename (full path to file) into a zipfile subpath - # usable with the zipimport directory cache for our target archive - if fspath.startswith(self.zip_pre): - return fspath[len(self.zip_pre):] - raise AssertionError( - "%s is not a subpath of %s" % (fspath, self.zip_pre) - ) - - def _parts(self, zip_path): - # Convert a zipfile subpath into an egg-relative path part list. - # pseudo-fs path - fspath = self.zip_pre+zip_path - if fspath.startswith(self.egg_root+os.sep): - return fspath[len(self.egg_root)+1:].split(os.sep) - raise AssertionError( - "%s is not a subpath of %s" % (fspath, self.egg_root) - ) - - @property - def zipinfo(self): - return self._zip_manifests.load(self.loader.archive) - - def get_resource_filename(self, manager, resource_name): - if not self.egg_name: - raise NotImplementedError( - "resource_filename() only supported for .egg, not .zip" - ) - # no need to lock for extraction, since we use temp names - zip_path = self._resource_to_zip(resource_name) - eagers = self._get_eager_resources() - if '/'.join(self._parts(zip_path)) in eagers: - for name in eagers: - self._extract_resource(manager, self._eager_to_zip(name)) - return self._extract_resource(manager, zip_path) - - @staticmethod - def _get_date_and_size(zip_stat): - size = zip_stat.file_size - # ymdhms+wday, yday, dst - date_time = zip_stat.date_time + (0, 0, -1) - # 1980 offset already done - timestamp = time.mktime(date_time) - return timestamp, size - - def _extract_resource(self, manager, zip_path): - - if zip_path in self._index(): - for name in self._index()[zip_path]: - last = self._extract_resource( - manager, os.path.join(zip_path, name) - ) - # return the extracted directory name - return os.path.dirname(last) - - timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) - - if not WRITE_SUPPORT: - raise IOError('"os.rename" and "os.unlink" are not supported ' - 'on this platform') - try: - - real_path = manager.get_cache_path( - self.egg_name, self._parts(zip_path) - ) - - if self._is_current(real_path, zip_path): - return real_path - - outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path)) - os.write(outf, self.loader.get_data(zip_path)) - os.close(outf) - utime(tmpnam, (timestamp, timestamp)) - manager.postprocess(tmpnam, real_path) - - try: - rename(tmpnam, real_path) - - except os.error: - if os.path.isfile(real_path): - if self._is_current(real_path, zip_path): - # the file became current since it was checked above, - # so proceed. - return real_path - # Windows, del old file and retry - elif os.name=='nt': - unlink(real_path) - rename(tmpnam, real_path) - return real_path - raise - - except os.error: - # report a user-friendly error - manager.extraction_error() - - return real_path - - def _is_current(self, file_path, zip_path): - """ - Return True if the file_path is current for this zip_path - """ - timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) - if not os.path.isfile(file_path): - return False - stat = os.stat(file_path) - if stat.st_size!=size or stat.st_mtime!=timestamp: - return False - # check that the contents match - zip_contents = self.loader.get_data(zip_path) - with open(file_path, 'rb') as f: - file_contents = f.read() - return zip_contents == file_contents - - def _get_eager_resources(self): - if self.eagers is None: - eagers = [] - for name in ('native_libs.txt', 'eager_resources.txt'): - if self.has_metadata(name): - eagers.extend(self.get_metadata_lines(name)) - self.eagers = eagers - return self.eagers - - def _index(self): - try: - return self._dirindex - except AttributeError: - ind = {} - for path in self.zipinfo: - parts = path.split(os.sep) - while parts: - parent = os.sep.join(parts[:-1]) - if parent in ind: - ind[parent].append(parts[-1]) - break - else: - ind[parent] = [parts.pop()] - self._dirindex = ind - return ind - - def _has(self, fspath): - zip_path = self._zipinfo_name(fspath) - return zip_path in self.zipinfo or zip_path in self._index() - - def _isdir(self, fspath): - return self._zipinfo_name(fspath) in self._index() - - def _listdir(self, fspath): - return list(self._index().get(self._zipinfo_name(fspath), ())) - - def _eager_to_zip(self, resource_name): - return self._zipinfo_name(self._fn(self.egg_root, resource_name)) - - def _resource_to_zip(self, resource_name): - return self._zipinfo_name(self._fn(self.module_path, resource_name)) - -register_loader_type(zipimport.zipimporter, ZipProvider) - - -class FileMetadata(EmptyProvider): - """Metadata handler for standalone PKG-INFO files - - Usage:: - - metadata = FileMetadata("/path/to/PKG-INFO") - - This provider rejects all data and metadata requests except for PKG-INFO, - which is treated as existing, and will be the contents of the file at - the provided location. - """ - - def __init__(self, path): - self.path = path - - def has_metadata(self, name): - return name=='PKG-INFO' - - def get_metadata(self, name): - if name=='PKG-INFO': - with open(self.path,'rU') as f: - metadata = f.read() - return metadata - raise KeyError("No metadata except PKG-INFO is available") - - def get_metadata_lines(self, name): - return yield_lines(self.get_metadata(name)) - - -class PathMetadata(DefaultProvider): - """Metadata provider for egg directories - - Usage:: - - # Development eggs: - - egg_info = "/path/to/PackageName.egg-info" - base_dir = os.path.dirname(egg_info) - metadata = PathMetadata(base_dir, egg_info) - dist_name = os.path.splitext(os.path.basename(egg_info))[0] - dist = Distribution(basedir, project_name=dist_name, metadata=metadata) - - # Unpacked egg directories: - - egg_path = "/path/to/PackageName-ver-pyver-etc.egg" - metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) - dist = Distribution.from_filename(egg_path, metadata=metadata) - """ - - def __init__(self, path, egg_info): - self.module_path = path - self.egg_info = egg_info - - -class EggMetadata(ZipProvider): - """Metadata provider for .egg files""" - - def __init__(self, importer): - """Create a metadata provider from a zipimporter""" - - self.zip_pre = importer.archive+os.sep - self.loader = importer - if importer.prefix: - self.module_path = os.path.join(importer.archive, importer.prefix) - else: - self.module_path = importer.archive - self._setup_prefix() - -_declare_state('dict', _distribution_finders = {}) - -def register_finder(importer_type, distribution_finder): - """Register `distribution_finder` to find distributions in sys.path items - - `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item - handler), and `distribution_finder` is a callable that, passed a path - item and the importer instance, yields ``Distribution`` instances found on - that path item. See ``pkg_resources.find_on_path`` for an example.""" - _distribution_finders[importer_type] = distribution_finder - - -def find_distributions(path_item, only=False): - """Yield distributions accessible via `path_item`""" - importer = get_importer(path_item) - finder = _find_adapter(_distribution_finders, importer) - return finder(importer, path_item, only) - -def find_eggs_in_zip(importer, path_item, only=False): - """ - Find eggs in zip files; possibly multiple nested eggs. - """ - if importer.archive.endswith('.whl'): - # wheels are not supported with this finder - # they don't have PKG-INFO metadata, and won't ever contain eggs - return - metadata = EggMetadata(importer) - if metadata.has_metadata('PKG-INFO'): - yield Distribution.from_filename(path_item, metadata=metadata) - if only: - # don't yield nested distros - return - for subitem in metadata.resource_listdir('/'): - if subitem.endswith('.egg'): - subpath = os.path.join(path_item, subitem) - for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath): - yield dist - -register_finder(zipimport.zipimporter, find_eggs_in_zip) - -def find_nothing(importer, path_item, only=False): - return () -register_finder(object, find_nothing) - -def find_on_path(importer, path_item, only=False): - """Yield distributions accessible on a sys.path directory""" - path_item = _normalize_cached(path_item) - - if os.path.isdir(path_item) and os.access(path_item, os.R_OK): - if path_item.lower().endswith('.egg'): - # unpacked egg - yield Distribution.from_filename( - path_item, metadata=PathMetadata( - path_item, os.path.join(path_item,'EGG-INFO') - ) - ) - else: - # scan for .egg and .egg-info in directory - for entry in os.listdir(path_item): - lower = entry.lower() - if lower.endswith('.egg-info') or lower.endswith('.dist-info'): - fullpath = os.path.join(path_item, entry) - if os.path.isdir(fullpath): - # egg-info directory, allow getting metadata - metadata = PathMetadata(path_item, fullpath) - else: - metadata = FileMetadata(fullpath) - yield Distribution.from_location( - path_item, entry, metadata, precedence=DEVELOP_DIST - ) - elif not only and lower.endswith('.egg'): - dists = find_distributions(os.path.join(path_item, entry)) - for dist in dists: - yield dist - elif not only and lower.endswith('.egg-link'): - with open(os.path.join(path_item, entry)) as entry_file: - entry_lines = entry_file.readlines() - for line in entry_lines: - if not line.strip(): - continue - path = os.path.join(path_item, line.rstrip()) - dists = find_distributions(path) - for item in dists: - yield item - break -register_finder(pkgutil.ImpImporter, find_on_path) - -if importlib_machinery is not None: - register_finder(importlib_machinery.FileFinder, find_on_path) - -_declare_state('dict', _namespace_handlers={}) -_declare_state('dict', _namespace_packages={}) - - -def register_namespace_handler(importer_type, namespace_handler): - """Register `namespace_handler` to declare namespace packages - - `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item - handler), and `namespace_handler` is a callable like this:: - - def namespace_handler(importer, path_entry, moduleName, module): - # return a path_entry to use for child packages - - Namespace handlers are only called if the importer object has already - agreed that it can handle the relevant path item, and they should only - return a subpath if the module __path__ does not already contain an - equivalent subpath. For an example namespace handler, see - ``pkg_resources.file_ns_handler``. - """ - _namespace_handlers[importer_type] = namespace_handler - -def _handle_ns(packageName, path_item): - """Ensure that named package includes a subpath of path_item (if needed)""" - - importer = get_importer(path_item) - if importer is None: - return None - loader = importer.find_module(packageName) - if loader is None: - return None - module = sys.modules.get(packageName) - if module is None: - module = sys.modules[packageName] = types.ModuleType(packageName) - module.__path__ = [] - _set_parent_ns(packageName) - elif not hasattr(module,'__path__'): - raise TypeError("Not a package:", packageName) - handler = _find_adapter(_namespace_handlers, importer) - subpath = handler(importer, path_item, packageName, module) - if subpath is not None: - path = module.__path__ - path.append(subpath) - loader.load_module(packageName) - for path_item in path: - if path_item not in module.__path__: - module.__path__.append(path_item) - return subpath - -def declare_namespace(packageName): - """Declare that package 'packageName' is a namespace package""" - - _imp.acquire_lock() - try: - if packageName in _namespace_packages: - return - - path, parent = sys.path, None - if '.' in packageName: - parent = '.'.join(packageName.split('.')[:-1]) - declare_namespace(parent) - if parent not in _namespace_packages: - __import__(parent) - try: - path = sys.modules[parent].__path__ - except AttributeError: - raise TypeError("Not a package:", parent) - - # Track what packages are namespaces, so when new path items are added, - # they can be updated - _namespace_packages.setdefault(parent,[]).append(packageName) - _namespace_packages.setdefault(packageName,[]) - - for path_item in path: - # Ensure all the parent's path items are reflected in the child, - # if they apply - _handle_ns(packageName, path_item) - - finally: - _imp.release_lock() - -def fixup_namespace_packages(path_item, parent=None): - """Ensure that previously-declared namespace packages include path_item""" - _imp.acquire_lock() - try: - for package in _namespace_packages.get(parent,()): - subpath = _handle_ns(package, path_item) - if subpath: - fixup_namespace_packages(subpath, package) - finally: - _imp.release_lock() - -def file_ns_handler(importer, path_item, packageName, module): - """Compute an ns-package subpath for a filesystem or zipfile importer""" - - subpath = os.path.join(path_item, packageName.split('.')[-1]) - normalized = _normalize_cached(subpath) - for item in module.__path__: - if _normalize_cached(item)==normalized: - break - else: - # Only return the path if it's not already there - return subpath - -register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) -register_namespace_handler(zipimport.zipimporter, file_ns_handler) - -if importlib_machinery is not None: - register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) - - -def null_ns_handler(importer, path_item, packageName, module): - return None - -register_namespace_handler(object, null_ns_handler) - - -def normalize_path(filename): - """Normalize a file/dir name for comparison purposes""" - return os.path.normcase(os.path.realpath(filename)) - -def _normalize_cached(filename, _cache={}): - try: - return _cache[filename] - except KeyError: - _cache[filename] = result = normalize_path(filename) - return result - -def _set_parent_ns(packageName): - parts = packageName.split('.') - name = parts.pop() - if parts: - parent = '.'.join(parts) - setattr(sys.modules[parent], name, sys.modules[packageName]) - - -def yield_lines(strs): - """Yield non-empty/non-comment lines of a string or sequence""" - if isinstance(strs, string_types): - for s in strs.splitlines(): - s = s.strip() - # skip blank lines/comments - if s and not s.startswith('#'): - yield s - else: - for ss in strs: - for s in yield_lines(ss): - yield s - -# whitespace and comment -LINE_END = re.compile(r"\s*(#.*)?$").match -# line continuation -CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match -# Distribution or extra -DISTRO = re.compile(r"\s*((\w|[-.])+)").match -# ver. info -VERSION = re.compile(r"\s*(<=?|>=?|===?|!=|~=)\s*((\w|[-.*_!+])+)").match -# comma between items -COMMA = re.compile(r"\s*,").match -OBRACKET = re.compile(r"\s*\[").match -CBRACKET = re.compile(r"\s*\]").match -MODULE = re.compile(r"\w+(\.\w+)*$").match -EGG_NAME = re.compile( - r""" - (?P[^-]+) ( - -(?P[^-]+) ( - -py(?P[^-]+) ( - -(?P.+) - )? - )? - )? - """, - re.VERBOSE | re.IGNORECASE, -).match - - -class EntryPoint(object): - """Object representing an advertised importable object""" - - def __init__(self, name, module_name, attrs=(), extras=(), dist=None): - if not MODULE(module_name): - raise ValueError("Invalid module name", module_name) - self.name = name - self.module_name = module_name - self.attrs = tuple(attrs) - self.extras = Requirement.parse(("x[%s]" % ','.join(extras))).extras - self.dist = dist - - def __str__(self): - s = "%s = %s" % (self.name, self.module_name) - if self.attrs: - s += ':' + '.'.join(self.attrs) - if self.extras: - s += ' [%s]' % ','.join(self.extras) - return s - - def __repr__(self): - return "EntryPoint.parse(%r)" % str(self) - - def load(self, require=True, *args, **kwargs): - """ - Require packages for this EntryPoint, then resolve it. - """ - if not require or args or kwargs: - warnings.warn( - "Parameters to load are deprecated. Call .resolve and " - ".require separately.", - DeprecationWarning, - stacklevel=2, - ) - if require: - self.require(*args, **kwargs) - return self.resolve() - - def resolve(self): - """ - Resolve the entry point from its module and attrs. - """ - module = __import__(self.module_name, fromlist=['__name__'], level=0) - try: - return functools.reduce(getattr, self.attrs, module) - except AttributeError as exc: - raise ImportError(str(exc)) - - def require(self, env=None, installer=None): - if self.extras and not self.dist: - raise UnknownExtra("Can't require() without a distribution", self) - reqs = self.dist.requires(self.extras) - items = working_set.resolve(reqs, env, installer) - list(map(working_set.add, items)) - - pattern = re.compile( - r'\s*' - r'(?P.+?)\s*' - r'=\s*' - r'(?P[\w.]+)\s*' - r'(:\s*(?P[\w.]+))?\s*' - r'(?P\[.*\])?\s*$' - ) - - @classmethod - def parse(cls, src, dist=None): - """Parse a single entry point from string `src` - - Entry point syntax follows the form:: - - name = some.module:some.attr [extra1, extra2] - - The entry name and module name are required, but the ``:attrs`` and - ``[extras]`` parts are optional - """ - m = cls.pattern.match(src) - if not m: - msg = "EntryPoint must be in 'name=module:attrs [extras]' format" - raise ValueError(msg, src) - res = m.groupdict() - extras = cls._parse_extras(res['extras']) - attrs = res['attr'].split('.') if res['attr'] else () - return cls(res['name'], res['module'], attrs, extras, dist) - - @classmethod - def _parse_extras(cls, extras_spec): - if not extras_spec: - return () - req = Requirement.parse('x' + extras_spec) - if req.specs: - raise ValueError() - return req.extras - - @classmethod - def parse_group(cls, group, lines, dist=None): - """Parse an entry point group""" - if not MODULE(group): - raise ValueError("Invalid group name", group) - this = {} - for line in yield_lines(lines): - ep = cls.parse(line, dist) - if ep.name in this: - raise ValueError("Duplicate entry point", group, ep.name) - this[ep.name]=ep - return this - - @classmethod - def parse_map(cls, data, dist=None): - """Parse a map of entry point groups""" - if isinstance(data, dict): - data = data.items() - else: - data = split_sections(data) - maps = {} - for group, lines in data: - if group is None: - if not lines: - continue - raise ValueError("Entry points must be listed in groups") - group = group.strip() - if group in maps: - raise ValueError("Duplicate group name", group) - maps[group] = cls.parse_group(group, lines, dist) - return maps - - -def _remove_md5_fragment(location): - if not location: - return '' - parsed = urlparse(location) - if parsed[-1].startswith('md5='): - return urlunparse(parsed[:-1] + ('',)) - return location - - -class Distribution(object): - """Wrap an actual or potential sys.path entry w/metadata""" - PKG_INFO = 'PKG-INFO' - - def __init__(self, location=None, metadata=None, project_name=None, - version=None, py_version=PY_MAJOR, platform=None, - precedence=EGG_DIST): - self.project_name = safe_name(project_name or 'Unknown') - if version is not None: - self._version = safe_version(version) - self.py_version = py_version - self.platform = platform - self.location = location - self.precedence = precedence - self._provider = metadata or empty_provider - - @classmethod - def from_location(cls, location, basename, metadata=None,**kw): - project_name, version, py_version, platform = [None]*4 - basename, ext = os.path.splitext(basename) - if ext.lower() in _distributionImpl: - # .dist-info gets much metadata differently - match = EGG_NAME(basename) - if match: - project_name, version, py_version, platform = match.group( - 'name','ver','pyver','plat' - ) - cls = _distributionImpl[ext.lower()] - return cls( - location, metadata, project_name=project_name, version=version, - py_version=py_version, platform=platform, **kw - ) - - @property - def hashcmp(self): - return ( - self.parsed_version, - self.precedence, - self.key, - _remove_md5_fragment(self.location), - self.py_version or '', - self.platform or '', - ) - - def __hash__(self): - return hash(self.hashcmp) - - def __lt__(self, other): - return self.hashcmp < other.hashcmp - - def __le__(self, other): - return self.hashcmp <= other.hashcmp - - def __gt__(self, other): - return self.hashcmp > other.hashcmp - - def __ge__(self, other): - return self.hashcmp >= other.hashcmp - - def __eq__(self, other): - if not isinstance(other, self.__class__): - # It's not a Distribution, so they are not equal - return False - return self.hashcmp == other.hashcmp - - def __ne__(self, other): - return not self == other - - # These properties have to be lazy so that we don't have to load any - # metadata until/unless it's actually needed. (i.e., some distributions - # may not know their name or version without loading PKG-INFO) - - @property - def key(self): - try: - return self._key - except AttributeError: - self._key = key = self.project_name.lower() - return key - - @property - def parsed_version(self): - if not hasattr(self, "_parsed_version"): - self._parsed_version = parse_version(self.version) - - return self._parsed_version - - def _warn_legacy_version(self): - LV = packaging.version.LegacyVersion - is_legacy = isinstance(self._parsed_version, LV) - if not is_legacy: - return - - # While an empty version is technically a legacy version and - # is not a valid PEP 440 version, it's also unlikely to - # actually come from someone and instead it is more likely that - # it comes from setuptools attempting to parse a filename and - # including it in the list. So for that we'll gate this warning - # on if the version is anything at all or not. - if not self.version: - return - - tmpl = textwrap.dedent(""" - '{project_name} ({version})' is being parsed as a legacy, - non PEP 440, - version. You may find odd behavior and sort order. - In particular it will be sorted as less than 0.0. It - is recommended to migrate to PEP 440 compatible - versions. - """).strip().replace('\n', ' ') - - warnings.warn(tmpl.format(**vars(self)), PEP440Warning) - - @property - def version(self): - try: - return self._version - except AttributeError: - for line in self._get_metadata(self.PKG_INFO): - if line.lower().startswith('version:'): - self._version = safe_version(line.split(':',1)[1].strip()) - return self._version - else: - tmpl = "Missing 'Version:' header and/or %s file" - raise ValueError(tmpl % self.PKG_INFO, self) - - @property - def _dep_map(self): - try: - return self.__dep_map - except AttributeError: - dm = self.__dep_map = {None: []} - for name in 'requires.txt', 'depends.txt': - for extra, reqs in split_sections(self._get_metadata(name)): - if extra: - if ':' in extra: - extra, marker = extra.split(':', 1) - if invalid_marker(marker): - # XXX warn - reqs=[] - elif not evaluate_marker(marker): - reqs=[] - extra = safe_extra(extra) or None - dm.setdefault(extra,[]).extend(parse_requirements(reqs)) - return dm - - def requires(self, extras=()): - """List of Requirements needed for this distro if `extras` are used""" - dm = self._dep_map - deps = [] - deps.extend(dm.get(None, ())) - for ext in extras: - try: - deps.extend(dm[safe_extra(ext)]) - except KeyError: - raise UnknownExtra( - "%s has no such extra feature %r" % (self, ext) - ) - return deps - - def _get_metadata(self, name): - if self.has_metadata(name): - for line in self.get_metadata_lines(name): - yield line - - def activate(self, path=None): - """Ensure distribution is importable on `path` (default=sys.path)""" - if path is None: - path = sys.path - self.insert_on(path) - if path is sys.path: - fixup_namespace_packages(self.location) - for pkg in self._get_metadata('namespace_packages.txt'): - if pkg in sys.modules: - declare_namespace(pkg) - - def egg_name(self): - """Return what this distribution's standard .egg filename should be""" - filename = "%s-%s-py%s" % ( - to_filename(self.project_name), to_filename(self.version), - self.py_version or PY_MAJOR - ) - - if self.platform: - filename += '-' + self.platform - return filename - - def __repr__(self): - if self.location: - return "%s (%s)" % (self, self.location) - else: - return str(self) - - def __str__(self): - try: - version = getattr(self, 'version', None) - except ValueError: - version = None - version = version or "[unknown version]" - return "%s %s" % (self.project_name, version) - - def __getattr__(self, attr): - """Delegate all unrecognized public attributes to .metadata provider""" - if attr.startswith('_'): - raise AttributeError(attr) - return getattr(self._provider, attr) - - @classmethod - def from_filename(cls, filename, metadata=None, **kw): - return cls.from_location( - _normalize_cached(filename), os.path.basename(filename), metadata, - **kw - ) - - def as_requirement(self): - """Return a ``Requirement`` that matches this distribution exactly""" - if isinstance(self.parsed_version, packaging.version.Version): - spec = "%s==%s" % (self.project_name, self.parsed_version) - else: - spec = "%s===%s" % (self.project_name, self.parsed_version) - - return Requirement.parse(spec) - - def load_entry_point(self, group, name): - """Return the `name` entry point of `group` or raise ImportError""" - ep = self.get_entry_info(group, name) - if ep is None: - raise ImportError("Entry point %r not found" % ((group, name),)) - return ep.load() - - def get_entry_map(self, group=None): - """Return the entry point map for `group`, or the full entry map""" - try: - ep_map = self._ep_map - except AttributeError: - ep_map = self._ep_map = EntryPoint.parse_map( - self._get_metadata('entry_points.txt'), self - ) - if group is not None: - return ep_map.get(group,{}) - return ep_map - - def get_entry_info(self, group, name): - """Return the EntryPoint object for `group`+`name`, or ``None``""" - return self.get_entry_map(group).get(name) - - def insert_on(self, path, loc = None): - """Insert self.location in path before its nearest parent directory""" - - loc = loc or self.location - if not loc: - return - - nloc = _normalize_cached(loc) - bdir = os.path.dirname(nloc) - npath= [(p and _normalize_cached(p) or p) for p in path] - - for p, item in enumerate(npath): - if item == nloc: - break - elif item == bdir and self.precedence == EGG_DIST: - # if it's an .egg, give it precedence over its directory - if path is sys.path: - self.check_version_conflict() - path.insert(p, loc) - npath.insert(p, nloc) - break - else: - if path is sys.path: - self.check_version_conflict() - path.append(loc) - return - - # p is the spot where we found or inserted loc; now remove duplicates - while True: - try: - np = npath.index(nloc, p+1) - except ValueError: - break - else: - del npath[np], path[np] - # ha! - p = np - - return - - def check_version_conflict(self): - if self.key == 'setuptools': - # ignore the inevitable setuptools self-conflicts :( - return - - nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) - loc = normalize_path(self.location) - for modname in self._get_metadata('top_level.txt'): - if (modname not in sys.modules or modname in nsp - or modname in _namespace_packages): - continue - if modname in ('pkg_resources', 'setuptools', 'site'): - continue - fn = getattr(sys.modules[modname], '__file__', None) - if fn and (normalize_path(fn).startswith(loc) or - fn.startswith(self.location)): - continue - issue_warning( - "Module %s was already imported from %s, but %s is being added" - " to sys.path" % (modname, fn, self.location), - ) - - def has_version(self): - try: - self.version - except ValueError: - issue_warning("Unbuilt egg for " + repr(self)) - return False - return True - - def clone(self,**kw): - """Copy this distribution, substituting in any changed keyword args""" - names = 'project_name version py_version platform location precedence' - for attr in names.split(): - kw.setdefault(attr, getattr(self, attr, None)) - kw.setdefault('metadata', self._provider) - return self.__class__(**kw) - - @property - def extras(self): - return [dep for dep in self._dep_map if dep] - - -class DistInfoDistribution(Distribution): - """Wrap an actual or potential sys.path entry w/metadata, .dist-info style""" - PKG_INFO = 'METADATA' - EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") - - @property - def _parsed_pkg_info(self): - """Parse and cache metadata""" - try: - return self._pkg_info - except AttributeError: - metadata = self.get_metadata(self.PKG_INFO) - self._pkg_info = email.parser.Parser().parsestr(metadata) - return self._pkg_info - - @property - def _dep_map(self): - try: - return self.__dep_map - except AttributeError: - self.__dep_map = self._compute_dependencies() - return self.__dep_map - - def _preparse_requirement(self, requires_dist): - """Convert 'Foobar (1); baz' to ('Foobar ==1', 'baz') - Split environment marker, add == prefix to version specifiers as - necessary, and remove parenthesis. - """ - parts = requires_dist.split(';', 1) + [''] - distvers = parts[0].strip() - mark = parts[1].strip() - distvers = re.sub(self.EQEQ, r"\1==\2\3", distvers) - distvers = distvers.replace('(', '').replace(')', '') - return (distvers, mark) - - def _compute_dependencies(self): - """Recompute this distribution's dependencies.""" - from _markerlib import compile as compile_marker - dm = self.__dep_map = {None: []} - - reqs = [] - # Including any condition expressions - for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: - distvers, mark = self._preparse_requirement(req) - parsed = next(parse_requirements(distvers)) - parsed.marker_fn = compile_marker(mark) - reqs.append(parsed) - - def reqs_for_extra(extra): - for req in reqs: - if req.marker_fn(override={'extra':extra}): - yield req - - common = frozenset(reqs_for_extra(None)) - dm[None].extend(common) - - for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: - extra = safe_extra(extra.strip()) - dm[extra] = list(frozenset(reqs_for_extra(extra)) - common) - - return dm - - -_distributionImpl = { - '.egg': Distribution, - '.egg-info': Distribution, - '.dist-info': DistInfoDistribution, - } - - -def issue_warning(*args,**kw): - level = 1 - g = globals() - try: - # find the first stack frame that is *not* code in - # the pkg_resources module, to use for the warning - while sys._getframe(level).f_globals is g: - level += 1 - except ValueError: - pass - warnings.warn(stacklevel=level + 1, *args, **kw) - - -class RequirementParseError(ValueError): - def __str__(self): - return ' '.join(self.args) - - -def parse_requirements(strs): - """Yield ``Requirement`` objects for each specification in `strs` - - `strs` must be a string, or a (possibly-nested) iterable thereof. - """ - # create a steppable iterator, so we can handle \-continuations - lines = iter(yield_lines(strs)) - - def scan_list(ITEM, TERMINATOR, line, p, groups, item_name): - - items = [] - - while not TERMINATOR(line, p): - if CONTINUE(line, p): - try: - line = next(lines) - p = 0 - except StopIteration: - msg = "\\ must not appear on the last nonblank line" - raise RequirementParseError(msg) - - match = ITEM(line, p) - if not match: - msg = "Expected " + item_name + " in" - raise RequirementParseError(msg, line, "at", line[p:]) - - items.append(match.group(*groups)) - p = match.end() - - match = COMMA(line, p) - if match: - # skip the comma - p = match.end() - elif not TERMINATOR(line, p): - msg = "Expected ',' or end-of-list in" - raise RequirementParseError(msg, line, "at", line[p:]) - - match = TERMINATOR(line, p) - # skip the terminator, if any - if match: - p = match.end() - return line, p, items - - for line in lines: - match = DISTRO(line) - if not match: - raise RequirementParseError("Missing distribution spec", line) - project_name = match.group(1) - p = match.end() - extras = [] - - match = OBRACKET(line, p) - if match: - p = match.end() - line, p, extras = scan_list( - DISTRO, CBRACKET, line, p, (1,), "'extra' name" - ) - - line, p, specs = scan_list(VERSION, LINE_END, line, p, (1, 2), - "version spec") - specs = [(op, val) for op, val in specs] - yield Requirement(project_name, specs, extras) - - -class Requirement: - def __init__(self, project_name, specs, extras): - """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" - self.unsafe_name, project_name = project_name, safe_name(project_name) - self.project_name, self.key = project_name, project_name.lower() - self.specifier = packaging.specifiers.SpecifierSet( - ",".join(["".join([x, y]) for x, y in specs]) - ) - self.specs = specs - self.extras = tuple(map(safe_extra, extras)) - self.hashCmp = ( - self.key, - self.specifier, - frozenset(self.extras), - ) - self.__hash = hash(self.hashCmp) - - def __str__(self): - extras = ','.join(self.extras) - if extras: - extras = '[%s]' % extras - return '%s%s%s' % (self.project_name, extras, self.specifier) - - def __eq__(self, other): - return ( - isinstance(other, Requirement) and - self.hashCmp == other.hashCmp - ) - - def __ne__(self, other): - return not self == other - - def __contains__(self, item): - if isinstance(item, Distribution): - if item.key != self.key: - return False - - item = item.version - - # Allow prereleases always in order to match the previous behavior of - # this method. In the future this should be smarter and follow PEP 440 - # more accurately. - return self.specifier.contains(item, prereleases=True) - - def __hash__(self): - return self.__hash - - def __repr__(self): return "Requirement.parse(%r)" % str(self) - - @staticmethod - def parse(s): - reqs = list(parse_requirements(s)) - if reqs: - if len(reqs) == 1: - return reqs[0] - raise ValueError("Expected only one requirement", s) - raise ValueError("No requirements found", s) - - -def _get_mro(cls): - """Get an mro for a type or classic class""" - if not isinstance(cls, type): - class cls(cls, object): pass - return cls.__mro__[1:] - return cls.__mro__ - -def _find_adapter(registry, ob): - """Return an adapter factory for `ob` from `registry`""" - for t in _get_mro(getattr(ob, '__class__', type(ob))): - if t in registry: - return registry[t] - - -def ensure_directory(path): - """Ensure that the parent directory of `path` exists""" - dirname = os.path.dirname(path) - if not os.path.isdir(dirname): - os.makedirs(dirname) - - -def _bypass_ensure_directory(path): - """Sandbox-bypassing version of ensure_directory()""" - if not WRITE_SUPPORT: - raise IOError('"os.mkdir" not supported on this platform.') - dirname, filename = split(path) - if dirname and filename and not isdir(dirname): - _bypass_ensure_directory(dirname) - mkdir(dirname, 0o755) - - -def split_sections(s): - """Split a string or iterable thereof into (section, content) pairs - - Each ``section`` is a stripped version of the section header ("[section]") - and each ``content`` is a list of stripped lines excluding blank lines and - comment-only lines. If there are any such lines before the first section - header, they're returned in a first ``section`` of ``None``. - """ - section = None - content = [] - for line in yield_lines(s): - if line.startswith("["): - if line.endswith("]"): - if section or content: - yield section, content - section = line[1:-1].strip() - content = [] - else: - raise ValueError("Invalid section heading", line) - else: - content.append(line) - - # wrap up last segment - yield section, content - -def _mkstemp(*args,**kw): - old_open = os.open - try: - # temporarily bypass sandboxing - os.open = os_open - return tempfile.mkstemp(*args,**kw) - finally: - # and then put it back - os.open = old_open - - -# Silence the PEP440Warning by default, so that end users don't get hit by it -# randomly just because they use pkg_resources. We want to append the rule -# because we want earlier uses of filterwarnings to take precedence over this -# one. -warnings.filterwarnings("ignore", category=PEP440Warning, append=True) - - -# from jaraco.functools 1.3 -def _call_aside(f, *args, **kwargs): - f(*args, **kwargs) - return f - - -@_call_aside -def _initialize(g=globals()): - "Set up global resource manager (deliberately not state-saved)" - manager = ResourceManager() - g['_manager'] = manager - for name in dir(manager): - if not name.startswith('_'): - g[name] = getattr(manager, name) - - -@_call_aside -def _initialize_master_working_set(): - """ - Prepare the master working set and make the ``require()`` - API available. - - This function has explicit effects on the global state - of pkg_resources. It is intended to be invoked once at - the initialization of this module. - - Invocation by other packages is unsupported and done - at their own risk. - """ - working_set = WorkingSet._build_master() - _declare_state('object', working_set=working_set) - - require = working_set.require - iter_entry_points = working_set.iter_entry_points - add_activation_listener = working_set.subscribe - run_script = working_set.run_script - # backward compatibility - run_main = run_script - # Activate all distributions already on sys.path, and ensure that - # all distributions added to the working set in the future (e.g. by - # calling ``require()``) will get activated as well. - add_activation_listener(lambda dist: dist.activate()) - working_set.entries=[] - # match order - list(map(working_set.add_entry, sys.path)) - globals().update(locals()) diff --git a/pymode/libs/pkg_resources/_vendor/__init__.py b/pymode/libs/pkg_resources/_vendor/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pymode/libs/pkg_resources/_vendor/packaging/__about__.py b/pymode/libs/pkg_resources/_vendor/packaging/__about__.py deleted file mode 100644 index eadb794e..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/__about__.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -__all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "15.3" - -__author__ = "Donald Stufft" -__email__ = "donald@stufft.io" - -__license__ = "Apache License, Version 2.0" -__copyright__ = "Copyright 2014 %s" % __author__ diff --git a/pymode/libs/pkg_resources/_vendor/packaging/__init__.py b/pymode/libs/pkg_resources/_vendor/packaging/__init__.py deleted file mode 100644 index c39a8eab..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -from .__about__ import ( - __author__, __copyright__, __email__, __license__, __summary__, __title__, - __uri__, __version__ -) - -__all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", -] diff --git a/pymode/libs/pkg_resources/_vendor/packaging/_compat.py b/pymode/libs/pkg_resources/_vendor/packaging/_compat.py deleted file mode 100644 index 5c396cea..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/_compat.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -import sys - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = str, -else: - string_types = basestring, - - -def with_metaclass(meta, *bases): - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/pymode/libs/pkg_resources/_vendor/packaging/_structures.py b/pymode/libs/pkg_resources/_vendor/packaging/_structures.py deleted file mode 100644 index 0ae9bb52..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/_structures.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - - -class Infinity(object): - - def __repr__(self): - return "Infinity" - - def __hash__(self): - return hash(repr(self)) - - def __lt__(self, other): - return False - - def __le__(self, other): - return False - - def __eq__(self, other): - return isinstance(other, self.__class__) - - def __ne__(self, other): - return not isinstance(other, self.__class__) - - def __gt__(self, other): - return True - - def __ge__(self, other): - return True - - def __neg__(self): - return NegativeInfinity - -Infinity = Infinity() - - -class NegativeInfinity(object): - - def __repr__(self): - return "-Infinity" - - def __hash__(self): - return hash(repr(self)) - - def __lt__(self, other): - return True - - def __le__(self, other): - return True - - def __eq__(self, other): - return isinstance(other, self.__class__) - - def __ne__(self, other): - return not isinstance(other, self.__class__) - - def __gt__(self, other): - return False - - def __ge__(self, other): - return False - - def __neg__(self): - return Infinity - -NegativeInfinity = NegativeInfinity() diff --git a/pymode/libs/pkg_resources/_vendor/packaging/specifiers.py b/pymode/libs/pkg_resources/_vendor/packaging/specifiers.py deleted file mode 100644 index 891664f0..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/specifiers.py +++ /dev/null @@ -1,784 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -import abc -import functools -import itertools -import re - -from ._compat import string_types, with_metaclass -from .version import Version, LegacyVersion, parse - - -class InvalidSpecifier(ValueError): - """ - An invalid specifier was found, users should refer to PEP 440. - """ - - -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): - - @abc.abstractmethod - def __str__(self): - """ - Returns the str representation of this Specifier like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self): - """ - Returns a hash value for this Specifier like object. - """ - - @abc.abstractmethod - def __eq__(self, other): - """ - Returns a boolean representing whether or not the two Specifier like - objects are equal. - """ - - @abc.abstractmethod - def __ne__(self, other): - """ - Returns a boolean representing whether or not the two Specifier like - objects are not equal. - """ - - @abc.abstractproperty - def prereleases(self): - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @prereleases.setter - def prereleases(self, value): - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @abc.abstractmethod - def contains(self, item, prereleases=None): - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter(self, iterable, prereleases=None): - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class _IndividualSpecifier(BaseSpecifier): - - _operators = {} - - def __init__(self, spec="", prereleases=None): - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - - self._spec = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self): - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "<{0}({1!r}{2})>".format( - self.__class__.__name__, - str(self), - pre, - ) - - def __str__(self): - return "{0}{1}".format(*self._spec) - - def __hash__(self): - return hash(self._spec) - - def __eq__(self, other): - if isinstance(other, string_types): - try: - other = self.__class__(other) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec == other._spec - - def __ne__(self, other): - if isinstance(other, string_types): - try: - other = self.__class__(other) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec != other._spec - - def _get_operator(self, op): - return getattr(self, "_compare_{0}".format(self._operators[op])) - - def _coerce_version(self, version): - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version - - @property - def operator(self): - return self._spec[0] - - @property - def version(self): - return self._spec[1] - - @property - def prereleases(self): - return self._prereleases - - @prereleases.setter - def prereleases(self, value): - self._prereleases = value - - def __contains__(self, item): - return self.contains(item) - - def contains(self, item, prereleases=None): - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - return self._get_operator(self.operator)(item, self.version) - - def filter(self, iterable, prereleases=None): - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing - # else matches this specifier. - if (parsed_version.is_prerelease - and not (prereleases or self.prereleases)): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the begining. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex = re.compile( - r""" - ^ - \s* - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^\s]* # We just match everything, except for whitespace since this - # is a "legacy" specifier and the version string can be just - # about anything. - ) - \s* - $ - """, - re.VERBOSE | re.IGNORECASE, - ) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def _coerce_version(self, version): - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective, spec): - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective, spec): - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective, spec): - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal(self, prospective, spec): - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective, spec): - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective, spec): - return prospective > self._coerce_version(spec) - - -def _require_version_compare(fn): - @functools.wraps(fn) - def wrapped(self, prospective, spec): - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - return wrapped - - -class Specifier(_IndividualSpecifier): - - _regex = re.compile( - r""" - ^ - \s* - (?P(~=|==|!=|<=|>=|<|>|===)) - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. - (?: - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - @_require_version_compare - def _compare_compatible(self, prospective, spec): - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. - prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") - and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return (self._get_operator(">=")(prospective, spec) - and self._get_operator("==")(prospective, prefix)) - - @_require_version_compare - def _compare_equal(self, prospective, spec): - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # Split the spec out by dots, and pretend that there is an implicit - # dot in between a release segment and a pre-release segment. - spec = _version_split(spec[:-2]) # Remove the trailing .* - - # Split the prospective version out by dots, and pretend that there - # is an implicit dot in between a release segment and a pre-release - # segment. - prospective = _version_split(str(prospective)) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - prospective = prospective[:len(spec)] - - # Pad out our two sides with zeros so that they both equal the same - # length. - spec, prospective = _pad_version(spec, prospective) - else: - # Convert our spec string into a Version - spec = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec.local: - prospective = Version(prospective.public) - - return prospective == spec - - @_require_version_compare - def _compare_not_equal(self, prospective, spec): - return not self._compare_equal(prospective, spec) - - @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - return prospective <= Version(spec) - - @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - return prospective >= Version(spec) - - @_require_version_compare - def _compare_less_than(self, prospective, spec): - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - @_require_version_compare - def _compare_greater_than(self, prospective, spec): - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is techincally greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective, spec): - return str(prospective).lower() == str(spec).lower() - - @property - def prereleases(self): - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value): - self._prereleases = value - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version): - result = [] - for item in version.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _pad_version(left, right): - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split):]) - right_split.append(left[len(right_split):]) - - # Insert our padding - left_split.insert( - 1, - ["0"] * max(0, len(right_split[0]) - len(left_split[0])), - ) - right_split.insert( - 1, - ["0"] * max(0, len(left_split[0]) - len(right_split[0])), - ) - - return ( - list(itertools.chain(*left_split)), - list(itertools.chain(*right_split)), - ) - - -class SpecifierSet(BaseSpecifier): - - def __init__(self, specifiers="", prereleases=None): - # Split on , to break each indidivual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed = set() - for specifier in specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) - - # Turn our parsed specifiers into a frozen set and save them for later. - self._specs = frozenset(parsed) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - def __repr__(self): - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "".format(str(self), pre) - - def __str__(self): - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self): - return hash(self._specs) - - def __and__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __ne__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs != other._specs - - def __len__(self): - return len(self._specs) - - def __iter__(self): - return iter(self._specs) - - @property - def prereleases(self): - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value): - self._prereleases = value - - def __contains__(self, item): - return self.contains(item) - - def contains(self, item, prereleases=None): - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all( - s.contains(item, prereleases=prereleases) - for s in self._specs - ) - - def filter(self, iterable, prereleases=None): - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. - else: - filtered = [] - found_prereleases = [] - - for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return found_prereleases - - return filtered diff --git a/pymode/libs/pkg_resources/_vendor/packaging/version.py b/pymode/libs/pkg_resources/_vendor/packaging/version.py deleted file mode 100644 index 4ba574b9..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/version.py +++ /dev/null @@ -1,403 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -import collections -import itertools -import re - -from ._structures import Infinity - - -__all__ = [ - "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" -] - - -_Version = collections.namedtuple( - "_Version", - ["epoch", "release", "dev", "pre", "post", "local"], -) - - -def parse(version): - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. - """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) - - -class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. - """ - - -class _BaseVersion(object): - - def __hash__(self): - return hash(self._key) - - def __lt__(self, other): - return self._compare(other, lambda s, o: s < o) - - def __le__(self, other): - return self._compare(other, lambda s, o: s <= o) - - def __eq__(self, other): - return self._compare(other, lambda s, o: s == o) - - def __ge__(self, other): - return self._compare(other, lambda s, o: s >= o) - - def __gt__(self, other): - return self._compare(other, lambda s, o: s > o) - - def __ne__(self, other): - return self._compare(other, lambda s, o: s != o) - - def _compare(self, other, method): - if not isinstance(other, _BaseVersion): - return NotImplemented - - return method(self._key, other._key) - - -class LegacyVersion(_BaseVersion): - - def __init__(self, version): - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - def __str__(self): - return self._version - - def __repr__(self): - return "".format(repr(str(self))) - - @property - def public(self): - return self._version - - @property - def base_version(self): - return self._version - - @property - def local(self): - return None - - @property - def is_prerelease(self): - return False - - @property - def is_postrelease(self): - return False - - -_legacy_version_component_re = re.compile( - r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, -) - -_legacy_version_replacement_map = { - "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", -} - - -def _parse_version_parts(s): - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version): - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts = [] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - parts = tuple(parts) - - return epoch, parts - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -VERSION_PATTERN = r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                              # pre-release
    -            [-_\.]?
    -            (?P(a|b|c|rc|alpha|beta|pre|preview))
    -            [-_\.]?
    -            (?P[0-9]+)?
    -        )?
    -        (?P                                         # post release
    -            (?:-(?P[0-9]+))
    -            |
    -            (?:
    -                [-_\.]?
    -                (?Ppost|rev|r)
    -                [-_\.]?
    -                (?P[0-9]+)?
    -            )
    -        )?
    -        (?P                                          # dev release
    -            [-_\.]?
    -            (?Pdev)
    -            [-_\.]?
    -            (?P[0-9]+)?
    -        )?
    -    )
    -    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
    -"""
    -
    -
    -class Version(_BaseVersion):
    -
    -    _regex = re.compile(
    -        r"^\s*" + VERSION_PATTERN + r"\s*$",
    -        re.VERBOSE | re.IGNORECASE,
    -    )
    -
    -    def __init__(self, version):
    -        # Validate the version and parse it into pieces
    -        match = self._regex.search(version)
    -        if not match:
    -            raise InvalidVersion("Invalid version: '{0}'".format(version))
    -
    -        # Store the parsed out pieces of the version
    -        self._version = _Version(
    -            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
    -            release=tuple(int(i) for i in match.group("release").split(".")),
    -            pre=_parse_letter_version(
    -                match.group("pre_l"),
    -                match.group("pre_n"),
    -            ),
    -            post=_parse_letter_version(
    -                match.group("post_l"),
    -                match.group("post_n1") or match.group("post_n2"),
    -            ),
    -            dev=_parse_letter_version(
    -                match.group("dev_l"),
    -                match.group("dev_n"),
    -            ),
    -            local=_parse_local_version(match.group("local")),
    -        )
    -
    -        # Generate a key which will be used for sorting
    -        self._key = _cmpkey(
    -            self._version.epoch,
    -            self._version.release,
    -            self._version.pre,
    -            self._version.post,
    -            self._version.dev,
    -            self._version.local,
    -        )
    -
    -    def __repr__(self):
    -        return "".format(repr(str(self)))
    -
    -    def __str__(self):
    -        parts = []
    -
    -        # Epoch
    -        if self._version.epoch != 0:
    -            parts.append("{0}!".format(self._version.epoch))
    -
    -        # Release segment
    -        parts.append(".".join(str(x) for x in self._version.release))
    -
    -        # Pre-release
    -        if self._version.pre is not None:
    -            parts.append("".join(str(x) for x in self._version.pre))
    -
    -        # Post-release
    -        if self._version.post is not None:
    -            parts.append(".post{0}".format(self._version.post[1]))
    -
    -        # Development release
    -        if self._version.dev is not None:
    -            parts.append(".dev{0}".format(self._version.dev[1]))
    -
    -        # Local version segment
    -        if self._version.local is not None:
    -            parts.append(
    -                "+{0}".format(".".join(str(x) for x in self._version.local))
    -            )
    -
    -        return "".join(parts)
    -
    -    @property
    -    def public(self):
    -        return str(self).split("+", 1)[0]
    -
    -    @property
    -    def base_version(self):
    -        parts = []
    -
    -        # Epoch
    -        if self._version.epoch != 0:
    -            parts.append("{0}!".format(self._version.epoch))
    -
    -        # Release segment
    -        parts.append(".".join(str(x) for x in self._version.release))
    -
    -        return "".join(parts)
    -
    -    @property
    -    def local(self):
    -        version_string = str(self)
    -        if "+" in version_string:
    -            return version_string.split("+", 1)[1]
    -
    -    @property
    -    def is_prerelease(self):
    -        return bool(self._version.dev or self._version.pre)
    -
    -    @property
    -    def is_postrelease(self):
    -        return bool(self._version.post)
    -
    -
    -def _parse_letter_version(letter, number):
    -    if letter:
    -        # We consider there to be an implicit 0 in a pre-release if there is
    -        # not a numeral associated with it.
    -        if number is None:
    -            number = 0
    -
    -        # We normalize any letters to their lower case form
    -        letter = letter.lower()
    -
    -        # We consider some words to be alternate spellings of other words and
    -        # in those cases we want to normalize the spellings to our preferred
    -        # spelling.
    -        if letter == "alpha":
    -            letter = "a"
    -        elif letter == "beta":
    -            letter = "b"
    -        elif letter in ["c", "pre", "preview"]:
    -            letter = "rc"
    -        elif letter in ["rev", "r"]:
    -            letter = "post"
    -
    -        return letter, int(number)
    -    if not letter and number:
    -        # We assume if we are given a number, but we are not given a letter
    -        # then this is using the implicit post release syntax (e.g. 1.0-1)
    -        letter = "post"
    -
    -        return letter, int(number)
    -
    -
    -_local_version_seperators = re.compile(r"[\._-]")
    -
    -
    -def _parse_local_version(local):
    -    """
    -    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
    -    """
    -    if local is not None:
    -        return tuple(
    -            part.lower() if not part.isdigit() else int(part)
    -            for part in _local_version_seperators.split(local)
    -        )
    -
    -
    -def _cmpkey(epoch, release, pre, post, dev, local):
    -    # When we compare a release version, we want to compare it with all of the
    -    # trailing zeros removed. So we'll use a reverse the list, drop all the now
    -    # leading zeros until we come to something non zero, then take the rest
    -    # re-reverse it back into the correct order and make it a tuple and use
    -    # that for our sorting key.
    -    release = tuple(
    -        reversed(list(
    -            itertools.dropwhile(
    -                lambda x: x == 0,
    -                reversed(release),
    -            )
    -        ))
    -    )
    -
    -    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
    -    # We'll do this by abusing the pre segment, but we _only_ want to do this
    -    # if there is not a pre or a post segment. If we have one of those then
    -    # the normal sorting rules will handle this case correctly.
    -    if pre is None and post is None and dev is not None:
    -        pre = -Infinity
    -    # Versions without a pre-release (except as noted above) should sort after
    -    # those with one.
    -    elif pre is None:
    -        pre = Infinity
    -
    -    # Versions without a post segment should sort before those with one.
    -    if post is None:
    -        post = -Infinity
    -
    -    # Versions without a development segment should sort after those with one.
    -    if dev is None:
    -        dev = Infinity
    -
    -    if local is None:
    -        # Versions without a local segment should sort before those with one.
    -        local = -Infinity
    -    else:
    -        # Versions with a local segment need that segment parsed to implement
    -        # the sorting rules in PEP440.
    -        # - Alpha numeric segments sort before numeric segments
    -        # - Alpha numeric segments sort lexicographically
    -        # - Numeric segments sort numerically
    -        # - Shorter versions sort before longer versions when the prefixes
    -        #   match exactly
    -        local = tuple(
    -            (i, "") if isinstance(i, int) else (-Infinity, i)
    -            for i in local
    -        )
    -
    -    return epoch, release, pre, post, dev, local
    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/__init__.py b/pymode/libs/pylint/extensions/__init__.py
    deleted file mode 100644
    index e69de29b..00000000
    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': '