diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 00000000..84607ec8 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,14 @@ +[bumpversion] +commit = True +current_version = 0.14.0 +files = plugin/pymode.vim +tag = True +tag_name = {new_version} + +[bumpversion:file:doc/pymode.txt] +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/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..be619481 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 150 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 21 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: inactive +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: > + Closed due to inactivity. 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/.gitignore b/.gitignore index f5674a78..40ca63ba 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ test.py todo.txt vendor vim.py +vim_session_*.vim +__*/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..59d00541 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,59 @@ +[submodule "submodules/autopep8"] + 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/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/.ruby-gemset b/.ruby-gemset deleted file mode 100644 index 5ded393e..00000000 --- a/.ruby-gemset +++ /dev/null @@ -1 +0,0 @@ -vim-flavor diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 67b8bc0d..00000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -ruby-1.9.3 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e10ed9f1..00000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: ruby -python: "2.7" -rvm: - - 1.9.3 -script: - - make travis diff --git a/AUTHORS b/AUTHORS index fedbb7c8..a4bcbf28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,62 +1,78 @@ -Maintainer: +Author: * Kirill Klenov +Maintainers: + +* 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) -* 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); * 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); * Matthew Moses (http://github.com/mlmoses); * Maxim (https://github.com/mpyatishev); * Mel Boyce (http://github.com/syngin); * 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); -* Piet Delport (http://github.com/pjdelport); +* Pi Delport (http://github.com/pjdelport); * Robert David Grant (http://github.com/bgrant); * Robin Schneider (https://github.com/ypid); * 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 e396eb69..00000000 --- a/Changelog.rst +++ /dev/null @@ -1,356 +0,0 @@ -Changelog -========= - -* 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/Gemfile b/Gemfile deleted file mode 100644 index a87f4e1a..00000000 --- a/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://rubygems.org' - -gem 'vim-flavor', '~> 1.1' diff --git a/Makefile b/Makefile deleted file mode 100644 index e27a8785..00000000 --- a/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -PYMODE = $(CURDIR)/pymode -LIBS = $(PYMODE)/libs -PYLAMA = $(LIBS)/pylama - -.PHONY: clean -clean: - find $(CURDIR) -name "*.pyc" -delete - rm -rf $(CURDIR)/build - rm -rf *.deb - -# Temporary disable rope tests on Travis -.PHONY: travis -travis: - rake test - -.PHONY: test t -test: - bundle install - rm -rf $(CURDIR)/.ropeproject - rake test -t: test - -.PHONY: pylama -pylama: - rm -rf $(PYLAMA) - make $(PYLAMA) - make $(PYLAMA)/lint/pylama_pylint - -.PHONY: rope -rope: - @git clone https://github.com/python-rope/rope.git $(CURDIR)/_/rope - @rm -rf $(CURDIR)/pymode/libs/rope - @cp -r $(CURDIR)/_/rope/rope $(CURDIR)/pymode/libs/. - -$(PYLAMA): - cp -r $$PRJDIR/pylama/pylama $(PYLAMA) - -$(PYLAMA)/lint/pylama_pylint: - cp -r $$PRJDIR/pylama/plugins/pylama_pylint/pylama_pylint/ $(PYLAMA)/lint/pylama_pylint - -$(CURDIR)/build: - mkdir -p $(CURDIR)/build/usr/share/vim/addons - mkdir -p $(CURDIR)/build/usr/share/vim/registry - cp -r after autoload doc ftplugin plugin pymode syntax $(CURDIR)/build/usr/share/vim/addons/. - cp -r python-mode.yaml $(CURDIR)/build/usr/share/vim/registry/. - -PACKAGE_VERSION?=$(shell git describe --tags `git rev-list master --tags --max-count=1`) -PACKAGE_NAME="vim-python-mode" -PACKAGE_MAINTAINER="Kirill Klenov " -PACKAGE_URL=http://github.com/klen/python-mode -deb: clean $(CURDIR)/build - @fpm -s dir -t deb -a all \ - -n $(PACKAGE_NAME) \ - -v $(PACKAGE_VERSION) \ - -m $(PACKAGE_MAINTAINER) \ - --url $(PACKAGE_URL) \ - --license "GNU lesser general public license" \ - --description "Vim-Swissknife for python" \ - --deb-user root \ - --deb-group root \ - -C $(CURDIR)/build \ - -d "python2.7" \ - -d "vim-addon-manager" \ - usr - @mv *.deb ~/Dropbox/projects/deb/load diff --git a/README.rst b/README.rst deleted file mode 100644 index 5c897bc1..00000000 --- a/README.rst +++ /dev/null @@ -1,349 +0,0 @@ -|logo| Python-mode, Python in VIM -################################# - -.. image:: https://travis-ci.org/klen/python-mode.png?branch=develop - :target: https://travis-ci.org/klen/python-mode - ------ - -*The project needs maintainers and contributors* - -Actually, I have no time for support the project, so if you feel yourself as -strong don't be hesitate to contact me. - ------ - -| -| Src: https://github.com/klen/python-mode -| Homepage: https://klen.github.io/python-mode/ -| Docs: https://github.com/klen/python-mode/blob/develop/doc/pymode.txt -| - -Python-mode is a vim plugin that helps you to create python code very quickly -by utilizing libraries including -`pylint`_, `rope`_, pydoc_, `pyflakes`_, `pep8`_, `autopep8`_, -`pep257`_ and `mccabe`_ -for features like static analysis, refactoring, folding, completion, -documentation, and more. - -The plugin contains all you need to develop python applications in Vim. - -There is no need to install `pylint`_, `rope`_ -or any other `Python Libraries`_ on your system. - -- Support Python version 2.6+ and 3.2+ -- Syntax highlighting -- Virtualenv support -- Run python code (``r``) -- Add/remove breakpoints (``b``) -- Improved Python indentation -- Python folding -- Python motions and operators (``]]``, ``3[[``, ``]]M``, ``vaC``, ``viM``, - ``daC``, ``ciM``, ...) -- Code checking (pylint_, pyflakes_, pylama_, ...) that can be run - simultaneously (``:PymodeLint``) -- Autofix PEP8 errors (``:PymodeLintAuto``) -- Search in python documentation (``K``) -- Code refactoring (rope_) -- Strong code completion (rope_) -- Go to definition (``g`` for `:RopeGotoDefinition`) -- And more, more ... - -See (very old) screencast here: http://www.youtube.com/watch?v=67OZNp9Z0CQ -(sorry for quality, this is my first screencast) Another old presentation here: -http://www.youtube.com/watch?v=YhqsjUUHj6g - -**To read python-mode documentation in Vim, see** ``:help pymode`` - - -.. contents:: - - -Requirements -============ - -- VIM >= 7.3 (mostly features needed `+python` or `+python3` support) - (also ``--with-features=big`` if you want ``g:pymode_lint_signs``) - - -How to install -============== - -Using pathogen (recommended) ----------------------------- -:: - - % cd ~/.vim - % mkdir -p bundle && cd bundle - % git clone https://github.com/klen/python-mode.git - -- Enable `pathogen `_ - in your ``~/.vimrc``: :: - - " Pathogen load - filetype off - - call pathogen#infect() - call pathogen#helptags() - - filetype plugin indent on - syntax on - - -Manually --------- -:: - - % git clone https://github.com/klen/python-mode.git - % cd python-mode - % cp -R * ~/.vim - -Then rebuild **helptags** in vim:: - - :helptags ~/.vim/doc/ - - -.. note:: **filetype-plugin** (``:help filetype-plugin-on``) and - **filetype-indent** (``:help filetype-indent-on``) - must be enabled to use python-mode. - - -Debian packages ---------------- -|Repository URL: https://klen.github.io/python-mode/deb/ - -Install with commands: - -:: - - add-apt-repository https://klen.github.io/python-mode/deb main - apt-get update - apt-get install vim-python-mode - -If you are getting the message: "The following signatures couldn't be verified because the public key is not available": :: - - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B5DF65307000E266 - -`vim-python-mode` using `vim-addons`, so after installation just enable -`python-mode` with command: :: - - vim-addons install python-mode - - -Troubleshooting -=============== - -If your python-mode doesn't work: - -1. Load Vim with only python-mode enabled (use `debug.vim` from pymode): :: - - vim -u /debug.vim - -And try to repeat your case. If no error occurs, seems like problem isn't in the -plugin. - -2. Type `:PymodeTroubleshooting` - -And fix any warnings or copy the output and send it to me. (For example, by -creating a `new github issue `_ -if one does not already exist for the problem). - - -Customization -============= - -You can override the default key bindings by redefining them in your `.vimrc`, for example: :: - - " Override go-to.definition key shortcut to Ctrl-] - let g:pymode_rope_goto_definition_bind = "" - - " Override run current python file key shortcut to Ctrl-Shift-e - let g:pymode_run_bind = "" - - " Override view python doc key shortcut to Ctrl-Shift-d - let g:pymode_doc_bind = "" - - -Documentation -============= - -Documentation is available in your vim ``:help pymode`` - - -Bugtracker -=========== - -If you have any suggestions, bug reports or -annoyances please report them to the issue tracker -at https://github.com/klen/python-mode/issues - - -Contributing -============ - -See the `AUTHORS` file. - -Development of python-mode happens at github: -https://github.com/klen/python-mode - -Please make a pull request to `development` branch and add yourself to -`AUTHORS`. - -Source Links -=================== -- `doc/pymode.txt - `__ - -- ``:help pymode`` -- `plugin/pymode.vim - `__ - -- python-mode VIM plugin -- `syntax/python.vim - `__ - -- python-mode ``python.vim`` VIM syntax -- `syntax/pyrex.vim - `__ - -- ``pyrex.vim`` VIM syntax (pyrex, Cython) -- `t/ - `__ - -- ``*.vim`` more python-mode VIM configuration -- `pymode/ - `__ - -- ``*.py`` -- python-mode Python module -- `pymode/libs/ - `__ - -- ``*.py`` -- `Python Libraries <#python-libraries>`__ - - -Python Libraries ------------------- -Vendored Python modules are located -mostly in -`pymode/libs/ `__. - - -====== -rope -====== -| PyPI: https://pypi.python.org/pypi/rope -| Src: https://github.com/python-rope/rope -| Docs: https://github.com/python-rope/rope/blob/master/docs/overview.rst -| Docs: https://github.com/python-rope/rope/blob/master/docs/library.rst - -======================== -ropemode -======================== -| PyPI: https://pypi.python.org/pypi/ropemode -| Src: https://github.com/python-rope/ropemode - -========= -ropevim -========= -| PyPI: https://pypi.python.org/pypi/ropevim -| Src: https://github.com/python-rope/ropevim -| Docs: https://github.com/python-rope/ropevim/blob/master/doc/ropevim.txt - -======= -pylama -======= -| PyPI: https://pypi.python.org/pypi/pylama -| Src: https://github.com/klen/pylama - -======== -pylint -======== -| PyPI: https://pypi.python.org/pypi/pylint -| Src: https://bitbucket.org/logilab/pylint -| Homepage: http://www.pylint.org/ -| Docs: http://docs.pylint.org/ -| Docs: http://docs.pylint.org/message-control.html -| Docs: http://docs.pylint.org/faq.html#message-control -| ErrCodes: http://pylint-messages.wikidot.com/all-codes -| ErrCodes: http://pylint-messages.wikidot.com/all-messages - -========== -pyflakes -========== -| PyPI: https://pypi.python.org/pypi/pyflakes -| Src: https://github.com/pyflakes/pyflakes -| ErrCodes: https://flake8.readthedocs.org/en/latest/warnings.html - -====== -pep8 -====== -| PyPI: https://pypi.python.org/pypi/pep8 -| Src: http://github.com/jcrocholl/pep8 -| PEP 8: http://www.python.org/dev/peps/pep-0008/ -| PEP 8: http://legacy.python.org/dev/peps/pep-0008/ -| Docs: https://pep8.readthedocs.org/en/latest/ -| Docs: https://pep8.readthedocs.org/en/latest/intro.html#configuration -| ErrCodes: https://pep8.readthedocs.org/en/latest/intro.html#error-codes - -========= -autopep8 -========= -| PyPI: https://pypi.python.org/pypi/autopep8 -| Src: https://github.com/hhatto/autopep8 - -======= -pep257 -======= -| PyPI: https://pypi.python.org/pypi/pep257 -| Src: http://github.com/GreenSteam/pep257 -| Docs: https://pep257.readthedocs.org/en/latest/ -| PEP 257: http://www.python.org/dev/peps/pep-0257/ -| ErrCodes: https://pep257.readthedocs.org/en/latest/error_codes.html - -======= -mccabe -======= -| PyPI: https://pypi.python.org/pypi/mccabe -| Src: https://github.com/flintwork/mccabe -| Docs: https://en.wikipedia.org/wiki/Cyclomatic_complexity - - -Vim Libraries ---------------- -Vendored Vim modules are located mostly in ``t/``. - -====================== -Python syntax for vim -====================== -| Src: http://www.hlabs.spb.ru/vim/python.vim - - -===================== -PEP8 VIM indentation -===================== -| Src: http://github.com/hynek/vim-python-pep8-indent - - - -Copyright -========= - -Copyright © 2013-2015 Kirill Klenov (klen_) - -License -======= - -Licensed under a `GNU lesser general public license`_. - -If you like this plugin, I would very appreciated if you kindly send me a postcard :) -My address is here: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill Klenov". -**Thanks for support!** - -.. _GNU lesser general public license: http://www.gnu.org/copyleft/lesser.html -.. _klen: https://klen.github.com/ -.. _pydoc: http://docs.python.org/library/pydoc.html -.. _pathogen: https://github.com/tpope/vim-pathogen -.. _rope_: https://pypi.python.org/pypi/rope -.. _pylama_: https://github.com/klen/pylama -.. _pylint_: https://bitbucket.org/logilab/pylint -.. _pyflakes_: https://pypi.python.org/pypi/pyflakes -.. _autopep8_: https://github.com/hhatto/autopep8 -.. _pep257_: http://github.com/GreenSteam/pep257 -.. _mccabe_: https://github.com/flintwork/mccabe -.. _pythonvim: http://www.hlabs.spb.ru/vim/python.vim -.. _pep8_: http://github.com/jcrocholl/pep8 -.. _pep8indent: http://github.com/hynek/vim-python-pep8-indent -.. |logo| image:: https://raw.github.com/klen/python-mode/develop/logo.png diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 63a3a361..00000000 --- a/Rakefile +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env rake - -task :ci => [:dump, :test] - -task :dump do - sh 'vim --version' -end - -task :test do - sh 'bundle exec vim-flavor test' -end diff --git a/after/ftplugin/python.vim b/after/ftplugin/python.vim index 0dec7542..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 @@ -53,6 +55,6 @@ if g:pymode_rope && g:pymode_rope_completion if tolower(g:pymode_rope_completion_bind) == '' exe "inoremap =pymode#rope#complete(0)" endif - end + endif -end +endif diff --git a/autoload/pymode.vim b/autoload/pymode.vim index 723af9b5..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 "}}} @@ -71,12 +71,11 @@ endfunction "}}} fun! pymode#trim_whitespaces() "{{{ if g:pymode_trim_whitespaces let cursor_pos = getpos('.') - silent! %s/\s\+$// + silent! %s/\s\+$//e call setpos('.', cursor_pos) endif endfunction "}}} - fun! pymode#save() "{{{ if &modifiable && &modified try @@ -120,9 +119,25 @@ fun! pymode#buffer_post_write() "{{{ endfunction "}}} fun! pymode#debug(msg) "{{{ + " Pymode's debug function. + " Should be called by other pymode's functions to report outputs. See + " the function PymodeDebugFolding for example. + " TODO: why echom here creates a problem? + " echom '' . a:msg + '|||||||||||' + + let l:info_separator = repeat('-', 79) + if g:pymode_debug - let g:pymode_debug += 1 - echom string(g:pymode_debug) . ': ' . string(a:msg) + if ! exists('g:pymode_debug_counter') + let g:pymode_debug_counter = 0 + endif + let g:pymode_debug_counter += 1 + " NOTE: Print a separator for every message except folding ones (since + " they could be many). + if a:msg !~ 'has folding:' + echom l:info_separator + endif + echom '' . 'pymode debug msg ' . g:pymode_debug_counter . ': ' . a:msg endif endfunction "}}} diff --git a/autoload/pymode/breakpoint.vim b/autoload/pymode/breakpoint.vim index cf7b95be..98639b57 100644 --- a/autoload/pymode/breakpoint.vim +++ b/autoload/pymode/breakpoint.vim @@ -1,41 +1,46 @@ fun! pymode#breakpoint#init() "{{{ - if !g:pymode_breakpoint + " If breakpoints are either disabled or already defined do nothing. + if ! g:pymode_breakpoint || g:pymode_breakpoint_cmd != '' return - endif - - if g:pymode_breakpoint_cmd == '' - let g:pymode_breakpoint_cmd = 'import pdb; pdb.set_trace() # XXX BREAKPOINT' - if g:pymode_python == 'disable' - return - endif - - endif + " Else go for a 'smart scan' of the defaults. + else PymodePython << EOF -from imp import find_module +from importlib.util import find_spec -for module in ('wdb', 'pudb', 'ipdb'): - 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 endfunction "}}} fun! pymode#breakpoint#operate(lnum) "{{{ + if g:pymode_breakpoint_cmd == '' + echoerr("g:pymode_breakpoint_cmd is empty") + return -1 + endif let line = getline(a:lnum) if strridx(line, g:pymode_breakpoint_cmd) != -1 normal dd else let plnum = prevnonblank(a:lnum) - call append(line('.')-1, repeat(' ', indent(plnum)).g:pymode_breakpoint_cmd) + if &expandtab + let indents = repeat(' ', indent(plnum)) + else + let indents = repeat("\t", plnum / &shiftwidth) + endif + + call append(line('.')-1, indents.g:pymode_breakpoint_cmd) normal k endif diff --git a/autoload/pymode/debug.vim b/autoload/pymode/debug.vim new file mode 100644 index 00000000..2be5149c --- /dev/null +++ b/autoload/pymode/debug.vim @@ -0,0 +1,67 @@ +" Set debugging functions. + +" DESC: Get debug information about pymode problem. +fun! pymode#debug#sysinfo() "{{{ + " OS info. {{{ + let l:os_name = "Unknown" + if has('win16') || has('win32') || has('win64') + let l:os_name = "Windows" + else + let l:os_name = substitute(system('uname'), "\n", "", "") + endif + call pymode#debug("Operating system: " . l:os_name) + " }}} + " Loaded scripts info. {{{ + call pymode#debug("Scriptnames:") + let l:scriptnames_var = execute('scriptnames') + " }}} + " Variables info. {{{ + " Drop verbose file temporarily to prevent the 'let' from showing up. + let l:tmp = &verbosefile + set verbosefile= + let l:all_variables = filter( + \ split(execute('let', 'silent!'), '\n'), + \ 'v:val =~ "^pymode"') + let &verbosefile = l:tmp + " NOTE: echom does not display multiline messages. Thus a for loop is + " needed. + call pymode#debug("Pymode variables:") + for pymodevar in sort(l:all_variables) + echom pymodevar + endfor + " }}} + " 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: ") + let l:pymode_folder = substitute( + \ filter( + \ split(l:scriptnames_var, '\n'), + \ 'v:val =~ "/python-mode/"')[0], + \ '\(^\s\+[0-9]\+:\s\+\)\([/~].*python-mode\/\)\(.*\)', + \ '\2', + \ '') + 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 "}}} + +" DESC: Define debug folding function. +function! pymode#debug#foldingexpr(lnum) "{{{ + let l:get_folding_result = pymode#folding#foldcase(a:lnum) + " NOTE: the 'has folding:' expression is special in the pymode#debug. + call pymode#debug( + \ 'line ' . a:lnum + \ . ' has folding: ' . l:get_folding_result['foldlevel'] + \ . ' with foldcase ' . l:get_folding_result['foldcase']) + return l:get_folding_result['foldlevel'] +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/folding.vim b/autoload/pymode/folding.vim index 3ed61bc5..9cbb64d3 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -1,24 +1,30 @@ -" Python-mode folding functions - +" Notice that folding is based on single line so complex regular expressions +" that take previous line into consideration are not fit for the job. +" Regex definitions for correct folding let s:def_regex = g:pymode_folding_regex let s:blank_regex = '^\s*$' -let s:decorator_regex = '^\s*@' -let s:doc_begin_regex = '^\s*\%("""\|''''''\)' -let s:doc_end_regex = '\%("""\|''''''\)\s*$' -let s:doc_line_regex = '^\s*\("""\|''''''\).\+\1\s*$' +" Spyder, a very popular IDE for python has a template which includes +" '@author:' ; thus the regex below. +let s:decorator_regex = '^\s*@\(author:\)\@!' +let s:docstring_line_regex = '^\s*[uUrR]\=\("""\|''''''\).\+\1\s*$' +let s:docstring_begin_regex = '^\s*[uUrR]\=\%("""\|''''''\).*\S' +let s:docstring_end_regex = '\%("""\|''''''\)\s*$' +" This one is needed for the while loop to count for opening and closing +" docstrings. +let s:docstring_general_regex = '\%("""\|''''''\)' let s:symbol = matchstr(&fillchars, 'fold:\zs.') " handles multibyte characters if s:symbol == '' let s:symbol = ' ' endif - +" '''''''' fun! pymode#folding#text() " {{{ let fs = v:foldstart - while getline(fs) !~ s:def_regex && getline(fs) !~ s:doc_begin_regex + while getline(fs) !~ s:def_regex && getline(fs) !~ s:docstring_begin_regex let fs = nextnonblank(fs + 1) endwhile - if getline(fs) =~ s:doc_end_regex && getline(fs) =~ s:doc_begin_regex + if getline(fs) =~ s:docstring_end_regex && getline(fs) =~ s:docstring_begin_regex let fs = nextnonblank(fs + 1) endif let line = getline(fs) @@ -33,27 +39,53 @@ fun! pymode#folding#text() " {{{ let line = substitute(line, '\t', onetab, 'g') let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount)) - let line = substitute(line, '\%("""\|''''''\)', '', '') + let line = substitute(line, '[uUrR]\=\%("""\|''''''\)', '', '') let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + 1 return line . ' ' . repeat(s:symbol, fillcharcount) . ' ' . foldedlinecount endfunction "}}} - fun! pymode#folding#expr(lnum) "{{{ + let l:return_value = pymode#folding#foldcase(a:lnum)['foldlevel'] + + return l:return_value + +endfunction "}}} + +fun! pymode#folding#foldcase(lnum) "{{{ + " Return a dictionary with a brief description of the foldcase and the + " evaluated foldlevel: {'foldcase': 'case description', 'foldlevel': 1}. + + let l:foldcase = 'general' + let l:foldlevel = 0 + let line = getline(a:lnum) let indent = indent(a:lnum) let prev_line = getline(a:lnum - 1) + let next_line = getline(a:lnum + 1) + " Decorators {{{ if line =~ s:decorator_regex - return ">".(indent / &shiftwidth + 1) - endif + let l:foldcase = 'decorator declaration' + let l:foldlevel = '>'.(indent / &shiftwidth + 1) + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + endif "}}} + " Definition {{{ if line =~ s:def_regex - " single line def - if indent(a:lnum) >= indent(a:lnum+1) && getline(prevnonblank(a:lnum)) !~ ':\s*$' - return '=' - endif + + " TODO: obscure case. + " If indent of this line is greater or equal than line below + " and previous non blank line does not end with : (that is, is not a + " definition) + " Keep the same indentation + " xxx " if indent(a:lnum) >= indent(a:lnum+1) + " xxx " \ && getline(prevnonblank(a:lnum)) !~ ':\s*$' + " xxx " let l:foldcase = 'definition' + " xxx " let l:foldlevel = '=' + " xxx " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " xxx " endif + " Check if last decorator is before the last def let decorated = 0 let lnum = a:lnum - 1 @@ -67,90 +99,143 @@ fun! pymode#folding#expr(lnum) "{{{ let lnum -= 1 endwhile if decorated - return '=' + let l:foldcase = 'decorated function declaration' + let l:foldlevel = '=' else - return ">".(indent / &shiftwidth + 1) + let l:foldcase = 'function declaration' + let l:foldlevel = '>'.(indent / &shiftwidth + 1) endif - endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + endif "}}} - if line =~ s:doc_begin_regex && line !~ s:doc_line_regex && prev_line =~ s:def_regex - return ">".(indent / &shiftwidth + 1) - endif + " Docstrings {{{ - if line =~ s:doc_end_regex && line !~ s:doc_line_regex - return "<".(indent / &shiftwidth + 1) + " TODO: A while loop now counts the number of open and closed folding in + " order to determine if it is a closing or opening folding. + " It is working but looks like it is an overkill. + + " Notice that an effect of this is that other docstring matches will not + " be one liners. + if line =~ s:docstring_line_regex + let l:foldcase = 'one-liner docstring' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif + if line =~ s:docstring_begin_regex + if s:Is_opening_folding(a:lnum) + let l:foldcase = 'open multiline docstring' + let l:foldlevel = 'a1' + endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + endif + if line =~ s:docstring_end_regex + if !s:Is_opening_folding(a:lnum) + let l:foldcase = 'close multiline docstring' + let l:foldlevel = 's1' + endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + endif "}}} - " Handle nested defs but only for files shorter than - " g:pymode_folding_nest_limit lines due to performance concerns - if line('$') < g:pymode_folding_nest_limit && indent(prevnonblank(a:lnum)) - let curpos = getpos('.') - try - let last_block = s:BlockStart(a:lnum) - let last_block_indent = indent(last_block) - - " Check if last class/def is not indented and therefore can't be - " nested. - if last_block_indent - call cursor(a:lnum, 0) - let next_def = searchpos(s:def_regex, 'nW')[0] - let next_def_indent = next_def ? indent(next_def) : -1 - let last_block_end = s:BlockEnd(last_block) - - " If the next def has greater indent than the previous def, it - " is nested one level deeper and will have its own fold. If - " the class/def containing the current line is on the first - " line it can't be nested, and if this block ends on the last - " line, it contains no trailing code that should not be - " folded. Finally, if the next non-blank line after the end of - " the previous def is less indented than the previous def, it - " is not part of the same fold as that def. Otherwise, we know - " the current line is at the end of a nested def. - if next_def_indent <= last_block_indent && last_block > 1 && last_block_end < line('$') - \ && indent(nextnonblank(last_block_end)) >= last_block_indent - - " Include up to one blank line in the fold - if getline(last_block_end) =~ s:blank_regex - let fold_end = min([prevnonblank(last_block_end - 1), last_block_end]) + 1 - else - let fold_end = last_block_end - endif - if a:lnum == fold_end - return 's1' - else - return '=' - endif - endif + " Blocks. {{{ + let s:save_cursor = getcurpos() + let line_block_start = s:BlockStart(a:lnum) + let line_block_end = s:BlockEnd(a:lnum) + let prev_line_block_start = s:BlockStart(a:lnum - 1) + if line !~ s:blank_regex + if line_block_start == prev_line_block_start + \ || a:lnum - line_block_start == 1 + let l:foldcase = 'non blank line; first line of block or part of it' + let l:foldlevel = '=' + elseif indent < indent(prevnonblank(a:lnum - 1)) + if indent == 0 + let l:foldcase = 'non blank line; zero indent' + let l:foldlevel = 0 + else + let l:foldcase = 'non blank line; non zero indent' + let l:foldlevel = indent(line_block_start) / &shiftwidth + 1 endif - finally - call setpos('.', curpos) - endtry + endif + call setpos('.', s:save_cursor) + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + else + call setpos('.', s:save_cursor) endif + " endif " }}} + " Blank Line {{{ + " Comments: cases of blank lines: + " 1. After non blank line: gets folded with previous line. + " 1. Just after a block; in this case it gets folded with the block. + " 1. Between docstrings and imports. + " 1. Inside docstrings. + " 2. Inside functions/methods. + " 3. Between functions/methods. if line =~ s:blank_regex - if prev_line =~ s:blank_regex - if indent(a:lnum + 1) == 0 && getline(a:lnum + 1) !~ s:blank_regex - return 0 - endif - return -1 - else - return '=' + if prev_line !~ s:blank_regex + let l:foldcase = 'blank line after non blank line' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + elseif a:lnum > line_block_start && a:lnum < line_block_end + let l:foldcase = 'blank line inside block' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif - endif + " if prev_line =~ s:blank_regex + " if indent(a:lnum + 1) == 0 && next_line !~ s:blank_regex && next_line !~ s:docstring_general_regex + " if s:Is_opening_folding(a:lnum) + " let l:foldcase = 'case 1' + " let l:foldlevel = '=' + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " else + " let l:foldcase = 'case 2' + " let l:foldlevel = 0 + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " endif + " endif + " let l:foldcase = 'case 3' + " let l:foldlevel = -1 + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " else + " let l:foldcase = 'case 4' + " let l:foldlevel = '=' + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " endif + endif " }}} - return '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endfunction "}}} fun! s:BlockStart(lnum) "{{{ + " Returns the definition statement line which encloses the current line. + + let line = getline(a:lnum) + if line !~ s:blank_regex + let l:inferred_indent = indent(a:lnum) + else + let l:inferred_indent = prevnonblank(a:lnum) + endif + " Note: Make sure to reset cursor position after using this function. call cursor(a:lnum, 0) " In case the end of the block is indented to a higher level than the def " statement plus one shiftwidth, we need to find the indent level at the " bottom of that if/for/try/while/etc. block. - let last_def = searchpos(s:def_regex, 'bcnW')[0] + " Flags from searchpos() (same as search()): + " b: search Backward instead of forward + " n: do Not move the cursor + " W: don't Wrap around the end of the file + let previous_definition = searchpos(s:def_regex, 'bnW') + + while previous_definition[0] != 1 && previous_definition != [0, 0] + \ && indent(previous_definition[0]) >= l:inferred_indent + let previous_definition = searchpos(s:def_regex, 'bnW') + call cursor(previous_definition[0] - 1, 0) + endwhile + let last_def = previous_definition[0] if last_def + call cursor(last_def, 0) let last_def_indent = indent(last_def) call cursor(last_def, 0) let next_stmt_at_def_indent = searchpos('\v^\s{'.last_def_indent.'}[^[:space:]#]', 'nW')[0] @@ -160,18 +245,84 @@ fun! s:BlockStart(lnum) "{{{ " Now find the class/def one shiftwidth lower than the start of the " aforementioned indent block. - if next_stmt_at_def_indent && next_stmt_at_def_indent < a:lnum + if next_stmt_at_def_indent && (next_stmt_at_def_indent < a:lnum) let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0]) else let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0]) endif - return searchpos('\v^\s{,'.max_indent.'}(def |class )\w', 'bcnW')[0] + + let result = searchpos('\v^\s{,'.max_indent.'}(def |class )\w', 'bcnW')[0] + + return result + endfunction "}}} +function! Blockstart(x) + let save_cursor = getcurpos() + return s:BlockStart(a:x) + call setpos('.', save_cursor) +endfunction fun! s:BlockEnd(lnum) "{{{ " Note: Make sure to reset cursor position after using this function. call cursor(a:lnum, 0) return searchpos('\v^\s{,'.indent('.').'}\S', 'nW')[0] - 1 endfunction "}}} +function! Blockend(lnum) + let save_cursor = getcurpos() + return s:BlockEnd(a:lnum) + call setpos('.', save_cursor) +endfunction + +function! s:Is_opening_folding(lnum) "{{{ + " Helper function to see if multi line docstring is opening or closing. + + " Cache the result so the loop runs only once per change. + if get(b:, 'fold_changenr', -1) == changenr() + return b:fold_cache[a:lnum - 1] "If odd then it is an opening + else + let b:fold_changenr = changenr() + let b:fold_cache = [] + endif + + " To be analized if odd/even to inform if it is opening or closing. + let fold_odd_even = 0 + " To inform is already has an open docstring. + let has_open_docstring = 0 + " To help skipping ''' and """ which are not docstrings. + let extra_docstrings = 0 + + " The idea of this part of the function is to identify real docstrings and + " not just triple quotes (that could be a regular string). + + " Iterater over all lines from the start until current line (inclusive) + for i in range(1, line('$')) + + let i_line = getline(i) + + if i_line =~ s:docstring_begin_regex && ! has_open_docstring + " This causes the loop to continue if there is a triple quote which + " is not a docstring. + if extra_docstrings > 0 + let extra_docstrings = extra_docstrings - 1 + else + let has_open_docstring = 1 + let fold_odd_even = fold_odd_even + 1 + endif + " If it is an end doc and has an open docstring. + elseif i_line =~ s:docstring_end_regex && has_open_docstring + let has_open_docstring = 0 + let fold_odd_even = fold_odd_even + 1 + + elseif i_line =~ s:docstring_general_regex + let extra_docstrings = extra_docstrings + 1 + endif + + call add(b:fold_cache, fold_odd_even % 2) + + endfor + + return b:fold_cache[a:lnum] + +endfunction "}}} " vim: fdm=marker:fdl=0 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/lint.vim b/autoload/pymode/lint.vim index e7dba8b5..29dd6168 100644 --- a/autoload/pymode/lint.vim +++ b/autoload/pymode/lint.vim @@ -5,7 +5,7 @@ call pymode#tools#loclist#init() fun! pymode#lint#auto() "{{{ - if !pymode#save() + if ! pymode#save() return 0 endif PymodePython from pymode import auto @@ -42,7 +42,7 @@ fun! pymode#lint#toggle() "{{{ call pymode#wide_message("Code checking is enabled.") else call pymode#wide_message("Code checking is disabled.") - end + endif endfunction "}}} @@ -61,6 +61,9 @@ fun! pymode#lint#check() "{{{ if loclist.is_empty() call pymode#wide_message('Code checking is completed. No errors found.') + call g:PymodeSigns.refresh(loclist) + call loclist.show() + return endif call g:PymodeSigns.refresh(loclist) @@ -68,7 +71,7 @@ fun! pymode#lint#check() "{{{ call loclist.show() call pymode#lint#show_errormessage() - call pymode#wide_message('Found errors and warnings: ' . len(loclist._loclist)) + call pymode#wide_message('Found ' . loclist.num_errors() . ' error(s) and ' . loclist.num_warnings() . ' warning(s)') endfunction " }}} diff --git a/autoload/pymode/motion.vim b/autoload/pymode/motion.vim index 67e99e6b..c88fb913 100644 --- a/autoload/pymode/motion.vim +++ b/autoload/pymode/motion.vim @@ -21,64 +21,72 @@ fun! pymode#motion#vmove(pattern, flags) range "{{{ call cursor(a:firstline, 0) normal! v call cursor(end) -endfunction "}}} +endfunction "}}} 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 + " Skip comments, deeper or equal lines + if line =~ '^\s*#' || test >= indent continue - elseif !test " Zero-level regular line - return lnum - elseif test >= indent " Skip deeper or equal lines - 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 a82a46d9..36344d0a 100644 --- a/autoload/pymode/rope.vim +++ b/autoload/pymode/rope.vim @@ -1,5 +1,9 @@ " Python-mode Rope support -" + +if ! g:pymode_rope + finish +endif + PymodePython from pymode import rope call pymode#tools#loclist#init() @@ -11,24 +15,28 @@ endfunction fun! pymode#rope#complete(dot) if pumvisible() - return "\" - end + if stridx('noselect', &completeopt) != -1 + return "\" + else + return "" + endif + endif if a:dot PymodePython rope.complete(True) else PymodePython rope.complete() - end - return pumvisible() ? "\\" : "" + endif + return pumvisible() && stridx('noselect', &completeopt) != -1 ? "\\" : "" endfunction fun! pymode#rope#complete_on_dot() "{{{ if !exists("*synstack") return "" - end + endif for group in map(synstack(line('.'), col('.') - 1), 'synIDattr(v:val, "name")') for name in ['pythonString', 'pythonComment', 'pythonNumber', 'pythonDocstring'] if group == name - return "" + return "" endif endfor endfor @@ -72,8 +80,11 @@ fun! pymode#rope#show_doc() setlocal nomodifiable setlocal nomodified setlocal filetype=rst + + normal gg + wincmd p - end + endif endfunction @@ -183,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/autoload/pymode/run.vim b/autoload/pymode/run.vim index 24c8729c..356409b6 100644 --- a/autoload/pymode/run.vim +++ b/autoload/pymode/run.vim @@ -74,8 +74,8 @@ fun! pymode#run#code_run(line1, line2) "{{{ cgetexpr(l:traceback) - " If a range is run (starting other than at line 1), fix the reported error line numbers for - " the current buffer + " If a range is run (starting other than at line 1), fix the reported + " error line numbers for the current buffer if a:line1 > 1 let qflist = getqflist() for i in qflist diff --git a/autoload/pymode/tools/loclist.vim b/autoload/pymode/tools/loclist.vim index 18b6d294..b9121bdf 100644 --- a/autoload/pymode/tools/loclist.vim +++ b/autoload/pymode/tools/loclist.vim @@ -24,19 +24,37 @@ endfunction "}}} fun! g:PymodeLocList.is_empty() "{{{ - return empty(self._loclist) + return empty(self._errlist) && empty(self._warnlist) +endfunction "}}} + +fun! g:PymodeLocList.loclist() "{{{ + let loclist = copy(self._errlist) + call extend(loclist, self._warnlist) + return loclist +endfunction "}}} + +fun! g:PymodeLocList.num_errors() "{{{ + return len(self._errlist) +endfunction "}}} + +fun! g:PymodeLocList.num_warnings() "{{{ + return len(self._warnlist) endfunction "}}} fun! g:PymodeLocList.clear() "{{{ - let self._loclist = [] + let self._errlist = [] + let self._warnlist = [] let self._messages = {} let self._name = expand('%:t') endfunction "}}} fun! g:PymodeLocList.extend(raw_list) "{{{ - call extend(self._loclist, a:raw_list) + let err_list = filter(copy(a:raw_list), 'v:val["type"] == "E"') + let warn_list = filter(copy(a:raw_list), 'v:val["type"] != "E"') + call extend(self._errlist, err_list) + call extend(self._warnlist, warn_list) for issue in a:raw_list let self._messages[issue.lnum] = issue.text endfor @@ -46,7 +64,7 @@ endfunction "}}} fun! g:PymodeLocList.filter(filters) "{{{ let loclist = [] - for error in self._loclist + for error in self.loclist() let passes_filters = 1 for key in keys(a:filters) if get(error, key, '') !=? a:filters[key] @@ -65,7 +83,7 @@ endfunction "}}} fun! g:PymodeLocList.show() "{{{ - call setloclist(0, self._loclist) + call setloclist(0, self.loclist()) if self.is_empty() lclose elseif g:pymode_lint_cwindow diff --git a/autoload/pymode/tools/signs.vim b/autoload/pymode/tools/signs.vim index 3487cf85..579573ed 100644 --- a/autoload/pymode/tools/signs.vim +++ b/autoload/pymode/tools/signs.vim @@ -46,7 +46,7 @@ endfunction "}}} fun! g:PymodeSigns.place(loclist) "{{{ let seen = {} - for issue in a:loclist._loclist + for issue in a:loclist.loclist() if !has_key(seen, issue.lnum) let seen[issue.lnum] = 1 call add(self._sign_ids, self._next_id) diff --git a/autoload/pymode/troubleshooting.vim b/autoload/pymode/troubleshooting.vim deleted file mode 100644 index 915a5c5e..00000000 --- a/autoload/pymode/troubleshooting.vim +++ /dev/null @@ -1,89 +0,0 @@ -" DESC: Get debug information about pymode problem -fun! pymode#troubleshooting#test() "{{{ - new - setlocal buftype=nofile bufhidden=delete noswapfile nowrap - - let os = "Unknown" - if has('win16') || has('win32') || has('win64') - let os = "Windows" - else - let os = substitute(system('uname'), "\n", "", "") - endif - - if !pymode#default('g:pymode_init', 1) - call pymode#init(expand(':p:h'), g:pymode_paths) - call pymode#virtualenv#init() - call pymode#breakpoint#init() - endif - - call append('0', ['Pymode diagnostic', - \ '===================', - \ 'VIM:' . v:version . ', OS: ' . os .', multi_byte:' . has('multi_byte') . ', pymode: ' . g:pymode_version . ', pymode-python: ' . g:pymode_python, - \ '']) - - if !exists('#filetypeplugin') - call append('$', ['WARNING: ', 'Python-mode required :filetype plugin indent on', '']) - endif - - call append('$', ['+python: ' . has('python')]) - call append('$', ['+python3: ' . has('python3'), '']) - - if g:pymode_python == 'disable' - - if !has('python') && !has('python3') - - call append('$', ['WARNING: Python-mode required vim compiled with +python or +python3.', - \ '"lint, rope, run, doc, virtualenv" features disabled.', '']) - - else - - call append('$', ['WARNING: Python is disabled by `pymode_python` option.', - \ '"lint, rope, run, doc, virtualenv" features disabled.', '']) - - endif - - else - - call append('$', 'VIM python paths:') - call append('$', '-----------------') - PymodePython << EOF -import vim -vim.command('let l:output = %s' % repr(sys.path)) -EOF - call append('$', output) - call append('$', '') - - endif - - call append('$', 'Pymode variables:') - call append('$', '-------------------') - call append('$', 'let pymode = ' . string(g:pymode)) - call append('$', 'let pymode_breakpoint = ' . string(g:pymode_breakpoint)) - call append('$', 'let pymode_breakpoint_bind = ' . string(g:pymode_breakpoint_bind)) - call append('$', 'let pymode_doc = ' . string(g:pymode_doc)) - call append('$', 'let pymode_doc_bind = ' . string(g:pymode_doc_bind)) - call append('$', 'let pymode_folding = ' . string(g:pymode_folding)) - call append('$', 'let pymode_indent = ' . string(g:pymode_indent)) - call append('$', 'let pymode_lint = ' . string(g:pymode_lint)) - call append('$', 'let pymode_lint_checkers = ' . string(g:pymode_lint_checkers)) - call append('$', 'let pymode_lint_cwindow = ' . string(g:pymode_lint_cwindow)) - call append('$', 'let pymode_lint_ignore = ' . string(g:pymode_lint_ignore)) - call append('$', 'let pymode_lint_message = ' . string(g:pymode_lint_message)) - call append('$', 'let pymode_lint_on_fly = ' . string(g:pymode_lint_on_fly)) - call append('$', 'let pymode_lint_on_write = ' . string(g:pymode_lint_on_write)) - call append('$', 'let pymode_lint_select = ' . string(g:pymode_lint_select)) - call append('$', 'let pymode_lint_signs = ' . string(g:pymode_lint_signs)) - call append('$', 'let pymode_motion = ' . string(g:pymode_motion)) - call append('$', 'let pymode_options = ' . string(g:pymode_options)) - call append('$', 'let pymode_paths = ' . string(g:pymode_paths)) - call append('$', 'let pymode_quickfix_maxheight = ' . string(g:pymode_quickfix_maxheight)) - call append('$', 'let pymode_quickfix_minheight = ' . string(g:pymode_quickfix_minheight)) - call append('$', 'let pymode_rope = ' . string(g:pymode_rope)) - call append('$', 'let pymode_run = ' . string(g:pymode_run)) - call append('$', 'let pymode_run_bind = ' . string(g:pymode_run_bind)) - call append('$', 'let pymode_trim_whitespaces = ' . string(g:pymode_trim_whitespaces)) - call append('$', 'let pymode_virtualenv = ' . string(g:pymode_virtualenv)) - call append('$', 'let pymode_virtualenv_enabled = ' . string(g:pymode_virtualenv_enabled)) - call append('$', 'let pymode_virtualenv_path = ' . string(g:pymode_virtualenv_path)) - -endfunction "}}} diff --git a/autoload/pymode/virtualenv.vim b/autoload/pymode/virtualenv.vim index e8207b04..7401e94b 100644 --- a/autoload/pymode/virtualenv.vim +++ b/autoload/pymode/virtualenv.vim @@ -11,7 +11,7 @@ fun! pymode#virtualenv#init() "{{{ endfunction "}}} -fun! pymode#virtualenv#activate(relpath) "{{{ - let g:pymode_virtualenv_path = getcwd() . '/' . a:relpath +fun! pymode#virtualenv#activate(path) "{{{ + let g:pymode_virtualenv_path = a:path call pymode#virtualenv#init() endfunction "}}} diff --git a/debug.vim b/debug.vim deleted file mode 100644 index c7d32661..00000000 --- a/debug.vim +++ /dev/null @@ -1,13 +0,0 @@ -" Use this settings for testing the plugin. -" Run vim with command -" -" $ vim -u debug.py -" -" Only python-mode will be loaded. - - -execute('set rtp+='. expand(':p:h')) -set rtp -=$HOME/.vim -set rtp -=$HOME/.vim/after -set nocp -syntax enable diff --git a/debugvimrc.vim b/debugvimrc.vim new file mode 100644 index 00000000..a8b3f188 --- /dev/null +++ b/debugvimrc.vim @@ -0,0 +1,32 @@ +" Use this settings for testing the plugin. +" +" Run vim with command: +" +" $ vim -u ./debug.vim /my/py/file.py +" +" Only python-mode will be loaded. + +" Disable all persistence between sessions. +let skip_defaults_vim=1 +" TODO XXX: this nevertheless keeps viminfo enabled. As a workaround the flag +" '-i NONE' should be added to vim's loading. +set viminfo= +set nobackup +set noswapfile + +" Modify vimrc configuration. +execute('set rtp+='. expand(':p:h')) +set rtp -=$HOME/.vim +set rtp -=$HOME/.vim/after +set nocompatible + +" Activate debugging. +let g:pymode_debug = 1 + +" Define a common shell for non Windows systems. +if ! (has('win16') || has('win32') || has('win64')) + set shell=/bin/bash +endif + +" IMPORTANT: Do note that the history of this session is saved on the log file. +" See the augroup in ./ftplugin/python/pymode.vim file. diff --git a/doc/pymode.txt b/doc/pymode.txt index e12d953b..7235b5d5 100644 --- a/doc/pymode.txt +++ b/doc/pymode.txt @@ -1,4 +1,4 @@ -*pymode.txt* *python-mode.txt* *pymode* *python-mode* +*pymode.txt* For Vim Version 8.0 Last change: 2019 March 08 ____ _ _ ____ _ _ _____ _ _ __ __ _____ ____ ____ ~ ( _ \( \/ )(_ _)( )_( )( _ )( \( )___( \/ )( _ )( _ \( ___) ~ @@ -6,36 +6,42 @@ (__) (__) (__) (_) (_)(_____)(_)\_) (_/\/\_)(_____)(____/(____) ~ - Version: 0.8.1 - -============================================================================== -CONTENTS *pymode-contents* - - 1.Intro.......................................................|pymode-intro| - 2.Common functionality.......................................|pymode-common| - 2.1 Python version...............................|pymode-python-version| - 2.2 Python indentation...................................|pymode-indent| - 2.3 Python folding......................................|pymode-folding| - 2.4 Vim motion...........................................|pymode-motion| - 2.5 Show documentation............................|pymode-documentation| - 2.6 Support virtualenv...............................|pymode-virtualenv| - 2.7 Run code................................................|pymode-run| - 2.8 Breakpoints.....................................|pymode-breakpoints| - 3. Code checking...............................................|pymode-lint| - 3.1 Code checkers options..........................|pymode-lint-options| - 4. Rope support................................................|pymode-rope| - 4.1 Code completion..................................|pymode-completion| - 4.2 Find definition.................................|pymode-rope-findit| - 4.3 Refactoring................................|pymode-rope-refactoring| - 4.4 Undo/Redo changes.................................|pymode-rope-undo| - 5. Syntax....................................................|pymode-syntax| - 6.FAQ...........................................................|pymode-faq| - 7.Credits...................................................|pymode-credits| - 8.License...................................................|pymode-license| - -============================================================================== + Version: 0.14.0 + +=============================================================================== +CONTENTS *pymode-contents* + +1. Intro...........................................................|pymode-intro| +2. Common functionality...........................................|pymode-common| + 2.1 Python version....................................|pymode-python-version| + 2.2 Python indentation........................................|pymode-indent| + 2.3 Python folding...........................................|pymode-folding| + 2.4 Vim motion................................................|pymode-motion| + 2.5 Show documentation.................................|pymode-documentation| + 2.6 Support virtualenv....................................|pymode-virtualenv| + 2.7 Run code.....................................................|pymode-run| + 2.8 Breakpoints..........................................|pymode-breakpoints| +3. Code checking....................................................|pymode-lint| + 3.1 Code checkers options...............................|pymode-lint-options| +4. Rope support.....................................................|pymode-rope| + 4.1 Code completion.......................................|pymode-completion| + 4.2 Find definition......................................|pymode-rope-findit| + 4.3 Refactoring.....................................|pymode-rope-refactoring| + 4.4 Undo/Redo changes......................................|pymode-rope-undo| +5. Syntax.........................................................|pymode-syntax| +6. FAQ...............................................................|pymode-faq| +7. Development...............................................|pymode-development| +8. Credits.......................................................|pymode-credits| +9. License.......................................................|pymode-license| + +=============================================================================== 1. Intro ~ - *pymode-intro* + *pymode-intro* + +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. 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, @@ -46,7 +52,7 @@ need to install the pylint or rope libraries on your system. Python-mode contains all you need to develop python applications in Vim. -Features: *pymode-features* +Features: *pymode-features* - Support Python version 2.6+ and 3.2+ - Syntax highlighting @@ -67,34 +73,34 @@ Features: *pymode-features* - And more, more ... -============================================================================== +=============================================================================== 2. Common functionality ~ - *pymode-common* + *pymode-common* This script provides the following options that can customizes the behavior of -PythonMode. These options should be set in your |vimrc|. +python-mode. These options should be set in your |vimrc|. - Below shows the default values. +Find below the default values: -Turn on the whole plugin *'g:pymode'* +Turn on the whole plugin. *'g:pymode'* > let g:pymode = 1 -Turn off plugin's warnings *'g:pymode_warnings'* +Turn off plugin's warnings. *'g:pymode_warnings'* > let g:pymode_warnings = 1 -Add paths to `sys.path` *'g:pymode_paths'* -Value is list of path's strings. +Add paths to `sys.path` *'g:pymode_paths'* +Value is list of path's strings. > let g:pymode_paths = [] -Trim unused white spaces on save *'g:pymode_trim_whitespaces'* +Trim unused white spaces on save. *'g:pymode_trim_whitespaces'* > let g:pymode_trim_whitespaces = 1 -Setup default python options *'g:pymode_options'* +Setup default python options. *'g:pymode_options'* > let g:pymode_options = 1 @@ -111,141 +117,166 @@ python buffers: > setlocal commentstring=#%s setlocal define=^\s*\\(def\\\\|class\\) -Setup max line length *'g:pymode_options_max_line_length'* +Setup max line length *'g:pymode_options_max_line_length'* > let g:pymode_options_max_line_length = 79 -Enable colorcolumn display at max_line_length *'g:pymode_options_colorcolumn'* +Enable colorcolumn display at max_line_length. *'g:pymode_options_colorcolumn'* > let g:pymode_options_colorcolumn = 1 -Setup pymode |quickfix| window +Setup pymode |quickfix| window. - *'g:pymode_quickfix_maxheight'* *'g:pymode_quickfix_minheight'* + *'g:pymode_quickfix_maxheight'* *'g:pymode_quickfix_minheight'* > 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* + *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'* + *'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 |exrc| ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.2 Python indentation ~ - *pymode-indent* + *pymode-indent* Pymode supports PEP8-compatible python indent. -Enable pymode indentation *'g:pymode_indent'* +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* + *pymode-folding* -Fast and usual python folding in Vim. -Enable pymode folding *'g:pymode_folding'* +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 ~ - *pymode-motion* + *pymode-motion* Support Vim motion (See |operator|) for python objects (such as functions, class and methods). `C` — means class `M` — means method or function - *pymode-motion-keys* - -================ ============================ -Key Command -================ ============================ -[[ Jump to previous class or function (normal, visual, operator modes) -]] 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) -================ ============================ - -Enable pymode-motion *'g:pymode_motion'* + *pymode-motion-keys* + +==== ============================ +Key Command +==== ============================ +[[ Jump to previous class or function (normal, visual, operator modes) +]] 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 (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'* > let g:pymode_motion = 1 ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.5 Show documentation ~ - *pymode-documentation* + *pymode-documentation* Pymode could show documentation for current word by `pydoc`. Commands: -*:PymodeDoc* — show documentation +*:PymodeDoc* — show documentation -Turns on the documentation script *'g:pymode_doc'* +Turns on the documentation script *'g:pymode_doc'* > let g:pymode_doc = 1 Bind keys to show documentation for current word (selection) - *'g:pymode_doc_bind'* + *'g:pymode_doc_bind'* > let g:pymode_doc_bind = 'K' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.6 Support virtualenv ~ - *pymode-virtualenv* + *pymode-virtualenv* Commands: -*:PymodeVirtualenv* -- Activate virtualenv (path is related to -current working directory) +*:PymodeVirtualenv* -- Activate virtualenv (path can be absolute or +relative to current working directory) Enable automatic virtualenv detection *'g:pymode_virtualenv'* > let g:pymode_virtualenv = 1 -Set path to virtualenv manually *'g:pymode_virtualenv_path'* +Set path to virtualenv manually *'g:pymode_virtualenv_path'* > let g:pymode_virtualenv_path = $VIRTUAL_ENV ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.7 Run code ~ - *pymode-run* + *pymode-run* Commands: *:PymodeRun* -- Run current buffer or selection -Turn on the run code script *'g:pymode_run'* +Turn on the run code script *'g:pymode_run'* > let g:pymode_run = 1 -Binds keys to run python code *'g:pymode_run_bind'* +Binds keys to run python code *'g:pymode_run_bind'* > let g:pymode_run_bind = 'r' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.8 Breakpoints ~ - *pymode-breakpoints* + *pymode-breakpoints* Pymode automatically detects available debugger (like pdb, ipdb, pudb) and user can set/unset breakpoint with one key and without code checking and etc. -Enable functionality *'g:pymode_breakpoint'* +Enable functionality *'g:pymode_breakpoint'* > let g:pymode_breakpoint = 1 @@ -258,11 +289,11 @@ Manually set breakpoint command (leave empty for automatic detection) let g:pymode_breakpoint_cmd = '' -============================================================================== +=============================================================================== 3. Code checking ~ - *pymode-lint* + *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 @@ -277,44 +308,44 @@ Commands: *:PymodeLintToggle* -- Toggle code checking *:PymodeLintAuto* -- Fix PEP8 errors in current buffer automatically -Turn on code checking *'g:pymode_lint'* +Turn on code checking *'g:pymode_lint'* > let g:pymode_lint = 1 -Check code on every save (if file has been modified) *'g:pymode_lint_on_write'* +Check code on every save (if file has been modified) *'g:pymode_lint_on_write'* > let g:pymode_lint_on_write = 1 -Check code on every save (every) *'g:pymode_lint_unmodified'* +Check code on every save (every) *'g:pymode_lint_unmodified'* > let g:pymode_lint_unmodified = 0 -Check code when editing (on the fly) *'g:pymode_lint_on_fly'* +Check code when editing (on the fly) *'g:pymode_lint_on_fly'* > let g:pymode_lint_on_fly = 0 -Show error message if cursor placed at the error line *'g:pymode_lint_message'* +Show error message if cursor placed at the error line *'g:pymode_lint_message'* > let g:pymode_lint_message = 1 -Default code checkers (you could set several) *'g:pymode_lint_checkers'* +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. "E501,W002", "E2,W" (Skip all Warnings and Errors that starts with E2) and etc +Skip errors and warnings *'g:pymode_lint_ignore'* +E.g. ["W", "E2"] (Skip all Warnings and the Errors starting with E2) etc. > - let g:pymode_lint_ignore = "E501,W" + let g:pymode_lint_ignore = ["E501", "W",] -Select some error or warnings. *'g:pymode_lint_select'* +Select some error or warnings. *'g:pymode_lint_select'* By example you disable all warnings starting from 'W', but want to see warning 'W0011' and warning 'W430' > - let g:pymode_lint_select = "E501,W0011,W430" + let g:pymode_lint_select = ["E501", "W0011", "W430"] -Sort errors by relevance *'g:pymode_lint_sort'* +Sort errors by relevance *'g:pymode_lint_sort'* If not empty, errors will be sort by defined relevance E.g. let g:pymode_lint_sort = ['E', 'C', 'I'] " Errors first 'E', after them 'C' and ... @@ -322,11 +353,11 @@ after them 'C' and ... let g:pymode_lint_sort = [] Auto open cwindow (quickfix) if any errors have been found - *'g:pymode_lint_cwindow'* + *'g:pymode_lint_cwindow'* > let g:pymode_lint_cwindow = 1 -Place error |signs| *'g:pymode_signs'* +Place error |signs| *'g:pymode_signs'* > let g:pymode_lint_signs = 1 @@ -339,44 +370,43 @@ Definitions for |signs| let g:pymode_lint_info_symbol = 'II' let g:pymode_lint_pyflakes_symbol = 'FF' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 3.1 Set code checkers options ~ - *pymode-lint-options* + *pymode-lint-options* 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. -Set Pyflakes options *'g:pymode_lint_options_pyflakes'* +Set Pyflakes options *'g:pymode_lint_options_pyflakes'* > let g:pymode_lint_options_pyflakes = { 'builtins': '_' } -Set mccabe options *'g:pymode_lint_options_mccabe'* +Set mccabe options *'g:pymode_lint_options_mccabe'* > let g:pymode_lint_options_mccabe = { 'complexity': 12 } -Set pep257 options *'g:pymode_lint_options_pep257'* +Set pep257 options *'g:pymode_lint_options_pep257'* > let g:pymode_lint_options_pep257 = {} -Set pylint options *'g:pymode_lint_options_pylint'* +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. - -============================================================================== -3. Rope support ~ - *pymode-rope* +=============================================================================== +4. Rope support ~ + *pymode-rope* Pymode supports Rope refactoring operations, code completion and code assists. @@ -390,12 +420,16 @@ Commands: |:PymodeRopeUndo| -- Undo changes from last refactoring -Turn on the rope script *'g:pymode_rope'* +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* + *.ropeproject* *:PymodeRopeNewProject* [] -- Open new Rope project in the given path *:PymodeRopeRegenerate* -- Regenerate the project cache @@ -422,13 +456,13 @@ all its child directories, which may slow scanning down (because of many, possibly unrelated, files) Enable searching for |.ropeproject| in parent directories - *'g:pymode_rope_lookup_project'* + *'g:pymode_rope_lookup_project'* > let g:pymode_rope_lookup_project = 0 You can also manually set the rope project directory. If not specified rope will use the current directory. - *'g:pymode_rope_project_root'* + *'g:pymode_rope_project_root'* > let g:pymode_rope_project_root = "" @@ -438,15 +472,14 @@ keep it outside of your project root. The rope library treats this folder as a project resource, so the path will always be relative to your project root (a leading '/' will be ignored). You may use `'..'` path segments to place the folder outside of your project root. - *'g:pymode_rope_ropefolder'* + *'g:pymode_rope_ropefolder'* > let g:pymode_rope_ropefolder='.ropeproject' - Show documentation for element under cursor ~ -Show documentation for object under cursor. *'g:pymode_rope_show_doc_bind'* +Show documentation for object under cursor. *'g:pymode_rope_show_doc_bind'* Leave empty to disable the key binding. > let g:pymode_rope_show_doc_bind = 'd' @@ -455,9 +488,9 @@ Regenerate project cache on every save (if file has been modified) > let g:pymode_rope_regenerate_on_write = 1 ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.1 Completion ~ - *pymode-completion* + *pymode-completion* By default you can use for autocompletion. The first entry will be automatically selected and you can press to insert the entry in @@ -465,52 +498,58 @@ 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'* +Turn on code completion support in the plugin *'g:pymode_rope_completion'* > let g:pymode_rope_completion = 1 Turn on autocompletion when typing a period - *'g:pymode_rope_complete_on_dot'* + *'g:pymode_rope_complete_on_dot'* > let g:pymode_rope_complete_on_dot = 1 -Keymap for autocomplete *'g:pymode_rope_completion_bind'* +Keymap for autocomplete *'g:pymode_rope_completion_bind'* > let g:pymode_rope_completion_bind = '' Extended autocompletion (rope could complete objects which have not been -imported) from project *'g:pymode_rope_autoimport'* +imported) from project *'g:pymode_rope_autoimport'* > let g:pymode_rope_autoimport = 0 -Load modules to autoimport by default *'g:pymode_rope_autoimport_modules'* +Load modules to autoimport by default *'g:pymode_rope_autoimport_modules'* > - let g:pymode_rope_autoimport_modules = ['os', 'shutil', 'datetime']) + let g:pymode_rope_autoimport_modules = ['os', 'shutil', 'datetime'] Offer to unresolved import object after completion. > let g:pymode_rope_autoimport_import_after_complete = 0 ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.2 Find definition ~ - *pymode-rope-findit* + *pymode-rope-findit* By default when you press *g* on any object in your code you will be moved -to definition. -Leave empty for disable key binding. *'g:pymode_rope_goto_definition_bind'* +to definition. +Leave empty for disable key binding. *'g:pymode_rope_goto_definition_bind'* > let g:pymode_rope_goto_definition_bind = 'g' Command for open window when definition has been found -Values are (`e`, `new`, `vnew`) *'g:pymode_rope_goto_definition_cmd'* +Values are (`e`, `new`, `vnew`) *'g:pymode_rope_goto_definition_cmd'* > let g:pymode_rope_goto_definition_cmd = 'new' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.3 Refactoring ~ - *pymode-rope-refactoring* + *pymode-rope-refactoring* Rename method/function/class/variable in the project ~ @@ -518,7 +557,7 @@ Pymode can rename everything: classes, functions, modules, packages, methods, variables and keyword arguments. Keymap for rename method/function/class/variables under cursor - *'g:pymode_rope_rename_bind'* + *'g:pymode_rope_rename_bind'* > let g:pymode_rope_rename_bind = 'rr' @@ -527,7 +566,7 @@ Rename a current module/package ~ *:PymodeRopeRenameModule* -- Rename current module -Keymap for rename current module *'g:pymode_rope_rename_module_bind'* +Keymap for rename current module *'g:pymode_rope_rename_module_bind'* > let g:pymode_rope_rename_module_bind = 'r1r' @@ -538,18 +577,18 @@ Imports ~ Organize imports sorts imports, too. It does that according to PEP8. Unused imports will be dropped. -Keymap *'g:pymode_rope_organize_imports_bind'* +Keymap *'g:pymode_rope_organize_imports_bind'* > let g:pymode_rope_organize_imports_bind = 'ro' -Insert import for current word under cursor *'g:pymode_rope_autoimport_bind'* +Insert import for current word under cursor *'g:pymode_rope_autoimport_bind'* Should be enabled |'g:pymode_rope_autoimport'| > let g:pymode_rope_autoimport_bind = 'ra' Convert module to package ~ - *'g:pymode_rope_module_to_package_bind'* + *'g:pymode_rope_module_to_package_bind'* *:PymodeRopeModuleToPackage* -- convert current module to package @@ -559,19 +598,19 @@ Keybinding: Extract method/variable ~ - *pymode-rope-extract* + *pymode-rope-extract* Extract method/variable from selected lines. - *'g:pymode_rope_extract_method_bind'* - *'g:pymode_rope_extract_variable_bind'* + *'g:pymode_rope_extract_method_bind'* + *'g:pymode_rope_extract_variable_bind'* > let g:pymode_rope_extract_method_bind = 'rm' let g:pymode_rope_extract_variable_bind = 'rl' Use function ~ - *pymode-rope-use* + *pymode-rope-use* It tries to find the places in which a function can be used and changes the code to call it instead. @@ -579,14 +618,31 @@ code to call it instead. let g:pymode_rope_use_function_bind = 'ru' -Move method/fields ~ - *pymode-rope-move* +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' @@ -595,10 +651,10 @@ Change function signature ~ let g:pymode_rope_change_signature_bind = 'rs' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.4 Undo/Redo changes ~ - *pymode-rope-undo* - *pymode-rope-redo* + *pymode-rope-undo* + *pymode-rope-redo* Commands: @@ -606,94 +662,118 @@ Commands: *:PymodeRopeRedo* -- Redo last changes in the project -============================================================================== +=============================================================================== 5. Syntax ~ - *pymode-syntax* + *pymode-syntax* -Turn on pymode syntax *'g:pymode_syntax'* +Turn on pymode syntax *'g:pymode_syntax'* > let g:pymode_syntax = 1 Slower syntax synchronization that is better at handling code blocks in docstrings. Consider disabling this on slower hardware. - *'g:pymode_syntax_slow_sync'* + *'g:pymode_syntax_slow_sync'* > let g:pymode_syntax_slow_sync = 1 -Enable all python highlights *'g:pymode_syntax_all'* +Enable all python highlights *'g:pymode_syntax_all'* > let g:pymode_syntax_all = 1 -Highlight "print" as a function *'g:pymode_syntax_print_as_function'* +Highlight "print" as a function *'g:pymode_syntax_print_as_function'* > let g:pymode_syntax_print_as_function = 0 -Highlight "async/await" keywords *'g:pymode_syntax_highlight_async_await'* +Highlight "async/await" keywords *'g:pymode_syntax_highlight_async_await'* > let g:pymode_syntax_highlight_async_await = g:pymode_syntax_all -Highlight '=' operator *'g:pymode_syntax_highlight_equal_operator'* +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_stars_operator'* +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 -Highlight 'self' keyword *'g:pymode_syntax_highlight_self'* +Highlight 'self' keyword *'g:pymode_syntax_highlight_self'* > let g:pymode_syntax_highlight_self = g:pymode_syntax_all -Highlight indent's errors *'g:pymode_syntax_indent_errors'* +Highlight indent's errors *'g:pymode_syntax_indent_errors'* > let g:pymode_syntax_indent_errors = g:pymode_syntax_all -Highlight space's errors *'g:pymode_syntax_space_errors'* +Highlight space's errors *'g:pymode_syntax_space_errors'* > let g:pymode_syntax_space_errors = g:pymode_syntax_all Highlight string formatting *'g:pymode_syntax_string_formatting'* - *'g:pymode_syntax_string_format'* - *'g:pymode_syntax_string_templates'* - *'g:pymode_syntax_doctests'* + *'g:pymode_syntax_string_format'* + *'g:pymode_syntax_string_templates'* + *'g:pymode_syntax_doctests'* > let g:pymode_syntax_string_formatting = g:pymode_syntax_all let g:pymode_syntax_string_format = g:pymode_syntax_all let g:pymode_syntax_string_templates = g:pymode_syntax_all let g:pymode_syntax_doctests = g:pymode_syntax_all -Highlight builtin objects (True, False, ...) *'g:pymode_syntax_builtin_objs'* +Highlight builtin objects (True, False, ...) *'g:pymode_syntax_builtin_objs'* > let g:pymode_syntax_builtin_objs = g:pymode_syntax_all -Highlight builtin types (str, list, ...) *'g:pymode_syntax_builtin_types'* +Highlight builtin types (str, list, ...) *'g:pymode_syntax_builtin_types'* > let g:pymode_syntax_builtin_types = g:pymode_syntax_all -Highlight exceptions (TypeError, ValueError, ...) - *'g:pymode_syntax_highlight_exceptions'* +Highlight exceptions (TypeError, ValueError, ...) + *'g:pymode_syntax_highlight_exceptions'* > let g:pymode_syntax_highlight_exceptions = g:pymode_syntax_all Highlight docstrings as pythonDocstring (otherwise as pythonString) - *'g:pymode_syntax_docstrings'* + *'g:pymode_syntax_docstrings'* > let g:pymode_syntax_docstrings = g:pymode_syntax_all -============================================================================== +=============================================================================== 6. FAQ ~ - *pymode-faq* + *pymode-faq* -Python-mode doesn't work ------------------------- +1. Python-mode doesn't work +--------------------------- -Open any python file and run ":call pymode#troubleshooting#test()", -fix the warning or send me the output. +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. + + *Underlined do check for sensitive information in the file before + *Underlined submitting! -Rope completion is very slow *pymode-rope-slow* ----------------------------- + + +2. Rope completion is very slow *pymode-rope-slow* +------------------------------- Rope creates a project-level service directory in |.ropeproject| @@ -715,9 +795,8 @@ You may also set |'g:pymode_rope_project_root'| to manually specify the project root path. - -Pylint check is very slow -------------------------- +3. Pylint check is very slow +---------------------------- In some projects pylint may check slowly, because it also scans imported modules if possible. Try using another code checker: see @@ -726,9 +805,8 @@ modules if possible. Try using another code checker: see You may set |exrc| and |secure| in your |vimrc| to auto-set custom settings from `.vimrc` from your projects directories. - -OSX cannot import urandom -------------------------- +4. OSX cannot import urandom +---------------------------- See: https://groups.google.com/forum/?fromgroups=#!topic/vim_dev/2NXKF6kDONo @@ -740,11 +818,53 @@ The sequence of commands that fixed this: brew install -v --force macvim brew link macvim brew link python -< -============================================================================== -7. Credits ~ - *pymode-credits* +5. Folding is slow +------------------ + +Python mode adds folding for definitions and multi line docstrings. These may +be costly to compute on large files. To disable them one simple has to to add: + + let g:pymode_folding = 1 + +to their vimrc file. + +Beware that when editing python files in multiple windows vim computes the +folding for every typed character. Thus it may be useful to define: + + augroup unset_folding_in_insert_mode + autocmd! + autocmd InsertEnter *.py setlocal foldmethod=marker + autocmd InsertLeave *.py setlocal foldmethod=expr + augroup END + +=============================================================================== +7. Development~ + *pymode-development* + +This section briefly defines development guidelines for python-mode. + +1. This help file uses vim's conventions defined at |help-writing|. +2. The name of the plugin shall be referred to as 'python-mode' throughout +documentation (except as a first word in a sentence in which case is +'Python-mode'). +3. All defined functions should use vim's conventions and start with 'Pymode'. +4. Special marks for project development are `XXX` and `TODO`. They provide a +easy way for developers to check pending issues. +5. If submitting a pull request then a test should be added which smartly +covers the found bug/new feature. Check out the `tests/test.sh` (1) file and +other executed files. +A suggested structure is the following: add your test to +`tests/test_bash` (2) and a vim script to be sourced at +`tests/test_procedures_vimscript` (3). Try to make use of the already existing +files at `tests/test_python_sample_code` (4). File (1) should be trigger the +newly added file (2). This latter file should invoke vim which in turn sources +file (3). File (3) may then read (4) as a first part of its assertion +structure and then execute the remaning of the instructions/assertions. + +=============================================================================== +8. Credits ~ + *pymode-credits* Kirill Klenov http://klen.github.com/ http://github.com/klen/ @@ -758,8 +878,9 @@ The sequence of commands that fixed this: http://www.logilab.fr/ Pyflakes: - Copyright (c) 2005 Divmod, Inc. - http://www.divmod.com/ + Copyright (c) 2005-2011 Divmod, Inc. + Copyright (c) 2013-2014 Florent Xicluna + https://github.com/PyCQA/pyflakes PEP8: Copyright (c) 2006 Johann C. Rocholl @@ -778,19 +899,20 @@ The sequence of commands that fixed this: http://github.com/hynek/vim-python-pep8-indent -============================================================================== -8. License ~ - *pymode-license* +=============================================================================== +9. License ~ + *pymode-license* Python-mode is released under the GNU lesser general public license. See: http://www.gnu.org/copyleft/lesser.html -If you like this plugin, I would very appreciated if you kindly send me a postcard :) +If you like this plugin, I would very appreciated if you kindly send me a +postcard :) -My address is: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill Klenov". -Thanks for your support! +My address is: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill +Klenov". Thanks for your support! ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- - vim:tw=78:ts=8:ft=help:norl: + vim:tw=79:ts=8:ft=help:norl: diff --git a/ftplugin/python/pymode.vim b/ftplugin/python/pymode.vim index 97daecca..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 @@ -110,7 +110,7 @@ if g:pymode_lint " let &l:updatetime = g:pymode_lint_async_updatetime " au! BufEnter call pymode#lint#start() " au! BufLeave call pymode#lint#stop() - end + endif endif @@ -124,7 +124,7 @@ if g:pymode_doc exe "nnoremap " g:pymode_doc_bind ":call pymode#doc#find()" exe "vnoremap " g:pymode_doc_bind ":call pymode#doc#show(@*)" -end +endif " Rope support if g:pymode_rope @@ -134,69 +134,69 @@ if g:pymode_rope endif if g:pymode_rope_show_doc_bind != "" exe "noremap " . g:pymode_rope_show_doc_bind . " :call pymode#rope#show_doc()" - end + endif if g:pymode_rope_find_it_bind != "" exe "noremap " . g:pymode_rope_find_it_bind . " :call pymode#rope#find_it()" - end + endif if g:pymode_rope_organize_imports_bind != "" exe "noremap " . g:pymode_rope_organize_imports_bind . " :call pymode#rope#organize_imports()" - end + endif if g:pymode_rope_rename_bind != "" exe "noremap " . g:pymode_rope_rename_bind . " :call pymode#rope#rename()" - end + endif if g:pymode_rope_rename_module_bind != "" exe "noremap " . g:pymode_rope_rename_module_bind . " :call pymode#rope#rename_module()" - end + endif if g:pymode_rope_extract_method_bind != "" exe "vnoremap " . g:pymode_rope_extract_method_bind . " :call pymode#rope#extract_method()" - end + endif if g:pymode_rope_extract_variable_bind != "" exe "vnoremap " . g:pymode_rope_extract_variable_bind . " :call pymode#rope#extract_variable()" - end + endif if g:pymode_rope_inline_bind != "" exe "noremap " . g:pymode_rope_inline_bind . " :call pymode#rope#inline()" - end + endif if g:pymode_rope_move_bind != "" exe "noremap " . g:pymode_rope_move_bind . " :call pymode#rope#move()" - end + endif if g:pymode_rope_change_signature_bind != "" exe "noremap " . g:pymode_rope_change_signature_bind . " :call pymode#rope#signature()" - end + endif if g:pymode_rope_use_function_bind != "" exe "noremap " . g:pymode_rope_use_function_bind . " :call pymode#rope#use_function()" - end + endif if g:pymode_rope_generate_function_bind != "" exe "noremap " . g:pymode_rope_generate_function_bind . " :call pymode#rope#generate_function()" - end + endif if g:pymode_rope_generate_package_bind != "" exe "noremap " . g:pymode_rope_generate_package_bind . " :call pymode#rope#generate_package()" - end + endif if g:pymode_rope_generate_class_bind != "" exe "noremap " . g:pymode_rope_generate_class_bind . " :call pymode#rope#generate_class()" - end + endif if g:pymode_rope_module_to_package_bind != "" exe "noremap " . g:pymode_rope_module_to_package_bind . " :call pymode#rope#module_to_package()" - end + endif if g:pymode_rope_autoimport_bind != "" exe "noremap " . g:pymode_rope_autoimport_bind . " :PymodeRopeAutoImport" - end + endif if g:pymode_rope_completion && g:pymode_rope_complete_on_dot inoremap . .=pymode#rope#complete_on_dot() - end + endif command! -buffer -nargs=? PymodeRopeNewProject call pymode#rope#new() command! -buffer PymodeRopeUndo call pymode#rope#undo() @@ -207,6 +207,52 @@ if g:pymode_rope if g:pymode_rope_autoimport command! -buffer PymodeRopeAutoImport call pymode#rope#autoimport(expand('')) - end + endif -end +endif + + +if g:pymode_debug + " Redefine functions to be debugged here functions here. + + " NOTE: The redraw seems to be necessary to force messages to get echoed to + " the screen. See: + " https://groups.google.com/forum/#!topic/vim_use/EfcXOjq_rKE + " for details. + " silent! redraw! + " TODO: when loading with 'vim -u ./debug.vim' the messages shown in vim + " are unduly cleared. Need a fix. + + " Start debbuging environment. {{{ + if ! &verbosefile + " Get a system independent temporary filename. The 'marker' variable is + " used to get rid of a null character getting inserted at position. + " substitute() was not able to remove it. + " TODO: see https://superuser.com/questions/935574/get-rid-of-null-character-in-vim-variable + let g:pymode_debug_tempfile=matchstr( + \ execute( + \ g:pymode_python + \ . " import os;import tempfile; marker='|';" + \ . " print(marker, tempfile.gettempdir(), os.sep, " + \ . "'pymode_debug_file.txt', marker, sep='', end='')"), + \ '|\zs.*\ze|') + execute "set verbosefile=" . g:pymode_debug_tempfile + endif + call pymode#debug('Starting debug on: ' + \ . strftime("\%Y-\%m-\%d \%H:\%M:\%S") + \ . ' with file ' . &verbosefile) + " }}} + " Redefine folding expression. {{{ + if g:pymode_folding + setlocal foldexpr=pymode#debug#foldingexpr(v:lnum) + endif + call pymode#debug#sysinfo() + " }}} + " Define auto commands for vim. {{{ + augroup augroup_save_issue_commands + autocmd! + autocmd VimLeave *.py | call pymode#debug('Session history:') | silent! history + augroup END + " }}} + + endif diff --git a/plugin/pymode.vim b/plugin/pymode.vim index 26541ae2..b0d99270 100644 --- a/plugin/pymode.vim +++ b/plugin/pymode.vim @@ -1,15 +1,16 @@ " vi: fdl=1 -let g:pymode_version = "0.8.1" +let g:pymode_version = "0.14.0" -com! PymodeVersion echomsg "Current python-mode version: " . g:pymode_version -com! PymodeTroubleshooting call pymode#troubleshooting#test() " Enable pymode by default :) call pymode#default('g:pymode', 1) call pymode#default('g:pymode_debug', 0) " DESC: Disable script loading -if !g:pymode || &cp +if !g:pymode || &cp || &diff + " Update pymode status to prevent loading in other files and adding this + " condition to all of them. + let g:pymode = 0 finish endif @@ -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,12 +39,18 @@ 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') -call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\) \w\+') +call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\|async\s\+def\) .\+\(:\s\+\w\)\@!') +" call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\|async\s\+def\)') " Enable/disable python motion operators call pymode#default("g:pymode_motion", 1) @@ -49,7 +60,7 @@ call pymode#default("g:pymode_trim_whitespaces", 1) " Set recomended python options call pymode#default("g:pymode_options", 1) -call pymode#default("g:pymode_options_max_line_length", 80) +call pymode#default("g:pymode_options_max_line_length", 79) call pymode#default("g:pymode_options_colorcolumn", 1) " Enable/disable vertical display of python documentation @@ -61,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 @@ -105,14 +122,14 @@ 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 -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", "") +call pymode#default("g:pymode_lint_ignore", []) " Select errors and warnings (e.g. E4,W) -call pymode#default("g:pymode_lint_select", "") +call pymode#default("g:pymode_lint_select", []) " Auto open cwindow if any errors has been finded call pymode#default("g:pymode_lint_cwindow", 1) @@ -135,7 +152,8 @@ call pymode#default("g:pymode_lint_info_symbol", "II") call pymode#default("g:pymode_lint_pyflakes_symbol", "FF") " Code checkers options -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", @@ -167,96 +185,99 @@ call pymode#default('g:pymode_breakpoint_cmd', '') " ROPE (refactoring, codeassist) {{{ " " Rope support -call pymode#default('g:pymode_rope', 1) +call pymode#default('g:pymode_rope', 0) +call pymode#default('g:pymode_rope_prefix', '') " System plugin variable -call pymode#default('g:pymode_rope_current', '') +if g:pymode_rope + call pymode#default('g:pymode_rope_current', '') -" Configurable rope project root -call pymode#default('g:pymode_rope_project_root', '') + " Configurable rope project root + call pymode#default('g:pymode_rope_project_root', '') -" Configurable rope project folder (always relative to project root) -call pymode#default('g:pymode_rope_ropefolder', '.ropeproject') + " Configurable rope project folder (always relative to project root) + call pymode#default('g:pymode_rope_ropefolder', '.ropeproject') -" If project hasnt been finded in current working directory, look at parents directory -call pymode#default('g:pymode_rope_lookup_project', 0) + " If project hasnt been finded in current working directory, look at parents directory + call pymode#default('g:pymode_rope_lookup_project', 0) -" Enable Rope completion -call pymode#default('g:pymode_rope_completion', 1) + " Enable Rope completion + call pymode#default('g:pymode_rope_completion', 1) -" Complete keywords from not imported modules (could make completion slower) -" Enable autoimport used modules -call pymode#default('g:pymode_rope_autoimport', 0) + " Complete keywords from not imported modules (could make completion slower) + " Enable autoimport used modules + call pymode#default('g:pymode_rope_autoimport', 0) -" Offer to import object after complete (if that not be imported before) -call pymode#default('g:pymode_rope_autoimport_import_after_complete', 0) + " Offer to import object after complete (if that not be imported before) + call pymode#default('g:pymode_rope_autoimport_import_after_complete', 0) -" Autoimported modules -call pymode#default('g:pymode_rope_autoimport_modules', ['os', 'shutil', 'datetime']) + " Autoimported modules + 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') + " Bind keys to autoimport module for object under cursor + 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) + " Automatic completion on dot + call pymode#default('g:pymode_rope_complete_on_dot', 1) -" Bind keys for autocomplete (leave empty for disable) -call pymode#default('g:pymode_rope_completion_bind', '') + " Bind keys for autocomplete (leave empty for disable) + 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') + " Bind keys for goto definition (leave empty for disable) + 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') + " 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') + " Bind keys for show documentation (leave empty for disable) + 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') + " Bind keys for find occurencies (leave empty for disable) + 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') + " Bind keys for organize imports (leave empty for disable) + 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') + " Bind keys for rename variable/method/class in the project (leave empty for disable) + 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') + " Bind keys for rename module + 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') + " Bind keys for convert module to package + 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') + " Creates a new function or method (depending on the context) from the selected lines + 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') + " Creates a variable from the selected lines + 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') + " Inline refactoring + call pymode#default('g:pymode_rope_inline_bind', g:pymode_rope_prefix . 'ri') -" Move refactoring -call pymode#default('g:pymode_rope_move_bind', 'rv') + " Move refactoring + 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') + " Generate function + 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') + " Generate class + 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') + " Generate package + 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') + " Change signature + 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') + " 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', g:pymode_rope_prefix . 'ru') -" Regenerate project cache on every save -call pymode#default('g:pymode_rope_regenerate_on_write', 1) + " Regenerate project cache on every save + call pymode#default('g:pymode_rope_regenerate_on_write', 1) +endif " }}} @@ -268,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 @@ -310,8 +316,6 @@ else endif - command! PymodeVersion echomsg "Pymode version: " . g:pymode_version . " interpreter: " . g:pymode_python . " lint: " . g:pymode_lint . " rope: " . g:pymode_rope augroup pymode - diff --git a/pylama.ini b/pylama.ini index b8d3f375..9579796e 100644 --- a/pylama.ini +++ b/pylama.ini @@ -1,8 +1,9 @@ [pylama] +ignore=D213 linters=pep8,pyflakes,pylint [pylama:pymode/libs*] skip=1 [pylama:pylint] -disable=E1120,E1130,E1103,W1401 +disable=E1120,E1130,E1103,W1401,F0001 diff --git a/pymode/__init__.py b/pymode/__init__.py index d5e63ba3..906d7059 100644 --- a/pymode/__init__.py +++ b/pymode/__init__.py @@ -1,23 +1,37 @@ -""" Pymode support functions. """ - -from __future__ import absolute_import +"""Pymode support functions.""" 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. """ + """Fix PEP8 erorrs in current buffer. + + pymode: uses it in command PymodeLintAuto with pymode#lint#auto() + + """ from .autopep8 import fix_file class Options(object): - aggressive = 2 + aggressive = 1 diff = False experimental = True ignore = vim.eval('g:pymode_lint_ignore') in_place = True indent_size = int(vim.eval('&tabstop')) line_range = None + hang_closing = False max_line_length = int(vim.eval('g:pymode_options_max_line_length')) pep8_passes = 100 recursive = False @@ -28,8 +42,8 @@ class Options(object): def get_documentation(): - """ Search documentation and append to current buffer. """ - from ._compat import StringIO + """Search documentation and append to current buffer.""" + 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/autopep8.py b/pymode/autopep8.py deleted file mode 100644 index 13308751..00000000 --- a/pymode/autopep8.py +++ /dev/null @@ -1,3654 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2010-2011 Hideo Hattori -# Copyright (C) 2011-2013 Hideo Hattori, Steven Myint -# Copyright (C) 2013-2015 Hideo Hattori, Steven Myint, Bill Wendling -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Automatically formats Python code to conform to the PEP 8 style guide. - -Fixes that only need be done once can be added by adding a function of the form -"fix_(source)" to this module. They should return the fixed source code. -These fixes are picked up by apply_global_fixes(). - -Fixes that depend on pep8 should be added as methods to FixPEP8. See the class -documentation for more information. - -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import codecs -import collections -import copy -import difflib -import fnmatch -import inspect -import io -import keyword -import locale -import os -import re -import signal -import sys -import textwrap -import token -import tokenize - -import pep8 - - -try: - unicode -except NameError: - unicode = str - - -__version__ = '1.2.1a0' - - -CR = '\r' -LF = '\n' -CRLF = '\r\n' - - -PYTHON_SHEBANG_REGEX = re.compile(r'^#!.*\bpython[23]?\b\s*$') - - -# For generating line shortening candidates. -SHORTEN_OPERATOR_GROUPS = frozenset([ - frozenset([',']), - frozenset(['%']), - frozenset([',', '(', '[', '{']), - frozenset(['%', '(', '[', '{']), - frozenset([',', '(', '[', '{', '%', '+', '-', '*', '/', '//']), - frozenset(['%', '+', '-', '*', '/', '//']), -]) - - -DEFAULT_IGNORE = 'E24' -DEFAULT_INDENT_SIZE = 4 - - -# W602 is handled separately due to the need to avoid "with_traceback". -CODE_TO_2TO3 = { - 'E231': ['ws_comma'], - 'E721': ['idioms'], - 'W601': ['has_key'], - 'W603': ['ne'], - 'W604': ['repr'], - 'W690': ['apply', - 'except', - 'exitfunc', - 'numliterals', - 'operator', - 'paren', - 'reduce', - 'renames', - 'standarderror', - 'sys_exc', - 'throw', - 'tuple_params', - 'xreadlines']} - - -if sys.platform == 'win32': # pragma: no cover - DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8') -else: - DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or - os.path.expanduser('~/.config'), 'pep8') -PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8') - - -def open_with_encoding(filename, encoding=None, mode='r'): - """Return opened file with a specific encoding.""" - if not encoding: - encoding = detect_encoding(filename) - - return io.open(filename, mode=mode, encoding=encoding, - newline='') # Preserve line endings - - -def detect_encoding(filename): - """Return file encoding.""" - try: - with open(filename, 'rb') as input_file: - from lib2to3.pgen2 import tokenize as lib2to3_tokenize - encoding = lib2to3_tokenize.detect_encoding(input_file.readline)[0] - - # Check for correctness of encoding - with open_with_encoding(filename, encoding) as test_file: - test_file.read() - - return encoding - except (LookupError, SyntaxError, UnicodeDecodeError): - return 'latin-1' - - -def readlines_from_file(filename): - """Return contents of file.""" - with open_with_encoding(filename) as input_file: - return input_file.readlines() - - -def extended_blank_lines(logical_line, - blank_lines, - blank_before, - indent_level, - previous_logical): - """Check for missing blank lines after class declaration.""" - if previous_logical.startswith('class '): - if logical_line.startswith(('def ', 'class ', '@')): - if indent_level and not blank_lines and not blank_before: - yield (0, 'E309 expected 1 blank line after class declaration') - elif previous_logical.startswith('def '): - if blank_lines and pep8.DOCSTRING_REGEX.match(logical_line): - yield (0, 'E303 too many blank lines ({0})'.format(blank_lines)) - elif pep8.DOCSTRING_REGEX.match(previous_logical): - # Missing blank line between class docstring and method declaration. - if ( - indent_level and - not blank_lines and - not blank_before and - logical_line.startswith(('def ')) and - '(self' in logical_line - ): - yield (0, 'E301 expected 1 blank line, found 0') -pep8.register_check(extended_blank_lines) - - -def continued_indentation(logical_line, tokens, indent_level, indent_char, - noqa): - """Override pep8's function to provide indentation information.""" - first_row = tokens[0][2][0] - nrows = 1 + tokens[-1][2][0] - first_row - if noqa or nrows == 1: - return - - # indent_next tells us whether the next block is indented. Assuming - # that it is indented by 4 spaces, then we should not allow 4-space - # indents on the final continuation line. In turn, some other - # indents are allowed to have an extra 4 spaces. - indent_next = logical_line.endswith(':') - - row = depth = 0 - valid_hangs = ( - (DEFAULT_INDENT_SIZE,) - if indent_char != '\t' else (DEFAULT_INDENT_SIZE, - 2 * DEFAULT_INDENT_SIZE) - ) - - # Remember how many brackets were opened on each line. - parens = [0] * nrows - - # Relative indents of physical lines. - rel_indent = [0] * nrows - - # For each depth, collect a list of opening rows. - open_rows = [[0]] - # For each depth, memorize the hanging indentation. - hangs = [None] - - # Visual indents. - indent_chances = {} - last_indent = tokens[0][2] - indent = [last_indent[1]] - - last_token_multiline = None - line = None - last_line = '' - last_line_begins_with_multiline = False - for token_type, text, start, end, line in tokens: - - newline = row < start[0] - first_row - if newline: - row = start[0] - first_row - newline = (not last_token_multiline and - token_type not in (tokenize.NL, tokenize.NEWLINE)) - last_line_begins_with_multiline = last_token_multiline - - if newline: - # This is the beginning of a continuation line. - last_indent = start - - # Record the initial indent. - rel_indent[row] = pep8.expand_indent(line) - indent_level - - # Identify closing bracket. - close_bracket = (token_type == tokenize.OP and text in ']})') - - # Is the indent relative to an opening bracket line? - for open_row in reversed(open_rows[depth]): - hang = rel_indent[row] - rel_indent[open_row] - hanging_indent = hang in valid_hangs - if hanging_indent: - break - if hangs[depth]: - hanging_indent = (hang == hangs[depth]) - - visual_indent = (not close_bracket and hang > 0 and - indent_chances.get(start[1])) - - if close_bracket and indent[depth]: - # Closing bracket for visual indent. - if start[1] != indent[depth]: - yield (start, 'E124 {0}'.format(indent[depth])) - elif close_bracket and not hang: - pass - elif indent[depth] and start[1] < indent[depth]: - # Visual indent is broken. - yield (start, 'E128 {0}'.format(indent[depth])) - elif (hanging_indent or - (indent_next and - rel_indent[row] == 2 * DEFAULT_INDENT_SIZE)): - # Hanging indent is verified. - if close_bracket: - yield (start, 'E123 {0}'.format(indent_level + - rel_indent[open_row])) - hangs[depth] = hang - elif visual_indent is True: - # Visual indent is verified. - indent[depth] = start[1] - elif visual_indent in (text, unicode): - # Ignore token lined up with matching one from a previous line. - pass - else: - one_indented = (indent_level + rel_indent[open_row] + - DEFAULT_INDENT_SIZE) - # Indent is broken. - if hang <= 0: - error = ('E122', one_indented) - elif indent[depth]: - error = ('E127', indent[depth]) - elif hang > DEFAULT_INDENT_SIZE: - error = ('E126', one_indented) - else: - hangs[depth] = hang - error = ('E121', one_indented) - - yield (start, '{0} {1}'.format(*error)) - - # Look for visual indenting. - if ( - parens[row] and - token_type not in (tokenize.NL, tokenize.COMMENT) and - not indent[depth] - ): - indent[depth] = start[1] - indent_chances[start[1]] = True - # Deal with implicit string concatenation. - elif (token_type in (tokenize.STRING, tokenize.COMMENT) or - text in ('u', 'ur', 'b', 'br')): - indent_chances[start[1]] = unicode - # Special case for the "if" statement because len("if (") is equal to - # 4. - elif not indent_chances and not row and not depth and text == 'if': - indent_chances[end[1] + 1] = True - elif text == ':' and line[end[1]:].isspace(): - open_rows[depth].append(row) - - # Keep track of bracket depth. - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - hangs.append(None) - if len(open_rows) == depth: - open_rows.append([]) - open_rows[depth].append(row) - parens[row] += 1 - elif text in ')]}' and depth > 0: - # Parent indents should not be more than this one. - prev_indent = indent.pop() or last_indent[1] - hangs.pop() - for d in range(depth): - if indent[d] > prev_indent: - indent[d] = 0 - for ind in list(indent_chances): - if ind >= prev_indent: - del indent_chances[ind] - del open_rows[depth + 1:] - depth -= 1 - if depth: - indent_chances[indent[depth]] = True - for idx in range(row, -1, -1): - if parens[idx]: - parens[idx] -= 1 - break - assert len(indent) == depth + 1 - if ( - start[1] not in indent_chances and - # This is for purposes of speeding up E121 (GitHub #90). - not last_line.rstrip().endswith(',') - ): - # Allow to line up tokens. - indent_chances[start[1]] = text - - last_token_multiline = (start[0] != end[0]) - if last_token_multiline: - rel_indent[end[0] - first_row] = rel_indent[row] - - last_line = line - - if ( - indent_next and - not last_line_begins_with_multiline and - pep8.expand_indent(line) == indent_level + DEFAULT_INDENT_SIZE - ): - pos = (start[0], indent[0] + 4) - yield (pos, 'E125 {0}'.format(indent_level + - 2 * DEFAULT_INDENT_SIZE)) -del pep8._checks['logical_line'][pep8.continued_indentation] -pep8.register_check(continued_indentation) - - -class FixPEP8(object): - - """Fix invalid code. - - Fixer methods are prefixed "fix_". The _fix_source() method looks for these - automatically. - - The fixer method can take either one or two arguments (in addition to - self). The first argument is "result", which is the error information from - pep8. The second argument, "logical", is required only for logical-line - fixes. - - The fixer method can return the list of modified lines or None. An empty - list would mean that no changes were made. None would mean that only the - line reported in the pep8 error was modified. Note that the modified line - numbers that are returned are indexed at 1. This typically would correspond - with the line number reported in the pep8 error information. - - [fixed method list] - - e121,e122,e123,e124,e125,e126,e127,e128,e129 - - e201,e202,e203 - - e211 - - e221,e222,e223,e224,e225 - - e231 - - e251 - - e261,e262 - - e271,e272,e273,e274 - - e301,e302,e303 - - e401 - - e502 - - e701,e702 - - e711 - - w291 - - """ - - def __init__(self, filename, - options, - contents=None, - long_line_ignore_cache=None): - self.filename = filename - if contents is None: - self.source = readlines_from_file(filename) - else: - sio = io.StringIO(contents) - self.source = sio.readlines() - self.options = options - self.indent_word = _get_indentword(''.join(self.source)) - - self.long_line_ignore_cache = ( - set() if long_line_ignore_cache is None - else long_line_ignore_cache) - - # Many fixers are the same even though pep8 categorizes them - # differently. - self.fix_e115 = self.fix_e112 - self.fix_e116 = self.fix_e113 - self.fix_e121 = self._fix_reindent - self.fix_e122 = self._fix_reindent - self.fix_e123 = self._fix_reindent - self.fix_e124 = self._fix_reindent - self.fix_e126 = self._fix_reindent - self.fix_e127 = self._fix_reindent - self.fix_e128 = self._fix_reindent - self.fix_e129 = self._fix_reindent - self.fix_e202 = self.fix_e201 - self.fix_e203 = self.fix_e201 - self.fix_e211 = self.fix_e201 - self.fix_e221 = self.fix_e271 - self.fix_e222 = self.fix_e271 - self.fix_e223 = self.fix_e271 - self.fix_e226 = self.fix_e225 - self.fix_e227 = self.fix_e225 - self.fix_e228 = self.fix_e225 - self.fix_e241 = self.fix_e271 - self.fix_e242 = self.fix_e224 - self.fix_e261 = self.fix_e262 - self.fix_e272 = self.fix_e271 - self.fix_e273 = self.fix_e271 - self.fix_e274 = self.fix_e271 - self.fix_e309 = self.fix_e301 - self.fix_e501 = ( - self.fix_long_line_logically if - options and (options.aggressive >= 2 or options.experimental) else - self.fix_long_line_physically) - self.fix_e703 = self.fix_e702 - self.fix_w293 = self.fix_w291 - - def _fix_source(self, results): - try: - (logical_start, logical_end) = _find_logical(self.source) - logical_support = True - except (SyntaxError, tokenize.TokenError): # pragma: no cover - logical_support = False - - completed_lines = set() - for result in sorted(results, key=_priority_key): - if result['line'] in completed_lines: - continue - - fixed_methodname = 'fix_' + result['id'].lower() - if hasattr(self, fixed_methodname): - fix = getattr(self, fixed_methodname) - - line_index = result['line'] - 1 - original_line = self.source[line_index] - - is_logical_fix = len(inspect.getargspec(fix).args) > 2 - if is_logical_fix: - logical = None - if logical_support: - logical = _get_logical(self.source, - result, - logical_start, - logical_end) - if logical and set(range( - logical[0][0] + 1, - logical[1][0] + 1)).intersection( - completed_lines): - continue - - modified_lines = fix(result, logical) - else: - modified_lines = fix(result) - - if modified_lines is None: - # Force logical fixes to report what they modified. - assert not is_logical_fix - - if self.source[line_index] == original_line: - modified_lines = [] - - if modified_lines: - completed_lines.update(modified_lines) - elif modified_lines == []: # Empty list means no fix - if self.options.verbose >= 2: - print( - '---> Not fixing {f} on line {l}'.format( - f=result['id'], l=result['line']), - file=sys.stderr) - else: # We assume one-line fix when None. - completed_lines.add(result['line']) - else: - if self.options.verbose >= 3: - print( - "---> '{0}' is not defined.".format(fixed_methodname), - file=sys.stderr) - - info = result['info'].strip() - print('---> {0}:{1}:{2}:{3}'.format(self.filename, - result['line'], - result['column'], - info), - file=sys.stderr) - - def fix(self): - """Return a version of the source code with PEP 8 violations fixed.""" - pep8_options = { - 'ignore': self.options.ignore, - 'select': self.options.select, - 'max_line_length': self.options.max_line_length, - } - results = _execute_pep8(pep8_options, self.source) - - if self.options.verbose: - progress = {} - for r in results: - if r['id'] not in progress: - progress[r['id']] = set() - progress[r['id']].add(r['line']) - print('---> {n} issue(s) to fix {progress}'.format( - n=len(results), progress=progress), file=sys.stderr) - - if self.options.line_range: - start, end = self.options.line_range - results = [r for r in results - if start <= r['line'] <= end] - - self._fix_source(filter_results(source=''.join(self.source), - results=results, - aggressive=self.options.aggressive)) - - if self.options.line_range: - # If number of lines has changed then change line_range. - count = sum(sline.count('\n') - for sline in self.source[start - 1:end]) - self.options.line_range[1] = start + count - 1 - - return ''.join(self.source) - - def _fix_reindent(self, result): - """Fix a badly indented line. - - This is done by adding or removing from its initial indent only. - - """ - num_indent_spaces = int(result['info'].split()[1]) - line_index = result['line'] - 1 - target = self.source[line_index] - - self.source[line_index] = ' ' * num_indent_spaces + target.lstrip() - - def fix_e112(self, result): - """Fix under-indented comments.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - if not target.lstrip().startswith('#'): - # Don't screw with invalid syntax. - return [] - - self.source[line_index] = self.indent_word + target - - def fix_e113(self, result): - """Fix over-indented comments.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - indent = _get_indentation(target) - stripped = target.lstrip() - - if not stripped.startswith('#'): - # Don't screw with invalid syntax. - return [] - - self.source[line_index] = indent[1:] + stripped - - def fix_e125(self, result): - """Fix indentation undistinguish from the next logical line.""" - num_indent_spaces = int(result['info'].split()[1]) - line_index = result['line'] - 1 - target = self.source[line_index] - - spaces_to_add = num_indent_spaces - len(_get_indentation(target)) - indent = len(_get_indentation(target)) - modified_lines = [] - - while len(_get_indentation(self.source[line_index])) >= indent: - self.source[line_index] = (' ' * spaces_to_add + - self.source[line_index]) - modified_lines.append(1 + line_index) # Line indexed at 1. - line_index -= 1 - - return modified_lines - - def fix_e201(self, result): - """Remove extraneous whitespace.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - if is_probably_part_of_multiline(target): - return [] - - fixed = fix_whitespace(target, - offset=offset, - replacement='') - - self.source[line_index] = fixed - - def fix_e224(self, result): - """Remove extraneous whitespace around operator.""" - target = self.source[result['line'] - 1] - offset = result['column'] - 1 - fixed = target[:offset] + target[offset:].replace('\t', ' ') - self.source[result['line'] - 1] = fixed - - def fix_e225(self, result): - """Fix missing whitespace around operator.""" - target = self.source[result['line'] - 1] - offset = result['column'] - 1 - fixed = target[:offset] + ' ' + target[offset:] - - # Only proceed if non-whitespace characters match. - # And make sure we don't break the indentation. - if ( - fixed.replace(' ', '') == target.replace(' ', '') and - _get_indentation(fixed) == _get_indentation(target) - ): - self.source[result['line'] - 1] = fixed - else: - return [] - - def fix_e231(self, result): - """Add missing whitespace.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - fixed = target[:offset] + ' ' + target[offset:] - self.source[line_index] = fixed - - def fix_e251(self, result): - """Remove whitespace around parameter '=' sign.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - # This is necessary since pep8 sometimes reports columns that goes - # past the end of the physical line. This happens in cases like, - # foo(bar\n=None) - c = min(result['column'] - 1, - len(target) - 1) - - if target[c].strip(): - fixed = target - else: - fixed = target[:c].rstrip() + target[c:].lstrip() - - # There could be an escaped newline - # - # def foo(a=\ - # 1) - if fixed.endswith(('=\\\n', '=\\\r\n', '=\\\r')): - self.source[line_index] = fixed.rstrip('\n\r \t\\') - self.source[line_index + 1] = self.source[line_index + 1].lstrip() - return [line_index + 1, line_index + 2] # Line indexed at 1 - - self.source[result['line'] - 1] = fixed - - def fix_e262(self, result): - """Fix spacing after comment hash.""" - target = self.source[result['line'] - 1] - offset = result['column'] - - code = target[:offset].rstrip(' \t#') - comment = target[offset:].lstrip(' \t#') - - fixed = code + (' # ' + comment if comment.strip() else '\n') - - self.source[result['line'] - 1] = fixed - - def fix_e271(self, result): - """Fix extraneous whitespace around keywords.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - if is_probably_part_of_multiline(target): - return [] - - fixed = fix_whitespace(target, - offset=offset, - replacement=' ') - - if fixed == target: - return [] - else: - self.source[line_index] = fixed - - def fix_e301(self, result): - """Add missing blank line.""" - cr = '\n' - self.source[result['line'] - 1] = cr + self.source[result['line'] - 1] - - def fix_e302(self, result): - """Add missing 2 blank lines.""" - add_linenum = 2 - int(result['info'].split()[-1]) - cr = '\n' * add_linenum - self.source[result['line'] - 1] = cr + self.source[result['line'] - 1] - - def fix_e303(self, result): - """Remove extra blank lines.""" - delete_linenum = int(result['info'].split('(')[1].split(')')[0]) - 2 - delete_linenum = max(1, delete_linenum) - - # We need to count because pep8 reports an offset line number if there - # are comments. - cnt = 0 - line = result['line'] - 2 - modified_lines = [] - while cnt < delete_linenum and line >= 0: - if not self.source[line].strip(): - self.source[line] = '' - modified_lines.append(1 + line) # Line indexed at 1 - cnt += 1 - line -= 1 - - return modified_lines - - def fix_e304(self, result): - """Remove blank line following function decorator.""" - line = result['line'] - 2 - if not self.source[line].strip(): - self.source[line] = '' - - def fix_e401(self, result): - """Put imports on separate lines.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - if not target.lstrip().startswith('import'): - return [] - - indentation = re.split(pattern=r'\bimport\b', - string=target, maxsplit=1)[0] - fixed = (target[:offset].rstrip('\t ,') + '\n' + - indentation + 'import ' + target[offset:].lstrip('\t ,')) - self.source[line_index] = fixed - - def fix_long_line_logically(self, result, logical): - """Try to make lines fit within --max-line-length characters.""" - if ( - not logical or - len(logical[2]) == 1 or - self.source[result['line'] - 1].lstrip().startswith('#') - ): - return self.fix_long_line_physically(result) - - start_line_index = logical[0][0] - end_line_index = logical[1][0] - logical_lines = logical[2] - - previous_line = get_item(self.source, start_line_index - 1, default='') - next_line = get_item(self.source, end_line_index + 1, default='') - - single_line = join_logical_line(''.join(logical_lines)) - - try: - fixed = self.fix_long_line( - target=single_line, - previous_line=previous_line, - next_line=next_line, - original=''.join(logical_lines)) - except (SyntaxError, tokenize.TokenError): - return self.fix_long_line_physically(result) - - if fixed: - for line_index in range(start_line_index, end_line_index + 1): - self.source[line_index] = '' - self.source[start_line_index] = fixed - return range(start_line_index + 1, end_line_index + 1) - else: - return [] - - def fix_long_line_physically(self, result): - """Try to make lines fit within --max-line-length characters.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - previous_line = get_item(self.source, line_index - 1, default='') - next_line = get_item(self.source, line_index + 1, default='') - - try: - fixed = self.fix_long_line( - target=target, - previous_line=previous_line, - next_line=next_line, - original=target) - except (SyntaxError, tokenize.TokenError): - return [] - - if fixed: - self.source[line_index] = fixed - return [line_index + 1] - else: - return [] - - def fix_long_line(self, target, previous_line, - next_line, original): - cache_entry = (target, previous_line, next_line) - if cache_entry in self.long_line_ignore_cache: - return [] - - if target.lstrip().startswith('#'): - # Wrap commented lines. - return shorten_comment( - line=target, - max_line_length=self.options.max_line_length, - last_comment=not next_line.lstrip().startswith('#')) - - fixed = get_fixed_long_line( - target=target, - previous_line=previous_line, - original=original, - indent_word=self.indent_word, - max_line_length=self.options.max_line_length, - aggressive=self.options.aggressive, - experimental=self.options.experimental, - verbose=self.options.verbose) - if fixed and not code_almost_equal(original, fixed): - return fixed - else: - self.long_line_ignore_cache.add(cache_entry) - return None - - def fix_e502(self, result): - """Remove extraneous escape of newline.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - self.source[line_index] = target.rstrip('\n\r \t\\') + '\n' - - def fix_e701(self, result): - """Put colon-separated compound statement on separate lines.""" - line_index = result['line'] - 1 - target = self.source[line_index] - c = result['column'] - - fixed_source = (target[:c] + '\n' + - _get_indentation(target) + self.indent_word + - target[c:].lstrip('\n\r \t\\')) - self.source[result['line'] - 1] = fixed_source - return [result['line'], result['line'] + 1] - - def fix_e702(self, result, logical): - """Put semicolon-separated compound statement on separate lines.""" - if not logical: - return [] # pragma: no cover - logical_lines = logical[2] - - line_index = result['line'] - 1 - target = self.source[line_index] - - if target.rstrip().endswith('\\'): - # Normalize '1; \\\n2' into '1; 2'. - self.source[line_index] = target.rstrip('\n \r\t\\') - self.source[line_index + 1] = self.source[line_index + 1].lstrip() - return [line_index + 1, line_index + 2] - - if target.rstrip().endswith(';'): - self.source[line_index] = target.rstrip('\n \r\t;') + '\n' - return [line_index + 1] - - offset = result['column'] - 1 - first = target[:offset].rstrip(';').rstrip() - second = (_get_indentation(logical_lines[0]) + - target[offset:].lstrip(';').lstrip()) - - # find inline commnet - inline_comment = None - if '# ' == target[offset:].lstrip(';').lstrip()[:2]: - inline_comment = target[offset:].lstrip(';') - - if inline_comment: - self.source[line_index] = first + inline_comment - else: - self.source[line_index] = first + '\n' + second - return [line_index + 1] - - def fix_e711(self, result): - """Fix comparison with None.""" - (line_index, offset, target) = get_index_offset_contents(result, - self.source) - - right_offset = offset + 2 - if right_offset >= len(target): - return [] - - left = target[:offset].rstrip() - center = target[offset:right_offset] - right = target[right_offset:].lstrip() - - if not right.startswith('None'): - return [] - - if center.strip() == '==': - new_center = 'is' - elif center.strip() == '!=': - new_center = 'is not' - else: - return [] - - self.source[line_index] = ' '.join([left, new_center, right]) - - def fix_e712(self, result): - """Fix (trivial case of) comparison with boolean.""" - (line_index, offset, target) = get_index_offset_contents(result, - self.source) - - # Handle very easy "not" special cases. - if re.match(r'^\s*if [\w.]+ == False:$', target): - self.source[line_index] = re.sub(r'if ([\w.]+) == False:', - r'if not \1:', target, count=1) - elif re.match(r'^\s*if [\w.]+ != True:$', target): - self.source[line_index] = re.sub(r'if ([\w.]+) != True:', - r'if not \1:', target, count=1) - else: - right_offset = offset + 2 - if right_offset >= len(target): - return [] - - left = target[:offset].rstrip() - center = target[offset:right_offset] - right = target[right_offset:].lstrip() - - # Handle simple cases only. - new_right = None - if center.strip() == '==': - if re.match(r'\bTrue\b', right): - new_right = re.sub(r'\bTrue\b *', '', right, count=1) - elif center.strip() == '!=': - if re.match(r'\bFalse\b', right): - new_right = re.sub(r'\bFalse\b *', '', right, count=1) - - if new_right is None: - return [] - - if new_right[0].isalnum(): - new_right = ' ' + new_right - - self.source[line_index] = left + new_right - - def fix_e713(self, result): - """Fix (trivial case of) non-membership check.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - - # Handle very easy case only. - if re.match(r'^\s*if not [\w.]+ in [\w.]+:$', target): - self.source[line_index] = re.sub(r'if not ([\w.]+) in ([\w.]+):', - r'if \1 not in \2:', - target, - count=1) - - def fix_w291(self, result): - """Remove trailing whitespace.""" - fixed_line = self.source[result['line'] - 1].rstrip() - self.source[result['line'] - 1] = fixed_line + '\n' - - def fix_w391(self, _): - """Remove trailing blank lines.""" - blank_count = 0 - for line in reversed(self.source): - line = line.rstrip() - if line: - break - else: - blank_count += 1 - - original_length = len(self.source) - self.source = self.source[:original_length - blank_count] - return range(1, 1 + original_length) - - -def get_index_offset_contents(result, source): - """Return (line_index, column_offset, line_contents).""" - line_index = result['line'] - 1 - return (line_index, - result['column'] - 1, - source[line_index]) - - -def get_fixed_long_line(target, previous_line, original, - indent_word=' ', max_line_length=79, - aggressive=False, experimental=False, verbose=False): - """Break up long line and return result. - - Do this by generating multiple reformatted candidates and then - ranking the candidates to heuristically select the best option. - - """ - indent = _get_indentation(target) - source = target[len(indent):] - assert source.lstrip() == source - - # Check for partial multiline. - tokens = list(generate_tokens(source)) - - candidates = shorten_line( - tokens, source, indent, - indent_word, - max_line_length, - aggressive=aggressive, - experimental=experimental, - previous_line=previous_line) - - # Also sort alphabetically as a tie breaker (for determinism). - candidates = sorted( - sorted(set(candidates).union([target, original])), - key=lambda x: line_shortening_rank( - x, - indent_word, - max_line_length, - experimental=experimental)) - - if verbose >= 4: - print(('-' * 79 + '\n').join([''] + candidates + ['']), - file=wrap_output(sys.stderr, 'utf-8')) - - if candidates: - best_candidate = candidates[0] - # Don't allow things to get longer. - if longest_line_length(best_candidate) > longest_line_length(original): - return None - else: - return best_candidate - - -def longest_line_length(code): - """Return length of longest line.""" - return max(len(line) for line in code.splitlines()) - - -def join_logical_line(logical_line): - """Return single line based on logical line input.""" - indentation = _get_indentation(logical_line) - - return indentation + untokenize_without_newlines( - generate_tokens(logical_line.lstrip())) + '\n' - - -def untokenize_without_newlines(tokens): - """Return source code based on tokens.""" - text = '' - last_row = 0 - last_column = -1 - - for t in tokens: - token_string = t[1] - (start_row, start_column) = t[2] - (end_row, end_column) = t[3] - - if start_row > last_row: - last_column = 0 - if ( - (start_column > last_column or token_string == '\n') and - not text.endswith(' ') - ): - text += ' ' - - if token_string != '\n': - text += token_string - - last_row = end_row - last_column = end_column - - return text.rstrip() - - -def _find_logical(source_lines): - # Make a variable which is the index of all the starts of lines. - logical_start = [] - logical_end = [] - last_newline = True - parens = 0 - for t in generate_tokens(''.join(source_lines)): - if t[0] in [tokenize.COMMENT, tokenize.DEDENT, - tokenize.INDENT, tokenize.NL, - tokenize.ENDMARKER]: - continue - if not parens and t[0] in [tokenize.NEWLINE, tokenize.SEMI]: - last_newline = True - logical_end.append((t[3][0] - 1, t[2][1])) - continue - if last_newline and not parens: - logical_start.append((t[2][0] - 1, t[2][1])) - last_newline = False - if t[0] == tokenize.OP: - if t[1] in '([{': - parens += 1 - elif t[1] in '}])': - parens -= 1 - return (logical_start, logical_end) - - -def _get_logical(source_lines, result, logical_start, logical_end): - """Return the logical line corresponding to the result. - - Assumes input is already E702-clean. - - """ - row = result['line'] - 1 - col = result['column'] - 1 - ls = None - le = None - for i in range(0, len(logical_start), 1): - assert logical_end - x = logical_end[i] - if x[0] > row or (x[0] == row and x[1] > col): - le = x - ls = logical_start[i] - break - if ls is None: - return None - original = source_lines[ls[0]:le[0] + 1] - return ls, le, original - - -def get_item(items, index, default=None): - if 0 <= index < len(items): - return items[index] - else: - return default - - -def reindent(source, indent_size): - """Reindent all lines.""" - reindenter = Reindenter(source) - return reindenter.run(indent_size) - - -def code_almost_equal(a, b): - """Return True if code is similar. - - Ignore whitespace when comparing specific line. - - """ - split_a = split_and_strip_non_empty_lines(a) - split_b = split_and_strip_non_empty_lines(b) - - if len(split_a) != len(split_b): - return False - - for index in range(len(split_a)): - if ''.join(split_a[index].split()) != ''.join(split_b[index].split()): - return False - - return True - - -def split_and_strip_non_empty_lines(text): - """Return lines split by newline. - - Ignore empty lines. - - """ - return [line.strip() for line in text.splitlines() if line.strip()] - - -def fix_e265(source, aggressive=False): # pylint: disable=unused-argument - """Format block comments.""" - if '#' not in source: - # Optimization. - return source - - ignored_line_numbers = multiline_string_lines( - source, - include_docstrings=True) | set(commented_out_code_lines(source)) - - fixed_lines = [] - sio = io.StringIO(source) - for (line_number, line) in enumerate(sio.readlines(), start=1): - if ( - line.lstrip().startswith('#') and - line_number not in ignored_line_numbers - ): - indentation = _get_indentation(line) - line = line.lstrip() - - # Normalize beginning if not a shebang. - if len(line) > 1: - pos = next((index for index, c in enumerate(line) - if c != '#')) - if ( - # Leave multiple spaces like '# ' alone. - (line[:pos].count('#') > 1 or line[1].isalnum()) and - # Leave stylistic outlined blocks alone. - not line.rstrip().endswith('#') - ): - line = '# ' + line.lstrip('# \t') - - fixed_lines.append(indentation + line) - else: - fixed_lines.append(line) - - return ''.join(fixed_lines) - - -def refactor(source, fixer_names, ignore=None, filename=''): - """Return refactored code using lib2to3. - - Skip if ignore string is produced in the refactored code. - - """ - from lib2to3 import pgen2 - try: - new_text = refactor_with_2to3(source, - fixer_names=fixer_names, - filename=filename) - except (pgen2.parse.ParseError, - SyntaxError, - UnicodeDecodeError, - UnicodeEncodeError): - return source - - if ignore: - if ignore in new_text and ignore not in source: - return source - - return new_text - - -def code_to_2to3(select, ignore): - fixes = set() - for code, fix in CODE_TO_2TO3.items(): - if code_match(code, select=select, ignore=ignore): - fixes |= set(fix) - return fixes - - -def fix_2to3(source, - aggressive=True, select=None, ignore=None, filename=''): - """Fix various deprecated code (via lib2to3).""" - if not aggressive: - return source - - select = select or [] - ignore = ignore or [] - - return refactor(source, - code_to_2to3(select=select, - ignore=ignore), - filename=filename) - - -def fix_w602(source, aggressive=True): - """Fix deprecated form of raising exception.""" - if not aggressive: - return source - - return refactor(source, ['raise'], - ignore='with_traceback') - - -def find_newline(source): - """Return type of newline used in source. - - Input is a list of lines. - - """ - assert not isinstance(source, unicode) - - counter = collections.defaultdict(int) - for line in source: - if line.endswith(CRLF): - counter[CRLF] += 1 - elif line.endswith(CR): - counter[CR] += 1 - elif line.endswith(LF): - counter[LF] += 1 - - return (sorted(counter, key=counter.get, reverse=True) or [LF])[0] - - -def _get_indentword(source): - """Return indentation type.""" - indent_word = ' ' # Default in case source has no indentation - try: - for t in generate_tokens(source): - if t[0] == token.INDENT: - indent_word = t[1] - break - except (SyntaxError, tokenize.TokenError): - pass - return indent_word - - -def _get_indentation(line): - """Return leading whitespace.""" - if line.strip(): - non_whitespace_index = len(line) - len(line.lstrip()) - return line[:non_whitespace_index] - else: - return '' - - -def get_diff_text(old, new, filename): - """Return text of unified diff between old and new.""" - newline = '\n' - diff = difflib.unified_diff( - old, new, - 'original/' + filename, - 'fixed/' + filename, - lineterm=newline) - - text = '' - for line in diff: - text += line - - # Work around missing newline (http://bugs.python.org/issue2142). - if text and not line.endswith(newline): - text += newline + r'\ No newline at end of file' + newline - - return text - - -def _priority_key(pep8_result): - """Key for sorting PEP8 results. - - Global fixes should be done first. This is important for things like - indentation. - - """ - priority = [ - # Fix multiline colon-based before semicolon based. - 'e701', - # Break multiline statements early. - 'e702', - # Things that make lines longer. - 'e225', 'e231', - # Remove extraneous whitespace before breaking lines. - 'e201', - # Shorten whitespace in comment before resorting to wrapping. - 'e262' - ] - middle_index = 10000 - lowest_priority = [ - # We need to shorten lines last since the logical fixer can get in a - # loop, which causes us to exit early. - 'e501' - ] - key = pep8_result['id'].lower() - try: - return priority.index(key) - except ValueError: - try: - return middle_index + lowest_priority.index(key) + 1 - except ValueError: - return middle_index - - -def shorten_line(tokens, source, indentation, indent_word, max_line_length, - aggressive=False, experimental=False, previous_line=''): - """Separate line at OPERATOR. - - Multiple candidates will be yielded. - - """ - for candidate in _shorten_line(tokens=tokens, - source=source, - indentation=indentation, - indent_word=indent_word, - aggressive=aggressive, - previous_line=previous_line): - yield candidate - - if aggressive: - for key_token_strings in SHORTEN_OPERATOR_GROUPS: - shortened = _shorten_line_at_tokens( - tokens=tokens, - source=source, - indentation=indentation, - indent_word=indent_word, - key_token_strings=key_token_strings, - aggressive=aggressive) - - if shortened is not None and shortened != source: - yield shortened - - if experimental: - for shortened in _shorten_line_at_tokens_new( - tokens=tokens, - source=source, - indentation=indentation, - max_line_length=max_line_length): - - yield shortened - - -def _shorten_line(tokens, source, indentation, indent_word, - aggressive=False, previous_line=''): - """Separate line at OPERATOR. - - The input is expected to be free of newlines except for inside multiline - strings and at the end. - - Multiple candidates will be yielded. - - """ - for (token_type, - token_string, - start_offset, - end_offset) in token_offsets(tokens): - - if ( - token_type == tokenize.COMMENT and - not is_probably_part_of_multiline(previous_line) and - not is_probably_part_of_multiline(source) and - not source[start_offset + 1:].strip().lower().startswith( - ('noqa', 'pragma:', 'pylint:')) - ): - # Move inline comments to previous line. - first = source[:start_offset] - second = source[start_offset:] - yield (indentation + second.strip() + '\n' + - indentation + first.strip() + '\n') - elif token_type == token.OP and token_string != '=': - # Don't break on '=' after keyword as this violates PEP 8. - - assert token_type != token.INDENT - - first = source[:end_offset] - - second_indent = indentation - if first.rstrip().endswith('('): - second_indent += indent_word - elif '(' in first: - second_indent += ' ' * (1 + first.find('(')) - else: - second_indent += indent_word - - second = (second_indent + source[end_offset:].lstrip()) - if ( - not second.strip() or - second.lstrip().startswith('#') - ): - continue - - # Do not begin a line with a comma - if second.lstrip().startswith(','): - continue - # Do end a line with a dot - if first.rstrip().endswith('.'): - continue - if token_string in '+-*/': - fixed = first + ' \\' + '\n' + second - else: - fixed = first + '\n' + second - - # Only fix if syntax is okay. - if check_syntax(normalize_multiline(fixed) - if aggressive else fixed): - yield indentation + fixed - - -# A convenient way to handle tokens. -Token = collections.namedtuple('Token', ['token_type', 'token_string', - 'spos', 'epos', 'line']) - - -class ReformattedLines(object): - - """The reflowed lines of atoms. - - Each part of the line is represented as an "atom." They can be moved - around when need be to get the optimal formatting. - - """ - - ########################################################################### - # Private Classes - - class _Indent(object): - - """Represent an indentation in the atom stream.""" - - def __init__(self, indent_amt): - self._indent_amt = indent_amt - - def emit(self): - return ' ' * self._indent_amt - - @property - def size(self): - return self._indent_amt - - class _Space(object): - - """Represent a space in the atom stream.""" - - def emit(self): - return ' ' - - @property - def size(self): - return 1 - - class _LineBreak(object): - - """Represent a line break in the atom stream.""" - - def emit(self): - return '\n' - - @property - def size(self): - return 0 - - def __init__(self, max_line_length): - self._max_line_length = max_line_length - self._lines = [] - self._bracket_depth = 0 - self._prev_item = None - self._prev_prev_item = None - - def __repr__(self): - return self.emit() - - ########################################################################### - # Public Methods - - def add(self, obj, indent_amt, break_after_open_bracket): - if isinstance(obj, Atom): - self._add_item(obj, indent_amt) - return - - self._add_container(obj, indent_amt, break_after_open_bracket) - - def add_comment(self, item): - num_spaces = 2 - if len(self._lines) > 1: - if isinstance(self._lines[-1], self._Space): - num_spaces -= 1 - if len(self._lines) > 2: - if isinstance(self._lines[-2], self._Space): - num_spaces -= 1 - - while num_spaces > 0: - self._lines.append(self._Space()) - num_spaces -= 1 - self._lines.append(item) - - def add_indent(self, indent_amt): - self._lines.append(self._Indent(indent_amt)) - - def add_line_break(self, indent): - self._lines.append(self._LineBreak()) - self.add_indent(len(indent)) - - def add_line_break_at(self, index, indent_amt): - self._lines.insert(index, self._LineBreak()) - self._lines.insert(index + 1, self._Indent(indent_amt)) - - def add_space_if_needed(self, curr_text, equal=False): - if ( - not self._lines or isinstance( - self._lines[-1], (self._LineBreak, self._Indent, self._Space)) - ): - return - - prev_text = unicode(self._prev_item) - prev_prev_text = ( - unicode(self._prev_prev_item) if self._prev_prev_item else '') - - if ( - # The previous item was a keyword or identifier and the current - # item isn't an operator that doesn't require a space. - ((self._prev_item.is_keyword or self._prev_item.is_string or - self._prev_item.is_name or self._prev_item.is_number) and - (curr_text[0] not in '([{.,:}])' or - (curr_text[0] == '=' and equal))) or - - # Don't place spaces around a '.', unless it's in an 'import' - # statement. - ((prev_prev_text != 'from' and prev_text[-1] != '.' and - curr_text != 'import') and - - # Don't place a space before a colon. - curr_text[0] != ':' and - - # Don't split up ending brackets by spaces. - ((prev_text[-1] in '}])' and curr_text[0] not in '.,}])') or - - # Put a space after a colon or comma. - prev_text[-1] in ':,' or - - # Put space around '=' if asked to. - (equal and prev_text == '=') or - - # Put spaces around non-unary arithmetic operators. - ((self._prev_prev_item and - (prev_text not in '+-' and - (self._prev_prev_item.is_name or - self._prev_prev_item.is_number or - self._prev_prev_item.is_string)) and - prev_text in ('+', '-', '%', '*', '/', '//', '**', 'in'))))) - ): - self._lines.append(self._Space()) - - def previous_item(self): - """Return the previous non-whitespace item.""" - return self._prev_item - - def fits_on_current_line(self, item_extent): - return self.current_size() + item_extent <= self._max_line_length - - def current_size(self): - """The size of the current line minus the indentation.""" - size = 0 - for item in reversed(self._lines): - size += item.size - if isinstance(item, self._LineBreak): - break - - return size - - def line_empty(self): - return (self._lines and - isinstance(self._lines[-1], - (self._LineBreak, self._Indent))) - - def emit(self): - string = '' - for item in self._lines: - if isinstance(item, self._LineBreak): - string = string.rstrip() - string += item.emit() - - return string.rstrip() + '\n' - - ########################################################################### - # Private Methods - - def _add_item(self, item, indent_amt): - """Add an item to the line. - - Reflow the line to get the best formatting after the item is - inserted. The bracket depth indicates if the item is being - inserted inside of a container or not. - - """ - if self._prev_item and self._prev_item.is_string and item.is_string: - # Place consecutive string literals on separate lines. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - - item_text = unicode(item) - if self._lines and self._bracket_depth: - # Adding the item into a container. - self._prevent_default_initializer_splitting(item, indent_amt) - - if item_text in '.,)]}': - self._split_after_delimiter(item, indent_amt) - - elif self._lines and not self.line_empty(): - # Adding the item outside of a container. - if self.fits_on_current_line(len(item_text)): - self._enforce_space(item) - - else: - # Line break for the new item. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - - self._lines.append(item) - self._prev_item, self._prev_prev_item = item, self._prev_item - - if item_text in '([{': - self._bracket_depth += 1 - - elif item_text in '}])': - self._bracket_depth -= 1 - assert self._bracket_depth >= 0 - - def _add_container(self, container, indent_amt, break_after_open_bracket): - actual_indent = indent_amt + 1 - - if ( - unicode(self._prev_item) != '=' and - not self.line_empty() and - not self.fits_on_current_line( - container.size + self._bracket_depth + 2) - ): - - if unicode(container)[0] == '(' and self._prev_item.is_name: - # Don't split before the opening bracket of a call. - break_after_open_bracket = True - actual_indent = indent_amt + 4 - elif ( - break_after_open_bracket or - unicode(self._prev_item) not in '([{' - ): - # If the container doesn't fit on the current line and the - # current line isn't empty, place the container on the next - # line. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - break_after_open_bracket = False - else: - actual_indent = self.current_size() + 1 - break_after_open_bracket = False - - if isinstance(container, (ListComprehension, IfExpression)): - actual_indent = indent_amt - - # Increase the continued indentation only if recursing on a - # container. - container.reflow(self, ' ' * actual_indent, - break_after_open_bracket=break_after_open_bracket) - - def _prevent_default_initializer_splitting(self, item, indent_amt): - """Prevent splitting between a default initializer. - - When there is a default initializer, it's best to keep it all on - the same line. It's nicer and more readable, even if it goes - over the maximum allowable line length. This goes back along the - current line to determine if we have a default initializer, and, - if so, to remove extraneous whitespaces and add a line - break/indent before it if needed. - - """ - if unicode(item) == '=': - # This is the assignment in the initializer. Just remove spaces for - # now. - self._delete_whitespace() - return - - if (not self._prev_item or not self._prev_prev_item or - unicode(self._prev_item) != '='): - return - - self._delete_whitespace() - prev_prev_index = self._lines.index(self._prev_prev_item) - - if ( - isinstance(self._lines[prev_prev_index - 1], self._Indent) or - self.fits_on_current_line(item.size + 1) - ): - # The default initializer is already the only item on this line. - # Don't insert a newline here. - return - - # Replace the space with a newline/indent combo. - if isinstance(self._lines[prev_prev_index - 1], self._Space): - del self._lines[prev_prev_index - 1] - - self.add_line_break_at(self._lines.index(self._prev_prev_item), - indent_amt) - - def _split_after_delimiter(self, item, indent_amt): - """Split the line only after a delimiter.""" - self._delete_whitespace() - - if self.fits_on_current_line(item.size): - return - - last_space = None - for item in reversed(self._lines): - if ( - last_space and - (not isinstance(item, Atom) or not item.is_colon) - ): - break - else: - last_space = None - if isinstance(item, self._Space): - last_space = item - if isinstance(item, (self._LineBreak, self._Indent)): - return - - if not last_space: - return - - self.add_line_break_at(self._lines.index(last_space), indent_amt) - - def _enforce_space(self, item): - """Enforce a space in certain situations. - - There are cases where we will want a space where normally we - wouldn't put one. This just enforces the addition of a space. - - """ - if isinstance(self._lines[-1], - (self._Space, self._LineBreak, self._Indent)): - return - - if not self._prev_item: - return - - item_text = unicode(item) - prev_text = unicode(self._prev_item) - - # Prefer a space around a '.' in an import statement, and between the - # 'import' and '('. - if ( - (item_text == '.' and prev_text == 'from') or - (item_text == 'import' and prev_text == '.') or - (item_text == '(' and prev_text == 'import') - ): - self._lines.append(self._Space()) - - def _delete_whitespace(self): - """Delete all whitespace from the end of the line.""" - while isinstance(self._lines[-1], (self._Space, self._LineBreak, - self._Indent)): - del self._lines[-1] - - -class Atom(object): - - """The smallest unbreakable unit that can be reflowed.""" - - def __init__(self, atom): - self._atom = atom - - def __repr__(self): - return self._atom.token_string - - def __len__(self): - return self.size - - def reflow( - self, reflowed_lines, continued_indent, extent, - break_after_open_bracket=False, - is_list_comp_or_if_expr=False, - next_is_dot=False - ): - if self._atom.token_type == tokenize.COMMENT: - reflowed_lines.add_comment(self) - return - - total_size = extent if extent else self.size - - if self._atom.token_string not in ',:([{}])': - # Some atoms will need an extra 1-sized space token after them. - total_size += 1 - - prev_item = reflowed_lines.previous_item() - if ( - not is_list_comp_or_if_expr and - not reflowed_lines.fits_on_current_line(total_size) and - not (next_is_dot and - reflowed_lines.fits_on_current_line(self.size + 1)) and - not reflowed_lines.line_empty() and - not self.is_colon and - not (prev_item and prev_item.is_name and - unicode(self) == '(') - ): - # Start a new line if there is already something on the line and - # adding this atom would make it go over the max line length. - reflowed_lines.add_line_break(continued_indent) - else: - reflowed_lines.add_space_if_needed(unicode(self)) - - reflowed_lines.add(self, len(continued_indent), - break_after_open_bracket) - - def emit(self): - return self.__repr__() - - @property - def is_keyword(self): - return keyword.iskeyword(self._atom.token_string) - - @property - def is_string(self): - return self._atom.token_type == tokenize.STRING - - @property - def is_name(self): - return self._atom.token_type == tokenize.NAME - - @property - def is_number(self): - return self._atom.token_type == tokenize.NUMBER - - @property - def is_comma(self): - return self._atom.token_string == ',' - - @property - def is_colon(self): - return self._atom.token_string == ':' - - @property - def size(self): - return len(self._atom.token_string) - - -class Container(object): - - """Base class for all container types.""" - - def __init__(self, items): - self._items = items - - def __repr__(self): - string = '' - last_was_keyword = False - - for item in self._items: - if item.is_comma: - string += ', ' - elif item.is_colon: - string += ': ' - else: - item_string = unicode(item) - if ( - string and - (last_was_keyword or - (not string.endswith(tuple('([{,.:}]) ')) and - not item_string.startswith(tuple('([{,.:}])')))) - ): - string += ' ' - string += item_string - - last_was_keyword = item.is_keyword - return string - - def __iter__(self): - for element in self._items: - yield element - - def __getitem__(self, idx): - return self._items[idx] - - def reflow(self, reflowed_lines, continued_indent, - break_after_open_bracket=False): - last_was_container = False - for (index, item) in enumerate(self._items): - next_item = get_item(self._items, index + 1) - - if isinstance(item, Atom): - is_list_comp_or_if_expr = ( - isinstance(self, (ListComprehension, IfExpression))) - item.reflow(reflowed_lines, continued_indent, - self._get_extent(index), - is_list_comp_or_if_expr=is_list_comp_or_if_expr, - next_is_dot=(next_item and - unicode(next_item) == '.')) - if last_was_container and item.is_comma: - reflowed_lines.add_line_break(continued_indent) - last_was_container = False - else: # isinstance(item, Container) - reflowed_lines.add(item, len(continued_indent), - break_after_open_bracket) - last_was_container = not isinstance(item, (ListComprehension, - IfExpression)) - - if ( - break_after_open_bracket and index == 0 and - # Prefer to keep empty containers together instead of - # separating them. - unicode(item) == self.open_bracket and - (not next_item or unicode(next_item) != self.close_bracket) and - (len(self._items) != 3 or not isinstance(next_item, Atom)) - ): - reflowed_lines.add_line_break(continued_indent) - break_after_open_bracket = False - else: - next_next_item = get_item(self._items, index + 2) - if ( - unicode(item) not in ['.', '%', 'in'] and - next_item and not isinstance(next_item, Container) and - unicode(next_item) != ':' and - next_next_item and (not isinstance(next_next_item, Atom) or - unicode(next_item) == 'not') and - not reflowed_lines.line_empty() and - not reflowed_lines.fits_on_current_line( - self._get_extent(index + 1) + 2) - ): - reflowed_lines.add_line_break(continued_indent) - - def _get_extent(self, index): - """The extent of the full element. - - E.g., the length of a function call or keyword. - - """ - extent = 0 - prev_item = get_item(self._items, index - 1) - seen_dot = prev_item and unicode(prev_item) == '.' - while index < len(self._items): - item = get_item(self._items, index) - index += 1 - - if isinstance(item, (ListComprehension, IfExpression)): - break - - if isinstance(item, Container): - if prev_item and prev_item.is_name: - if seen_dot: - extent += 1 - else: - extent += item.size - - prev_item = item - continue - elif (unicode(item) not in ['.', '=', ':', 'not'] and - not item.is_name and not item.is_string): - break - - if unicode(item) == '.': - seen_dot = True - - extent += item.size - prev_item = item - - return extent - - @property - def is_string(self): - return False - - @property - def size(self): - return len(self.__repr__()) - - @property - def is_keyword(self): - return False - - @property - def is_name(self): - return False - - @property - def is_comma(self): - return False - - @property - def is_colon(self): - return False - - @property - def open_bracket(self): - return None - - @property - def close_bracket(self): - return None - - -class Tuple(Container): - - """A high-level representation of a tuple.""" - - @property - def open_bracket(self): - return '(' - - @property - def close_bracket(self): - return ')' - - -class List(Container): - - """A high-level representation of a list.""" - - @property - def open_bracket(self): - return '[' - - @property - def close_bracket(self): - return ']' - - -class DictOrSet(Container): - - """A high-level representation of a dictionary or set.""" - - @property - def open_bracket(self): - return '{' - - @property - def close_bracket(self): - return '}' - - -class ListComprehension(Container): - - """A high-level representation of a list comprehension.""" - - @property - def size(self): - length = 0 - for item in self._items: - if isinstance(item, IfExpression): - break - length += item.size - return length - - -class IfExpression(Container): - - """A high-level representation of an if-expression.""" - - -def _parse_container(tokens, index, for_or_if=None): - """Parse a high-level container, such as a list, tuple, etc.""" - - # Store the opening bracket. - items = [Atom(Token(*tokens[index]))] - index += 1 - - num_tokens = len(tokens) - while index < num_tokens: - tok = Token(*tokens[index]) - - if tok.token_string in ',)]}': - # First check if we're at the end of a list comprehension or - # if-expression. Don't add the ending token as part of the list - # comprehension or if-expression, because they aren't part of those - # constructs. - if for_or_if == 'for': - return (ListComprehension(items), index - 1) - - elif for_or_if == 'if': - return (IfExpression(items), index - 1) - - # We've reached the end of a container. - items.append(Atom(tok)) - - # If not, then we are at the end of a container. - if tok.token_string == ')': - # The end of a tuple. - return (Tuple(items), index) - - elif tok.token_string == ']': - # The end of a list. - return (List(items), index) - - elif tok.token_string == '}': - # The end of a dictionary or set. - return (DictOrSet(items), index) - - elif tok.token_string in '([{': - # A sub-container is being defined. - (container, index) = _parse_container(tokens, index) - items.append(container) - - elif tok.token_string == 'for': - (container, index) = _parse_container(tokens, index, 'for') - items.append(container) - - elif tok.token_string == 'if': - (container, index) = _parse_container(tokens, index, 'if') - items.append(container) - - else: - items.append(Atom(tok)) - - index += 1 - - return (None, None) - - -def _parse_tokens(tokens): - """Parse the tokens. - - This converts the tokens into a form where we can manipulate them - more easily. - - """ - - index = 0 - parsed_tokens = [] - - num_tokens = len(tokens) - while index < num_tokens: - tok = Token(*tokens[index]) - - assert tok.token_type != token.INDENT - if tok.token_type == tokenize.NEWLINE: - # There's only one newline and it's at the end. - break - - if tok.token_string in '([{': - (container, index) = _parse_container(tokens, index) - if not container: - return None - parsed_tokens.append(container) - else: - parsed_tokens.append(Atom(tok)) - - index += 1 - - return parsed_tokens - - -def _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line): - """Reflow the lines so that it looks nice.""" - - if unicode(parsed_tokens[0]) == 'def': - # A function definition gets indented a bit more. - continued_indent = indentation + ' ' * 2 * DEFAULT_INDENT_SIZE - else: - continued_indent = indentation + ' ' * DEFAULT_INDENT_SIZE - - break_after_open_bracket = not start_on_prefix_line - - lines = ReformattedLines(max_line_length) - lines.add_indent(len(indentation.lstrip('\r\n'))) - - if not start_on_prefix_line: - # If splitting after the opening bracket will cause the first element - # to be aligned weirdly, don't try it. - first_token = get_item(parsed_tokens, 0) - second_token = get_item(parsed_tokens, 1) - - if ( - first_token and second_token and - unicode(second_token)[0] == '(' and - len(indentation) + len(first_token) + 1 == len(continued_indent) - ): - return None - - for item in parsed_tokens: - lines.add_space_if_needed(unicode(item), equal=True) - - save_continued_indent = continued_indent - if start_on_prefix_line and isinstance(item, Container): - start_on_prefix_line = False - continued_indent = ' ' * (lines.current_size() + 1) - - item.reflow(lines, continued_indent, break_after_open_bracket) - continued_indent = save_continued_indent - - return lines.emit() - - -def _shorten_line_at_tokens_new(tokens, source, indentation, - max_line_length): - """Shorten the line taking its length into account. - - The input is expected to be free of newlines except for inside - multiline strings and at the end. - - """ - # Yield the original source so to see if it's a better choice than the - # shortened candidate lines we generate here. - yield indentation + source - - parsed_tokens = _parse_tokens(tokens) - - if parsed_tokens: - # Perform two reflows. The first one starts on the same line as the - # prefix. The second starts on the line after the prefix. - fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line=True) - if fixed and check_syntax(normalize_multiline(fixed.lstrip())): - yield fixed - - fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line=False) - if fixed and check_syntax(normalize_multiline(fixed.lstrip())): - yield fixed - - -def _shorten_line_at_tokens(tokens, source, indentation, indent_word, - key_token_strings, aggressive): - """Separate line by breaking at tokens in key_token_strings. - - The input is expected to be free of newlines except for inside - multiline strings and at the end. - - """ - offsets = [] - for (index, _t) in enumerate(token_offsets(tokens)): - (token_type, - token_string, - start_offset, - end_offset) = _t - - assert token_type != token.INDENT - - if token_string in key_token_strings: - # Do not break in containers with zero or one items. - unwanted_next_token = { - '(': ')', - '[': ']', - '{': '}'}.get(token_string) - if unwanted_next_token: - if ( - get_item(tokens, - index + 1, - default=[None, None])[1] == unwanted_next_token or - get_item(tokens, - index + 2, - default=[None, None])[1] == unwanted_next_token - ): - continue - - if ( - index > 2 and token_string == '(' and - tokens[index - 1][1] in ',(%[' - ): - # Don't split after a tuple start, or before a tuple start if - # the tuple is in a list. - continue - - if end_offset < len(source) - 1: - # Don't split right before newline. - offsets.append(end_offset) - else: - # Break at adjacent strings. These were probably meant to be on - # separate lines in the first place. - previous_token = get_item(tokens, index - 1) - if ( - token_type == tokenize.STRING and - previous_token and previous_token[0] == tokenize.STRING - ): - offsets.append(start_offset) - - current_indent = None - fixed = None - for line in split_at_offsets(source, offsets): - if fixed: - fixed += '\n' + current_indent + line - - for symbol in '([{': - if line.endswith(symbol): - current_indent += indent_word - else: - # First line. - fixed = line - assert not current_indent - current_indent = indent_word - - assert fixed is not None - - if check_syntax(normalize_multiline(fixed) - if aggressive > 1 else fixed): - return indentation + fixed - else: - return None - - -def token_offsets(tokens): - """Yield tokens and offsets.""" - end_offset = 0 - previous_end_row = 0 - previous_end_column = 0 - for t in tokens: - token_type = t[0] - token_string = t[1] - (start_row, start_column) = t[2] - (end_row, end_column) = t[3] - - # Account for the whitespace between tokens. - end_offset += start_column - if previous_end_row == start_row: - end_offset -= previous_end_column - - # Record the start offset of the token. - start_offset = end_offset - - # Account for the length of the token itself. - end_offset += len(token_string) - - yield (token_type, - token_string, - start_offset, - end_offset) - - previous_end_row = end_row - previous_end_column = end_column - - -def normalize_multiline(line): - """Normalize multiline-related code that will cause syntax error. - - This is for purposes of checking syntax. - - """ - if line.startswith('def ') and line.rstrip().endswith(':'): - return line + ' pass' - elif line.startswith('return '): - return 'def _(): ' + line - elif line.startswith('@'): - return line + 'def _(): pass' - elif line.startswith('class '): - return line + ' pass' - elif line.startswith(('if ', 'elif ', 'for ', 'while ')): - return line + ' pass' - else: - return line - - -def fix_whitespace(line, offset, replacement): - """Replace whitespace at offset and return fixed line.""" - # Replace escaped newlines too - left = line[:offset].rstrip('\n\r \t\\') - right = line[offset:].lstrip('\n\r \t\\') - if right.startswith('#'): - return line - else: - return left + replacement + right - - -def _execute_pep8(pep8_options, source): - """Execute pep8 via python method calls.""" - class QuietReport(pep8.BaseReport): - - """Version of checker that does not print.""" - - def __init__(self, options): - super(QuietReport, self).__init__(options) - self.__full_error_results = [] - - def error(self, line_number, offset, text, check): - """Collect errors.""" - code = super(QuietReport, self).error(line_number, - offset, - text, - check) - if code: - self.__full_error_results.append( - {'id': code, - 'line': line_number, - 'column': offset + 1, - 'info': text}) - - def full_error_results(self): - """Return error results in detail. - - Results are in the form of a list of dictionaries. Each - dictionary contains 'id', 'line', 'column', and 'info'. - - """ - return self.__full_error_results - - checker = pep8.Checker('', lines=source, - reporter=QuietReport, **pep8_options) - checker.check_all() - return checker.report.full_error_results() - - -def _remove_leading_and_normalize(line): - return line.lstrip().rstrip(CR + LF) + '\n' - - -class Reindenter(object): - - """Reindents badly-indented code to uniformly use four-space indentation. - - Released to the public domain, by Tim Peters, 03 October 2000. - - """ - - def __init__(self, input_text): - sio = io.StringIO(input_text) - source_lines = sio.readlines() - - self.string_content_line_numbers = multiline_string_lines(input_text) - - # File lines, rstripped & tab-expanded. Dummy at start is so - # that we can use tokenize's 1-based line numbering easily. - # Note that a line is all-blank iff it is a newline. - self.lines = [] - for line_number, line in enumerate(source_lines, start=1): - # Do not modify if inside a multiline string. - if line_number in self.string_content_line_numbers: - self.lines.append(line) - else: - # Only expand leading tabs. - self.lines.append(_get_indentation(line).expandtabs() + - _remove_leading_and_normalize(line)) - - self.lines.insert(0, None) - self.index = 1 # index into self.lines of next line - self.input_text = input_text - - def run(self, indent_size=DEFAULT_INDENT_SIZE): - """Fix indentation and return modified line numbers. - - Line numbers are indexed at 1. - - """ - if indent_size < 1: - return self.input_text - - try: - stats = _reindent_stats(tokenize.generate_tokens(self.getline)) - except (SyntaxError, tokenize.TokenError): - return self.input_text - # Remove trailing empty lines. - lines = self.lines - # Sentinel. - stats.append((len(lines), 0)) - # Map count of leading spaces to # we want. - have2want = {} - # Program after transformation. - after = [] - # Copy over initial empty lines -- there's nothing to do until - # we see a line with *something* on it. - i = stats[0][0] - after.extend(lines[1:i]) - for i in range(len(stats) - 1): - thisstmt, thislevel = stats[i] - nextstmt = stats[i + 1][0] - have = _leading_space_count(lines[thisstmt]) - want = thislevel * indent_size - if want < 0: - # A comment line. - if have: - # An indented comment line. If we saw the same - # indentation before, reuse what it most recently - # mapped to. - want = have2want.get(have, -1) - if want < 0: - # Then it probably belongs to the next real stmt. - for j in range(i + 1, len(stats) - 1): - jline, jlevel = stats[j] - if jlevel >= 0: - if have == _leading_space_count(lines[jline]): - want = jlevel * indent_size - break - if want < 0: # Maybe it's a hanging - # comment like this one, - # in which case we should shift it like its base - # line got shifted. - for j in range(i - 1, -1, -1): - jline, jlevel = stats[j] - if jlevel >= 0: - want = (have + _leading_space_count( - after[jline - 1]) - - _leading_space_count(lines[jline])) - break - if want < 0: - # Still no luck -- leave it alone. - want = have - else: - want = 0 - assert want >= 0 - have2want[have] = want - diff = want - have - if diff == 0 or have == 0: - after.extend(lines[thisstmt:nextstmt]) - else: - for line_number, line in enumerate(lines[thisstmt:nextstmt], - start=thisstmt): - if line_number in self.string_content_line_numbers: - after.append(line) - elif diff > 0: - if line == '\n': - after.append(line) - else: - after.append(' ' * diff + line) - else: - remove = min(_leading_space_count(line), -diff) - after.append(line[remove:]) - - return ''.join(after) - - def getline(self): - """Line-getter for tokenize.""" - if self.index >= len(self.lines): - line = '' - else: - line = self.lines[self.index] - self.index += 1 - return line - - -def _reindent_stats(tokens): - """Return list of (lineno, indentlevel) pairs. - - One for each stmt and comment line. indentlevel is -1 for comment lines, as - a signal that tokenize doesn't know what to do about them; indeed, they're - our headache! - - """ - find_stmt = 1 # Next token begins a fresh stmt? - level = 0 # Current indent level. - stats = [] - - for t in tokens: - token_type = t[0] - sline = t[2][0] - line = t[4] - - if token_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+)? - find_stmt = 1 - - elif token_type == tokenize.INDENT: - find_stmt = 1 - level += 1 - - elif token_type == tokenize.DEDENT: - find_stmt = 1 - level -= 1 - - elif token_type == tokenize.COMMENT: - if find_stmt: - stats.append((sline, -1)) - # But we're still looking for a new stmt, so leave - # find_stmt alone. - - elif token_type == tokenize.NL: - pass - - elif find_stmt: - # This is the first "real token" following a NEWLINE, so it - # must be the first token of the next program statement, or an - # ENDMARKER. - find_stmt = 0 - if line: # Not endmarker. - stats.append((sline, level)) - - return stats - - -def _leading_space_count(line): - """Return number of leading spaces in line.""" - i = 0 - while i < len(line) and line[i] == ' ': - i += 1 - return i - - -def refactor_with_2to3(source_text, fixer_names, filename=''): - """Use lib2to3 to refactor the source. - - Return the refactored source code. - - """ - from lib2to3.refactor import RefactoringTool - fixers = ['lib2to3.fixes.fix_' + name for name in fixer_names] - tool = RefactoringTool(fixer_names=fixers, explicit=fixers) - - from lib2to3.pgen2 import tokenize as lib2to3_tokenize - try: - # The name parameter is necessary particularly for the "import" fixer. - return unicode(tool.refactor_string(source_text, name=filename)) - except lib2to3_tokenize.TokenError: - return source_text - - -def check_syntax(code): - """Return True if syntax is okay.""" - try: - return compile(code, '', 'exec') - except (SyntaxError, TypeError, UnicodeDecodeError): - return False - - -def filter_results(source, results, aggressive): - """Filter out spurious reports from pep8. - - If aggressive is True, we allow possibly unsafe fixes (E711, E712). - - """ - non_docstring_string_line_numbers = multiline_string_lines( - source, include_docstrings=False) - all_string_line_numbers = multiline_string_lines( - source, include_docstrings=True) - - commented_out_code_line_numbers = commented_out_code_lines(source) - - has_e901 = any(result['id'].lower() == 'e901' for result in results) - - for r in results: - issue_id = r['id'].lower() - - if r['line'] in non_docstring_string_line_numbers: - if issue_id.startswith(('e1', 'e501', 'w191')): - continue - - if r['line'] in all_string_line_numbers: - if issue_id in ['e501']: - continue - - # We must offset by 1 for lines that contain the trailing contents of - # multiline strings. - if not aggressive and (r['line'] + 1) in all_string_line_numbers: - # Do not modify multiline strings in non-aggressive mode. Remove - # trailing whitespace could break doctests. - if issue_id.startswith(('w29', 'w39')): - continue - - if aggressive <= 0: - if issue_id.startswith(('e711', 'w6')): - continue - - if aggressive <= 1: - if issue_id.startswith(('e712', 'e713')): - continue - - if r['line'] in commented_out_code_line_numbers: - if issue_id.startswith(('e26', 'e501')): - continue - - # Do not touch indentation if there is a token error caused by - # incomplete multi-line statement. Otherwise, we risk screwing up the - # indentation. - if has_e901: - if issue_id.startswith(('e1', 'e7')): - continue - - yield r - - -def multiline_string_lines(source, include_docstrings=False): - """Return line numbers that are within multiline strings. - - The line numbers are indexed at 1. - - Docstrings are ignored. - - """ - line_numbers = set() - previous_token_type = '' - try: - for t in generate_tokens(source): - token_type = t[0] - start_row = t[2][0] - end_row = t[3][0] - - if token_type == tokenize.STRING and start_row != end_row: - if ( - include_docstrings or - previous_token_type != tokenize.INDENT - ): - # We increment by one since we want the contents of the - # string. - line_numbers |= set(range(1 + start_row, 1 + end_row)) - - previous_token_type = token_type - except (SyntaxError, tokenize.TokenError): - pass - - return line_numbers - - -def commented_out_code_lines(source): - """Return line numbers of comments that are likely code. - - Commented-out code is bad practice, but modifying it just adds even more - clutter. - - """ - line_numbers = [] - try: - for t in generate_tokens(source): - token_type = t[0] - token_string = t[1] - start_row = t[2][0] - line = t[4] - - # Ignore inline comments. - if not line.lstrip().startswith('#'): - continue - - if token_type == tokenize.COMMENT: - stripped_line = token_string.lstrip('#').strip() - if ( - ' ' in stripped_line and - '#' not in stripped_line and - check_syntax(stripped_line) - ): - line_numbers.append(start_row) - except (SyntaxError, tokenize.TokenError): - pass - - return line_numbers - - -def shorten_comment(line, max_line_length, last_comment=False): - """Return trimmed or split long comment line. - - If there are no comments immediately following it, do a text wrap. - Doing this wrapping on all comments in general would lead to jagged - comment text. - - """ - assert len(line) > max_line_length - line = line.rstrip() - - # PEP 8 recommends 72 characters for comment text. - indentation = _get_indentation(line) + '# ' - max_line_length = min(max_line_length, - len(indentation) + 72) - - MIN_CHARACTER_REPEAT = 5 - if ( - len(line) - len(line.rstrip(line[-1])) >= MIN_CHARACTER_REPEAT and - not line[-1].isalnum() - ): - # Trim comments that end with things like --------- - return line[:max_line_length] + '\n' - elif last_comment and re.match(r'\s*#+\s*\w+', line): - split_lines = textwrap.wrap(line.lstrip(' \t#'), - initial_indent=indentation, - subsequent_indent=indentation, - width=max_line_length, - break_long_words=False, - break_on_hyphens=False) - return '\n'.join(split_lines) + '\n' - else: - return line + '\n' - - -def normalize_line_endings(lines, newline): - """Return fixed line endings. - - All lines will be modified to use the most common line ending. - - """ - return [line.rstrip('\n\r') + newline for line in lines] - - -def mutual_startswith(a, b): - return b.startswith(a) or a.startswith(b) - - -def code_match(code, select, ignore): - if ignore: - assert not isinstance(ignore, unicode) - for ignored_code in [c.strip() for c in ignore]: - if mutual_startswith(code.lower(), ignored_code.lower()): - return False - - if select: - assert not isinstance(select, unicode) - for selected_code in [c.strip() for c in select]: - if mutual_startswith(code.lower(), selected_code.lower()): - return True - return False - - return True - - -def fix_code(source, options=None, encoding=None, apply_config=False): - """Return fixed source code. - - "encoding" will be used to decode "source" if it is a byte string. - - """ - options = _get_options(options, apply_config) - - if not isinstance(source, unicode): - source = source.decode(encoding or get_encoding()) - - sio = io.StringIO(source) - return fix_lines(sio.readlines(), options=options) - - -def _get_options(raw_options, apply_config): - """Return parsed options.""" - if not raw_options: - return parse_args([''], apply_config=apply_config) - - if isinstance(raw_options, dict): - options = parse_args([''], apply_config=apply_config) - for name, value in raw_options.items(): - if not hasattr(options, name): - raise ValueError("No such option '{}'".format(name)) - - # Check for very basic type errors. - expected_type = type(getattr(options, name)) - if not isinstance(expected_type, (str, unicode)): - if isinstance(value, (str, unicode)): - raise ValueError( - "Option '{}' should not be a string".format(name)) - setattr(options, name, value) - else: - options = raw_options - - return options - - -def fix_lines(source_lines, options, filename=''): - """Return fixed source code.""" - # Transform everything to line feed. Then change them back to original - # before returning fixed source code. - original_newline = find_newline(source_lines) - tmp_source = ''.join(normalize_line_endings(source_lines, '\n')) - - # Keep a history to break out of cycles. - previous_hashes = set() - - if options.line_range: - # Disable "apply_local_fixes()" for now due to issue #175. - fixed_source = tmp_source - else: - # Apply global fixes only once (for efficiency). - fixed_source = apply_global_fixes(tmp_source, - options, - filename=filename) - - passes = 0 - long_line_ignore_cache = set() - while hash(fixed_source) not in previous_hashes: - if options.pep8_passes >= 0 and passes > options.pep8_passes: - break - passes += 1 - - previous_hashes.add(hash(fixed_source)) - - tmp_source = copy.copy(fixed_source) - - fix = FixPEP8( - filename, - options, - contents=tmp_source, - long_line_ignore_cache=long_line_ignore_cache) - - fixed_source = fix.fix() - - sio = io.StringIO(fixed_source) - return ''.join(normalize_line_endings(sio.readlines(), original_newline)) - - -def fix_file(filename, options=None, output=None, apply_config=False): - if not options: - options = parse_args([filename], apply_config=apply_config) - - original_source = readlines_from_file(filename) - - fixed_source = original_source - - if options.in_place or output: - encoding = detect_encoding(filename) - - if output: - output = LineEndingWrapper(wrap_output(output, encoding=encoding)) - - fixed_source = fix_lines(fixed_source, options, filename=filename) - - if options.diff: - new = io.StringIO(fixed_source) - new = new.readlines() - diff = get_diff_text(original_source, new, filename) - if output: - output.write(diff) - output.flush() - else: - return diff - elif options.in_place: - fp = open_with_encoding(filename, encoding=encoding, - mode='w') - fp.write(fixed_source) - fp.close() - else: - if output: - output.write(fixed_source) - output.flush() - else: - return fixed_source - - -def global_fixes(): - """Yield multiple (code, function) tuples.""" - for function in list(globals().values()): - if inspect.isfunction(function): - arguments = inspect.getargspec(function)[0] - if arguments[:1] != ['source']: - continue - - code = extract_code_from_function(function) - if code: - yield (code, function) - - -def apply_global_fixes(source, options, where='global', filename=''): - """Run global fixes on source code. - - These are fixes that only need be done once (unlike those in - FixPEP8, which are dependent on pep8). - - """ - if any(code_match(code, select=options.select, ignore=options.ignore) - for code in ['E101', 'E111']): - source = reindent(source, - indent_size=options.indent_size) - - for (code, function) in global_fixes(): - if code_match(code, select=options.select, ignore=options.ignore): - if options.verbose: - print('---> Applying {0} fix for {1}'.format(where, - code.upper()), - file=sys.stderr) - source = function(source, - aggressive=options.aggressive) - - source = fix_2to3(source, - aggressive=options.aggressive, - select=options.select, - ignore=options.ignore, - filename=filename) - - return source - - -def extract_code_from_function(function): - """Return code handled by function.""" - if not function.__name__.startswith('fix_'): - return None - - code = re.sub('^fix_', '', function.__name__) - if not code: - return None - - try: - int(code[1:]) - except ValueError: - return None - - return code - - -def create_parser(): - """Return command-line parser.""" - # Do import locally to be friendly to those who use autopep8 as a library - # and are supporting Python 2.6. - import argparse - - parser = argparse.ArgumentParser(description=docstring_summary(__doc__), - prog='autopep8') - parser.add_argument('--version', action='version', - version='%(prog)s ' + __version__) - parser.add_argument('-v', '--verbose', action='count', - default=0, - help='print verbose messages; ' - 'multiple -v result in more verbose messages') - parser.add_argument('-d', '--diff', action='store_true', - help='print the diff for the fixed source') - parser.add_argument('-i', '--in-place', action='store_true', - help='make changes to files in place') - parser.add_argument('--global-config', metavar='filename', - default=DEFAULT_CONFIG, - help='path to a global pep8 config file; if this file ' - 'does not exist then this is ignored ' - '(default: {0})'.format(DEFAULT_CONFIG)) - parser.add_argument('--ignore-local-config', action='store_true', - help="don't look for and apply local config files; " - 'if not passed, defaults are updated with any ' - "config files in the project's root directory") - parser.add_argument('-r', '--recursive', action='store_true', - help='run recursively over directories; ' - 'must be used with --in-place or --diff') - parser.add_argument('-j', '--jobs', type=int, metavar='n', default=1, - help='number of parallel jobs; ' - 'match CPU count if value is less than 1') - parser.add_argument('-p', '--pep8-passes', metavar='n', - default=-1, type=int, - help='maximum number of additional pep8 passes ' - '(default: infinite)') - parser.add_argument('-a', '--aggressive', action='count', default=0, - help='enable non-whitespace changes; ' - 'multiple -a result in more aggressive changes') - parser.add_argument('--experimental', action='store_true', - help='enable experimental fixes') - parser.add_argument('--exclude', metavar='globs', - help='exclude file/directory names that match these ' - 'comma-separated globs') - parser.add_argument('--list-fixes', action='store_true', - help='list codes for fixes; ' - 'used by --ignore and --select') - parser.add_argument('--ignore', metavar='errors', default='', - help='do not fix these errors/warnings ' - '(default: {0})'.format(DEFAULT_IGNORE)) - parser.add_argument('--select', metavar='errors', default='', - help='fix only these errors/warnings (e.g. E4,W)') - parser.add_argument('--max-line-length', metavar='n', default=79, type=int, - help='set maximum allowed line length ' - '(default: %(default)s)') - parser.add_argument('--line-range', '--range', metavar='line', - default=None, type=int, nargs=2, - help='only fix errors found within this inclusive ' - 'range of line numbers (e.g. 1 99); ' - 'line numbers are indexed at 1') - parser.add_argument('--indent-size', default=DEFAULT_INDENT_SIZE, - type=int, metavar='n', - help='number of spaces per indent level ' - '(default %(default)s)') - parser.add_argument('files', nargs='*', - help="files to format or '-' for standard in") - - return parser - - -def parse_args(arguments, apply_config=False): - """Parse command-line options.""" - parser = create_parser() - args = parser.parse_args(arguments) - - if not args.files and not args.list_fixes: - parser.error('incorrect number of arguments') - - args.files = [decode_filename(name) for name in args.files] - - if apply_config: - parser = read_config(args, parser) - args = parser.parse_args(arguments) - args.files = [decode_filename(name) for name in args.files] - - if '-' in args.files: - if len(args.files) > 1: - parser.error('cannot mix stdin and regular files') - - if args.diff: - parser.error('--diff cannot be used with standard input') - - if args.in_place: - parser.error('--in-place cannot be used with standard input') - - if args.recursive: - parser.error('--recursive cannot be used with standard input') - - if len(args.files) > 1 and not (args.in_place or args.diff): - parser.error('autopep8 only takes one filename as argument ' - 'unless the "--in-place" or "--diff" args are ' - 'used') - - if args.recursive and not (args.in_place or args.diff): - parser.error('--recursive must be used with --in-place or --diff') - - if args.in_place and args.diff: - parser.error('--in-place and --diff are mutually exclusive') - - if args.max_line_length <= 0: - parser.error('--max-line-length must be greater than 0') - - if args.select: - args.select = _split_comma_separated(args.select) - - if args.ignore: - args.ignore = _split_comma_separated(args.ignore) - elif not args.select: - if args.aggressive: - # Enable everything by default if aggressive. - args.select = ['E', 'W'] - else: - args.ignore = _split_comma_separated(DEFAULT_IGNORE) - - if args.exclude: - args.exclude = _split_comma_separated(args.exclude) - else: - args.exclude = [] - - if args.jobs < 1: - # Do not import multiprocessing globally in case it is not supported - # on the platform. - import multiprocessing - args.jobs = multiprocessing.cpu_count() - - if args.jobs > 1 and not args.in_place: - parser.error('parallel jobs requires --in-place') - - if args.line_range: - if args.line_range[0] <= 0: - parser.error('--range must be positive numbers') - if args.line_range[0] > args.line_range[1]: - parser.error('First value of --range should be less than or equal ' - 'to the second') - - return args - - -def read_config(args, parser): - """Read both user configuration and local configuration.""" - try: - from configparser import ConfigParser as SafeConfigParser - from configparser import Error - except ImportError: - from ConfigParser import SafeConfigParser - from ConfigParser import Error - - config = SafeConfigParser() - - try: - config.read(args.global_config) - - if not args.ignore_local_config: - parent = tail = args.files and os.path.abspath( - os.path.commonprefix(args.files)) - while tail: - if config.read([os.path.join(parent, fn) - for fn in PROJECT_CONFIG]): - break - (parent, tail) = os.path.split(parent) - - defaults = dict((k.lstrip('-').replace('-', '_'), v) - for k, v in config.items('pep8')) - parser.set_defaults(**defaults) - except Error: - # Ignore for now. - pass - - return parser - - -def _split_comma_separated(string): - """Return a set of strings.""" - return set(text.strip() for text in string.split(',') if text.strip()) - - -def decode_filename(filename): - """Return Unicode filename.""" - if isinstance(filename, unicode): - return filename - else: - return filename.decode(sys.getfilesystemencoding()) - - -def supported_fixes(): - """Yield pep8 error codes that autopep8 fixes. - - Each item we yield is a tuple of the code followed by its - description. - - """ - yield ('E101', docstring_summary(reindent.__doc__)) - - instance = FixPEP8(filename=None, options=None, contents='') - for attribute in dir(instance): - code = re.match('fix_([ew][0-9][0-9][0-9])', attribute) - if code: - yield ( - code.group(1).upper(), - re.sub(r'\s+', ' ', - docstring_summary(getattr(instance, attribute).__doc__)) - ) - - for (code, function) in sorted(global_fixes()): - yield (code.upper() + (4 - len(code)) * ' ', - re.sub(r'\s+', ' ', docstring_summary(function.__doc__))) - - for code in sorted(CODE_TO_2TO3): - yield (code.upper() + (4 - len(code)) * ' ', - re.sub(r'\s+', ' ', docstring_summary(fix_2to3.__doc__))) - - -def docstring_summary(docstring): - """Return summary of docstring.""" - return docstring.split('\n')[0] - - -def line_shortening_rank(candidate, indent_word, max_line_length, - experimental=False): - """Return rank of candidate. - - This is for sorting candidates. - - """ - if not candidate.strip(): - return 0 - - rank = 0 - lines = candidate.rstrip().split('\n') - - offset = 0 - if ( - not lines[0].lstrip().startswith('#') and - lines[0].rstrip()[-1] not in '([{' - ): - for (opening, closing) in ('()', '[]', '{}'): - # Don't penalize empty containers that aren't split up. Things like - # this "foo(\n )" aren't particularly good. - opening_loc = lines[0].find(opening) - closing_loc = lines[0].find(closing) - if opening_loc >= 0: - if closing_loc < 0 or closing_loc != opening_loc + 1: - offset = max(offset, 1 + opening_loc) - - current_longest = max(offset + len(x.strip()) for x in lines) - - rank += 4 * max(0, current_longest - max_line_length) - - rank += len(lines) - - # Too much variation in line length is ugly. - rank += 2 * standard_deviation(len(line) for line in lines) - - bad_staring_symbol = { - '(': ')', - '[': ']', - '{': '}'}.get(lines[0][-1]) - - if len(lines) > 1: - if ( - bad_staring_symbol and - lines[1].lstrip().startswith(bad_staring_symbol) - ): - rank += 20 - - for lineno, current_line in enumerate(lines): - current_line = current_line.strip() - - if current_line.startswith('#'): - continue - - for bad_start in ['.', '%', '+', '-', '/']: - if current_line.startswith(bad_start): - rank += 100 - - # Do not tolerate operators on their own line. - if current_line == bad_start: - rank += 1000 - - if ( - current_line.endswith(('.', '%', '+', '-', '/')) and - "': " in current_line - ): - rank += 1000 - - if current_line.endswith(('(', '[', '{', '.')): - # Avoid lonely opening. They result in longer lines. - if len(current_line) <= len(indent_word): - rank += 100 - - # Avoid the ugliness of ", (\n". - if ( - current_line.endswith('(') and - current_line[:-1].rstrip().endswith(',') - ): - rank += 100 - - # Also avoid the ugliness of "foo.\nbar" - if current_line.endswith('.'): - rank += 100 - - if has_arithmetic_operator(current_line): - rank += 100 - - # Avoid breaking at unary operators. - if re.match(r'.*[(\[{]\s*[\-\+~]$', current_line.rstrip('\\ ')): - rank += 1000 - - if re.match(r'.*lambda\s*\*$', current_line.rstrip('\\ ')): - rank += 1000 - - if current_line.endswith(('%', '(', '[', '{')): - rank -= 20 - - # Try to break list comprehensions at the "for". - if current_line.startswith('for '): - rank -= 50 - - if current_line.endswith('\\'): - # If a line ends in \-newline, it may be part of a - # multiline string. In that case, we would like to know - # how long that line is without the \-newline. If it's - # longer than the maximum, or has comments, then we assume - # that the \-newline is an okay candidate and only - # penalize it a bit. - total_len = len(current_line) - lineno += 1 - while lineno < len(lines): - total_len += len(lines[lineno]) - - if lines[lineno].lstrip().startswith('#'): - total_len = max_line_length - break - - if not lines[lineno].endswith('\\'): - break - - lineno += 1 - - if total_len < max_line_length: - rank += 10 - else: - rank += 100 if experimental else 1 - - # Prefer breaking at commas rather than colon. - if ',' in current_line and current_line.endswith(':'): - rank += 10 - - # Avoid splitting dictionaries between key and value. - if current_line.endswith(':'): - rank += 100 - - rank += 10 * count_unbalanced_brackets(current_line) - - return max(0, rank) - - -def standard_deviation(numbers): - """Return standard devation.""" - numbers = list(numbers) - if not numbers: - return 0 - mean = sum(numbers) / len(numbers) - return (sum((n - mean) ** 2 for n in numbers) / - len(numbers)) ** .5 - - -def has_arithmetic_operator(line): - """Return True if line contains any arithmetic operators.""" - for operator in pep8.ARITHMETIC_OP: - if operator in line: - return True - - return False - - -def count_unbalanced_brackets(line): - """Return number of unmatched open/close brackets.""" - count = 0 - for opening, closing in ['()', '[]', '{}']: - count += abs(line.count(opening) - line.count(closing)) - - return count - - -def split_at_offsets(line, offsets): - """Split line at offsets. - - Return list of strings. - - """ - result = [] - - previous_offset = 0 - current_offset = 0 - for current_offset in sorted(offsets): - if current_offset < len(line) and previous_offset != current_offset: - result.append(line[previous_offset:current_offset].strip()) - previous_offset = current_offset - - result.append(line[current_offset:]) - - return result - - -class LineEndingWrapper(object): - - r"""Replace line endings to work with sys.stdout. - - It seems that sys.stdout expects only '\n' as the line ending, no matter - the platform. Otherwise, we get repeated line endings. - - """ - - def __init__(self, output): - self.__output = output - - def write(self, s): - self.__output.write(s.replace('\r\n', '\n').replace('\r', '\n')) - - def flush(self): - self.__output.flush() - - -def match_file(filename, exclude): - """Return True if file is okay for modifying/recursing.""" - base_name = os.path.basename(filename) - - if base_name.startswith('.'): - return False - - for pattern in exclude: - if fnmatch.fnmatch(base_name, pattern): - return False - if fnmatch.fnmatch(filename, pattern): - return False - - if not os.path.isdir(filename) and not is_python_file(filename): - return False - - return True - - -def find_files(filenames, recursive, exclude): - """Yield filenames.""" - while filenames: - name = filenames.pop(0) - if recursive and os.path.isdir(name): - for root, directories, children in os.walk(name): - filenames += [os.path.join(root, f) for f in children - if match_file(os.path.join(root, f), - exclude)] - directories[:] = [d for d in directories - if match_file(os.path.join(root, d), - exclude)] - else: - yield name - - -def _fix_file(parameters): - """Helper function for optionally running fix_file() in parallel.""" - if parameters[1].verbose: - print('[file:{0}]'.format(parameters[0]), file=sys.stderr) - try: - fix_file(*parameters) - except IOError as error: - print(unicode(error), file=sys.stderr) - - -def fix_multiple_files(filenames, options, output=None): - """Fix list of files. - - Optionally fix files recursively. - - """ - filenames = find_files(filenames, options.recursive, options.exclude) - if options.jobs > 1: - import multiprocessing - pool = multiprocessing.Pool(options.jobs) - pool.map(_fix_file, - [(name, options) for name in filenames]) - else: - for name in filenames: - _fix_file((name, options, output)) - - -def is_python_file(filename): - """Return True if filename is Python file.""" - if filename.endswith('.py'): - return True - - try: - with open_with_encoding(filename) as f: - first_line = f.readlines(1)[0] - except (IOError, IndexError): - return False - - if not PYTHON_SHEBANG_REGEX.match(first_line): - return False - - return True - - -def is_probably_part_of_multiline(line): - """Return True if line is likely part of a multiline string. - - When multiline strings are involved, pep8 reports the error as being - at the start of the multiline string, which doesn't work for us. - - """ - return ( - '"""' in line or - "'''" in line or - line.rstrip().endswith('\\') - ) - - -def wrap_output(output, encoding): - """Return output with specified encoding.""" - return codecs.getwriter(encoding)(output.buffer - if hasattr(output, 'buffer') - else output) - - -def get_encoding(): - """Return preferred encoding.""" - return locale.getpreferredencoding() or sys.getdefaultencoding() - - -def main(argv=None, apply_config=True): - """Command-line entry.""" - if argv is None: - argv = sys.argv - - try: - # Exit on broken pipe. - signal.signal(signal.SIGPIPE, signal.SIG_DFL) - except AttributeError: # pragma: no cover - # SIGPIPE is not available on Windows. - pass - - try: - args = parse_args(argv[1:], apply_config=apply_config) - - if args.list_fixes: - for code, description in sorted(supported_fixes()): - print('{code} - {description}'.format( - code=code, description=description)) - return 0 - - if args.files == ['-']: - assert not args.in_place - - encoding = sys.stdin.encoding or get_encoding() - - # LineEndingWrapper is unnecessary here due to the symmetry between - # standard in and standard out. - wrap_output(sys.stdout, encoding=encoding).write( - fix_code(sys.stdin.read(), args, encoding=encoding)) - else: - if args.in_place or args.diff: - args.files = list(set(args.files)) - else: - assert len(args.files) == 1 - assert not args.recursive - - fix_multiple_files(args.files, args, sys.stdout) - except KeyboardInterrupt: - return 1 # pragma: no cover - - -class CachedTokenizer(object): - - """A one-element cache around tokenize.generate_tokens(). - - Original code written by Ned Batchelder, in coverage.py. - - """ - - def __init__(self): - self.last_text = None - self.last_tokens = None - - def generate_tokens(self, text): - """A stand-in for tokenize.generate_tokens().""" - if text != self.last_text: - string_io = io.StringIO(text) - self.last_tokens = list( - tokenize.generate_tokens(string_io.readline) - ) - self.last_text = text - return self.last_tokens - -_cached_tokenizer = CachedTokenizer() -generate_tokens = _cached_tokenizer.generate_tokens - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/pymode/autopep8.py b/pymode/autopep8.py new file mode 120000 index 00000000..0a26e017 --- /dev/null +++ b/pymode/autopep8.py @@ -0,0 +1 @@ +../submodules/autopep8/autopep8.py \ No newline at end of file diff --git a/pymode/environment.py b/pymode/environment.py index c146ea6e..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,13 +49,10 @@ 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): + def var(name, to_bool=False, silence=False, default=None): """Get vim variable. :return vimobj: @@ -69,7 +62,7 @@ def var(name, to_bool=False, silence=False): value = vim.eval(name) except vim.error: if silence: - return None + return default raise if to_bool: @@ -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/pkg_resources/_vendor/__init__.py b/pymode/libs/__init__.py similarity index 100% rename from pymode/libs/pkg_resources/_vendor/__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 d4fd12c5..00000000 --- a/pymode/libs/astroid/__init__.py +++ /dev/null @@ -1,131 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""Python Abstract Syntax Tree New Generation - -The aim of this module is to provide a common base representation of -python source code for projects such as pychecker, pyreverse, -pylint... Well, actually the development of this library is essentially -governed by pylint's needs. - -It extends class defined in the python's _ast module with some -additional methods and attributes. Instance attributes are added by a -builder object, which can either generate extended ast (let's call -them astroid ;) by visiting an existent ast tree or by inspecting living -object. Methods are added by monkey patching ast classes. - -Main modules are: - -* nodes and scoped_nodes for more information about methods and - attributes added to different node classes - -* the manager contains a high level object to get astroid trees from - source files and living objects. It maintains a cache of previously - constructed tree for quick access - -* builder contains the class responsible to build astroid trees -""" -__doctype__ = "restructuredtext en" - -import sys -import re -from operator import attrgetter - -# WARNING: internal imports order matters ! - -# make all exception classes accessible from astroid package -from astroid.exceptions import * - -# make all node classes accessible from astroid package -from astroid.nodes import * - -# trigger extra monkey-patching -from astroid import inference - -# more stuff available -from astroid import raw_building -from astroid.bases import YES, Instance, BoundMethod, UnboundMethod -from astroid.node_classes import are_exclusive, unpack_infer -from astroid.scoped_nodes import builtin_lookup - -# make a manager instance (borg) as well as Project and Package classes -# accessible from astroid package -from astroid.manager import AstroidManager, Project -MANAGER = AstroidManager() -del AstroidManager - -# transform utilities (filters and decorator) - -class AsStringRegexpPredicate(object): - """Class to be used as predicate that may be given to `register_transform` - - First argument is a regular expression that will be searched against the `as_string` - representation of the node onto which it's applied. - - If specified, the second argument is an `attrgetter` expression that will be - applied on the node first to get the actual node on which `as_string` should - be called. - - WARNING: This can be fairly slow, as it has to convert every AST node back - to Python code; you should consider examining the AST directly instead. - """ - def __init__(self, regexp, expression=None): - self.regexp = re.compile(regexp) - self.expression = expression - - def __call__(self, node): - if self.expression is not None: - node = attrgetter(self.expression)(node) - return self.regexp.search(node.as_string()) - -def inference_tip(infer_function): - """Given an instance specific inference function, return a function to be - given to MANAGER.register_transform to set this inference function. - - Typical usage - - .. sourcecode:: python - - MANAGER.register_transform(CallFunc, inference_tip(infer_named_tuple), - predicate) - """ - def transform(node, infer_function=infer_function): - node._explicit_inference = infer_function - return node - return transform - - -def register_module_extender(manager, module_name, get_extension_mod): - def transform(node): - extension_module = get_extension_mod() - for name, obj in extension_module.locals.items(): - node.locals[name] = obj - - manager.register_transform(Module, transform, lambda n: n.name == module_name) - - -# load brain plugins -from os import listdir -from os.path import join, dirname -BRAIN_MODULES_DIR = join(dirname(__file__), 'brain') -if BRAIN_MODULES_DIR not in sys.path: - # add it to the end of the list so user path take precedence - sys.path.append(BRAIN_MODULES_DIR) -# load modules in this directory -for module in listdir(BRAIN_MODULES_DIR): - if module.endswith('.py'): - __import__(module[:-3]) diff --git a/pymode/libs/astroid/__pkginfo__.py b/pymode/libs/astroid/__pkginfo__.py deleted file mode 100644 index 3fb45aa4..00000000 --- a/pymode/libs/astroid/__pkginfo__.py +++ /dev/null @@ -1,42 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""astroid packaging information""" -distname = 'astroid' - -modname = 'astroid' - -numversion = (1, 3, 8) -version = '.'.join([str(num) for num in numversion]) - -install_requires = ['logilab-common>=0.63.0', 'six'] - -license = 'LGPL' - -author = 'Logilab' -author_email = 'pylint-dev@lists.logilab.org' -mailinglist = "mailto://%s" % author_email -web = 'http://bitbucket.org/logilab/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/as_string.py b/pymode/libs/astroid/as_string.py deleted file mode 100644 index f627f9e8..00000000 --- a/pymode/libs/astroid/as_string.py +++ /dev/null @@ -1,499 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module renders Astroid nodes as string: - -* :func:`to_code` function return equivalent (hopefuly valid) python string - -* :func:`dump` function return an internal representation of nodes found - in the tree, useful for debugging or understanding the tree structure -""" - -import sys - -INDENT = ' ' # 4 spaces ; keep indentation variable - - -def dump(node, ids=False): - """print a nice astroid tree representation. - - :param ids: if true, we also print the ids (usefull for debugging) - """ - result = [] - _repr_tree(node, result, ids=ids) - return "\n".join(result) - -def _repr_tree(node, result, indent='', _done=None, ids=False): - """built a tree representation of a node as a list of lines""" - if _done is None: - _done = set() - if not hasattr(node, '_astroid_fields'): # not a astroid node - return - if node in _done: - result.append(indent + 'loop in tree: %s' % node) - return - _done.add(node) - node_str = str(node) - if ids: - node_str += ' . \t%x' % id(node) - result.append(indent + node_str) - indent += INDENT - for field in node._astroid_fields: - value = getattr(node, field) - if isinstance(value, (list, tuple)): - result.append(indent + field + " = [") - for child in value: - if isinstance(child, (list, tuple)): - # special case for Dict # FIXME - _repr_tree(child[0], result, indent, _done, ids) - _repr_tree(child[1], result, indent, _done, ids) - result.append(indent + ',') - else: - _repr_tree(child, result, indent, _done, ids) - result.append(indent + "]") - else: - result.append(indent + field + " = ") - _repr_tree(value, result, indent, _done, ids) - - -class AsStringVisitor(object): - """Visitor to render an Astroid node as a valid python code string""" - - def __call__(self, node): - """Makes this visitor behave as a simple function""" - return node.accept(self) - - def _stmt_list(self, stmts): - """return a list of nodes to string""" - stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr]) - return INDENT + stmts.replace('\n', '\n'+INDENT) - - - ## visit_ methods ########################################### - - def visit_arguments(self, node): - """return an astroid.Function node as string""" - return node.format_args() - - def visit_assattr(self, node): - """return an astroid.AssAttr node as string""" - return self.visit_getattr(node) - - def visit_assert(self, node): - """return an astroid.Assert node as string""" - if node.fail: - return 'assert %s, %s' % (node.test.accept(self), - node.fail.accept(self)) - return 'assert %s' % node.test.accept(self) - - def visit_assname(self, node): - """return an astroid.AssName node as string""" - return node.name - - def visit_assign(self, node): - """return an astroid.Assign node as string""" - lhs = ' = '.join([n.accept(self) for n in node.targets]) - return '%s = %s' % (lhs, node.value.accept(self)) - - def visit_augassign(self, node): - """return an astroid.AugAssign node as string""" - return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) - - def visit_backquote(self, node): - """return an astroid.Backquote node as string""" - return '`%s`' % node.value.accept(self) - - def visit_binop(self, node): - """return an astroid.BinOp node as string""" - return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self)) - - def visit_boolop(self, node): - """return an astroid.BoolOp node as string""" - return (' %s ' % node.op).join(['(%s)' % n.accept(self) - for n in node.values]) - - def visit_break(self, node): - """return an astroid.Break node as string""" - return 'break' - - def visit_callfunc(self, node): - """return an astroid.CallFunc node as string""" - expr_str = node.func.accept(self) - args = [arg.accept(self) for arg in node.args] - if node.starargs: - args.append('*' + node.starargs.accept(self)) - if node.kwargs: - args.append('**' + node.kwargs.accept(self)) - return '%s(%s)' % (expr_str, ', '.join(args)) - - def visit_class(self, node): - """return an astroid.Class node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - bases = ', '.join([n.accept(self) for n in node.bases]) - if sys.version_info[0] == 2: - bases = bases and '(%s)' % bases or '' - else: - metaclass = node.metaclass() - if metaclass and not node.has_metaclass_hack(): - if bases: - bases = '(%s, metaclass=%s)' % (bases, metaclass.name) - else: - bases = '(metaclass=%s)' % metaclass.name - else: - bases = bases and '(%s)' % bases or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, - self._stmt_list(node.body)) - - def visit_compare(self, node): - """return an astroid.Compare node as string""" - rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) - for op, expr in node.ops]) - return '%s %s' % (node.left.accept(self), rhs_str) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - ifs = ''.join([' if %s' % n.accept(self) for n in node.ifs]) - return 'for %s in %s%s' % (node.target.accept(self), - node.iter.accept(self), ifs) - - def visit_const(self, node): - """return an astroid.Const node as string""" - return repr(node.value) - - def visit_continue(self, node): - """return an astroid.Continue node as string""" - return 'continue' - - def visit_delete(self, node): # XXX check if correct - """return an astroid.Delete node as string""" - return 'del %s' % ', '.join([child.accept(self) - for child in node.targets]) - - def visit_delattr(self, node): - """return an astroid.DelAttr node as string""" - return self.visit_getattr(node) - - def visit_delname(self, node): - """return an astroid.DelName node as string""" - return node.name - - def visit_decorators(self, node): - """return an astroid.Decorators node as string""" - return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) - - def visit_dict(self, node): - """return an astroid.Dict node as string""" - return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), - value.accept(self)) - for key, value in node.items]) - - def visit_dictcomp(self, node): - """return an astroid.DictComp node as string""" - return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_discard(self, node): - """return an astroid.Discard node as string""" - return node.value.accept(self) - - def visit_emptynode(self, node): - """dummy method for visiting an Empty node""" - return '' - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s, %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_ellipsis(self, node): - """return an astroid.Ellipsis node as string""" - return '...' - - def visit_empty(self, node): - """return an Empty node as string""" - return '' - - def visit_exec(self, node): - """return an astroid.Exec node as string""" - if node.locals: - return 'exec %s in %s, %s' % (node.expr.accept(self), - node.locals.accept(self), - node.globals.accept(self)) - if node.globals: - return 'exec %s in %s' % (node.expr.accept(self), - node.globals.accept(self)) - return 'exec %s' % node.expr.accept(self) - - def visit_extslice(self, node): - """return an astroid.ExtSlice node as string""" - return ','.join([dim.accept(self) for dim in node.dims]) - - def visit_for(self, node): - """return an astroid.For node as string""" - fors = 'for %s in %s:\n%s' % (node.target.accept(self), - node.iter.accept(self), - self._stmt_list(node.body)) - if node.orelse: - fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) - return fors - - def visit_from(self, node): - """return an astroid.From node as string""" - return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, - _import_string(node.names)) - - def visit_function(self, node): - """return an astroid.Function node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self), - docs, self._stmt_list(node.body)) - - def visit_genexpr(self, node): - """return an astroid.GenExpr node as string""" - return '(%s %s)' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_getattr(self, node): - """return an astroid.Getattr node as string""" - return '%s.%s' % (node.expr.accept(self), node.attrname) - - def visit_global(self, node): - """return an astroid.Global node as string""" - return 'global %s' % ', '.join(node.names) - - def visit_if(self, node): - """return an astroid.If node as string""" - ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))] - if node.orelse:# XXX use elif ??? - ifs.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(ifs) - - def visit_ifexp(self, node): - """return an astroid.IfExp node as string""" - return '%s if %s else %s' % (node.body.accept(self), - node.test.accept(self), - node.orelse.accept(self)) - - def visit_import(self, node): - """return an astroid.Import node as string""" - return 'import %s' % _import_string(node.names) - - def visit_keyword(self, node): - """return an astroid.Keyword node as string""" - return '%s=%s' % (node.arg, node.value.accept(self)) - - def visit_lambda(self, node): - """return an astroid.Lambda node as string""" - return 'lambda %s: %s' % (node.args.accept(self), - node.body.accept(self)) - - def visit_list(self, node): - """return an astroid.List node as string""" - return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_listcomp(self, node): - """return an astroid.ListComp node as string""" - return '[%s %s]' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_module(self, node): - """return an astroid.Module node as string""" - docs = node.doc and '"""%s"""\n\n' % node.doc or '' - return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' - - def visit_name(self, node): - """return an astroid.Name node as string""" - return node.name - - def visit_pass(self, node): - """return an astroid.Pass node as string""" - return 'pass' - - def visit_print(self, node): - """return an astroid.Print node as string""" - nodes = ', '.join([n.accept(self) for n in node.values]) - if not node.nl: - nodes = '%s,' % nodes - if node.dest: - return 'print >> %s, %s' % (node.dest.accept(self), nodes) - return 'print %s' % nodes - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.inst: - if node.tback: - return 'raise %s, %s, %s' % (node.exc.accept(self), - node.inst.accept(self), - node.tback.accept(self)) - return 'raise %s, %s' % (node.exc.accept(self), - node.inst.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_return(self, node): - """return an astroid.Return node as string""" - if node.value: - return 'return %s' % node.value.accept(self) - else: - return 'return' - - def visit_index(self, node): - """return a astroid.Index node as string""" - return node.value.accept(self) - - def visit_set(self, node): - """return an astroid.Set node as string""" - return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_setcomp(self, node): - """return an astroid.SetComp node as string""" - return '{%s %s}' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_slice(self, node): - """return a astroid.Slice node as string""" - lower = node.lower and node.lower.accept(self) or '' - upper = node.upper and node.upper.accept(self) or '' - step = node.step and node.step.accept(self) or '' - if step: - return '%s:%s:%s' % (lower, upper, step) - return '%s:%s' % (lower, upper) - - def visit_subscript(self, node): - """return an astroid.Subscript node as string""" - return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) - - def visit_tryexcept(self, node): - """return an astroid.TryExcept node as string""" - trys = ['try:\n%s' % self._stmt_list(node.body)] - for handler in node.handlers: - trys.append(handler.accept(self)) - if node.orelse: - trys.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(trys) - - def visit_tryfinally(self, node): - """return an astroid.TryFinally node as string""" - return 'try:\n%s\nfinally:\n%s' % (self._stmt_list(node.body), - self._stmt_list(node.finalbody)) - - def visit_tuple(self, node): - """return an astroid.Tuple node as string""" - if len(node.elts) == 1: - return '(%s, )' % node.elts[0].accept(self) - return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_unaryop(self, node): - """return an astroid.UnaryOp node as string""" - if node.op == 'not': - operator = 'not ' - else: - operator = node.op - return '%s%s' % (operator, node.operand.accept(self)) - - def visit_while(self, node): - """return an astroid.While node as string""" - whiles = 'while %s:\n%s' % (node.test.accept(self), - self._stmt_list(node.body)) - if node.orelse: - whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) - return whiles - - def visit_with(self, node): # 'with' without 'as' is possible - """return an astroid.With node as string""" - items = ', '.join(('(%s)' % expr.accept(self)) + - (vars and ' as (%s)' % (vars.accept(self)) or '') - for expr, vars in node.items) - return 'with %s:\n%s' % (items, self._stmt_list(node.body)) - - def visit_yield(self, node): - """yield an ast.Yield node as string""" - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -class AsStringVisitor3k(AsStringVisitor): - """AsStringVisitor3k overwrites some AsStringVisitor methods""" - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s as %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_nonlocal(self, node): - """return an astroid.Nonlocal node as string""" - return 'nonlocal %s' % ', '.join(node.names) - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.cause: - return 'raise %s from %s' % (node.exc.accept(self), - node.cause.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_starred(self, node): - """return Starred node as string""" - return "*" + node.value.accept(self) - - def visit_yieldfrom(self, node): - """ Return an astroid.YieldFrom node as string. """ - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield from' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -def _import_string(names): - """return a list of (name, asname) formatted as a string""" - _names = [] - for name, asname in names: - if asname is not None: - _names.append('%s as %s' % (name, asname)) - else: - _names.append(name) - return ', '.join(_names) - - -if sys.version_info >= (3, 0): - AsStringVisitor = AsStringVisitor3k - -# this visitor is stateless, thus it can be reused -to_code = AsStringVisitor() - diff --git a/pymode/libs/astroid/astpeephole.py b/pymode/libs/astroid/astpeephole.py deleted file mode 100644 index af03462a..00000000 --- a/pymode/libs/astroid/astpeephole.py +++ /dev/null @@ -1,86 +0,0 @@ -# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""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): - """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(type(element) is not known - for element in ast_nodes[1:]): - return - - value = known().join(reversed(ast_nodes)) - newnode = nodes.Const(value) - return newnode diff --git a/pymode/libs/astroid/bases.py b/pymode/libs/astroid/bases.py deleted file mode 100644 index ee8ee1c3..00000000 --- a/pymode/libs/astroid/bases.py +++ /dev/null @@ -1,652 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module contains base classes and functions for the nodes and some -inference utils. -""" - -__docformat__ = "restructuredtext en" - -import sys -from contextlib import contextmanager - -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import (InferenceError, AstroidError, NotFoundError, - UnresolvableName, UseInferenceDefault) - - -if sys.version_info >= (3, 0): - BUILTINS = 'builtins' -else: - BUILTINS = '__builtin__' - - -class Proxy(object): - """a simple proxy object""" - - _proxied = None # proxied object may be set by class or by instance - - def __init__(self, proxied=None): - if proxied is not None: - self._proxied = proxied - - def __getattr__(self, name): - if name == '_proxied': - return getattr(self.__class__, '_proxied') - if name in self.__dict__: - return self.__dict__[name] - return getattr(self._proxied, name) - - def infer(self, context=None): - yield self - - -# Inference ################################################################## - -class InferenceContext(object): - __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'infered') - - def __init__(self, path=None, infered=None): - self.path = path or set() - self.lookupname = None - self.callcontext = None - self.boundnode = None - self.infered = infered or {} - - def push(self, node): - name = self.lookupname - if (node, name) in self.path: - raise StopIteration() - self.path.add((node, name)) - - def clone(self): - # XXX copy lookupname/callcontext ? - clone = InferenceContext(self.path, infered=self.infered) - 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.infered[key] = tuple(results) - return - - @contextmanager - def restore_path(self): - path = set(self.path) - yield - self.path = path - -def copy_context(context): - if context is not None: - return context.clone() - else: - return InferenceContext() - - -def _infer_stmts(stmts, context, frame=None): - """return an iterator on statements inferred by each statement in - """ - stmt = None - infered = False - if context is not None: - name = context.lookupname - context = context.clone() - else: - name = None - context = InferenceContext() - for stmt in stmts: - if stmt is YES: - yield stmt - infered = True - continue - context.lookupname = stmt._infer_name(frame, name) - try: - for infered in stmt.infer(context): - yield infered - infered = True - except UnresolvableName: - continue - except InferenceError: - yield YES - infered = True - if not infered: - raise InferenceError(str(stmt)) - - -# special inference objects (e.g. may be returned as nodes by .infer()) ####### - -class _Yes(object): - """a yes object""" - def __repr__(self): - return 'YES' - def __getattribute__(self, name): - if name == 'next': - raise AttributeError('next method should not be called') - if name.startswith('__') and name.endswith('__'): - # to avoid inspection pb - return super(_Yes, self).__getattribute__(name) - return self - def __call__(self, *args, **kwargs): - return self - - -YES = _Yes() - - -class Instance(Proxy): - """a special node representing a class instance""" - def getattr(self, name, context=None, lookupclass=True): - try: - values = self._proxied.instance_attr(name, context) - except NotFoundError: - if name == '__class__': - return [self._proxied] - if lookupclass: - # class attributes not available through the instance - # unless they are explicitly defined - if name in ('__name__', '__bases__', '__mro__', '__subclasses__'): - return self._proxied.local_attr(name) - return self._proxied.getattr(name, context) - raise NotFoundError(name) - # since we've no context information, return matching class members as - # well - if lookupclass: - try: - return values + self._proxied.getattr(name, context) - except NotFoundError: - pass - return values - - def igetattr(self, name, context=None): - """inferred getattr""" - if not context: - context = InferenceContext() - try: - # avoid recursively inferring the same attr on the same class - - context.push((self._proxied, name)) - # XXX frame should be self._proxied, or not ? - get_attr = self.getattr(name, context, lookupclass=False) - return _infer_stmts( - self._wrap_attr(get_attr, context), - context, - frame=self, - ) - except NotFoundError: - try: - # fallback to class'igetattr since it has some logic to handle - # descriptors - return self._wrap_attr(self._proxied.igetattr(name, context), - context) - except NotFoundError: - raise InferenceError(name) - - def _wrap_attr(self, attrs, context=None): - """wrap bound methods of attrs in a InstanceMethod proxies""" - for attr in attrs: - if isinstance(attr, UnboundMethod): - if BUILTINS + '.property' in attr.decoratornames(): - for infered in attr.infer_call_result(self, context): - yield infered - else: - yield BoundMethod(attr, self) - else: - yield attr - - def infer_call_result(self, caller, context=None): - """infer what a class instance is returning when called""" - infered = False - for node in self._proxied.igetattr('__call__', context): - if node is YES: - continue - for res in node.infer_call_result(caller, context): - infered = True - yield res - if not infered: - raise InferenceError() - - def __repr__(self): - return '' % (self._proxied.root().name, - self._proxied.name, - id(self)) - def __str__(self): - return 'Instance of %s.%s' % (self._proxied.root().name, - self._proxied.name) - - def callable(self): - try: - self._proxied.getattr('__call__') - return True - except NotFoundError: - return False - - def pytype(self): - return self._proxied.qname() - - def display_type(self): - return 'Instance of' - - -class UnboundMethod(Proxy): - """a special node representing a method not bound to an instance""" - def __repr__(self): - frame = self._proxied.parent.frame() - return '<%s %s of %s at 0x%s' % (self.__class__.__name__, - self._proxied.name, - frame.qname(), id(self)) - - def is_bound(self): - return False - - def getattr(self, name, context=None): - if name == 'im_func': - return [self._proxied] - return super(UnboundMethod, self).getattr(name, context) - - def igetattr(self, name, context=None): - if name == 'im_func': - return iter((self._proxied,)) - return super(UnboundMethod, self).igetattr(name, context) - - def infer_call_result(self, caller, context): - # If we're unbound method __new__ of builtin object, the result is an - # instance of the class given as first argument. - if (self._proxied.name == '__new__' and - self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - infer = caller.args[0].infer() if caller.args else [] - return ((x is YES and x or Instance(x)) for x in infer) - return self._proxied.infer_call_result(caller, context) - - -class BoundMethod(UnboundMethod): - """a special node representing a method bound to an instance""" - def __init__(self, proxy, bound): - UnboundMethod.__init__(self, proxy) - self.bound = bound - - def is_bound(self): - return True - - def infer_call_result(self, caller, context): - context = context.clone() - context.boundnode = self.bound - return self._proxied.infer_call_result(caller, context) - - -class Generator(Instance): - """a special node representing a generator. - - Proxied class is set once for all in raw_building. - """ - def callable(self): - return False - - def pytype(self): - return '%s.generator' % BUILTINS - - def display_type(self): - return 'Generator' - - def __repr__(self): - return '' % (self._proxied.name, self.lineno, id(self)) - - def __str__(self): - return 'Generator(%s)' % (self._proxied.name) - - -# decorators ################################################################## - -def path_wrapper(func): - """return the given infer function wrapped to handle the path""" - def wrapped(node, context=None, _func=func, **kwargs): - """wrapper function handling context""" - if context is None: - context = InferenceContext() - context.push(node) - yielded = set() - for res in _func(node, context, **kwargs): - # unproxy only true instance, not const, tuple, dict... - if res.__class__ is Instance: - ares = res._proxied - else: - ares = res - if not ares in yielded: - yield res - yielded.add(ares) - return wrapped - -def yes_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - yield YES - return wrapper - -def raise_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - raise InferenceError() - return wrapper - - -# Node ###################################################################### - -class NodeNG(object): - """Base Class for all Astroid node classes. - - It represents a node of the new abstract syntax tree. - """ - is_statement = False - optional_assign = False # True for For (and for Comprehension if py <3.0) - is_function = False # True for Function nodes - # attributes below are set by the builder module or by raw factories - lineno = None - fromlineno = None - tolineno = None - col_offset = None - # parent node in the tree - parent = None - # attributes containing child node(s) redefined in most concrete classes: - _astroid_fields = () - # instance specific inference function infer(node, context) - _explicit_inference = None - - def infer(self, context=None, **kwargs): - """main interface to the interface system, return a generator on infered - values. - - If the instance has some explicit inference function set, it will be - called instead of the default interface. - """ - if self._explicit_inference is not None: - # explicit_inference is not bound, give it self explicitly - try: - return self._explicit_inference(self, context, **kwargs) - except UseInferenceDefault: - pass - - if not context: - return self._infer(context, **kwargs) - - key = (self, context.lookupname, - context.callcontext, context.boundnode) - if key in context.infered: - return iter(context.infered[key]) - - return context.cache_generator(key, self._infer(context, **kwargs)) - - def _repr_name(self): - """return self.name or self.attrname or '' for nice representation""" - return getattr(self, 'name', getattr(self, 'attrname', '')) - - def __str__(self): - return '%s(%s)' % (self.__class__.__name__, self._repr_name()) - - def __repr__(self): - return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__, - self._repr_name(), - self.fromlineno, - self.root().name, - id(self)) - - - def accept(self, visitor): - func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) - return func(self) - - def get_children(self): - for field in self._astroid_fields: - attr = getattr(self, field) - if attr is None: - continue - if isinstance(attr, (list, tuple)): - for elt in attr: - yield elt - else: - yield attr - - def last_child(self): - """an optimized version of list(get_children())[-1]""" - for field in self._astroid_fields[::-1]: - attr = getattr(self, field) - if not attr: # None or empty listy / tuple - continue - if attr.__class__ in (list, tuple): - return attr[-1] - else: - return attr - return None - - def parent_of(self, node): - """return true if i'm a parent of the given node""" - parent = node.parent - while parent is not None: - if self is parent: - return True - parent = parent.parent - return False - - def statement(self): - """return the first parent node marked as statement node""" - if self.is_statement: - return self - return self.parent.statement() - - def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) - """ - return self.parent.frame() - - def scope(self): - """return the first node defining a new scope (i.e. Module, Function, - Class, Lambda but also GenExpr) - """ - return self.parent.scope() - - def root(self): - """return the root node of the tree, (i.e. a Module)""" - if self.parent: - return self.parent.root() - return self - - def child_sequence(self, child): - """search for the right sequence where the child lies in""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - if node_or_sequence is child: - return [node_or_sequence] - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: - return node_or_sequence - else: - msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) - - def locate_child(self, child): - """return a 2-uple (child attribute name, sequence or node)""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if child is node_or_sequence: - return field, child - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: - return field, node_or_sequence - msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) - # FIXME : should we merge child_sequence and locate_child ? locate_child - # is only used in are_exclusive, child_sequence one time in pylint. - - def next_sibling(self): - """return the next sibling statement""" - return self.parent.next_sibling() - - def previous_sibling(self): - """return the previous sibling statement""" - return self.parent.previous_sibling() - - def nearest(self, nodes): - """return the node which is the nearest before this one in the - given list of nodes - """ - myroot = self.root() - mylineno = self.fromlineno - nearest = None, 0 - for node in nodes: - assert node.root() is myroot, \ - 'nodes %s and %s are not from the same module' % (self, node) - lineno = node.fromlineno - if node.fromlineno > mylineno: - break - if lineno > nearest[1]: - nearest = node, lineno - # FIXME: raise an exception if nearest is None ? - return nearest[0] - - # these are lazy because they're relatively expensive to compute for every - # single node, and they rarely get looked at - - @cachedproperty - def fromlineno(self): - if self.lineno is None: - return self._fixed_source_line() - else: - return self.lineno - - @cachedproperty - def tolineno(self): - if not self._astroid_fields: - # can't have children - lastchild = None - else: - lastchild = self.last_child() - if lastchild is None: - return self.fromlineno - else: - return lastchild.tolineno - - # TODO / FIXME: - assert self.fromlineno is not None, self - assert self.tolineno is not None, self - - def _fixed_source_line(self): - """return the line number where the given node appears - - we need this method since not all nodes have the lineno attribute - correctly set... - """ - line = self.lineno - _node = self - try: - while line is None: - _node = next(_node.get_children()) - line = _node.lineno - except StopIteration: - _node = self.parent - while _node and line is None: - line = _node.lineno - _node = _node.parent - return line - - def block_range(self, lineno): - """handle block line numbers range for non block opening statements - """ - return lineno, self.tolineno - - def set_local(self, name, stmt): - """delegate to a scoped parent handling a locals dictionary""" - self.parent.set_local(name, stmt) - - def nodes_of_class(self, klass, skip_klass=None): - """return an iterator on nodes which are instance of the given class(es) - - klass may be a class object or a tuple of class objects - """ - if isinstance(self, klass): - yield self - for child_node in self.get_children(): - if skip_klass is not None and isinstance(child_node, skip_klass): - continue - for matching in child_node.nodes_of_class(klass, skip_klass): - yield matching - - def _infer_name(self, frame, name): - # overridden for From, Import, Global, TryExcept and Arguments - return None - - def _infer(self, context=None): - """we don't know how to resolve a statement by default""" - # this method is overridden by most concrete classes - raise InferenceError(self.__class__.__name__) - - def infered(self): - '''return list of infered values for a more simple inference usage''' - return list(self.infer()) - - def instanciate_class(self): - """instanciate a node if it is a Class node, else return self""" - return self - - def has_base(self, node): - return False - - def callable(self): - return False - - def eq(self, value): - return False - - def as_string(self): - from astroid.as_string import to_code - return to_code(self) - - def repr_tree(self, ids=False): - from astroid.as_string import dump - return dump(self) - - -class Statement(NodeNG): - """Statement node adding a few attributes""" - is_statement = True - - def next_sibling(self): - """return the next sibling statement""" - stmts = self.parent.child_sequence(self) - index = stmts.index(self) - try: - return stmts[index +1] - except IndexError: - pass - - def previous_sibling(self): - """return the previous sibling statement""" - stmts = self.parent.child_sequence(self) - index = stmts.index(self) - if index >= 1: - return stmts[index -1] diff --git a/pymode/libs/astroid/brain/builtin_inference.py b/pymode/libs/astroid/brain/builtin_inference.py deleted file mode 100644 index f60e7913..00000000 --- a/pymode/libs/astroid/brain/builtin_inference.py +++ /dev/null @@ -1,245 +0,0 @@ -"""Astroid hooks for various builtins.""" - -import sys -from functools import partial -from textwrap import dedent - -import six -from astroid import (MANAGER, UseInferenceDefault, - inference_tip, YES, InferenceError, UnresolvableName) -from astroid import nodes -from astroid.builder import AstroidBuilder - - -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: - result.parent = node - result.lineno = node.lineno - result.col_offset = node.col_offset - return iter([result]) - - MANAGER.register_transform(nodes.CallFunc, - 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: - infered = next(arg.infer(context=context)) - except (InferenceError, StopIteration): - raise UseInferenceDefault() - if infered is YES: - raise UseInferenceDefault() - transformed = transform(infered) - if not transformed or transformed is YES: - 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(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), - build_elts=tuple) - -infer_list = partial( - _infer_builtin, - klass=nodes.List, - iterables=(nodes.Tuple, nodes.Set), - build_elts=list) - -infer_set = partial( - _infer_builtin, - klass=nodes.Set, - iterables=(nodes.List, nodes.Tuple), - build_elts=set) - - -def _get_elts(arg, context): - is_iterable = lambda n: isinstance(n, - (nodes.List, nodes.Tuple, nodes.Set)) - try: - infered = next(arg.infer(context)) - except (InferenceError, UnresolvableName): - raise UseInferenceDefault() - if isinstance(infered, nodes.Dict): - items = infered.items - elif is_iterable(infered): - items = [] - for elt in infered.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 infered, we'll fallback to default inference. - """ - has_keywords = lambda args: all(isinstance(arg, nodes.Keyword) - for arg in args) - if not node.args and not node.kwargs: - # dict() - return nodes.Dict() - elif has_keywords(node.args) and node.args: - # dict(a=1, b=2, c=4) - items = [(nodes.Const(arg.arg), arg.value) for arg in node.args] - elif (len(node.args) >= 2 and - has_keywords(node.args[1:])): - # dict(some_iterable, b=2, c=4) - elts = _get_elts(node.args[0], context) - keys = [(nodes.Const(arg.arg), arg.value) for arg in node.args[1:]] - items = elts + keys - elif len(node.args) == 1: - items = _get_elts(node.args[0], context) - else: - raise UseInferenceDefault() - - empty = nodes.Dict() - empty.items = items - return empty - -# Builtins inference -register_builtin_transform(infer_tuple, 'tuple') -register_builtin_transform(infer_set, 'set') -register_builtin_transform(infer_list, 'list') -register_builtin_transform(infer_dict, 'dict') diff --git a/pymode/libs/astroid/brain/py2gi.py b/pymode/libs/astroid/brain/py2gi.py deleted file mode 100644 index 6747898d..00000000 --- a/pymode/libs/astroid/brain/py2gi.py +++ /dev/null @@ -1,155 +0,0 @@ -"""Astroid hooks for the Python 2 GObject introspection bindings. - -Helps with understanding everything imported from 'gi.repository' -""" - -import inspect -import itertools -import sys -import re - -from astroid import MANAGER, AstroidBuildingException -from astroid.builder import AstroidBuilder - - -_inspected_modules = {} - -_identifier_re = r'^[A-Za-z_]\w*$' - -def _gi_build_stub(parent): - """ - Inspect the passed module recursively and build stubs for functions, - classes, etc. - """ - classes = {} - functions = {} - constants = {} - methods = {} - for name in dir(parent): - if name.startswith("__"): - continue - - # Check if this is a valid name in python - if not re.match(_identifier_re, name): - continue - - try: - obj = getattr(parent, name) - except: - continue - - if inspect.isclass(obj): - classes[name] = obj - elif (inspect.isfunction(obj) or - inspect.isbuiltin(obj)): - functions[name] = obj - elif (inspect.ismethod(obj) or - inspect.ismethoddescriptor(obj)): - methods[name] = obj - elif type(obj) in [int, str]: - constants[name] = obj - elif (str(obj).startswith(" (3, 0) -PY33 = sys.version_info >= (3, 3) - -# general function - -def infer_func_form(node, base_type, context=None, enum=False): - """Specific inference function for namedtuple or Python 3 enum. """ - def infer_first(node): - try: - value = next(node.infer(context=context)) - if value is YES: - raise UseInferenceDefault() - else: - return value - except StopIteration: - raise InferenceError() - - # node is a CallFunc node, class name as first argument and generated class - # attributes as second argument - if len(node.args) != 2: - # something weird here, go back to class implementation - raise UseInferenceDefault() - # namedtuple or enums list of attributes can be a list of strings or a - # whitespace-separate string - try: - name = infer_first(node.args[0]).value - names = infer_first(node.args[1]) - try: - attributes = names.value.replace(',', ' ').split() - except AttributeError: - if not enum: - attributes = [infer_first(const).value for const in names.elts] - else: - # Enums supports either iterator of (name, value) pairs - # or mappings. - # TODO: support only list, tuples and mappings. - if hasattr(names, 'items') and isinstance(names.items, list): - attributes = [infer_first(const[0]).value - for const in names.items - if isinstance(const[0], nodes.Const)] - elif hasattr(names, 'elts'): - # Enums can support either ["a", "b", "c"] - # or [("a", 1), ("b", 2), ...], but they can't - # be mixed. - if all(isinstance(const, nodes.Tuple) - for const in names.elts): - attributes = [infer_first(const.elts[0]).value - for const in names.elts - if isinstance(const, nodes.Tuple)] - else: - attributes = [infer_first(const).value - for const in names.elts] - else: - raise AttributeError - if not attributes: - raise AttributeError - except (AttributeError, exceptions.InferenceError) as exc: - raise UseInferenceDefault() - # we want to return a Class node instance with proper attributes set - class_node = nodes.Class(name, 'docstring') - class_node.parent = node.parent - # set base class=tuple - class_node.bases.append(base_type) - # XXX add __init__(*attributes) method - for attr in attributes: - fake_node = nodes.EmptyNode() - fake_node.parent = class_node - class_node.instance_attrs[attr] = [fake_node] - return class_node, name, attributes - - -# module specific transformation functions ##################################### - -def 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 PY3K else '""'} - for hashfunc in algorithms) - return AstroidBuilder(MANAGER).string_build(classes) - - -def collections_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -class defaultdict(dict): - default_factory = None - def __missing__(self, key): pass - -class deque(object): - maxlen = 0 - def __init__(self, iterable=None, maxlen=None): pass - def append(self, x): pass - def appendleft(self, x): pass - def clear(self): pass - def count(self, x): return 0 - def extend(self, iterable): pass - def extendleft(self, iterable): pass - def pop(self): pass - def popleft(self): pass - def remove(self, value): pass - def reverse(self): pass - def rotate(self, n): pass - def __iter__(self): return self - -''') - - -def pkg_resources_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -def resource_exists(package_or_requirement, resource_name): - pass - -def resource_isdir(package_or_requirement, resource_name): - pass - -def resource_filename(package_or_requirement, resource_name): - pass - -def resource_stream(package_or_requirement, resource_name): - pass - -def resource_string(package_or_requirement, resource_name): - pass - -def resource_listdir(package_or_requirement, resource_name): - pass - -def extraction_error(): - pass - -def get_cache_path(archive_name, names=()): - pass - -def postprocess(tempname, filename): - pass - -def set_extraction_path(path): - pass - -def cleanup_resources(force=False): - pass - -''') - - -def subprocess_transform(): - if PY3K: - communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=()): - pass - """ - else: - communicate = ('string', 'string') - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - pass - """ - if PY33: - wait_signature = 'def wait(self, timeout=None)' - else: - wait_signature = 'def wait(self)' - return AstroidBuilder(MANAGER).string_build(''' - -class Popen(object): - returncode = pid = 0 - stdin = stdout = stderr = file() - - %(init)s - - def communicate(self, input=None): - return %(communicate)r - %(wait_signature)s: - return self.returncode - def poll(self): - return self.returncode - def send_signal(self, signal): - pass - def terminate(self): - pass - def kill(self): - pass - ''' % {'init': init, - 'communicate': communicate, - 'wait_signature': wait_signature}) - - -# namedtuple support ########################################################### - -def looks_like_namedtuple(node): - func = node.func - if type(func) is nodes.Getattr: - return func.attrname == 'namedtuple' - if type(func) is nodes.Name: - return func.name == 'namedtuple' - return False - -def infer_named_tuple(node, context=None): - """Specific inference function for namedtuple CallFunc node""" - class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, - context=context) - fake = AstroidBuilder(MANAGER).string_build(''' -class %(name)s(tuple): - _fields = %(fields)r - def _asdict(self): - return self.__dict__ - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - return new(cls, iterable) - def _replace(_self, **kwds): - result = _self._make(map(kwds.pop, %(fields)r, _self)) - if kwds: - raise ValueError('Got unexpected field names: %%r' %% list(kwds)) - return result - ''' % {'name': name, 'fields': attributes}) - class_node.locals['_asdict'] = fake.body[0].locals['_asdict'] - class_node.locals['_make'] = fake.body[0].locals['_make'] - class_node.locals['_replace'] = fake.body[0].locals['_replace'] - class_node.locals['_fields'] = fake.body[0].locals['_fields'] - # we use UseInferenceDefault, we can't be a generator so return an iterator - return iter([class_node]) - -def infer_enum(node, context=None): - """ Specific inference function for enum CallFunc node. """ - enum_meta = nodes.Class("EnumMeta", 'docstring') - class_node = infer_func_form(node, enum_meta, - context=context, enum=True)[0] - return iter([class_node.instanciate_class()]) - -def infer_enum_class(node): - """ Specific inference for enums. """ - names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) - for basename in node.basenames: - # TODO: doesn't handle subclasses yet. This implementation - # is a hack to support enums. - if basename not in names: - continue - if node.root().name == 'enum': - # Skip if the class is directly from enum module. - break - for local, values in node.locals.items(): - if any(not isinstance(value, nodes.AssName) - for value in values): - continue - - stmt = values[0].statement() - if isinstance(stmt.targets[0], nodes.Tuple): - targets = stmt.targets[0].itered() - else: - targets = stmt.targets - - new_targets = [] - for target in targets: - # Replace all the assignments with our mocked class. - classdef = dedent(''' - class %(name)s(object): - @property - def value(self): - # Not the best return. - return None - @property - def name(self): - return %(name)r - ''' % {'name': target.name}) - fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] - fake.parent = target.parent - for method in node.mymethods(): - fake.locals[method.name] = [method] - new_targets.append(fake.instanciate_class()) - node.locals[local] = new_targets - break - return node - - -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_named_tuple), - looks_like_namedtuple) -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_enum), - AsStringRegexpPredicate('Enum', 'func')) -MANAGER.register_transform(nodes.Class, infer_enum_class) -register_module_extender(MANAGER, 'hashlib', hashlib_transform) -register_module_extender(MANAGER, 'collections', collections_transform) -register_module_extender(MANAGER, 'pkg_resources', pkg_resources_transform) -register_module_extender(MANAGER, 'subprocess', subprocess_transform) diff --git a/pymode/libs/astroid/brain/pynose.py b/pymode/libs/astroid/brain/pynose.py deleted file mode 100644 index 67a6fb8f..00000000 --- a/pymode/libs/astroid/brain/pynose.py +++ /dev/null @@ -1,79 +0,0 @@ -# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . - -"""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) - - -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/pysix_moves.py b/pymode/libs/astroid/brain/pysix_moves.py deleted file mode 100644 index 548d9761..00000000 --- a/pymode/libs/astroid/brain/pysix_moves.py +++ /dev/null @@ -1,261 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# astroid is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . - -"""Astroid hooks for six.moves.""" - -import sys -from textwrap import dedent - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid.exceptions import AstroidBuildingException - -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 AstroidBuildingException - module = AstroidBuilder(MANAGER).string_build(_IMPORTS) - module.name = 'six.moves' - return module - - -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) diff --git a/pymode/libs/astroid/builder.py b/pymode/libs/astroid/builder.py deleted file mode 100644 index 1fe7a36d..00000000 --- a/pymode/libs/astroid/builder.py +++ /dev/null @@ -1,240 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""The AstroidBuilder makes astroid from living object and / or from _ast - -The builder is not thread safe and can't be used to parse different sources -at the same time. -""" -from __future__ import with_statement - -__docformat__ = "restructuredtext en" - -import sys -from os.path import splitext, basename, exists, abspath - -from astroid.exceptions import AstroidBuildingException, InferenceError -from astroid.raw_building import InspectBuilder -from astroid.rebuilder import TreeRebuilder -from astroid.manager import AstroidManager -from astroid.bases import YES, Instance -from astroid.modutils import modpath_from_file - -from _ast import PyCF_ONLY_AST -def parse(string): - return compile(string, "", 'exec', PyCF_ONLY_AST) - -if sys.version_info >= (3, 0): - from tokenize import detect_encoding - - def open_source_file(filename): - with open(filename, 'rb') as byte_stream: - encoding = detect_encoding(byte_stream.readline)[0] - stream = open(filename, 'r', newline=None, encoding=encoding) - try: - data = stream.read() - except UnicodeError: # wrong encodingg - # detect_encoding returns utf-8 if no encoding specified - msg = 'Wrong (%s) or no encoding specified' % encoding - raise AstroidBuildingException(msg) - return stream, encoding, data - -else: - import re - - _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)") - - def _guess_encoding(string): - """get encoding from a python file as string or return None if not found - """ - # check for UTF-8 byte-order mark - if string.startswith('\xef\xbb\xbf'): - return 'UTF-8' - for line in string.split('\n', 2)[:2]: - # check for encoding declaration - match = _ENCODING_RGX.match(line) - if match is not None: - return match.group(1) - - def open_source_file(filename): - """get data for parsing a file""" - stream = open(filename, 'U') - data = stream.read() - encoding = _guess_encoding(data) - return stream, encoding, data - -# ast NG builder ############################################################## - -MANAGER = AstroidManager() - -class AstroidBuilder(InspectBuilder): - """provide astroid building methods""" - - def __init__(self, manager=None): - InspectBuilder.__init__(self) - self._manager = manager or MANAGER - - def module_build(self, module, modname=None): - """build an astroid from a living module instance - """ - node = None - path = getattr(module, '__file__', None) - if path is not None: - path_, ext = splitext(module.__file__) - if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'): - node = self.file_build(path_ + '.py', modname) - if node is None: - # this is a built-in module - # get a partial representation by introspection - node = self.inspect_build(module, modname=modname, path=path) - # we have to handle transformation by ourselves since the rebuilder - # isn't called for builtin nodes - # - # XXX it's then only called for Module nodes, not for underlying - # nodes - node = self._manager.transform(node) - return node - - def file_build(self, path, modname=None): - """build astroid from a source code file (i.e. from an ast) - - path is expected to be a python source file - """ - try: - stream, encoding, data = open_source_file(path) - except IOError as exc: - msg = 'Unable to load file %r (%s)' % (path, exc) - raise AstroidBuildingException(msg) - except SyntaxError as exc: # py3k encoding specification error - raise AstroidBuildingException(exc) - except LookupError as exc: # unknown encoding - raise AstroidBuildingException(exc) - with stream: - # get module name if necessary - if modname is None: - try: - modname = '.'.join(modpath_from_file(path)) - except ImportError: - modname = splitext(basename(path))[0] - # build astroid representation - module = self._data_build(data, modname, path) - return self._post_build(module, encoding) - - def string_build(self, data, modname='', path=None): - """build astroid from source code string and return rebuilded astroid""" - module = self._data_build(data, modname, path) - module.file_bytes = data.encode('utf-8') - return self._post_build(module, 'utf-8') - - def _post_build(self, module, encoding): - """handles encoding and delayed nodes - after a module has been built - """ - module.file_encoding = encoding - self._manager.cache_module(module) - # post tree building steps after we stored the module in the cache: - for from_node in module._from_nodes: - if from_node.modname == '__future__': - for symbol, _ in from_node.names: - module.future_imports.add(symbol) - self.add_from_names_to_locals(from_node) - # handle delayed assattr nodes - for delayed in module._delayed_assattr: - self.delayed_assattr(delayed) - return module - - def _data_build(self, data, modname, path): - """build tree node from data and add some informations""" - # this method could be wrapped with a pickle/cache function - try: - node = parse(data + '\n') - except TypeError as exc: - raise AstroidBuildingException(exc) - if path is not None: - node_file = abspath(path) - else: - node_file = '' - if modname.endswith('.__init__'): - modname = modname[:-9] - package = True - else: - package = path and path.find('__init__.py') > -1 or False - rebuilder = TreeRebuilder(self._manager) - module = rebuilder.visit_module(node, modname, node_file, package) - module._from_nodes = rebuilder._from_nodes - module._delayed_assattr = rebuilder._delayed_assattr - return module - - def add_from_names_to_locals(self, node): - """store imported names to the locals; - resort the locals if coming from a delayed node - """ - - _key_func = lambda node: node.fromlineno - def sort_locals(my_list): - my_list.sort(key=_key_func) - for (name, asname) in node.names: - if name == '*': - try: - imported = node.do_import_module() - except InferenceError: - continue - for name in imported.wildcard_import_names(): - node.parent.set_local(name, node) - sort_locals(node.parent.scope().locals[name]) - else: - node.parent.set_local(asname or name, node) - sort_locals(node.parent.scope().locals[asname or name]) - - def delayed_assattr(self, node): - """visit a AssAttr node -> add name to locals, handle members - definition - """ - try: - frame = node.frame() - for infered in node.expr.infer(): - if infered is YES: - continue - try: - if infered.__class__ is Instance: - infered = infered._proxied - iattrs = infered.instance_attrs - elif isinstance(infered, Instance): - # Const, Tuple, ... we may be wrong, may be not, but - # anyway we don't want to pollute builtin's namespace - continue - elif infered.is_function: - iattrs = infered.instance_attrs - else: - iattrs = infered.locals - except AttributeError: - # XXX log error - #import traceback - #traceback.print_exc() - continue - values = iattrs.setdefault(node.attrname, []) - if node in values: - continue - # get assign in __init__ first XXX useful ? - if frame.name == '__init__' and values and not \ - values[0].frame().name == '__init__': - values.insert(0, node) - else: - values.append(node) - except InferenceError: - pass - diff --git a/pymode/libs/astroid/exceptions.py b/pymode/libs/astroid/exceptions.py deleted file mode 100644 index 3889e2e7..00000000 --- a/pymode/libs/astroid/exceptions.py +++ /dev/null @@ -1,51 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains exceptions used in the astroid library - -""" - -__doctype__ = "restructuredtext en" - -class AstroidError(Exception): - """base exception class for all astroid related exceptions""" - -class AstroidBuildingException(AstroidError): - """exception class when we are unable to build an astroid representation""" - -class ResolveError(AstroidError): - """base class of astroid resolution/inference error""" - -class NotFoundError(ResolveError): - """raised when we are unable to resolve a name""" - -class InferenceError(ResolveError): - """raised when we are unable to infer a node""" - -class UseInferenceDefault(Exception): - """exception to be raised in custom inference function to indicate that it - should go back to the default behaviour - """ - -class UnresolvableName(InferenceError): - """raised when we are unable to resolve a name""" - -class NoDefault(AstroidError): - """raised by function's `default_value` method when an argument has - no default value - """ - diff --git a/pymode/libs/astroid/inference.py b/pymode/libs/astroid/inference.py deleted file mode 100644 index 22807049..00000000 --- a/pymode/libs/astroid/inference.py +++ /dev/null @@ -1,405 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to handle inference on astroid trees -""" - -__doctype__ = "restructuredtext en" - -from itertools import chain - -from astroid import nodes - -from astroid.manager import AstroidManager -from astroid.exceptions import (AstroidError, InferenceError, NoDefault, - NotFoundError, UnresolvableName) -from astroid.bases import (YES, Instance, InferenceContext, - _infer_stmts, copy_context, path_wrapper, - raise_if_nothing_infered) -from astroid.protocols import ( - _arguments_infer_argname, - BIN_OP_METHOD, UNARY_OP_METHOD) - -MANAGER = AstroidManager() - - -class CallContext(object): - """when inferring a function call, this class is used to remember values - given as argument - """ - def __init__(self, args, starargs, dstarargs): - self.args = [] - self.nargs = {} - for arg in args: - if isinstance(arg, nodes.Keyword): - self.nargs[arg.arg] = arg.value - else: - self.args.append(arg) - self.starargs = starargs - self.dstarargs = dstarargs - - def infer_argument(self, funcnode, name, context): - """infer a function argument value according to the call context""" - # 1. search in named keywords - try: - return self.nargs[name].infer(context) - except KeyError: - # Function.args.args can be None in astroid (means that we don't have - # information on argnames) - argindex = funcnode.args.find_argname(name)[0] - if argindex is not None: - # 2. first argument of instance/class method - if argindex == 0 and funcnode.type in ('method', 'classmethod'): - if context.boundnode is not None: - boundnode = context.boundnode - else: - # XXX can do better ? - boundnode = funcnode.parent.frame() - if funcnode.type == 'method': - if not isinstance(boundnode, Instance): - boundnode = Instance(boundnode) - return iter((boundnode,)) - if funcnode.type == 'classmethod': - return iter((boundnode,)) - # if we have a method, extract one position - # from the index, so we'll take in account - # the extra parameter represented by `self` or `cls` - if funcnode.type in ('method', 'classmethod'): - argindex -= 1 - # 2. search arg index - try: - return self.args[argindex].infer(context) - except IndexError: - pass - # 3. search in *args (.starargs) - if self.starargs is not None: - its = [] - for infered in self.starargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(argindex, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 4. XXX search in **kwargs (.dstarargs) - if self.dstarargs is not None: - its = [] - for infered in self.dstarargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(name, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 5. */** argument, (Tuple or Dict) - if name == funcnode.args.vararg: - return iter((nodes.const_factory(()))) - if name == funcnode.args.kwarg: - return iter((nodes.const_factory({}))) - # 6. return default value if any - try: - return funcnode.args.default_value(name).infer(context) - except NoDefault: - raise InferenceError(name) - - -# .infer method ############################################################### - - -def infer_end(self, context=None): - """inference's end for node such as Module, Class, Function, Const... - """ - yield self -nodes.Module._infer = infer_end -nodes.Class._infer = infer_end -nodes.Function._infer = infer_end -nodes.Lambda._infer = infer_end -nodes.Const._infer = infer_end -nodes.List._infer = infer_end -nodes.Tuple._infer = infer_end -nodes.Dict._infer = infer_end -nodes.Set._infer = infer_end - -def _higher_function_scope(node): - """ Search for the first function which encloses the given - scope. This can be used for looking up in that function's - scope, in case looking up in a lower scope for a particular - name fails. - - :param node: A scope node. - :returns: - ``None``, if no parent function scope was found, - otherwise an instance of :class:`astroid.scoped_nodes.Function`, - which encloses the given node. - """ - current = node - while current.parent and not isinstance(current.parent, nodes.Function): - current = current.parent - if current and current.parent: - return current.parent - -def infer_name(self, context=None): - """infer a Name: use name lookup rules""" - frame, stmts = self.lookup(self.name) - if not stmts: - # Try to see if the name is enclosed in a nested function - # and use the higher (first function) scope for searching. - # TODO: should this be promoted to other nodes as well? - parent_function = _higher_function_scope(self.scope()) - if parent_function: - _, stmts = parent_function.lookup(self.name) - - if not stmts: - raise UnresolvableName(self.name) - context = context.clone() - context.lookupname = self.name - return _infer_stmts(stmts, context, frame) -nodes.Name._infer = path_wrapper(infer_name) -nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper - - -def infer_callfunc(self, context=None): - """infer a CallFunc node by trying to guess what the function returns""" - callcontext = context.clone() - callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs) - callcontext.boundnode = None - for callee in self.func.infer(context): - if callee is YES: - yield callee - continue - try: - if hasattr(callee, 'infer_call_result'): - for infered in callee.infer_call_result(self, callcontext): - yield infered - except InferenceError: - ## XXX log error ? - continue -nodes.CallFunc._infer = path_wrapper(raise_if_nothing_infered(infer_callfunc)) - - -def infer_import(self, context=None, asname=True): - """infer an Import node: return the imported module/object""" - name = context.lookupname - if name is None: - raise InferenceError() - if asname: - yield self.do_import_module(self.real_name(name)) - else: - yield self.do_import_module(name) -nodes.Import._infer = path_wrapper(infer_import) - -def infer_name_module(self, name): - context = InferenceContext() - context.lookupname = name - return self.infer(context, asname=False) -nodes.Import.infer_name_module = infer_name_module - - -def infer_from(self, context=None, asname=True): - """infer a From nodes: return the imported module/object""" - name = context.lookupname - if name is None: - raise InferenceError() - if asname: - name = self.real_name(name) - module = self.do_import_module() - try: - context = copy_context(context) - context.lookupname = name - return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context) - except NotFoundError: - raise InferenceError(name) -nodes.From._infer = path_wrapper(infer_from) - - -def infer_getattr(self, context=None): - """infer a Getattr node by using getattr on the associated object""" - for owner in self.expr.infer(context): - if owner is YES: - yield owner - continue - try: - context.boundnode = owner - for obj in owner.igetattr(self.attrname, context): - yield obj - context.boundnode = None - except (NotFoundError, InferenceError): - context.boundnode = None - except AttributeError: - # XXX method / function - context.boundnode = None -nodes.Getattr._infer = path_wrapper(raise_if_nothing_infered(infer_getattr)) -nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper - - -def infer_global(self, context=None): - if context.lookupname is None: - raise InferenceError() - try: - return _infer_stmts(self.root().getattr(context.lookupname), context) - except NotFoundError: - raise InferenceError() -nodes.Global._infer = path_wrapper(infer_global) - - -def infer_subscript(self, context=None): - """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]""" - value = next(self.value.infer(context)) - if value is YES: - yield YES - return - - index = next(self.slice.infer(context)) - if index is YES: - yield YES - return - - if isinstance(index, nodes.Const): - try: - assigned = value.getitem(index.value, context) - except AttributeError: - raise InferenceError() - except (IndexError, TypeError): - yield YES - return - - # Prevent inferring if the infered subscript - # is the same as the original subscripted object. - if self is assigned: - yield YES - return - for infered in assigned.infer(context): - yield infered - else: - raise InferenceError() -nodes.Subscript._infer = path_wrapper(infer_subscript) -nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript) - -def infer_unaryop(self, context=None): - for operand in self.operand.infer(context): - try: - yield operand.infer_unary_op(self.op) - except TypeError: - continue - except AttributeError: - meth = UNARY_OP_METHOD[self.op] - if meth is None: - yield YES - else: - try: - # XXX just suppose if the type implement meth, returned type - # will be the same - operand.getattr(meth) - yield operand - except GeneratorExit: - raise - except: - yield YES -nodes.UnaryOp._infer = path_wrapper(infer_unaryop) - -def _infer_binop(operator, operand1, operand2, context, failures=None): - if operand1 is YES: - yield operand1 - return - try: - for valnode in operand1.infer_binary_op(operator, operand2, context): - yield valnode - except AttributeError: - try: - # XXX just suppose if the type implement meth, returned type - # will be the same - operand1.getattr(BIN_OP_METHOD[operator]) - yield operand1 - except: - if failures is None: - yield YES - else: - failures.append(operand1) - -def infer_binop(self, context=None): - failures = [] - for lhs in self.left.infer(context): - for val in _infer_binop(self.op, lhs, self.right, context, failures): - yield val - for lhs in failures: - for rhs in self.right.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): - yield val -nodes.BinOp._infer = path_wrapper(infer_binop) - - -def infer_arguments(self, context=None): - name = context.lookupname - if name is None: - raise InferenceError() - return _arguments_infer_argname(self, name, context) -nodes.Arguments._infer = infer_arguments - - -def infer_ass(self, context=None): - """infer a AssName/AssAttr: need to inspect the RHS part of the - assign node - """ - stmt = self.statement() - if isinstance(stmt, nodes.AugAssign): - return stmt.infer(context) - stmts = list(self.assigned_stmts(context=context)) - return _infer_stmts(stmts, context) -nodes.AssName._infer = path_wrapper(infer_ass) -nodes.AssAttr._infer = path_wrapper(infer_ass) - -def infer_augassign(self, context=None): - failures = [] - for lhs in self.target.infer_lhs(context): - for val in _infer_binop(self.op, lhs, self.value, context, failures): - yield val - for lhs in failures: - for rhs in self.value.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): - yield val -nodes.AugAssign._infer = path_wrapper(infer_augassign) - - -# no infer method on DelName and DelAttr (expected InferenceError) - - -def infer_empty_node(self, context=None): - if not self.has_underlying_object(): - yield YES - else: - try: - for infered in MANAGER.infer_ast_from_something(self.object, - context=context): - yield infered - except AstroidError: - yield YES -nodes.EmptyNode._infer = path_wrapper(infer_empty_node) - - -def infer_index(self, context=None): - return self.value.infer(context) -nodes.Index._infer = infer_index diff --git a/pymode/libs/astroid/inspector.py b/pymode/libs/astroid/inspector.py deleted file mode 100644 index 1fc31926..00000000 --- a/pymode/libs/astroid/inspector.py +++ /dev/null @@ -1,273 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""visitor doing some postprocessing on the astroid tree. -Try to resolve definitions (namespace) dictionary, relationship... - -This module has been imported from pyreverse -""" - -__docformat__ = "restructuredtext en" - -from os.path import dirname - -import astroid -from astroid.exceptions import InferenceError -from astroid.utils import LocalsVisitor -from astroid.modutils import get_module_part, is_relative, is_standard_module - -class IdGeneratorMixIn(object): - """ - Mixin adding the ability to generate integer uid - """ - def __init__(self, start_value=0): - self.id_count = start_value - - def init_counter(self, start_value=0): - """init the id counter - """ - self.id_count = start_value - - def generate_id(self): - """generate a new identifier - """ - self.id_count += 1 - return self.id_count - - -class Linker(IdGeneratorMixIn, LocalsVisitor): - """ - walk on the project tree and resolve relationships. - - According to options the following attributes may be added to visited nodes: - - * uid, - a unique identifier for the node (on astroid.Project, astroid.Module, - astroid.Class and astroid.locals_type). Only if the linker has been instantiated - with tag=True parameter (False by default). - - * Function - a mapping from locals names to their bounded value, which may be a - constant like a string or an integer, or an astroid node (on astroid.Module, - astroid.Class and astroid.Function). - - * instance_attrs_type - as locals_type but for klass member attributes (only on astroid.Class) - - * implements, - list of implemented interface _objects_ (only on astroid.Class nodes) - """ - - def __init__(self, project, inherited_interfaces=0, tag=False): - IdGeneratorMixIn.__init__(self) - LocalsVisitor.__init__(self) - # take inherited interface in consideration or not - self.inherited_interfaces = inherited_interfaces - # tag nodes or not - self.tag = tag - # visited project - self.project = project - - - def visit_project(self, node): - """visit an astroid.Project node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for module in node.modules: - self.visit(module) - - def visit_package(self, node): - """visit an astroid.Package node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for subelmt in node.values(): - self.visit(subelmt) - - def visit_module(self, node): - """visit an astroid.Module node - - * set the locals_type mapping - * set the depends mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - node.depends = [] - if self.tag: - node.uid = self.generate_id() - - def visit_class(self, node): - """visit an astroid.Class node - - * set the locals_type and instance_attrs_type mappings - * set the implements list and build it - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - # resolve ancestors - for baseobj in node.ancestors(recurs=False): - specializations = getattr(baseobj, 'specializations', []) - specializations.append(node) - baseobj.specializations = specializations - # resolve instance attributes - node.instance_attrs_type = {} - for assattrs in node.instance_attrs.values(): - for assattr in assattrs: - self.handle_assattr_type(assattr, node) - # resolve implemented interface - try: - node.implements = list(node.interfaces(self.inherited_interfaces)) - except InferenceError: - node.implements = () - - def visit_function(self, node): - """visit an astroid.Function node - - * set the locals_type mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - - link_project = visit_project - link_module = visit_module - link_class = visit_class - link_function = visit_function - - def visit_assname(self, node): - """visit an astroid.AssName node - - handle locals_type - """ - # avoid double parsing done by different Linkers.visit - # running over the same project: - if hasattr(node, '_handled'): - return - node._handled = True - if node.name in node.frame(): - frame = node.frame() - else: - # the name has been defined as 'global' in the frame and belongs - # there. Btw the frame is not yet visited as the name is in the - # root locals; the frame hence has no locals_type attribute - frame = node.root() - try: - values = node.infered() - try: - already_infered = frame.locals_type[node.name] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - frame.locals_type[node.name] = values - except astroid.InferenceError: - pass - - def handle_assattr_type(self, node, parent): - """handle an astroid.AssAttr node - - handle instance_attrs_type - """ - try: - values = list(node.infer()) - try: - already_infered = parent.instance_attrs_type[node.attrname] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - parent.instance_attrs_type[node.attrname] = values - except astroid.InferenceError: - pass - - def visit_import(self, node): - """visit an astroid.Import node - - resolve module dependencies - """ - context_file = node.root().file - for name in node.names: - relative = is_relative(name[0], context_file) - self._imported_module(node, name[0], relative) - - - def visit_from(self, node): - """visit an astroid.From node - - resolve module dependencies - """ - basename = node.modname - context_file = node.root().file - if context_file is not None: - relative = is_relative(basename, context_file) - else: - relative = False - for name in node.names: - if name[0] == '*': - continue - # analyze dependencies - fullname = '%s.%s' % (basename, name[0]) - if fullname.find('.') > -1: - try: - # XXX: don't use get_module_part, missing package precedence - fullname = get_module_part(fullname, context_file) - except ImportError: - continue - if fullname != basename: - self._imported_module(node, fullname, relative) - - - def compute_module(self, context_name, mod_path): - """return true if the module should be added to dependencies""" - package_dir = dirname(self.project.path) - if context_name == mod_path: - return 0 - elif is_standard_module(mod_path, (package_dir,)): - return 1 - return 0 - - # protected methods ######################################################## - - def _imported_module(self, node, mod_path, relative): - """notify an imported module, used to analyze dependencies - """ - module = node.root() - context_name = module.name - if relative: - mod_path = '%s.%s' % ('.'.join(context_name.split('.')[:-1]), - mod_path) - if self.compute_module(context_name, mod_path): - # handle dependencies - if not hasattr(module, 'depends'): - module.depends = [] - mod_paths = module.depends - if not mod_path in mod_paths: - mod_paths.append(mod_path) diff --git a/pymode/libs/astroid/manager.py b/pymode/libs/astroid/manager.py deleted file mode 100644 index b1fb3058..00000000 --- a/pymode/libs/astroid/manager.py +++ /dev/null @@ -1,391 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""astroid manager: avoid multiple astroid build of a same module when -possible by providing a class responsible to get astroid representation -from various source and using a cache of built modules) -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import collections -import imp -import os -from os.path import dirname, join, isdir, exists -from warnings import warn -import zipimport - -from logilab.common.configuration import OptionsProviderMixIn - -from astroid.exceptions import AstroidBuildingException -from astroid import modutils - - -def astroid_wrapper(func, modname): - """wrapper to give to AstroidManager.project_from_files""" - print('parsing %s...' % modname) - try: - return func(modname) - except AstroidBuildingException as exc: - print(exc) - except Exception as exc: - import traceback - traceback.print_exc() - -def _silent_no_wrap(func, modname): - """silent wrapper that doesn't do anything; can be used for tests""" - return func(modname) - -def safe_repr(obj): - try: - return repr(obj) - except: - return '???' - - - -class AstroidManager(OptionsProviderMixIn): - """the astroid manager, responsible to build astroid from files - or modules. - - Use the Borg pattern. - """ - - name = 'astroid loader' - options = (("ignore", - {'type' : "csv", 'metavar' : "", - 'dest' : "black_list", "default" : ('CVS',), - 'help' : "add (may be a directory) to the black list\ -. It should be a base name, not a path. You may set this option multiple times\ -."}), - ("project", - {'default': "No Name", 'type' : 'string', 'short': 'p', - 'metavar' : '', - 'help' : 'set the project name.'}), - ) - brain = {} - def __init__(self): - self.__dict__ = AstroidManager.brain - if not self.__dict__: - OptionsProviderMixIn.__init__(self) - self.load_defaults() - # NOTE: cache entries are added by the [re]builder - self.astroid_cache = {} - self._mod_file_cache = {} - self.transforms = collections.defaultdict(list) - self._failed_import_hooks = [] - self.always_load_extensions = False - self.optimize_ast = False - self.extension_package_whitelist = set() - - 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 AstroidBuildingException('unable to get astroid for file %s' % - filepath) - - def _build_stub_module(self, modname): - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).string_build('', modname) - - 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(dirname(context_file)) - try: - filepath, mp_type = self.file_from_module_name(modname, context_file) - if mp_type == modutils.PY_ZIPMODULE: - module = self.zip_import_data(filepath) - if module is not None: - return module - elif mp_type in (imp.C_BUILTIN, imp.C_EXTENSION): - if mp_type == imp.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: - msg = 'Unable to load module %s (%s)' % (modname, ex) - raise AstroidBuildingException(msg) - return self.ast_from_module(module, modname) - elif mp_type == imp.PY_COMPILED: - raise AstroidBuildingException("Unable to load compiled module %s" % (modname,)) - if filepath is None: - raise AstroidBuildingException("Unable to load module %s" % (modname,)) - return self.ast_from_file(filepath, modname, fallback=False) - except AstroidBuildingException as e: - for hook in self._failed_import_hooks: - try: - return hook(modname) - except AstroidBuildingException: - 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: - continue - return None - - def file_from_module_name(self, modname, contextfile): - try: - value = self._mod_file_cache[(modname, contextfile)] - except KeyError: - try: - value = modutils.file_info_from_modpath( - modname.split('.'), context_file=contextfile) - except ImportError as ex: - msg = 'Unable to load module %s (%s)' % (modname, ex) - value = AstroidBuildingException(msg) - self._mod_file_cache[(modname, contextfile)] = value - if isinstance(value, AstroidBuildingException): - raise value - return value - - def ast_from_module(self, module, modname=None): - """given an imported module, return the astroid object""" - modname = modname or module.__name__ - if modname in self.astroid_cache: - return self.astroid_cache[modname] - try: - # some builtin modules don't have __file__ attribute - filepath = module.__file__ - if 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: - raise AstroidBuildingException( - 'Unable to get module for class %s' % safe_repr(klass)) - modastroid = self.ast_from_module_name(modname) - return modastroid.getattr(klass.__name__)[0] # XXX - - - def infer_ast_from_something(self, obj, context=None): - """infer astroid for the given class""" - if hasattr(obj, '__class__') and not isinstance(obj, type): - klass = obj.__class__ - else: - klass = obj - try: - modname = klass.__module__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for %s' % safe_repr(klass)) - except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving module for %s: %s' - % (safe_repr(klass), ex)) - try: - name = klass.__name__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get name for %s' % safe_repr(klass)) - except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving name for %s: %s' - % (safe_repr(klass), ex)) - # take care, on living object __module__ is regularly wrong :( - modastroid = self.ast_from_module_name(modname) - if klass is obj: - for infered in modastroid.igetattr(name, context): - yield infered - else: - for infered in modastroid.igetattr(name, context): - yield infered.instanciate_class() - - def project_from_files(self, files, func_wrapper=astroid_wrapper, - project_name=None, black_list=None): - """return a Project from a list of files or modules""" - # build the project representation - project_name = project_name or self.config.project - black_list = black_list or self.config.black_list - project = Project(project_name) - for something in files: - if not exists(something): - fpath = modutils.file_from_modpath(something.split('.')) - elif isdir(something): - fpath = join(something, '__init__.py') - else: - fpath = something - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None: - continue - # XXX why is first file defining the project.path ? - project.path = project.path or astroid.file - project.add_module(astroid) - base_name = astroid.name - # recurse in package except if __init__ was explicitly given - if astroid.package and something.find('__init__') == -1: - # recurse on others packages / modules if this is a package - for fpath in modutils.get_module_files(dirname(astroid.file), - black_list): - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None or astroid.name == base_name: - continue - project.add_module(astroid) - return project - - def register_transform(self, node_class, transform, predicate=None): - """Register `transform(node)` function to be applied on the given - Astroid's `node_class` if `predicate` is None or 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 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 `AstroidBuildingException`. - """ - self._failed_import_hooks.append(hook) - - def transform(self, node): - """Call matching transforms for the given node if any and return the - transformed node. - """ - cls = node.__class__ - if cls not in self.transforms: - # no transform registered for this class of node - return node - - transforms = self.transforms[cls] - orig_node = node # copy the reference - for transform_func, predicate in transforms: - if predicate is None or predicate(node): - ret = transform_func(node) - # if the transformation function returns something, it's - # expected to be a replacement for the node - if ret is not None: - if node is not orig_node: - # node has already be modified by some previous - # transformation, warn about it - warn('node %s substituted multiple times' % node) - node = ret - return node - - def cache_module(self, module): - """Cache a module if no module with the same name is known yet.""" - self.astroid_cache.setdefault(module.name, module) - - def clear_cache(self, 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) - - -class Project(object): - """a project handle a set of modules / packages""" - def __init__(self, name=''): - self.name = name - self.path = None - self.modules = [] - self.locals = {} - self.__getitem__ = self.locals.__getitem__ - self.__iter__ = self.locals.__iter__ - self.values = self.locals.values - self.keys = self.locals.keys - self.items = self.locals.items - - def add_module(self, node): - self.locals[node.name] = node - self.modules.append(node) - - def get_module(self, name): - return self.locals[name] - - def get_children(self): - return self.modules - - def __repr__(self): - return '' % (self.name, id(self), - len(self.modules)) - - diff --git a/pymode/libs/astroid/mixins.py b/pymode/libs/astroid/mixins.py deleted file mode 100644 index dbf1673a..00000000 --- a/pymode/libs/astroid/mixins.py +++ /dev/null @@ -1,124 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module contains some mixins for the different nodes. -""" - -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import (AstroidBuildingException, InferenceError, - NotFoundError) - - -class BlockRangeMixIn(object): - """override block range """ - - @cachedproperty - def blockstart_tolineno(self): - return self.lineno - - def _elsed_block_range(self, lineno, orelse, last=None): - """handle block line numbers range for try/finally, for, if and while - statements - """ - if lineno == self.fromlineno: - return lineno, lineno - if orelse: - if lineno >= orelse[0].fromlineno: - return lineno, orelse[-1].tolineno - return lineno, orelse[0].fromlineno - 1 - return lineno, last or self.tolineno - - -class FilterStmtsMixin(object): - """Mixin for statement filtering and assignment type""" - - def _get_filtered_stmts(self, _, node, _stmts, mystmt): - """method used in _filter_stmts to get statemtents and trigger break""" - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - def ass_type(self): - return self - - -class AssignTypeMixin(object): - - def ass_type(self): - return self - - def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - return _stmts, True - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - -class ParentAssignTypeMixin(AssignTypeMixin): - - def ass_type(self): - return self.parent.ass_type() - - -class FromImportMixIn(FilterStmtsMixin): - """MixIn for From and Import Nodes""" - - def _infer_name(self, frame, name): - return name - - def do_import_module(self, modname=None): - """return the ast for a module whose name is imported by - """ - # handle special case where we are on a package node importing a module - # using the same name as the package, which may end in an infinite loop - # on relative imports - # XXX: no more needed ? - mymodule = self.root() - level = getattr(self, 'level', None) # Import as no level - if modname is None: - modname = self.modname - # XXX we should investigate deeper if we really want to check - # importing itself: modname and mymodule.name be relative or absolute - if mymodule.relative_to_absolute_name(modname, level) == mymodule.name: - # FIXME: we used to raise InferenceError here, but why ? - return mymodule - try: - return mymodule.import_module(modname, level=level) - except AstroidBuildingException: - raise InferenceError(modname) - except SyntaxError as ex: - raise InferenceError(str(ex)) - - def real_name(self, asname): - """get name from 'as' name""" - for name, _asname in self.names: - if name == '*': - return asname - if not _asname: - name = name.split('.', 1)[0] - _asname = name - if asname == _asname: - return name - raise NotFoundError(asname) - diff --git a/pymode/libs/astroid/modutils.py b/pymode/libs/astroid/modutils.py deleted file mode 100644 index c547f3e6..00000000 --- a/pymode/libs/astroid/modutils.py +++ /dev/null @@ -1,670 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# astroid is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""Python modules manipulation utility functions. - -:type PY_SOURCE_EXTS: tuple(str) -:var PY_SOURCE_EXTS: list of possible python source file extension - -:type STD_LIB_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 -""" -from __future__ import with_statement - -__docformat__ = "restructuredtext en" - -import imp -import os -import sys -from distutils.sysconfig import get_python_lib -from distutils.errors import DistutilsPlatformError -import zipimport - -try: - import pkg_resources -except ImportError: - pkg_resources = None - -from logilab.common import _handle_blacklist - -PY_ZIPMODULE = object() - -if sys.platform.startswith('win'): - PY_SOURCE_EXTS = ('py', 'pyw') - PY_COMPILED_EXTS = ('dll', 'pyd') -else: - PY_SOURCE_EXTS = ('py',) - PY_COMPILED_EXTS = ('so',) - -# Notes about STD_LIB_DIRS -# Consider arch-specific installation for STD_LIB_DIRS definition -# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on -# -# :see: `Problems with /usr/lib64 builds `_ -# :see: `FHS `_ -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)]) - if os.name == 'nt': - STD_LIB_DIRS.add(os.path.join(sys.prefix, 'dlls')) - try: - # real_prefix is defined when running inside virtualenv. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'dlls')) - except AttributeError: - pass -# 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() - -EXT_LIB_DIR = get_python_lib() - -BUILTIN_MODULES = dict(zip(sys.builtin_module_names, - [1]*len(sys.builtin_module_names))) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - -def _normalize_path(path): - return os.path.normcase(os.path.abspath(path)) - - -_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=1): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=1): - """Load a python module from its splitted name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package splitted on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = 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__', '') - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):])) - path = [os.path.dirname(_file)] - prevmodule = module - return module - - -def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def _check_init(path, mod_path): - """check there are some __init__.py all along the way""" - for part in mod_path: - path = os.path.join(path, part) - if not _has_init(path): - return False - return True - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding splitted module's name - (i.e name of a module or package splitted on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package splitted in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding splitted module's name - """ - base = os.path.splitext(os.path.abspath(filename))[0] - if extrapath is not None: - for path_ in 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 _check_init(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - for path in 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 _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): - return file_info_from_modpath(modpath, path, context_file)[0] - -def file_info_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, 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 _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__, imp.PY_SOURCE - 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 = 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 not i >= max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_module_files(src_directory, blacklist): - """given a package directory return a list of all available python - module's files in the package and its subpackages - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all available python module's files in the package and - its subpackages - """ - files = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not '__init__.py' in filenames: - dirnames[:] = () - continue - for filename in filenames: - if _is_python_file(filename): - src = 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 - """ - base, orig_ext = os.path.splitext(os.path.abspath(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: - return True - 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 _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 == imp.PY_COMPILED: - try: - return get_source_file(mp_filename), imp.PY_SOURCE - except NoSourceFile: - return mp_filename, imp.PY_COMPILED - elif mtype == imp.C_BUILTIN: - # integrated builtin module - return None, imp.C_BUILTIN - elif mtype == imp.PKG_DIRECTORY: - mp_filename = _has_init(mp_filename) - mtype = imp.PY_SOURCE - return mp_filename, mtype - -def _search_zip(modpath, pic): - for filepath, importer in pic.items(): - if importer is not None: - if importer.find_module(modpath[0]): - if not importer.find_module(os.path.sep.join(modpath)): - raise ImportError('No module named %s in %s/%s' % ( - '.'.join(modpath[1:]), filepath, modpath)) - return PY_ZIPMODULE, os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), filepath - raise ImportError('No module named %s' % '.'.join(modpath)) - - -def _module_file(modpath, path=None): - """get a module type / file path - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.'), with leading empty strings for explicit relative import - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - - :rtype: tuple(int, str) - :return: the module type flag and the file path for a module - """ - # egg support compat - try: - pic = sys.path_importer_cache - _path = (path is None and sys.path or path) - for __path in _path: - if not __path in pic: - try: - pic[__path] = zipimport.zipimporter(__path) - except zipimport.ZipImportError: - pic[__path] = None - checkeggs = True - except AttributeError: - checkeggs = False - # pkg_resources support (aka setuptools namespace packages) - if (pkg_resources is not None - and modpath[0] in pkg_resources._namespace_packages - and modpath[0] in sys.modules - and len(modpath) > 1): - # setuptools has added into sys.modules a module object with proper - # __path__, get back information from there - module = sys.modules[modpath.pop(0)] - path = module.__path__ - imported = [] - while modpath: - modname = modpath[0] - # take care to changes in find_module implementation wrt builtin modules - # - # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) - # >>> imp.find_module('posix') - # (None, 'posix', ('', '', 6)) - # - # Python 3.3.1 (default, Apr 26 2013, 12:08:46) - # >>> imp.find_module('posix') - # (None, None, ('', '', 6)) - try: - stream, mp_filename, mp_desc = imp.find_module(modname, path) - except ImportError: - if checkeggs: - return _search_zip(modpath, pic)[:2] - raise - else: - # Don't forget to close the stream to avoid - # spurious ResourceWarnings. - if stream: - stream.close() - - if checkeggs and mp_filename: - fullabspath = [_cache_normalize_path(x) for x in _path] - try: - pathindex = fullabspath.index(os.path.dirname(_normalize_path(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 != imp.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(os.path.join(mp_filename, '__init__.py'), 'rb') as stream: - data = stream.read(4096) - except IOError: - path = [mp_filename] - else: - if b'pkgutil' in data and b'extend_path' in data: - # extend_path is called, search sys.path for module/packages - # of this name see pkgutil.extend_path documentation - path = [os.path.join(p, *imported) for p in sys.path - if os.path.isdir(os.path.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 = 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 diff --git a/pymode/libs/astroid/node_classes.py b/pymode/libs/astroid/node_classes.py deleted file mode 100644 index 4b413ef8..00000000 --- a/pymode/libs/astroid/node_classes.py +++ /dev/null @@ -1,966 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""Module for some node classes. More nodes in scoped_nodes.py -""" - -import sys - -import six -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import NoDefault -from astroid.bases import (NodeNG, Statement, Instance, InferenceContext, - _infer_stmts, YES, BUILTINS) -from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin, - ParentAssignTypeMixin, FromImportMixIn) - -PY3K = sys.version_info >= (3, 0) - - -def unpack_infer(stmt, context=None): - """recursively generate nodes inferred by the given statement. - If the inferred value is a list or a tuple, recurse on the elements - """ - if isinstance(stmt, (List, Tuple)): - for elt in stmt.elts: - for infered_elt in unpack_infer(elt, context): - yield infered_elt - return - # if infered is a final node, return it and stop - infered = next(stmt.infer(context)) - if infered is stmt: - yield infered - return - # else, infer recursivly, except YES object that should be returned as is - for infered in stmt.infer(context): - if infered is YES: - yield infered - else: - for inf_inf in unpack_infer(infered, context): - yield inf_inf - - -def are_exclusive(stmt1, stmt2, exceptions=None): - """return true if the two given statements are mutually exclusive - - `exceptions` may be a list of exception names. If specified, discard If - branches and check one of the statement is in an exception handler catching - one of the given exceptions. - - algorithm : - 1) index stmt1's parents - 2) climb among stmt2's parents until we find a common parent - 3) if the common parent is a If or TryExcept statement, look if nodes are - in exclusive branches - """ - # index stmt1's parents - stmt1_parents = {} - children = {} - node = stmt1.parent - previous = stmt1 - while node: - stmt1_parents[node] = 1 - children[node] = previous - previous = node - node = node.parent - # climb among stmt2's parents until we find a common parent - node = stmt2.parent - previous = stmt2 - while node: - if node in stmt1_parents: - # if the common parent is a If or TryExcept statement, look if - # nodes are in exclusive branches - if isinstance(node, If) and exceptions is None: - if (node.locate_child(previous)[1] - is not node.locate_child(children[node])[1]): - return True - elif isinstance(node, TryExcept): - c2attr, c2node = node.locate_child(previous) - c1attr, c1node = node.locate_child(children[node]) - if c1node is not c2node: - if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'orelse') or - (c2attr == 'orelse' and c1attr == 'handlers')): - return True - elif c2attr == 'handlers' and c1attr == 'handlers': - return previous is not children[node] - return False - previous = node - node = node.parent - return False - - -class LookupMixIn(object): - """Mixin looking up a name in the right scope - """ - - def lookup(self, name): - """lookup a variable name - - return the scope node and the list of assignments associated to the - given name according to the scope where it has been found (locals, - globals or builtin) - - The lookup is starting from self's scope. If self is not a frame itself - and the name is found in the inner frame locals, statements will be - filtered to remove ignorable statements according to self's location - """ - return self.scope().scope_lookup(self, name) - - def ilookup(self, name): - """infered lookup - - return an iterator on infered values of the statements returned by - the lookup method - """ - frame, stmts = self.lookup(name) - context = InferenceContext() - return _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() - if not myframe is frame or self is frame: - return stmts - mystmt = self.statement() - # line filtering if we are in the same frame - # - # take care node may be missing lineno information (this is the case for - # nodes inserted for living objects) - if myframe is frame and mystmt.fromlineno is not None: - assert mystmt.fromlineno is not None, mystmt - mylineno = mystmt.fromlineno + offset - else: - # disabling lineno filtering - mylineno = 0 - _stmts = [] - _stmt_parents = [] - for node in stmts: - stmt = node.statement() - # line filtering is on and we have reached our location, break - if mylineno > 0 and stmt.fromlineno > mylineno: - break - assert hasattr(node, 'ass_type'), (node, node.scope(), - node.scope().locals) - ass_type = node.ass_type() - - if node.has_base(self): - break - - _stmts, done = ass_type._get_filtered_stmts(self, node, _stmts, mystmt) - if done: - break - - optional_assign = ass_type.optional_assign - if optional_assign and ass_type.parent_of(self): - # we are inside a loop, loop var assigment is hidding previous - # assigment - _stmts = [node] - _stmt_parents = [stmt.parent] - continue - - # XXX comment various branches below!!! - try: - pindex = _stmt_parents.index(stmt.parent) - except ValueError: - pass - else: - # we got a parent index, this means the currently visited node - # is at the same block level as a previously visited node - if _stmts[pindex].ass_type().parent_of(ass_type): - # both statements are not at the same block level - continue - # if currently visited node is following previously considered - # assignement and both are not exclusive, we can drop the - # previous one. For instance in the following code :: - # - # if a: - # x = 1 - # else: - # x = 2 - # print x - # - # we can't remove neither x = 1 nor x = 2 when looking for 'x' - # of 'print x'; while in the following :: - # - # x = 1 - # x = 2 - # print x - # - # we can remove x = 1 when we see x = 2 - # - # moreover, on loop assignment types, assignment won't - # necessarily be done if the loop has no iteration, so we don't - # want to clear previous assigments if any (hence the test on - # optional_assign) - if not (optional_assign or are_exclusive(_stmts[pindex], node)): - del _stmt_parents[pindex] - del _stmts[pindex] - if isinstance(node, AssName): - if not optional_assign and stmt.parent is mystmt.parent: - _stmts = [] - _stmt_parents = [] - elif isinstance(node, DelName): - _stmts = [] - _stmt_parents = [] - continue - if not are_exclusive(self, node): - _stmts.append(node) - _stmt_parents.append(stmt.parent) - return _stmts - -# Name classes - -class AssName(LookupMixIn, ParentAssignTypeMixin, NodeNG): - """class representing an AssName node""" - - -class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG): - """class representing a DelName node""" - - -class Name(LookupMixIn, NodeNG): - """class representing a Name node""" - - - - -##################### node classes ######################################## - -class Arguments(NodeNG, AssignTypeMixin): - """class representing an Arguments node""" - if PY3K: - # Python 3.4+ uses a different approach regarding annotations, - # each argument is a new class, _ast.arg, which exposes an - # 'annotation' attribute. In astroid though, arguments are exposed - # as is in the Arguments node and the only way to expose annotations - # is by using something similar with Python 3.3: - # - we expose 'varargannotation' and 'kwargannotation' of annotations - # of varargs and kwargs. - # - we expose 'annotation', a list with annotations for - # for each normal argument. If an argument doesn't have an - # annotation, its value will be None. - - _astroid_fields = ('args', 'defaults', 'kwonlyargs', - 'kw_defaults', 'annotations', - 'varargannotation', 'kwargannotation') - annotations = None - varargannotation = None - kwargannotation = None - else: - _astroid_fields = ('args', 'defaults', 'kwonlyargs', 'kw_defaults') - args = None - defaults = None - kwonlyargs = None - kw_defaults = None - - def __init__(self, vararg=None, kwarg=None): - self.vararg = vararg - self.kwarg = kwarg - - def _infer_name(self, frame, name): - if self.parent is frame: - return name - return None - - @cachedproperty - def fromlineno(self): - lineno = super(Arguments, self).fromlineno - return max(lineno, self.parent.fromlineno or 0) - - def format_args(self): - """return arguments formatted as string""" - result = [] - if self.args: - result.append(_format_args(self.args, self.defaults)) - if self.vararg: - result.append('*%s' % self.vararg) - if self.kwarg: - result.append('**%s' % self.kwarg) - if self.kwonlyargs: - if not self.vararg: - result.append('*') - result.append(_format_args(self.kwonlyargs, self.kw_defaults)) - return ', '.join(result) - - def default_value(self, argname): - """return the default value for an argument - - :raise `NoDefault`: if there is no default value defined - """ - i = _find_arg(argname, self.args)[0] - if i is not None: - idx = i - (len(self.args) - len(self.defaults)) - if idx >= 0: - return self.defaults[idx] - i = _find_arg(argname, self.kwonlyargs)[0] - if i is not None and self.kw_defaults[i] is not None: - return self.kw_defaults[i] - raise NoDefault() - - def is_argument(self, name): - """return True if the name is defined in arguments""" - if name == self.vararg: - return True - if name == self.kwarg: - return True - return self.find_argname(name, True)[1] is not None - - def find_argname(self, argname, rec=False): - """return index and Name node with given name""" - if self.args: # self.args may be None in some cases (builtin function) - return _find_arg(argname, self.args, rec) - return None, None - - def get_children(self): - """override get_children to skip over None elements in kw_defaults""" - for child in super(Arguments, self).get_children(): - if child is not None: - yield child - - -def _find_arg(argname, args, rec=False): - for i, arg in enumerate(args): - if isinstance(arg, Tuple): - if rec: - found = _find_arg(argname, arg.elts) - if found[0] is not None: - return found - elif arg.name == argname: - return i, arg - return None, None - - -def _format_args(args, defaults=None): - values = [] - if args is None: - return '' - if defaults is not None: - default_offset = len(args) - len(defaults) - for i, arg in enumerate(args): - if isinstance(arg, Tuple): - values.append('(%s)' % _format_args(arg.elts)) - else: - values.append(arg.name) - if defaults is not None and i >= default_offset: - if defaults[i-default_offset] is not None: - values[-1] += '=' + defaults[i-default_offset].as_string() - return ', '.join(values) - - -class AssAttr(NodeNG, ParentAssignTypeMixin): - """class representing an AssAttr node""" - _astroid_fields = ('expr',) - expr = None - -class Assert(Statement): - """class representing an Assert node""" - _astroid_fields = ('test', 'fail',) - test = None - fail = None - -class Assign(Statement, AssignTypeMixin): - """class representing an Assign node""" - _astroid_fields = ('targets', 'value',) - targets = None - value = None - -class AugAssign(Statement, AssignTypeMixin): - """class representing an AugAssign node""" - _astroid_fields = ('target', 'value',) - target = None - value = None - -class Backquote(NodeNG): - """class representing a Backquote node""" - _astroid_fields = ('value',) - value = None - -class BinOp(NodeNG): - """class representing a BinOp node""" - _astroid_fields = ('left', 'right',) - left = None - right = None - -class BoolOp(NodeNG): - """class representing a BoolOp node""" - _astroid_fields = ('values',) - values = None - -class Break(Statement): - """class representing a Break node""" - - -class CallFunc(NodeNG): - """class representing a CallFunc node""" - _astroid_fields = ('func', 'args', 'starargs', 'kwargs') - func = None - args = None - starargs = None - kwargs = None - - def __init__(self): - self.starargs = None - self.kwargs = None - -class Compare(NodeNG): - """class representing a Compare node""" - _astroid_fields = ('left', 'ops',) - left = None - ops = None - - def get_children(self): - """override get_children for tuple fields""" - yield self.left - for _, comparator in self.ops: - yield comparator # we don't want the 'op' - - def last_child(self): - """override last_child""" - # XXX maybe if self.ops: - return self.ops[-1][1] - #return self.left - -class Comprehension(NodeNG): - """class representing a Comprehension node""" - _astroid_fields = ('target', 'iter', 'ifs') - target = None - iter = None - ifs = None - - optional_assign = True - def ass_type(self): - return self - - def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - if isinstance(lookup_node, (Const, Name)): - return [lookup_node], True - - elif self.statement() is mystmt: - # original node's statement is the assignment, only keeps - # current node (gen exp, list comp) - - return [node], True - - return stmts, False - - -class Const(NodeNG, Instance): - """represent a constant node like num, str, bool, None, bytes""" - - def __init__(self, value=None): - self.value = value - - def getitem(self, index, context=None): - if isinstance(self.value, six.string_types): - return Const(self.value[index]) - raise TypeError('%r (value=%s)' % (self, self.value)) - - def has_dynamic_getattr(self): - return False - - def itered(self): - if isinstance(self.value, six.string_types): - return self.value - raise TypeError() - - def pytype(self): - return self._proxied.qname() - - -class Continue(Statement): - """class representing a Continue node""" - - -class Decorators(NodeNG): - """class representing a Decorators node""" - _astroid_fields = ('nodes',) - nodes = None - - def __init__(self, nodes=None): - self.nodes = nodes - - def scope(self): - # skip the function node to go directly to the upper level scope - return self.parent.parent.scope() - -class DelAttr(NodeNG, ParentAssignTypeMixin): - """class representing a DelAttr node""" - _astroid_fields = ('expr',) - expr = None - - -class Delete(Statement, AssignTypeMixin): - """class representing a Delete node""" - _astroid_fields = ('targets',) - targets = None - - -class Dict(NodeNG, Instance): - """class representing a Dict node""" - _astroid_fields = ('items',) - - def __init__(self, items=None): - if items is None: - self.items = [] - else: - self.items = [(const_factory(k), const_factory(v)) - for k, v in items.items()] - - def pytype(self): - return '%s.dict' % BUILTINS - - def get_children(self): - """get children of a Dict node""" - # overrides get_children - for key, value in self.items: - yield key - yield value - - def last_child(self): - """override last_child""" - if self.items: - return self.items[-1][1] - return None - - def itered(self): - return self.items[::2] - - def getitem(self, lookup_key, context=None): - for key, value in self.items: - for inferedkey in key.infer(context): - if inferedkey is YES: - continue - if isinstance(inferedkey, Const) \ - and inferedkey.value == lookup_key: - return value - # This should raise KeyError, but all call sites only catch - # IndexError. Let's leave it like that for now. - raise IndexError(lookup_key) - - -class Discard(Statement): - """class representing a Discard node""" - _astroid_fields = ('value',) - value = None - - -class Ellipsis(NodeNG): - """class representing an Ellipsis node""" - - -class EmptyNode(NodeNG): - """class representing an EmptyNode node""" - - -class ExceptHandler(Statement, AssignTypeMixin): - """class representing an ExceptHandler node""" - _astroid_fields = ('type', 'name', 'body',) - type = None - name = None - body = None - - @cachedproperty - def blockstart_tolineno(self): - if self.name: - return self.name.tolineno - elif self.type: - return self.type.tolineno - else: - return self.lineno - - def catch(self, exceptions): - if self.type is None or exceptions is None: - return True - for node in self.type.nodes_of_class(Name): - if node.name in exceptions: - return True - - -class Exec(Statement): - """class representing an Exec node""" - _astroid_fields = ('expr', 'globals', 'locals',) - expr = None - globals = None - locals = None - - -class ExtSlice(NodeNG): - """class representing an ExtSlice node""" - _astroid_fields = ('dims',) - dims = None - -class For(BlockRangeMixIn, AssignTypeMixin, Statement): - """class representing a For node""" - _astroid_fields = ('target', 'iter', 'body', 'orelse',) - target = None - iter = None - body = None - orelse = None - - optional_assign = True - @cachedproperty - def blockstart_tolineno(self): - return self.iter.tolineno - - -class From(FromImportMixIn, Statement): - """class representing a From node""" - - def __init__(self, fromname, names, level=0): - self.modname = fromname - self.names = names - self.level = level - -class Getattr(NodeNG): - """class representing a Getattr node""" - _astroid_fields = ('expr',) - expr = None - - -class Global(Statement): - """class representing a Global node""" - - def __init__(self, names): - self.names = names - - def _infer_name(self, frame, name): - return name - - -class If(BlockRangeMixIn, Statement): - """class representing an If node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - @cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for if statements""" - if lineno == self.body[0].fromlineno: - return lineno, lineno - if lineno <= self.body[-1].tolineno: - return lineno, self.body[-1].tolineno - return self._elsed_block_range(lineno, self.orelse, - self.body[0].fromlineno - 1) - - -class IfExp(NodeNG): - """class representing an IfExp node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - -class Import(FromImportMixIn, Statement): - """class representing an Import node""" - - -class Index(NodeNG): - """class representing an Index node""" - _astroid_fields = ('value',) - value = None - - -class Keyword(NodeNG): - """class representing a Keyword node""" - _astroid_fields = ('value',) - value = None - - -class List(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a List node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] - - def pytype(self): - return '%s.list' % BUILTINS - - def getitem(self, index, context=None): - return self.elts[index] - - def itered(self): - return self.elts - - -class Nonlocal(Statement): - """class representing a Nonlocal node""" - - def __init__(self, names): - self.names = names - - def _infer_name(self, frame, name): - return name - - -class Pass(Statement): - """class representing a Pass node""" - - -class Print(Statement): - """class representing a Print node""" - _astroid_fields = ('dest', 'values',) - dest = None - values = None - - -class Raise(Statement): - """class representing a Raise node""" - exc = None - if sys.version_info < (3, 0): - _astroid_fields = ('exc', 'inst', 'tback') - inst = None - tback = None - else: - _astroid_fields = ('exc', 'cause') - exc = None - cause = None - - def raises_not_implemented(self): - if not self.exc: - return - for name in self.exc.nodes_of_class(Name): - if name.name == 'NotImplementedError': - return True - - -class Return(Statement): - """class representing a Return node""" - _astroid_fields = ('value',) - value = None - - -class Set(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a Set node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] - - def pytype(self): - return '%s.set' % BUILTINS - - def itered(self): - return self.elts - - -class Slice(NodeNG): - """class representing a Slice node""" - _astroid_fields = ('lower', 'upper', 'step') - lower = None - upper = None - step = None - -class Starred(NodeNG, ParentAssignTypeMixin): - """class representing a Starred node""" - _astroid_fields = ('value',) - value = None - - -class Subscript(NodeNG): - """class representing a Subscript node""" - _astroid_fields = ('value', 'slice') - value = None - slice = None - - -class TryExcept(BlockRangeMixIn, Statement): - """class representing a TryExcept node""" - _astroid_fields = ('body', 'handlers', 'orelse',) - body = None - handlers = None - orelse = None - - def _infer_name(self, frame, name): - return name - - def block_range(self, lineno): - """handle block line numbers range for try/except statements""" - last = None - for exhandler in self.handlers: - if exhandler.type and lineno == exhandler.type.fromlineno: - return lineno, lineno - if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: - return lineno, exhandler.body[-1].tolineno - if last is None: - last = exhandler.body[0].fromlineno - 1 - return self._elsed_block_range(lineno, self.orelse, last) - - -class TryFinally(BlockRangeMixIn, Statement): - """class representing a TryFinally node""" - _astroid_fields = ('body', 'finalbody',) - body = None - finalbody = None - - def block_range(self, lineno): - """handle block line numbers range for try/finally statements""" - child = self.body[0] - # py2.5 try: except: finally: - if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno - and lineno > self.fromlineno and lineno <= child.tolineno): - return child.block_range(lineno) - return self._elsed_block_range(lineno, self.finalbody) - - -class Tuple(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a Tuple node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] - - def pytype(self): - return '%s.tuple' % BUILTINS - - def getitem(self, index, context=None): - return self.elts[index] - - def itered(self): - return self.elts - - -class UnaryOp(NodeNG): - """class representing an UnaryOp node""" - _astroid_fields = ('operand',) - operand = None - - -class While(BlockRangeMixIn, Statement): - """class representing a While node""" - _astroid_fields = ('test', 'body', 'orelse',) - test = None - body = None - orelse = None - - @cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for for and while statements""" - return self. _elsed_block_range(lineno, self.orelse) - - -class With(BlockRangeMixIn, AssignTypeMixin, Statement): - """class representing a With node""" - _astroid_fields = ('items', 'body') - items = None - body = None - - @cachedproperty - def blockstart_tolineno(self): - return self.items[-1][0].tolineno - - def get_children(self): - for expr, var in self.items: - yield expr - if var: - yield var - for elt in self.body: - yield elt - -class Yield(NodeNG): - """class representing a Yield node""" - _astroid_fields = ('value',) - value = None - -class YieldFrom(Yield): - """ Class representing a YieldFrom node. """ - -# constants ############################################################## - -CONST_CLS = { - list: List, - tuple: Tuple, - dict: Dict, - set: Set, - type(None): Const, - } - -def _update_const_classes(): - """update constant classes, so the keys of CONST_CLS can be reused""" - klasses = (bool, int, float, complex, str) - if sys.version_info < (3, 0): - klasses += (unicode, long) - if sys.version_info >= (2, 6): - klasses += (bytes,) - for kls in klasses: - CONST_CLS[kls] = Const -_update_const_classes() - -def const_factory(value): - """return an astroid node for a python value""" - # XXX we should probably be stricter here and only consider stuff in - # CONST_CLS or do better treatment: in case where value is not in CONST_CLS, - # we should rather recall the builder on this value than returning an empty - # node (another option being that const_factory shouldn't be called with something - # not in CONST_CLS) - assert not isinstance(value, NodeNG) - try: - return CONST_CLS[value.__class__](value) - except (KeyError, AttributeError): - node = EmptyNode() - node.object = value - return node diff --git a/pymode/libs/astroid/nodes.py b/pymode/libs/astroid/nodes.py deleted file mode 100644 index 67c2f8e8..00000000 --- a/pymode/libs/astroid/nodes.py +++ /dev/null @@ -1,74 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -""" -on all nodes : - .is_statement, returning true if the node should be considered as a - statement node - .root(), returning the root node of the tree (i.e. a Module) - .previous_sibling(), returning previous sibling statement node - .next_sibling(), returning next sibling statement node - .statement(), returning the first parent node marked as statement node - .frame(), returning the first node defining a new local scope (i.e. - Module, Function or Class) - .set_local(name, node), define an identifier on the first parent frame, - with the node defining it. This is used by the astroid builder and should not - be used from out there. - -on From and Import : - .real_name(name), - - -""" -# pylint: disable=unused-import - -__docformat__ = "restructuredtext en" - -from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \ - AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \ - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \ - Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \ - From, Getattr, Global, If, IfExp, Import, Index, Keyword, \ - List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \ - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \ - const_factory -from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \ - ListComp, SetComp, Function, Class - -ALL_NODE_CLASSES = ( - Arguments, AssAttr, Assert, Assign, AssName, AugAssign, - Backquote, BinOp, BoolOp, Break, - CallFunc, Class, Compare, Comprehension, Const, Continue, - Decorators, DelAttr, DelName, Delete, - Dict, DictComp, Discard, - Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, - For, From, Function, - Getattr, GenExpr, Global, - If, IfExp, Import, Index, - Keyword, - Lambda, List, ListComp, - Name, Nonlocal, - Module, - Pass, Print, - Raise, Return, - Set, SetComp, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, - UnaryOp, - While, With, - Yield, YieldFrom - ) - diff --git a/pymode/libs/astroid/protocols.py b/pymode/libs/astroid/protocols.py deleted file mode 100644 index 4c11f9cf..00000000 --- a/pymode/libs/astroid/protocols.py +++ /dev/null @@ -1,415 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to handle python protocols for nodes -where it makes sense. -""" - -__doctype__ = "restructuredtext en" -import collections - -from astroid.exceptions import InferenceError, NoDefault, NotFoundError -from astroid.node_classes import unpack_infer -from astroid.bases import InferenceContext, copy_context, \ - raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES -from astroid.nodes import const_factory -from astroid import nodes - -BIN_OP_METHOD = {'+': '__add__', - '-': '__sub__', - '/': '__div__', - '//': '__floordiv__', - '*': '__mul__', - '**': '__power__', - '%': '__mod__', - '&': '__and__', - '|': '__or__', - '^': '__xor__', - '<<': '__lshift__', - '>>': '__rshift__', - } - -UNARY_OP_METHOD = {'+': '__pos__', - '-': '__neg__', - '~': '__invert__', - 'not': None, # XXX not '__nonzero__' - } - -# unary operations ############################################################ - -def tl_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not bool(self.elts)) - raise TypeError() # XXX log unsupported operation -nodes.Tuple.infer_unary_op = tl_infer_unary_op -nodes.List.infer_unary_op = tl_infer_unary_op - - -def dict_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not bool(self.items)) - raise TypeError() # XXX log unsupported operation -nodes.Dict.infer_unary_op = dict_infer_unary_op - - -def const_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not self.value) - # XXX log potentially raised TypeError - elif operator == '+': - return const_factory(+self.value) - else: # operator == '-': - return const_factory(-self.value) -nodes.Const.infer_unary_op = const_infer_unary_op - - -# binary operations ########################################################### - -BIN_OP_IMPL = {'+': lambda a, b: a + b, - '-': lambda a, b: a - b, - '/': lambda a, b: a / b, - '//': lambda a, b: a // b, - '*': lambda a, b: a * b, - '**': lambda a, b: a ** b, - '%': lambda a, b: a % b, - '&': lambda a, b: a & b, - '|': lambda a, b: a | b, - '^': lambda a, b: a ^ b, - '<<': lambda a, b: a << b, - '>>': lambda a, b: a >> b, - } -for key, impl in list(BIN_OP_IMPL.items()): - BIN_OP_IMPL[key+'='] = impl - -def const_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, nodes.Const): - try: - impl = BIN_OP_IMPL[operator] - - try: - yield const_factory(impl(self.value, other.value)) - except Exception: - # ArithmeticError is not enough: float >> float is a TypeError - # TODO : let pylint know about the problem - pass - except TypeError: - # XXX log TypeError - continue - elif other is YES: - yield other - else: - try: - for val in other.infer_binary_op(operator, self, context): - yield val - except AttributeError: - yield YES -nodes.Const.infer_binary_op = yes_if_nothing_infered(const_infer_binary_op) - - -def tl_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, self.__class__) and operator == '+': - node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] - elts += [n for elt in other.elts for n in elt.infer(context) - if not n is YES] - node.elts = elts - yield node - elif isinstance(other, nodes.Const) and operator == '*': - if not isinstance(other.value, int): - yield YES - continue - node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] * other.value - node.elts = elts - yield node - elif isinstance(other, Instance) and not isinstance(other, nodes.Const): - yield YES - # XXX else log TypeError -nodes.Tuple.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) -nodes.List.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) - - -def dict_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, Instance) and isinstance(other._proxied, nodes.Class): - yield YES - # XXX else log TypeError -nodes.Dict.infer_binary_op = yes_if_nothing_infered(dict_infer_binary_op) - -def instance_infer_binary_op(self, operator, other, context): - try: - methods = self.getattr(BIN_OP_METHOD[operator]) - except (NotFoundError, KeyError): - # Unknown operator - yield YES - else: - for method in methods: - if not isinstance(method, nodes.Function): - continue - for result in method.infer_call_result(self, context): - if result is not YES: - yield result - # We are interested only in the first infered method, - # don't go looking in the rest of the methods of the ancestors. - break - -Instance.infer_binary_op = yes_if_nothing_infered(instance_infer_binary_op) - - -# assignment ################################################################## - -"""the assigned_stmts method is responsible to return the assigned statement -(e.g. not inferred) according to the assignment type. - -The `asspath` argument is used to record the lhs path of the original node. -For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath -will be [1, 1] once arrived to the Assign node. - -The `context` argument is the current inference context which should be given -to any intermediary inference necessary. -""" - -def _resolve_looppart(parts, asspath, context): - """recursive function to resolve multiple assignments on loops""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if part is YES: - continue - # XXX handle __iter__ and log potentially detected errors - if not hasattr(part, 'itered'): - continue - try: - itered = part.itered() - except TypeError: - continue # XXX log error - for stmt in itered: - try: - assigned = stmt.getitem(index, context) - except (AttributeError, IndexError): - continue - except TypeError: # stmt is unsubscriptable Const - continue - if not asspath: - # we achieved to resolved the assignment path, - # don't infer the last part - yield assigned - elif assigned is YES: - break - else: - # we are not yet on the last part of the path - # search on each possibly inferred value - try: - for infered in _resolve_looppart(assigned.infer(context), - asspath, context): - yield infered - except InferenceError: - break - - -def for_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - for lst in self.iter.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.elts: - yield item - else: - for infered in _resolve_looppart(self.iter.infer(context), - asspath, context): - yield infered - -nodes.For.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) -nodes.Comprehension.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) - - -def mulass_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - asspath = [] - asspath.insert(0, self.elts.index(node)) - return self.parent.assigned_stmts(self, context, asspath) -nodes.Tuple.assigned_stmts = mulass_assigned_stmts -nodes.List.assigned_stmts = mulass_assigned_stmts - - -def assend_assigned_stmts(self, context=None): - return self.parent.assigned_stmts(self, context=context) -nodes.AssName.assigned_stmts = assend_assigned_stmts -nodes.AssAttr.assigned_stmts = assend_assigned_stmts - - -def _arguments_infer_argname(self, name, context): - # arguments information may be missing, in which case we can't do anything - # more - if not (self.args or self.vararg or self.kwarg): - yield YES - return - # first argument of instance/class method - if self.args and getattr(self.args[0], 'name', None) == name: - functype = self.parent.type - if functype == 'method': - yield Instance(self.parent.parent.frame()) - return - if functype == 'classmethod': - yield self.parent.parent.frame() - return - if name == self.vararg: - vararg = const_factory(()) - vararg.parent = self - yield vararg - return - if name == self.kwarg: - kwarg = const_factory({}) - kwarg.parent = self - yield kwarg - return - # if there is a default value, yield it. And then yield YES to reflect - # we can't guess given argument value - try: - context = copy_context(context) - for infered in self.default_value(name).infer(context): - yield infered - yield YES - except NoDefault: - yield YES - - -def arguments_assigned_stmts(self, node, context, asspath=None): - if context.callcontext: - # reset call context/name - callcontext = context.callcontext - context = copy_context(context) - context.callcontext = None - return callcontext.infer_argument(self.parent, node.name, context) - return _arguments_infer_argname(self, node.name, context) -nodes.Arguments.assigned_stmts = arguments_assigned_stmts - - -def assign_assigned_stmts(self, node, context=None, asspath=None): - if not asspath: - yield self.value - return - for infered in _resolve_asspart(self.value.infer(context), asspath, context): - yield infered -nodes.Assign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) -nodes.AugAssign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) - - -def _resolve_asspart(parts, asspath, context): - """recursive function to resolve multiple assignments""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if hasattr(part, 'getitem'): - try: - assigned = part.getitem(index, context) - # XXX raise a specific exception to avoid potential hiding of - # unexpected exception ? - except (TypeError, IndexError): - return - if not asspath: - # we achieved to resolved the assignment path, don't infer the - # last part - yield assigned - elif assigned is YES: - return - else: - # we are not yet on the last part of the path search on each - # possibly inferred value - try: - for infered in _resolve_asspart(assigned.infer(context), - asspath, context): - yield infered - except InferenceError: - return - - -def excepthandler_assigned_stmts(self, node, context=None, asspath=None): - for assigned in unpack_infer(self.type): - if isinstance(assigned, nodes.Class): - assigned = Instance(assigned) - yield assigned -nodes.ExceptHandler.assigned_stmts = raise_if_nothing_infered(excepthandler_assigned_stmts) - - -def with_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - for _, vars in self.items: - if vars is None: - continue - for lst in vars.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.nodes: - yield item -nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts) - - -def starred_assigned_stmts(self, node=None, context=None, asspath=None): - stmt = self.statement() - if not isinstance(stmt, (nodes.Assign, nodes.For)): - raise InferenceError() - - 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: - # Too many starred arguments in the expression. - raise InferenceError() - - if context is None: - context = InferenceContext() - try: - rhs = next(value.infer(context)) - except InferenceError: - yield YES - return - if rhs is YES or not hasattr(rhs, 'elts'): - # Not interested in inferred values without elts. - yield YES - return - - elts = collections.deque(rhs.elts[:]) - if len(lhs.elts) > len(rhs.elts): - # a, *b, c = (1, 2) - raise InferenceError() - - # 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 remvoe anything after the starred node. - - for index, node in enumerate(lhs.elts): - if not isinstance(node, nodes.Starred): - elts.popleft() - continue - lhs_elts = collections.deque(reversed(lhs.elts[index:])) - for node in lhs_elts: - if not isinstance(node, nodes.Starred): - elts.pop() - continue - # We're done - for elt in elts: - yield elt - 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 99a026a7..00000000 --- a/pymode/libs/astroid/raw_building.py +++ /dev/null @@ -1,366 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to create astroid trees from scratch -(build_* functions) or from living object (object_build_* functions) -""" - -__docformat__ = "restructuredtext en" - -import sys -from os.path import abspath -from inspect import (getargspec, isdatadescriptor, isfunction, ismethod, - ismethoddescriptor, isclass, isbuiltin, ismodule) -import six - -from astroid.node_classes import CONST_CLS -from astroid.nodes import (Module, Class, Const, const_factory, From, - Function, EmptyNode, Name, Arguments) -from astroid.bases import BUILTINS, Generator -from astroid.manager import AstroidManager -MANAGER = AstroidManager() - -_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types - -def _io_discrepancy(member): - # _io module names itself `io`: http://bugs.python.org/issue18602 - member_self = getattr(member, '__self__', None) - return (member_self and - ismodule(member_self) and - member_self.__name__ == '_io' and - member.__module__ == 'io') - -def _attach_local_node(parent, node, name): - node.name = name # needed by add_local_node - parent.add_local_node(node) - -_marker = object() - -def attach_dummy_node(node, name, object=_marker): - """create a dummy node and register it in the locals of the given - node with the specified name - """ - enode = EmptyNode() - enode.object = object - _attach_local_node(node, enode, name) - -def _has_underlying_object(self): - return hasattr(self, 'object') and self.object is not _marker - -EmptyNode.has_underlying_object = _has_underlying_object - -def attach_const_node(node, name, value): - """create a Const node and register it in the locals of the given - node with the specified name - """ - if not name in node.special_attributes: - _attach_local_node(node, const_factory(value), name) - -def attach_import_node(node, modname, membername): - """create a From node and register it in the locals of the given - node with the specified name - """ - from_node = From(modname, [(membername, None)]) - _attach_local_node(node, from_node, membername) - - -def build_module(name, doc=None): - """create and initialize a astroid Module node""" - node = Module(name, doc, pure_python=False) - node.package = False - node.parent = None - return node - -def build_class(name, basenames=(), doc=None): - """create and initialize a astroid Class node""" - node = Class(name, doc) - for base in basenames: - basenode = Name() - basenode.name = base - node.bases.append(basenode) - basenode.parent = node - return node - -def build_function(name, args=None, defaults=None, flag=0, doc=None): - """create and initialize a astroid Function node""" - args, defaults = args or [], defaults or [] - # first argument is now a list of decorators - func = Function(name, doc) - func.args = argsnode = Arguments() - argsnode.args = [] - for arg in args: - argsnode.args.append(Name()) - argsnode.args[-1].name = arg - argsnode.args[-1].parent = argsnode - argsnode.defaults = [] - for default in defaults: - argsnode.defaults.append(const_factory(default)) - argsnode.defaults[-1].parent = argsnode - argsnode.kwarg = None - argsnode.vararg = None - argsnode.parent = func - if args: - register_arguments(func) - return func - - -def build_from_import(fromname, names): - """create and initialize an astroid From import statement""" - return From(fromname, [(name, None) for name in names]) - -def register_arguments(func, args=None): - """add given arguments to local - - args is a list that may contains nested lists - (i.e. def func(a, (b, c, d)): ...) - """ - if args is None: - args = func.args.args - if func.args.vararg: - func.set_local(func.args.vararg, func.args) - if func.args.kwarg: - func.set_local(func.args.kwarg, func.args) - for arg in args: - if isinstance(arg, Name): - func.set_local(arg.name, arg) - else: - register_arguments(func, arg.elts) - -def object_build_class(node, member, localname): - """create astroid for a living class object""" - basenames = [base.__name__ for base in member.__bases__] - return _base_class_object_build(node, member, basenames, - localname=localname) - -def object_build_function(node, member, localname): - """create astroid for a living function object""" - args, varargs, varkw, defaults = getargspec(member) - if varargs is not None: - args.append(varargs) - if varkw is not None: - args.append(varkw) - func = build_function(getattr(member, '__name__', None) or localname, args, - defaults, six.get_function_code(member).co_flags, member.__doc__) - node.add_local_node(func, localname) - -def object_build_datadescriptor(node, member, name): - """create astroid for a living data descriptor object""" - return _base_class_object_build(node, member, [], name) - -def object_build_methoddescriptor(node, member, localname): - """create astroid for a living method descriptor object""" - # FIXME get arguments ? - func = build_function(getattr(member, '__name__', None) or localname, - doc=member.__doc__) - # set node's arguments to None to notice that we have no information, not - # and empty argument list - func.args.args = None - node.add_local_node(func, localname) - -def _base_class_object_build(node, member, basenames, name=None, localname=None): - """create astroid for a living class object, with a given set of base names - (e.g. ancestors) - """ - klass = build_class(name or getattr(member, '__name__', None) or localname, - basenames, member.__doc__) - klass._newstyle = isinstance(member, type) - node.add_local_node(klass, localname) - try: - # limit the instantiation trick since it's too dangerous - # (such as infinite test execution...) - # this at least resolves common case such as Exception.args, - # OSError.errno - if issubclass(member, Exception): - instdict = member().__dict__ - else: - raise TypeError - except: - pass - else: - for name, obj in instdict.items(): - valnode = EmptyNode() - valnode.object = obj - valnode.parent = klass - valnode.lineno = 1 - klass.instance_attrs[name] = [valnode] - return klass - - - - -class InspectBuilder(object): - """class for building nodes from living object - - this is actually a really minimal representation, including only Module, - Function and Class nodes and some others as guessed. - """ - - # astroid from living objects ############################################### - - def __init__(self): - self._done = {} - self._module = None - - def inspect_build(self, module, modname=None, path=None): - """build astroid from a living module (i.e. using inspect) - this is used when there is no python source code available (either - because it's a built-in module or because the .py is not available) - """ - self._module = module - if modname is None: - modname = module.__name__ - try: - node = build_module(modname, module.__doc__) - except AttributeError: - # in jython, java modules have no __doc__ (see #109562) - node = build_module(modname) - node.file = node.path = path and abspath(path) or path - node.name = modname - MANAGER.cache_module(node) - node.package = hasattr(module, '__path__') - self._done = {} - self.object_build(node, module) - return node - - def object_build(self, node, obj): - """recursive method which create a partial ast from real objects - (only function, class, and method are handled) - """ - if obj in self._done: - return self._done[obj] - self._done[obj] = node - for name in dir(obj): - try: - member = getattr(obj, name) - except AttributeError: - # damned ExtensionClass.Base, I know you're there ! - attach_dummy_node(node, name) - continue - if ismethod(member): - member = six.get_method_function(member) - if isfunction(member): - # verify this is not an imported function - filename = getattr(six.get_function_code(member), - 'co_filename', None) - if filename is None: - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif filename != getattr(self._module, '__file__', None): - attach_dummy_node(node, name, member) - else: - object_build_function(node, member, name) - elif isbuiltin(member): - if (not _io_discrepancy(member) and - self.imported_member(node, member, name)): - continue - object_build_methoddescriptor(node, member, name) - elif isclass(member): - if self.imported_member(node, member, name): - continue - if member in self._done: - class_node = self._done[member] - if not class_node in node.locals.get(name, ()): - node.add_local_node(class_node, name) - else: - class_node = object_build_class(node, member, name) - # recursion - self.object_build(class_node, member) - if name == '__class__' and class_node.parent is None: - class_node.parent = self._done[self._module] - elif ismethoddescriptor(member): - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif isdatadescriptor(member): - assert isinstance(member, object) - object_build_datadescriptor(node, member, name) - elif type(member) in _CONSTANTS: - attach_const_node(node, name, member) - else: - # create an empty node so that the name is actually defined - attach_dummy_node(node, name, member) - - def imported_member(self, node, member, name): - """verify this is not an imported class or handle it""" - # /!\ some classes like ExtensionClass doesn't have a __module__ - # attribute ! Also, this may trigger an exception on badly built module - # (see http://www.logilab.org/ticket/57299 for instance) - try: - modname = getattr(member, '__module__', None) - except: - # XXX use logging - print('unexpected error while building astroid from living object') - import traceback - traceback.print_exc() - modname = None - if modname is None: - if name in ('__new__', '__subclasshook__'): - # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14) - # >>> print object.__new__.__module__ - # None - modname = BUILTINS - else: - attach_dummy_node(node, name, member) - return True - if {'gtk': 'gtk._gtk'}.get(modname, modname) != self._module.__name__: - # check if it sounds valid and then add an import node, else use a - # dummy node - try: - getattr(sys.modules[modname], name) - except (KeyError, AttributeError): - attach_dummy_node(node, name, member) - else: - attach_import_node(node, modname, name) - return True - return False - - -### astroid bootstrapping ###################################################### -Astroid_BUILDER = InspectBuilder() - -_CONST_PROXY = {} -def _astroid_bootstrapping(astroid_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 logilab.common.compat import builtins - astroid_builtin = Astroid_BUILDER.inspect_build(builtins) - - for cls, node_cls in CONST_CLS.items(): - if cls is type(None): - proxy = build_class('NoneType') - proxy.parent = astroid_builtin - else: - proxy = astroid_builtin.getattr(cls.__name__)[0] - if cls in (dict, list, set, tuple): - node_cls._proxied = proxy - else: - _CONST_PROXY[cls] = proxy - -_astroid_bootstrapping() - -# TODO : find a nicer way to handle this situation; -# However __proxied introduced an -# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870) -def _set_proxied(const): - return _CONST_PROXY[const.value.__class__] -Const._proxied = property(_set_proxied) - -from types import GeneratorType -Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__) -Astroid_BUILDER.object_build(Generator._proxied, GeneratorType) - diff --git a/pymode/libs/astroid/rebuilder.py b/pymode/libs/astroid/rebuilder.py deleted file mode 100644 index 013479a8..00000000 --- a/pymode/libs/astroid/rebuilder.py +++ /dev/null @@ -1,926 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains utilities for rebuilding a _ast tree in -order to get a single Astroid representation -""" - -import sys -from _ast import ( - Expr as Discard, Str, - # binary operators - Add, BinOp, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor, - LShift, RShift, - # logical operators - And, Or, - # unary operators - UAdd, USub, Not, Invert, - # comparison operators - Eq, Gt, GtE, In, Is, IsNot, Lt, LtE, NotEq, NotIn, - ) - -from astroid import nodes as new -from astroid import astpeephole - - -_BIN_OP_CLASSES = {Add: '+', - BitAnd: '&', - BitOr: '|', - BitXor: '^', - Div: '/', - FloorDiv: '//', - Mod: '%', - Mult: '*', - Pow: '**', - Sub: '-', - LShift: '<<', - RShift: '>>', - } - -_BOOL_OP_CLASSES = {And: 'and', - Or: 'or', - } - -_UNARY_OP_CLASSES = {UAdd: '+', - USub: '-', - Not: 'not', - Invert: '~', - } - -_CMP_OP_CLASSES = {Eq: '==', - Gt: '>', - GtE: '>=', - In: 'in', - Is: 'is', - IsNot: 'is not', - Lt: '<', - LtE: '<=', - NotEq: '!=', - NotIn: 'not in', - } - -CONST_NAME_TRANSFORMS = {'None': None, - 'True': True, - 'False': False, - } - -REDIRECT = {'arguments': 'Arguments', - 'Attribute': 'Getattr', - 'comprehension': 'Comprehension', - 'Call': 'CallFunc', - 'ClassDef': 'Class', - "ListCompFor": 'Comprehension', - "GenExprFor": 'Comprehension', - 'excepthandler': 'ExceptHandler', - 'Expr': 'Discard', - 'FunctionDef': 'Function', - 'GeneratorExp': 'GenExpr', - 'ImportFrom': 'From', - 'keyword': 'Keyword', - 'Repr': 'Backquote', - } -PY3K = sys.version_info >= (3, 0) -PY34 = sys.version_info >= (3, 4) - -def _init_set_doc(node, newnode): - newnode.doc = None - try: - if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str): - newnode.doc = node.body[0].value.s - node.body = node.body[1:] - - except IndexError: - pass # ast built from scratch - -def _lineno_parent(oldnode, newnode, parent): - newnode.parent = parent - newnode.lineno = oldnode.lineno - newnode.col_offset = oldnode.col_offset - -def _set_infos(oldnode, newnode, parent): - newnode.parent = parent - if hasattr(oldnode, 'lineno'): - newnode.lineno = oldnode.lineno - if hasattr(oldnode, 'col_offset'): - newnode.col_offset = oldnode.col_offset - -def _create_yield_node(node, parent, rebuilder, factory): - newnode = factory() - _lineno_parent(node, newnode, parent) - if node.value is not None: - newnode.value = rebuilder.visit(node.value, newnode) - return newnode - - -class TreeRebuilder(object): - """Rebuilds the _ast tree to become an Astroid tree""" - - def __init__(self, manager): - self._manager = manager - self.asscontext = None - self._global_names = [] - self._from_nodes = [] - self._delayed_assattr = [] - self._visit_meths = {} - self._transform = manager.transform - self._peepholer = astpeephole.ASTPeepholeOptimizer() - - def visit_module(self, node, modname, modpath, package): - """visit a Module node by returning a fresh instance of it""" - newnode = new.Module(modname, None) - newnode.package = package - newnode.parent = None - _init_set_doc(node, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.file = newnode.path = modpath - return self._transform(newnode) - - def visit(self, node, parent): - cls = node.__class__ - if cls in self._visit_meths: - visit_method = self._visit_meths[cls] - else: - cls_name = cls.__name__ - visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower() - visit_method = getattr(self, visit_name) - self._visit_meths[cls] = visit_method - return self._transform(visit_method(node, parent)) - - def _save_assignment(self, node, name=None): - """save assignement situation since node.parent is not available yet""" - if self._global_names and node.name in self._global_names[-1]: - node.root().set_local(node.name, node) - else: - node.parent.set_local(node.name, node) - - - def visit_arguments(self, node, parent): - """visit a Arguments node by returning a fresh instance of it""" - newnode = new.Arguments() - newnode.parent = parent - self.asscontext = "Ass" - newnode.args = [self.visit(child, newnode) for child in node.args] - self.asscontext = None - newnode.defaults = [self.visit(child, newnode) for child in node.defaults] - newnode.kwonlyargs = [] - newnode.kw_defaults = [] - vararg, kwarg = node.vararg, node.kwarg - # change added in 82732 (7c5c678e4164), vararg and kwarg - # are instances of `_ast.arg`, not strings - if vararg: - if PY34: - if vararg.annotation: - newnode.varargannotation = self.visit(vararg.annotation, - newnode) - vararg = vararg.arg - elif PY3K and node.varargannotation: - newnode.varargannotation = self.visit(node.varargannotation, - newnode) - if kwarg: - if PY34: - if kwarg.annotation: - newnode.kwargannotation = self.visit(kwarg.annotation, - newnode) - kwarg = kwarg.arg - elif PY3K: - if node.kwargannotation: - newnode.kwargannotation = self.visit(node.kwargannotation, - newnode) - newnode.vararg = vararg - newnode.kwarg = kwarg - # save argument names in locals: - if vararg: - newnode.parent.set_local(vararg, newnode) - if kwarg: - newnode.parent.set_local(kwarg, newnode) - return newnode - - def visit_assattr(self, node, parent): - """visit a AssAttr node by returning a fresh instance of it""" - assc, self.asscontext = self.asscontext, None - newnode = new.AssAttr() - _lineno_parent(node, newnode, parent) - newnode.expr = self.visit(node.expr, newnode) - self.asscontext = assc - self._delayed_assattr.append(newnode) - return newnode - - def visit_assert(self, node, parent): - """visit a Assert node by returning a fresh instance of it""" - newnode = new.Assert() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - if node.msg is not None: - newnode.fail = self.visit(node.msg, newnode) - return newnode - - def visit_assign(self, node, parent): - """visit a Assign node by returning a fresh instance of it""" - newnode = new.Assign() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None - newnode.value = self.visit(node.value, newnode) - # set some function or metaclass infos XXX explain ? - klass = newnode.parent.frame() - if (isinstance(klass, new.Class) - and isinstance(newnode.value, new.CallFunc) - and isinstance(newnode.value.func, new.Name)): - func_name = newnode.value.func.name - for ass_node in newnode.targets: - try: - meth = klass[ass_node.name] - if isinstance(meth, new.Function): - if func_name in ('classmethod', 'staticmethod'): - meth.type = func_name - elif func_name == 'classproperty': # see lgc.decorators - meth.type = 'classmethod' - meth.extra_decorators.append(newnode.value) - except (AttributeError, KeyError): - continue - return newnode - - def visit_assname(self, node, parent, node_name=None): - '''visit a node and return a AssName node''' - newnode = new.AssName() - _set_infos(node, newnode, parent) - newnode.name = node_name - self._save_assignment(newnode) - return newnode - - def visit_augassign(self, node, parent): - """visit a AugAssign node by returning a fresh instance of it""" - newnode = new.AugAssign() - _lineno_parent(node, newnode, parent) - newnode.op = _BIN_OP_CLASSES[node.op.__class__] + "=" - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_backquote(self, node, parent): - """visit a Backquote node by returning a fresh instance of it""" - newnode = new.Backquote() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_binop(self, node, parent): - """visit a BinOp node by returning a fresh instance of it""" - if isinstance(node.left, 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") - newnode = self._peepholer.optimize_binop(node) - if newnode: - _lineno_parent(node, newnode, parent) - return newnode - - newnode = new.BinOp() - _lineno_parent(node, newnode, parent) - newnode.left = self.visit(node.left, newnode) - newnode.right = self.visit(node.right, newnode) - newnode.op = _BIN_OP_CLASSES[node.op.__class__] - return newnode - - def visit_boolop(self, node, parent): - """visit a BoolOp node by returning a fresh instance of it""" - newnode = new.BoolOp() - _lineno_parent(node, newnode, parent) - newnode.values = [self.visit(child, newnode) for child in node.values] - newnode.op = _BOOL_OP_CLASSES[node.op.__class__] - return newnode - - def visit_break(self, node, parent): - """visit a Break node by returning a fresh instance of it""" - newnode = new.Break() - _set_infos(node, newnode, parent) - return newnode - - def visit_callfunc(self, node, parent): - """visit a CallFunc node by returning a fresh instance of it""" - newnode = new.CallFunc() - _lineno_parent(node, newnode, parent) - newnode.func = self.visit(node.func, newnode) - newnode.args = [self.visit(child, newnode) for child in node.args] - if node.starargs is not None: - newnode.starargs = self.visit(node.starargs, newnode) - if node.kwargs is not None: - newnode.kwargs = self.visit(node.kwargs, newnode) - for child in node.keywords: - newnode.args.append(self.visit(child, newnode)) - return newnode - - def visit_class(self, node, parent): - """visit a Class node to become astroid""" - newnode = new.Class(node.name, None) - _lineno_parent(node, newnode, parent) - _init_set_doc(node, newnode) - newnode.bases = [self.visit(child, newnode) for child in node.bases] - newnode.body = [self.visit(child, newnode) for child in node.body] - if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6 - newnode.decorators = self.visit_decorators(node, newnode) - newnode.parent.frame().set_local(newnode.name, newnode) - return newnode - - def visit_const(self, node, parent): - """visit a Const node by returning a fresh instance of it""" - newnode = new.Const(node.value) - _set_infos(node, newnode, parent) - return newnode - - def visit_continue(self, node, parent): - """visit a Continue node by returning a fresh instance of it""" - newnode = new.Continue() - _set_infos(node, newnode, parent) - return newnode - - def visit_compare(self, node, parent): - """visit a Compare node by returning a fresh instance of it""" - newnode = new.Compare() - _lineno_parent(node, newnode, parent) - newnode.left = self.visit(node.left, newnode) - newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode)) - for (op, expr) in zip(node.ops, node.comparators)] - return newnode - - def visit_comprehension(self, node, parent): - """visit a Comprehension node by returning a fresh instance of it""" - newnode = new.Comprehension() - newnode.parent = parent - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.ifs = [self.visit(child, newnode) for child in node.ifs] - return newnode - - def visit_decorators(self, node, parent): - """visit a Decorators node by returning a fresh instance of it""" - # /!\ node is actually a _ast.Function node while - # parent is a astroid.nodes.Function node - newnode = new.Decorators() - _lineno_parent(node, newnode, parent) - if 'decorators' in node._fields: # py < 2.6, i.e. 2.5 - decorators = node.decorators - else: - decorators = node.decorator_list - newnode.nodes = [self.visit(child, newnode) for child in decorators] - return newnode - - def visit_delete(self, node, parent): - """visit a Delete node by returning a fresh instance of it""" - newnode = new.Delete() - _lineno_parent(node, newnode, parent) - self.asscontext = "Del" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None - return newnode - - def visit_dict(self, node, parent): - """visit a Dict node by returning a fresh instance of it""" - newnode = new.Dict() - _lineno_parent(node, newnode, parent) - newnode.items = [(self.visit(key, newnode), self.visit(value, newnode)) - for key, value in zip(node.keys, node.values)] - return newnode - - def visit_dictcomp(self, node, parent): - """visit a DictComp node by returning a fresh instance of it""" - newnode = new.DictComp() - _lineno_parent(node, newnode, parent) - newnode.key = self.visit(node.key, newnode) - newnode.value = self.visit(node.value, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - return newnode - - def visit_discard(self, node, parent): - """visit a Discard node by returning a fresh instance of it""" - newnode = new.Discard() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_ellipsis(self, node, parent): - """visit an Ellipsis node by returning a fresh instance of it""" - newnode = new.Ellipsis() - _set_infos(node, newnode, parent) - return newnode - - def visit_emptynode(self, node, parent): - """visit an EmptyNode node by returning a fresh instance of it""" - newnode = new.EmptyNode() - _set_infos(node, newnode, parent) - return newnode - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = new.ExceptHandler() - _lineno_parent(node, newnode, parent) - if node.type is not None: - newnode.type = self.visit(node.type, newnode) - if node.name is not None: - # /!\ node.name can be a tuple - self.asscontext = "Ass" - newnode.name = self.visit(node.name, newnode) - self.asscontext = None - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_exec(self, node, parent): - """visit an Exec node by returning a fresh instance of it""" - newnode = new.Exec() - _lineno_parent(node, newnode, parent) - newnode.expr = self.visit(node.body, newnode) - if node.globals is not None: - newnode.globals = self.visit(node.globals, newnode) - if node.locals is not None: - newnode.locals = self.visit(node.locals, newnode) - return newnode - - def visit_extslice(self, node, parent): - """visit an ExtSlice node by returning a fresh instance of it""" - newnode = new.ExtSlice() - newnode.parent = parent - newnode.dims = [self.visit(dim, newnode) for dim in node.dims] - return newnode - - def visit_for(self, node, parent): - """visit a For node by returning a fresh instance of it""" - newnode = new.For() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_from(self, node, parent): - """visit a From node by returning a fresh instance of it""" - names = [(alias.name, alias.asname) for alias in node.names] - newnode = new.From(node.module or '', names, node.level or None) - _set_infos(node, newnode, parent) - # store From names to add them to locals after building - self._from_nodes.append(newnode) - return newnode - - def visit_function(self, node, parent): - """visit an Function node to become astroid""" - self._global_names.append({}) - newnode = new.Function(node.name, None) - _lineno_parent(node, newnode, parent) - _init_set_doc(node, newnode) - newnode.args = self.visit(node.args, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - if 'decorators' in node._fields: # py < 2.6 - attr = 'decorators' - else: - attr = 'decorator_list' - decorators = getattr(node, attr) - if decorators: - newnode.decorators = self.visit_decorators(node, newnode) - if PY3K and node.returns: - newnode.returns = self.visit(node.returns, newnode) - self._global_names.pop() - frame = newnode.parent.frame() - if isinstance(frame, new.Class): - if newnode.name == '__new__': - newnode._type = 'classmethod' - else: - newnode._type = 'method' - if newnode.decorators is not None: - for decorator_expr in newnode.decorators.nodes: - if isinstance(decorator_expr, new.Name): - if decorator_expr.name in ('classmethod', 'staticmethod'): - newnode._type = decorator_expr.name - elif decorator_expr.name == 'classproperty': - newnode._type = 'classmethod' - frame.set_local(newnode.name, newnode) - return newnode - - def visit_genexpr(self, node, parent): - """visit a GenExpr node by returning a fresh instance of it""" - newnode = new.GenExpr() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) for child in node.generators] - return newnode - - def visit_getattr(self, node, parent): - """visit a Getattr node by returning a fresh instance of it""" - if self.asscontext == "Del": - # FIXME : maybe we should reintroduce and visit_delattr ? - # for instance, deactivating asscontext - newnode = new.DelAttr() - elif self.asscontext == "Ass": - # FIXME : maybe we should call visit_assattr ? - newnode = new.AssAttr() - self._delayed_assattr.append(newnode) - else: - newnode = new.Getattr() - _lineno_parent(node, newnode, parent) - asscontext, self.asscontext = self.asscontext, None - newnode.expr = self.visit(node.value, newnode) - self.asscontext = asscontext - newnode.attrname = node.attr - return newnode - - def visit_global(self, node, parent): - """visit an Global node to become astroid""" - newnode = new.Global(node.names) - _set_infos(node, newnode, parent) - if self._global_names: # global at the module level, no effect - for name in node.names: - self._global_names[-1].setdefault(name, []).append(newnode) - return newnode - - def visit_if(self, node, parent): - """visit a If node by returning a fresh instance of it""" - newnode = new.If() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_ifexp(self, node, parent): - """visit a IfExp node by returning a fresh instance of it""" - newnode = new.IfExp() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = self.visit(node.body, newnode) - newnode.orelse = self.visit(node.orelse, newnode) - return newnode - - def visit_import(self, node, parent): - """visit a Import node by returning a fresh instance of it""" - newnode = new.Import() - _set_infos(node, newnode, parent) - newnode.names = [(alias.name, alias.asname) for alias in node.names] - # save import names in parent's locals: - for (name, asname) in newnode.names: - name = asname or name - newnode.parent.set_local(name.split('.')[0], newnode) - return newnode - - def visit_index(self, node, parent): - """visit a Index node by returning a fresh instance of it""" - newnode = new.Index() - newnode.parent = parent - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_keyword(self, node, parent): - """visit a Keyword node by returning a fresh instance of it""" - newnode = new.Keyword() - newnode.parent = parent - newnode.arg = node.arg - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_lambda(self, node, parent): - """visit a Lambda node by returning a fresh instance of it""" - newnode = new.Lambda() - _lineno_parent(node, newnode, parent) - newnode.args = self.visit(node.args, newnode) - newnode.body = self.visit(node.body, newnode) - return newnode - - def visit_list(self, node, parent): - """visit a List node by returning a fresh instance of it""" - newnode = new.List() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - return newnode - - def visit_listcomp(self, node, parent): - """visit a ListComp node by returning a fresh instance of it""" - newnode = new.ListComp() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - return newnode - - def visit_name(self, node, parent): - """visit a Name node by returning a fresh instance of it""" - # True and False can be assigned to something in py2x, so we have to - # check first the asscontext - if self.asscontext == "Del": - newnode = new.DelName() - elif self.asscontext is not None: # Ass - assert self.asscontext == "Ass" - newnode = new.AssName() - elif node.id in CONST_NAME_TRANSFORMS: - newnode = new.Const(CONST_NAME_TRANSFORMS[node.id]) - _set_infos(node, newnode, parent) - return newnode - else: - newnode = new.Name() - _lineno_parent(node, newnode, parent) - newnode.name = node.id - # XXX REMOVE me : - if self.asscontext in ('Del', 'Ass'): # 'Aug' ?? - self._save_assignment(newnode) - return newnode - - def visit_bytes(self, node, parent): - """visit a Bytes node by returning a fresh instance of Const""" - newnode = new.Const(node.s) - _set_infos(node, newnode, parent) - return newnode - - def visit_num(self, node, parent): - """visit a Num node by returning a fresh instance of Const""" - newnode = new.Const(node.n) - _set_infos(node, newnode, parent) - return newnode - - def visit_pass(self, node, parent): - """visit a Pass node by returning a fresh instance of it""" - newnode = new.Pass() - _set_infos(node, newnode, parent) - return newnode - - def visit_str(self, node, parent): - """visit a Str node by returning a fresh instance of Const""" - newnode = new.Const(node.s) - _set_infos(node, newnode, parent) - return newnode - - def visit_print(self, node, parent): - """visit a Print node by returning a fresh instance of it""" - newnode = new.Print() - _lineno_parent(node, newnode, parent) - newnode.nl = node.nl - if node.dest is not None: - newnode.dest = self.visit(node.dest, newnode) - newnode.values = [self.visit(child, newnode) for child in node.values] - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = new.Raise() - _lineno_parent(node, newnode, parent) - if node.type is not None: - newnode.exc = self.visit(node.type, newnode) - if node.inst is not None: - newnode.inst = self.visit(node.inst, newnode) - if node.tback is not None: - newnode.tback = self.visit(node.tback, newnode) - return newnode - - def visit_return(self, node, parent): - """visit a Return node by returning a fresh instance of it""" - newnode = new.Return() - _lineno_parent(node, newnode, parent) - if node.value is not None: - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_set(self, node, parent): - """visit a Set node by returning a fresh instance of it""" - newnode = new.Set() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - return newnode - - def visit_setcomp(self, node, parent): - """visit a SetComp node by returning a fresh instance of it""" - newnode = new.SetComp() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - return newnode - - def visit_slice(self, node, parent): - """visit a Slice node by returning a fresh instance of it""" - newnode = new.Slice() - newnode.parent = parent - if node.lower is not None: - newnode.lower = self.visit(node.lower, newnode) - if node.upper is not None: - newnode.upper = self.visit(node.upper, newnode) - if node.step is not None: - newnode.step = self.visit(node.step, newnode) - return newnode - - def visit_subscript(self, node, parent): - """visit a Subscript node by returning a fresh instance of it""" - newnode = new.Subscript() - _lineno_parent(node, newnode, parent) - subcontext, self.asscontext = self.asscontext, None - newnode.value = self.visit(node.value, newnode) - newnode.slice = self.visit(node.slice, newnode) - self.asscontext = subcontext - return newnode - - def visit_tryexcept(self, node, parent): - """visit a TryExcept node by returning a fresh instance of it""" - newnode = new.TryExcept() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.handlers = [self.visit(child, newnode) for child in node.handlers] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_tryfinally(self, node, parent): - """visit a TryFinally node by returning a fresh instance of it""" - newnode = new.TryFinally() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] - return newnode - - def visit_tuple(self, node, parent): - """visit a Tuple node by returning a fresh instance of it""" - newnode = new.Tuple() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - return newnode - - def visit_unaryop(self, node, parent): - """visit a UnaryOp node by returning a fresh instance of it""" - newnode = new.UnaryOp() - _lineno_parent(node, newnode, parent) - newnode.operand = self.visit(node.operand, newnode) - newnode.op = _UNARY_OP_CLASSES[node.op.__class__] - return newnode - - def visit_while(self, node, parent): - """visit a While node by returning a fresh instance of it""" - newnode = new.While() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_with(self, node, parent): - newnode = new.With() - _lineno_parent(node, newnode, parent) - expr = self.visit(node.context_expr, newnode) - self.asscontext = "Ass" - if node.optional_vars is not None: - vars = self.visit(node.optional_vars, newnode) - else: - vars = None - self.asscontext = None - newnode.items = [(expr, vars)] - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_yield(self, node, parent): - """visit a Yield node by returning a fresh instance of it""" - return _create_yield_node(node, parent, self, new.Yield) - -class TreeRebuilder3k(TreeRebuilder): - """extend and overwrite TreeRebuilder for python3k""" - - def visit_arg(self, node, parent): - """visit a arg node by returning a fresh AssName instance""" - # the node is coming from py>=3.0, but we use AssName in py2.x - # XXX or we should instead introduce a Arg node in astroid ? - return self.visit_assname(node, parent, node.arg) - - def visit_nameconstant(self, node, parent): - # in Python 3.4 we have NameConstant for True / False / None - newnode = new.Const(node.value) - _set_infos(node, newnode, parent) - return newnode - - def visit_arguments(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_arguments(node, parent) - self.asscontext = "Ass" - newnode.kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs] - self.asscontext = None - newnode.kw_defaults = [self.visit(child, newnode) if child else None for child in node.kw_defaults] - newnode.annotations = [ - self.visit(arg.annotation, newnode) if arg.annotation else None - for arg in node.args] - return newnode - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = new.ExceptHandler() - _lineno_parent(node, newnode, parent) - if node.type is not None: - newnode.type = self.visit(node.type, newnode) - if node.name is not None: - newnode.name = self.visit_assname(node, newnode, node.name) - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_nonlocal(self, node, parent): - """visit a Nonlocal node and return a new instance of it""" - newnode = new.Nonlocal(node.names) - _set_infos(node, newnode, parent) - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = new.Raise() - _lineno_parent(node, newnode, parent) - # no traceback; anyway it is not used in Pylint - if node.exc is not None: - newnode.exc = self.visit(node.exc, newnode) - if node.cause is not None: - newnode.cause = self.visit(node.cause, newnode) - return newnode - - def visit_starred(self, node, parent): - """visit a Starred node and return a new instance of it""" - newnode = new.Starred() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_try(self, node, parent): - # python 3.3 introduce a new Try node replacing TryFinally/TryExcept nodes - if node.finalbody: - newnode = new.TryFinally() - _lineno_parent(node, newnode, parent) - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] - if node.handlers: - excnode = new.TryExcept() - _lineno_parent(node, excnode, newnode) - excnode.body = [self.visit(child, excnode) for child in node.body] - excnode.handlers = [self.visit(child, excnode) for child in node.handlers] - excnode.orelse = [self.visit(child, excnode) for child in node.orelse] - newnode.body = [excnode] - else: - newnode.body = [self.visit(child, newnode) for child in node.body] - elif node.handlers: - newnode = new.TryExcept() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.handlers = [self.visit(child, newnode) for child in node.handlers] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_with(self, node, parent): - if 'items' not in node._fields: - # python < 3.3 - return super(TreeRebuilder3k, self).visit_with(node, parent) - - newnode = new.With() - _lineno_parent(node, newnode, parent) - def visit_child(child): - expr = self.visit(child.context_expr, newnode) - self.asscontext = 'Ass' - if child.optional_vars: - var = self.visit(child.optional_vars, newnode) - else: - var = None - self.asscontext = None - return expr, var - newnode.items = [visit_child(child) - for child in node.items] - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_yieldfrom(self, node, parent): - return _create_yield_node(node, parent, self, new.YieldFrom) - - def visit_class(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_class(node, parent) - newnode._newstyle = True - for keyword in node.keywords: - if keyword.arg == 'metaclass': - newnode._metaclass = self.visit(keyword, newnode).value - break - return newnode - -if sys.version_info >= (3, 0): - TreeRebuilder = TreeRebuilder3k - - diff --git a/pymode/libs/astroid/scoped_nodes.py b/pymode/libs/astroid/scoped_nodes.py deleted file mode 100644 index ac90f878..00000000 --- a/pymode/libs/astroid/scoped_nodes.py +++ /dev/null @@ -1,1484 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module contains the classes for "scoped" node, i.e. which are opening a -new local scope in the language definition : Module, Class, Function (and -Lambda, GenExpr, DictComp and SetComp to some extent). -""" -from __future__ import with_statement - -__doctype__ = "restructuredtext en" - -import sys -import warnings -from itertools import chain -try: - from io import BytesIO -except ImportError: - from cStringIO import StringIO as BytesIO - -import six -from logilab.common.compat import builtins -from logilab.common.decorators import cached, cachedproperty - -from astroid.exceptions import NotFoundError, \ - AstroidBuildingException, InferenceError, ResolveError -from astroid.node_classes import Const, DelName, DelAttr, \ - Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ - LookupMixIn, const_factory as cf, unpack_infer, CallFunc -from astroid.bases import NodeNG, InferenceContext, Instance, copy_context, \ - YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, \ - BUILTINS -from astroid.mixins import FilterStmtsMixin -from astroid.bases import Statement -from astroid.manager import AstroidManager - -ITER_METHODS = ('__iter__', '__getitem__') -PY3K = sys.version_info >= (3, 0) - -def _c3_merge(sequences): - """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. - bases = ["({})".format(", ".join(base.name - for base in subsequence)) - for subsequence in sequences] - raise ResolveError("Cannot create a consistent method resolution " - "order for bases %s" % ", ".join(bases)) - result.append(candidate) - # remove the chosen candidate - for seq in sequences: - if seq[0] == candidate: - del seq[0] - - -def _verify_duplicates_mro(sequences): - for sequence in sequences: - names = [node.qname() for node in sequence] - if len(names) != len(set(names)): - raise ResolveError('Duplicates found in the mro.') - - -def remove_nodes(func, cls): - def wrapper(*args, **kwargs): - nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)] - if not nodes: - raise NotFoundError() - return nodes - return wrapper - - -def function_to_method(n, klass): - if isinstance(n, Function): - if n.type == 'classmethod': - return BoundMethod(n, klass) - if n.type != 'staticmethod': - return UnboundMethod(n) - return n - -def std_special_attributes(self, name, add_locals=True): - if add_locals: - locals = self.locals - else: - locals = {} - if name == '__name__': - return [cf(self.name)] + locals.get(name, []) - if name == '__doc__': - return [cf(self.doc)] + locals.get(name, []) - if name == '__dict__': - return [Dict()] + locals.get(name, []) - raise NotFoundError(name) - -MANAGER = AstroidManager() -def builtin_lookup(name): - """lookup a name into the builtin module - return the list of matching statements and the astroid for the builtin - module - """ - builtin_astroid = MANAGER.ast_from_module(builtins) - if name == '__dict__': - return builtin_astroid, () - try: - stmts = builtin_astroid.locals[name] - except KeyError: - stmts = () - return builtin_astroid, stmts - - -# TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup -class LocalsDictNodeNG(LookupMixIn, NodeNG): - """ this class provides locals handling common to Module, Function - and Class nodes, including a dict like interface for direct access - to locals information - """ - - # attributes below are set by the builder module or by raw factories - - # dictionary of locals with name as key and node defining the local as - # value - - def qname(self): - """return the 'qualified' name of the node, eg module.name, - module.class.name ... - """ - if self.parent is None: - return self.name - return '%s.%s' % (self.parent.frame().qname(), self.name) - - def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) - """ - return self - - def scope(self): - """return the first node defining a new scope (i.e. Module, - Function, Class, Lambda but also GenExpr, DictComp and SetComp) - """ - return self - - - def _scope_lookup(self, node, name, offset=0): - """XXX method for interfacing the scope lookup""" - try: - stmts = node._filter_stmts(self.locals[name], self, offset) - except KeyError: - stmts = () - if stmts: - return self, stmts - if self.parent: # i.e. not Module - # nested scope: if parent scope is a function, that's fine - # else jump to the module - pscope = self.parent.scope() - if not pscope.is_function: - pscope = pscope.root() - return pscope.scope_lookup(node, name) - return builtin_lookup(name) # Module - - - - def set_local(self, name, stmt): - """define in locals ( is the node defining the name) - if the node is a Module node (i.e. has globals), add the name to - globals - - if the name is already defined, ignore it - """ - #assert not stmt in self.locals.get(name, ()), (self, stmt) - self.locals.setdefault(name, []).append(stmt) - - __setitem__ = set_local - - def _append_node(self, child): - """append a child, linking it in the tree""" - self.body.append(child) - child.parent = self - - def add_local_node(self, child_node, name=None): - """append a child which should alter locals to the given node""" - if name != '__class__': - # add __class__ node as a child will cause infinite recursion later! - self._append_node(child_node) - self.set_local(name or child_node.name, child_node) - - - def __getitem__(self, item): - """method from the `dict` interface returning the first node - associated with the given name in the locals dictionary - - :type item: str - :param item: the name of the locally defined object - :raises KeyError: if the name is not defined - """ - return self.locals[item][0] - - def __iter__(self): - """method from the `dict` interface returning an iterator on - `self.keys()` - """ - return iter(self.keys()) - - def keys(self): - """method from the `dict` interface returning a tuple containing - locally defined names - """ - return list(self.locals.keys()) - - def values(self): - """method from the `dict` interface returning a tuple containing - locally defined nodes which are instance of `Function` or `Class` - """ - return [self[key] for key in self.keys()] - - def items(self): - """method from the `dict` interface returning a list of tuple - containing each locally defined name with its associated node, - which is an instance of `Function` or `Class` - """ - return list(zip(self.keys(), self.values())) - - - def __contains__(self, name): - return name in self.locals - has_key = __contains__ - -# Module ##################################################################### - -class Module(LocalsDictNodeNG): - _astroid_fields = ('body',) - - fromlineno = 0 - lineno = 0 - - # attributes below are set by the builder module or by raw factories - - # the file from which as been extracted the astroid representation. It may - # be None if the representation has been built from a built-in module - file = None - # Alternatively, if built from a string/bytes, this can be set - file_bytes = None - # encoding of python source file, so we can get unicode out of it (python2 - # only) - file_encoding = None - # the module name - name = None - # boolean for astroid built from source (i.e. ast) - pure_python = None - # boolean for package module - package = None - # dictionary of globals with name as key and node defining the global - # as value - globals = None - - # Future imports - future_imports = None - - # names of python special attributes (handled by getattr impl.) - special_attributes = set(('__name__', '__doc__', '__file__', '__path__', - '__dict__')) - # names of module attributes available through the global scope - scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) - - def __init__(self, name, doc, pure_python=True): - self.name = name - self.doc = doc - self.pure_python = pure_python - self.locals = self.globals = {} - self.body = [] - self.future_imports = set() - - def _get_stream(self): - if self.file_bytes is not None: - return 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 not name in self.locals: - try: - return self, self.getattr(name) - except NotFoundError: - return self, () - return self._scope_lookup(node, name, offset) - - def pytype(self): - return '%s.module' % BUILTINS - - def display_type(self): - return 'Module' - - def getattr(self, name, context=None, ignore_locals=False): - if name in self.special_attributes: - if name == '__file__': - return [cf(self.file)] + self.locals.get(name, []) - if name == '__path__' and self.package: - return [List()] + self.locals.get(name, []) - return std_special_attributes(self, name) - if not ignore_locals and name in self.locals: - return self.locals[name] - if self.package: - try: - return [self.import_module(name, relative_only=True)] - except AstroidBuildingException: - raise NotFoundError(name) - except SyntaxError: - raise NotFoundError(name) - except Exception:# XXX pylint tests never pass here; do we need it? - import traceback - traceback.print_exc() - raise NotFoundError(name) - getattr = remove_nodes(getattr, DelName) - - def igetattr(self, name, context=None): - """inferred getattr""" - # set lookup name since this is necessary to infer on import nodes for - # instance - context = copy_context(context) - context.lookupname = name - try: - return _infer_stmts(self.getattr(name, context), context, frame=self) - except NotFoundError: - raise InferenceError(name) - - def fully_defined(self): - """return True if this module has been built from a .py file - and so contains a complete representation including the code - """ - return self.file is not None and self.file.endswith('.py') - - def statement(self): - """return the first parent node marked as statement node - consider a module as a statement... - """ - return self - - def previous_sibling(self): - """module has no sibling""" - return - - def next_sibling(self): - """module has no sibling""" - return - - if sys.version_info < (2, 8): - @cachedproperty - def _absolute_import_activated(self): - for stmt in self.locals.get('absolute_import', ()): - if isinstance(stmt, From) and stmt.modname == '__future__': - return True - return False - else: - _absolute_import_activated = True - - def absolute_import_activated(self): - return self._absolute_import_activated - - def import_module(self, modname, relative_only=False, level=None): - """import the given module considering self as context""" - if relative_only and level is None: - level = 0 - absmodname = self.relative_to_absolute_name(modname, level) - try: - return MANAGER.ast_from_module_name(absmodname) - except AstroidBuildingException: - # we only want to import a sub module or package of this module, - # skip here - if relative_only: - raise - return MANAGER.ast_from_module_name(modname) - - def relative_to_absolute_name(self, modname, level): - """return the absolute module name for a relative import. - - The relative import can be implicit or explicit. - """ - # XXX this returns non sens when called on an absolute import - # like 'pylint.checkers.astroid.utils' - # XXX doesn't return absolute name if self.name isn't absolute name - if self.absolute_import_activated() and level is None: - return modname - if level: - if self.package: - level = level - 1 - package_name = self.name.rsplit('.', level)[0] - elif self.package: - package_name = self.name - else: - package_name = self.name.rsplit('.', 1)[0] - if package_name: - if not modname: - return package_name - return '%s.%s' % (package_name, modname) - return modname - - - def wildcard_import_names(self): - """return the list of imported names when this module is 'wildcard - imported' - - It doesn't include the '__builtins__' name which is added by the - current CPython implementation of wildcard imports. - """ - # take advantage of a living module if it exists - try: - living = sys.modules[self.name] - except KeyError: - pass - else: - try: - return living.__all__ - except AttributeError: - return [name for name in living.__dict__.keys() - if not name.startswith('_')] - # else lookup the astroid - # - # We separate the different steps of lookup in try/excepts - # to avoid catching too many Exceptions - default = [name for name in self.keys() if not name.startswith('_')] - try: - all = self['__all__'] - except KeyError: - return default - try: - explicit = next(all.assigned_stmts()) - except InferenceError: - return default - except AttributeError: - # not an assignment node - # XXX infer? - return default - - # Try our best to detect the exported name. - infered = [] - try: - explicit = next(explicit.infer()) - except InferenceError: - return default - if not isinstance(explicit, (Tuple, List)): - return default - - str_const = lambda node: (isinstance(node, Const) and - isinstance(node.value, six.string_types)) - for node in explicit.elts: - if str_const(node): - infered.append(node.value) - else: - try: - infered_node = next(node.infer()) - except InferenceError: - continue - if str_const(infered_node): - infered.append(infered_node.value) - return infered - - - -class ComprehensionScope(LocalsDictNodeNG): - def frame(self): - return self.parent.frame() - - scope_lookup = LocalsDictNodeNG._scope_lookup - - -class GenExpr(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - - def __init__(self): - self.locals = {} - self.elt = None - self.generators = [] - - -class DictComp(ComprehensionScope): - _astroid_fields = ('key', 'value', 'generators') - - def __init__(self): - self.locals = {} - self.key = None - self.value = None - self.generators = [] - - -class SetComp(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - - def __init__(self): - self.locals = {} - self.elt = None - self.generators = [] - - -class _ListComp(NodeNG): - """class representing a ListComp node""" - _astroid_fields = ('elt', 'generators') - elt = None - generators = None - -if sys.version_info >= (3, 0): - class ListComp(_ListComp, ComprehensionScope): - """class representing a ListComp node""" - def __init__(self): - self.locals = {} -else: - class ListComp(_ListComp): - """class representing a ListComp node""" - -# Function ################################################################### - -def _infer_decorator_callchain(node): - """Detect decorator call chaining and see if the end result is a - static or a classmethod. - """ - if not isinstance(node, Function): - 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, InferenceError): - return - if isinstance(result, Instance): - result = result._proxied - if isinstance(result, Class): - if result.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - if result.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - - -def _function_type(self): - """ - Function type, possible values are: - method, function, staticmethod, classmethod. - """ - # Can't infer that this node is decorated - # with a subclass of `classmethod` where `type` is first set, - # so do it here. - if self.decorators: - for node in self.decorators.nodes: - if isinstance(node, CallFunc): - # Handle the following case: - # @some_decorator(arg1, arg2) - # def func(...) - # - try: - current = next(node.func.infer()) - except InferenceError: - continue - _type = _infer_decorator_callchain(current) - if _type is not None: - return _type - - try: - for infered in node.infer(): - # Check to see if this returns a static or a class method. - _type = _infer_decorator_callchain(infered) - if _type is not None: - return _type - - if not isinstance(infered, Class): - continue - for ancestor in infered.ancestors(): - if not isinstance(ancestor, Class): - continue - if ancestor.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - except InferenceError: - pass - return self._type - - -class Lambda(LocalsDictNodeNG, FilterStmtsMixin): - _astroid_fields = ('args', 'body',) - name = '' - - # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' - type = 'function' - - def __init__(self): - self.locals = {} - self.args = [] - self.body = [] - - def pytype(self): - if 'method' in self.type: - return '%s.instancemethod' % BUILTINS - return '%s.function' % BUILTINS - - def display_type(self): - if 'method' in self.type: - return 'Method' - return 'Function' - - def callable(self): - return True - - def argnames(self): - """return a list of argument names""" - if self.args.args: # maybe None with builtin functions - names = _rec_get_names(self.args.args) - else: - names = [] - if self.args.vararg: - names.append(self.args.vararg) - if self.args.kwarg: - names.append(self.args.kwarg) - return names - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - return self.body.infer(context) - - def scope_lookup(self, node, name, offset=0): - if node in self.args.defaults or node in self.args.kw_defaults: - frame = self.parent.frame() - # line offset to avoid that def func(f=func) resolve the default - # value to the defined function - offset = -1 - else: - # check this is not used in function decorators - frame = self - return frame._scope_lookup(node, name, offset) - - -class Function(Statement, Lambda): - if PY3K: - _astroid_fields = ('decorators', 'args', 'body', 'returns') - returns = None - else: - _astroid_fields = ('decorators', 'args', 'body') - - special_attributes = set(('__name__', '__doc__', '__dict__')) - is_function = True - # attributes below are set by the builder module or by raw factories - blockstart_tolineno = None - decorators = None - _type = "function" - type = cachedproperty(_function_type) - - def __init__(self, name, doc): - self.locals = {} - self.args = [] - self.body = [] - self.name = name - self.doc = doc - self.extra_decorators = [] - self.instance_attrs = {} - - @cachedproperty - def fromlineno(self): - # lineno is the line number of the first decorator, we want the def - # statement lineno - lineno = self.lineno - if self.decorators is not None: - lineno += sum(node.tolineno - node.lineno + 1 - for node in self.decorators.nodes) - - return lineno - - @cachedproperty - def blockstart_tolineno(self): - return self.args.tolineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "def" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - """ - if name == '__module__': - return [cf(self.root().qname())] - if name in self.instance_attrs: - return self.instance_attrs[name] - return std_special_attributes(self, name, False) - - def is_method(self): - """return true if the function node should be considered as a method""" - # check we are defined in a Class, because this is usually expected - # (e.g. pylint...) when is_method() return True - return self.type != 'function' and isinstance(self.parent.frame(), Class) - - def decoratornames(self): - """return a list of decorator qualified names""" - result = set() - decoratornodes = [] - if self.decorators is not None: - decoratornodes += self.decorators.nodes - decoratornodes += self.extra_decorators - for decnode in decoratornodes: - for infnode in decnode.infer(): - result.add(infnode.qname()) - return result - decoratornames = cached(decoratornames) - - def is_bound(self): - """return true if the function is bound to an Instance or a class""" - return self.type == 'classmethod' - - def is_abstract(self, pass_is_abstract=True): - """Returns True if the method is abstract. - - A method is considered abstract if - - the only statement is 'raise NotImplementedError', or - - the only statement is 'pass' and pass_is_abstract is True, or - - the method is annotated with abc.astractproperty/abc.abstractmethod - """ - if self.decorators: - for node in self.decorators.nodes: - try: - infered = next(node.infer()) - except InferenceError: - continue - if infered and infered.qname() in ('abc.abstractproperty', - 'abc.abstractmethod'): - return True - - for child_node in self.body: - if isinstance(child_node, Raise): - if child_node.raises_not_implemented(): - return True - if pass_is_abstract and isinstance(child_node, Pass): - return True - return False - # empty function is the same as function with a single "pass" statement - if pass_is_abstract: - return True - - def is_generator(self): - """return true if this is a generator function""" - # XXX should be flagged, not computed - return next(self.nodes_of_class((Yield, YieldFrom), - skip_klass=(Function, Lambda)), False) - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - if self.is_generator(): - yield Generator() - return - # 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, Class): - c = Class('temporary_class', None) - c.hide = True - c.parent = self - bases = [next(b.infer(context)) for b in caller.args[1:]] - c.bases = [base for base in bases if base != YES] - c._metaclass = metaclass - yield c - return - returns = self.nodes_of_class(Return, skip_klass=Function) - for returnnode in returns: - if returnnode.value is None: - yield Const(None) - else: - try: - for infered in returnnode.value.infer(context): - yield infered - except InferenceError: - yield YES - - -def _rec_get_names(args, names=None): - """return a list of all argument names""" - if names is None: - names = [] - for arg in args: - if isinstance(arg, Tuple): - _rec_get_names(arg.elts, names) - else: - names.append(arg.name) - return names - - -# Class ###################################################################### - - -def _is_metaclass(klass, seen=None): - """ Return if the given class can be - used as a metaclass. - """ - if klass.name == 'type': - return True - if seen is None: - seen = set() - for base in klass.bases: - try: - for baseobj in base.infer(): - if baseobj in seen: - continue - else: - seen.add(baseobj) - if isinstance(baseobj, Instance): - # not abstract - return False - if baseobj is YES: - continue - if baseobj is klass: - continue - if not isinstance(baseobj, Class): - continue - if baseobj._type == 'metaclass': - return True - if _is_metaclass(baseobj, seen): - return True - except InferenceError: - continue - return False - - -def _class_type(klass, ancestors=None): - """return a Class node type to differ metaclass, interface and exception - from 'regular' classes - """ - # XXX we have to store ancestors in case we have a ancestor loop - if klass._type is not None: - return klass._type - if _is_metaclass(klass): - klass._type = 'metaclass' - elif klass.name.endswith('Interface'): - klass._type = 'interface' - elif klass.name.endswith('Exception'): - klass._type = 'exception' - else: - if ancestors is None: - ancestors = set() - if klass in ancestors: - # XXX we are in loop ancestors, and have found no type - klass._type = 'class' - return 'class' - ancestors.add(klass) - for base in klass.ancestors(recurs=False): - name = _class_type(base, ancestors) - if name != 'class': - if name == 'metaclass' and not _is_metaclass(klass): - # don't propagate it if the current class - # can't be a metaclass - continue - klass._type = base.type - break - if klass._type is None: - klass._type = 'class' - return klass._type - -def _iface_hdlr(iface_node): - """a handler function used by interfaces to handle suspicious - interface nodes - """ - return True - - -class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): - - # some of the attributes below are set by the builder module or - # by a raw factories - - # a dictionary of class instances attributes - _astroid_fields = ('decorators', 'bases', 'body') # name - - decorators = None - special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', - '__bases__', '__mro__', '__subclasses__')) - blockstart_tolineno = None - - _type = None - _metaclass_hack = False - hide = False - type = property(_class_type, - doc="class'type, possible values are 'class' | " - "'metaclass' | 'interface' | 'exception'") - - def __init__(self, name, doc): - self.instance_attrs = {} - self.locals = {} - self.bases = [] - self.body = [] - self.name = name - self.doc = doc - - def _newstyle_impl(self, context=None): - if context is None: - context = InferenceContext() - if self._newstyle is not None: - return self._newstyle - for base in self.ancestors(recurs=False, context=context): - if base._newstyle_impl(context): - self._newstyle = True - break - klass = self._explicit_metaclass() - # could be any callable, we'd need to infer the result of klass(name, - # bases, dict). punt if it's not a class node. - if klass is not None and isinstance(klass, Class): - self._newstyle = klass._newstyle_impl(context) - if self._newstyle is None: - self._newstyle = False - return self._newstyle - - _newstyle = None - newstyle = property(_newstyle_impl, - doc="boolean indicating if it's a new style class" - "or not") - - @cachedproperty - def blockstart_tolineno(self): - if self.bases: - return self.bases[-1].tolineno - else: - return self.fromlineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "class" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def pytype(self): - if self.newstyle: - return '%s.type' % BUILTINS - return '%s.classobj' % BUILTINS - - def display_type(self): - return 'Class' - - def callable(self): - return True - - def is_subtype_of(self, type_name, 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_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: - name_node = next(caller.args[0].infer(context)) - if (isinstance(name_node, Const) and - isinstance(name_node.value, six.string_types)): - name = name_node.value - else: - yield YES - return - result = Class(name, None) - bases = next(caller.args[1].infer(context)) - if isinstance(bases, (Tuple, List)): - result.bases = bases.itered() - else: - # There is currently no AST node that can represent an 'unknown' - # node (YES is not an AST node), therefore we simply return YES here - # although we know at least the name of the class. - yield YES - return - result.parent = caller.parent - yield result - else: - yield Instance(self) - - def scope_lookup(self, node, name, offset=0): - if any(node == base or base.parent_of(node) - for base in self.bases): - # 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) - - # list of parent class as a list of string (i.e. names as they appear - # in the class definition) XXX bw compat - def basenames(self): - return [bnode.as_string() for bnode in self.bases] - basenames = property(basenames) - - def ancestors(self, recurs=True, context=None): - """return an iterator on the node base classes in a prefixed - depth first order - - :param recurs: - boolean indicating if it should recurse or return direct - ancestors only - """ - # FIXME: should be possible to choose the resolution order - # FIXME: inference make infinite loops possible here - yielded = set([self]) - if context is None: - context = InferenceContext() - if sys.version_info[0] >= 3: - 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, Class): - if isinstance(baseobj, Instance): - baseobj = baseobj._proxied - else: - # duh ? - continue - if not baseobj.hide: - if baseobj in yielded: - continue # cf xxx above - yielded.add(baseobj) - yield baseobj - if recurs: - for grandpa in baseobj.ancestors(recurs=True, - context=context): - if grandpa is self: - # This class is the ancestor of itself. - break - if grandpa in yielded: - continue # cf xxx above - yielded.add(grandpa) - yield grandpa - except InferenceError: - # XXX log error ? - continue - - def local_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their locals - """ - for astroid in self.ancestors(context=context): - if name in astroid: - yield astroid - - def instance_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their instance attribute dictionary - """ - for astroid in self.ancestors(context=context): - if name in astroid.instance_attrs: - yield astroid - - def has_base(self, node): - return node in self.bases - - def local_attr(self, name, context=None): - """return the list of assign node associated to name in this class - locals or in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - try: - return self.locals[name] - except KeyError: - # get if from the first parent implementing it if any - for class_node in self.local_attr_ancestors(name, context): - return class_node.locals[name] - raise NotFoundError(name) - local_attr = remove_nodes(local_attr, DelAttr) - - def instance_attr(self, name, context=None): - """return the astroid nodes associated to name in this class instance - attributes dictionary and in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - # Return a copy, so we don't modify self.instance_attrs, - # which could lead to infinite loop. - values = list(self.instance_attrs.get(name, [])) - # get all values from parents - for class_node in self.instance_attr_ancestors(name, context): - values += class_node.instance_attrs[name] - if not values: - raise NotFoundError(name) - return values - instance_attr = remove_nodes(instance_attr, DelAttr) - - def instanciate_class(self): - """return Instance of Class node, else return self""" - return Instance(self) - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - - It may return a YES object if the attribute has not been actually - found but a __getattr__ or __getattribute__ method is defined - """ - values = self.locals.get(name, []) - if name in self.special_attributes: - if name == '__module__': - return [cf(self.root().qname())] + values - # FIXME: do we really need the actual list of ancestors? - # returning [Tuple()] + values don't break any test - # this is ticket http://www.logilab.org/ticket/52785 - # XXX need proper meta class handling + MRO implementation - if name == '__bases__' or (name == '__mro__' and self.newstyle): - node = Tuple() - node.items = self.ancestors(recurs=True, context=context) - return [node] + values - return std_special_attributes(self, name) - # don't modify the list in self.locals! - values = list(values) - for classnode in self.ancestors(recurs=True, context=context): - values += classnode.locals.get(name, []) - if not values: - raise NotFoundError(name) - return values - - def igetattr(self, name, context=None): - """inferred getattr, need special treatment in class to handle - descriptors - """ - # set lookup name since this is necessary to infer on import nodes for - # instance - context = copy_context(context) - context.lookupname = name - try: - for infered in _infer_stmts(self.getattr(name, context), context, - frame=self): - # yield YES object instead of descriptors when necessary - if not isinstance(infered, Const) and isinstance(infered, Instance): - try: - infered._proxied.getattr('__get__', context) - except NotFoundError: - yield infered - else: - yield YES - else: - yield function_to_method(infered, self) - except NotFoundError: - if not name.startswith('__') and self.has_dynamic_getattr(context): - # class handle some dynamic attributes, return a YES object - yield YES - else: - raise InferenceError(name) - - def has_dynamic_getattr(self, context=None): - """return True if the class has a custom __getattr__ or - __getattribute__ method - """ - # need to explicitly handle optparse.Values (setattr is not detected) - if self.name == 'Values' and self.root().name == 'optparse': - return True - try: - self.getattr('__getattr__', context) - return True - except NotFoundError: - #if self.newstyle: XXX cause an infinite recursion error - try: - getattribute = self.getattr('__getattribute__', context)[0] - if getattribute.root().name != BUILTINS: - # class has a custom __getattribute__ defined - return True - except NotFoundError: - pass - return False - - def methods(self): - """return an iterator on all methods defined in the class and - its ancestors - """ - done = {} - for astroid in chain(iter((self,)), self.ancestors()): - for meth in astroid.mymethods(): - if meth.name in done: - continue - done[meth.name] = None - yield meth - - def mymethods(self): - """return an iterator on all methods defined in the class""" - for member in self.values(): - if isinstance(member, Function): - yield member - - def interfaces(self, herited=True, handler_func=_iface_hdlr): - """return an iterator on interfaces implemented by the given - class node - """ - # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... - try: - implements = Instance(self).getattr('__implements__')[0] - except NotFoundError: - return - if not herited and not implements.frame() is self: - return - found = set() - missing = False - for iface in unpack_infer(implements): - if iface is YES: - missing = True - continue - if not iface in found and handler_func(iface): - found.add(iface) - yield iface - if missing: - raise InferenceError() - - _metaclass = None - def _explicit_metaclass(self): - """ Return the explicit defined metaclass - for the current class. - - An explicit defined metaclass is defined - either by passing the ``metaclass`` keyword argument - in the class definition line (Python 3) or (Python 2) by - having a ``__metaclass__`` class attribute, or if there are - no explicit bases but there is a global ``__metaclass__`` variable. - """ - for base in self.bases: - try: - for baseobj in base.infer(): - if isinstance(baseobj, Class) and baseobj.hide: - self._metaclass = baseobj._metaclass - self._metaclass_hack = True - break - except InferenceError: - pass - - if self._metaclass: - # Expects this from Py3k TreeRebuilder - try: - return next(node for node in self._metaclass.infer() - if node is not YES) - except (InferenceError, StopIteration): - return None - if sys.version_info >= (3, ): - return None - - if '__metaclass__' in self.locals: - assignment = self.locals['__metaclass__'][-1] - elif self.bases: - return None - elif '__metaclass__' in self.root().locals: - assignments = [ass for ass in self.root().locals['__metaclass__'] - if ass.lineno < self.lineno] - if not assignments: - return None - assignment = assignments[-1] - else: - return None - - try: - infered = next(assignment.infer()) - except InferenceError: - return - if infered is YES: # don't expose this - return None - return infered - - def metaclass(self): - """ Return the metaclass of this class. - - If this class does not define explicitly a metaclass, - then the first defined metaclass in ancestors will be used - instead. - """ - klass = self._explicit_metaclass() - if klass is None: - for parent in self.ancestors(): - klass = parent.metaclass() - if klass is not None: - break - return klass - - def 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 NotFoundError: - continue - else: - continue - - if isinstance(slots, Const): - # a string. Ignore the following checks, - # but yield the node, only if it has a value - if slots.value: - yield slots - continue - if not hasattr(slots, 'itered'): - # we can't obtain the values, maybe a .deque? - continue - - if isinstance(slots, Dict): - values = [item[0] for item in slots.items] - else: - values = slots.itered() - if values is YES: - continue - - for elt in values: - try: - for infered in elt.infer(): - if infered is YES: - continue - if (not isinstance(infered, Const) or - not isinstance(infered.value, - six.string_types)): - continue - if not infered.value: - continue - yield infered - except InferenceError: - continue - - # Cached, because inferring them all the time is expensive - @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. - """ - 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: - # The class doesn't have a __slots__ definition. - return None - return [first] + list(slots) - - def _inferred_bases(self, recurs=True, 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 = InferenceContext() - if sys.version_info[0] >= 3: - 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 InferenceError: - # XXX log error ? - continue - if isinstance(baseobj, Instance): - baseobj = baseobj._proxied - if not isinstance(baseobj, Class): - continue - if not baseobj.hide: - yield baseobj - - 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.") - - bases = list(self._inferred_bases(context=context)) - unmerged_mro = ([[self]] + - [base.mro() for base in bases if base is not self] + - [bases]) - - _verify_duplicates_mro(unmerged_mro) - return _c3_merge(unmerged_mro) diff --git a/pymode/libs/astroid/test_utils.py b/pymode/libs/astroid/test_utils.py deleted file mode 100644 index 19bd7b96..00000000 --- a/pymode/libs/astroid/test_utils.py +++ /dev/null @@ -1,218 +0,0 @@ -"""Utility functions for test code that uses astroid ASTs as input.""" -import functools -import sys -import textwrap - -from astroid import nodes -from astroid import builder -# The name of the transient function that is used to -# wrap expressions to be extracted when calling -# extract_node. -_TRANSIENT_FUNCTION = '__' - -# The comment used to select a statement to be extracted -# when calling extract_node. -_STATEMENT_SELECTOR = '#@' - - -def _extract_expressions(node): - """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. - - The function walks the AST recursively to search for expressions that - are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an - expression, it completely removes the function call node from the tree, - replacing it by the wrapped expression inside the parent. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :yields: The sequence of wrapped expressions on the modified tree - expression can be found. - """ - if (isinstance(node, nodes.CallFunc) - and isinstance(node.func, nodes.Name) - and node.func.name == _TRANSIENT_FUNCTION): - real_expr = node.args[0] - real_expr.parent = node.parent - # Search for node in all _astng_fields (the fields checked when - # get_children is called) of its parent. Some of those fields may - # be lists or tuples, in which case the elements need to be checked. - # When we find it, replace it by real_expr, so that the AST looks - # like no call to _TRANSIENT_FUNCTION ever took place. - for name in node.parent._astroid_fields: - child = getattr(node.parent, name) - if isinstance(child, (list, tuple)): - for idx, compound_child in enumerate(child): - if compound_child is node: - child[idx] = real_expr - elif child is node: - setattr(node.parent, name, real_expr) - yield real_expr - else: - for child in node.get_children(): - for result in _extract_expressions(child): - yield result - - -def _find_statement_by_line(node, line): - """Extracts the statement on a specific line from an AST. - - If the line number of node matches line, it will be returned; - otherwise its children are iterated and the function is called - recursively. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :param line: The line number of the statement to extract. - :type line: int - :returns: The statement on the line, or None if no statement for the line - can be found. - :rtype: astroid.bases.NodeNG or None - """ - if isinstance(node, (nodes.Class, nodes.Function)): - # This is an inaccuracy in the AST: the nodes that can be - # decorated do not carry explicit information on which line - # the actual definition (class/def), but .fromline seems to - # be close enough. - node_line = node.fromlineno - else: - node_line = node.lineno - - if node_line == line: - return node - - for child in node.get_children(): - result = _find_statement_by_line(child, line) - if result: - return result - - return None - -def extract_node(code, module_name=''): - """Parses some Python code as a module and extracts a designated AST node. - - Statements: - To extract one or more statement nodes, append #@ to the end of the line - - Examples: - >>> def x(): - >>> def y(): - >>> return 1 #@ - - The return statement will be extracted. - - >>> class X(object): - >>> def meth(self): #@ - >>> pass - - The funcion object 'meth' will be extracted. - - Expressions: - To extract arbitrary expressions, surround them with the fake - function call __(...). After parsing, the surrounded expression - will be returned and the whole AST (accessible via the returned - node's parent attribute) will look like the function call was - never there in the first place. - - Examples: - >>> a = __(1) - - The const node will be extracted. - - >>> def x(d=__(foo.bar)): pass - - The node containing the default argument will be extracted. - - >>> def foo(a, b): - >>> return 0 < __(len(a)) < b - - The node containing the function call 'len' will be extracted. - - If no statements or expressions are selected, the last toplevel - statement will be returned. - - If the selected statement is a discard statement, (i.e. an expression - turned into a statement), the wrapped expression is returned instead. - - For convenience, singleton lists are unpacked. - - :param str code: A piece of Python code that is parsed as - a module. Will be passed through textwrap.dedent first. - :param str module_name: The name of the module. - :returns: The designated node from the parse tree, or a list of nodes. - :rtype: astroid.bases.NodeNG, or a list of nodes. - """ - def _extract(node): - if isinstance(node, nodes.Discard): - return node.value - else: - return node - - requested_lines = [] - for idx, line in enumerate(code.splitlines()): - if line.strip().endswith(_STATEMENT_SELECTOR): - requested_lines.append(idx + 1) - - tree = build_module(code, module_name=module_name) - extracted = [] - if requested_lines: - for line in requested_lines: - extracted.append(_find_statement_by_line(tree, line)) - - # 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] - else: - return extracted - - -def build_module(code, module_name='', path=None): - """Parses a string module with a builder. - :param code: The code for the module. - :type code: str - :param module_name: The name for the module - :type module_name: str - :param path: The path for the module - :type module_name: str - :returns: The module AST. - :rtype: astroid.bases.NodeNG - """ - code = textwrap.dedent(code) - return builder.AstroidBuilder(None).string_build(code, modname=module_name, path=path) - - -def require_version(minver=None, maxver=None): - """ Compare version of python interpreter to the given one. Skip the test - if older. - """ - def parse(string, default=None): - string = string or default - try: - return tuple(int(v) for v in string.split('.')) - except ValueError: - raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version) - - def check_require_version(f): - current = sys.version_info[:3] - if parse(minver, "0") < current <= parse(maxver, "4"): - return f - else: - str_version = '.'.join(str(v) for v in sys.version_info) - @functools.wraps(f) - def new_f(self, *args, **kwargs): - if minver is not None: - self.skipTest('Needs Python > %s. Current version is %s.' % (minver, str_version)) - elif maxver is not None: - self.skipTest('Needs Python <= %s. Current version is %s.' % (maxver, str_version)) - return new_f - - - return check_require_version - -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] diff --git a/pymode/libs/astroid/utils.py b/pymode/libs/astroid/utils.py deleted file mode 100644 index ae72a92c..00000000 --- a/pymode/libs/astroid/utils.py +++ /dev/null @@ -1,239 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains some utilities to navigate in the tree or to -extract information from it -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from astroid.exceptions import AstroidBuildingException -from astroid.builder import parse - - -class ASTWalker(object): - """a walker visiting a tree in preorder, calling on the handler: - - * visit_ on entering a node, where class name is the class of - the node in lower case - - * leave_ on leaving a node, where class name is the class of - the node in lower case - """ - - def __init__(self, handler): - self.handler = handler - self._cache = {} - - def walk(self, node, _done=None): - """walk on the tree from , getting callbacks from handler""" - if _done is None: - _done = set() - if node in _done: - raise AssertionError((id(node), node, node.parent)) - _done.add(node) - self.visit(node) - for child_node in node.get_children(): - self.handler.set_context(node, child_node) - assert child_node is not node - self.walk(child_node, _done) - self.leave(node) - assert node.parent is not node - - def get_callbacks(self, node): - """get callbacks from handler for the visited node""" - klass = node.__class__ - methods = self._cache.get(klass) - if methods is None: - handler = self.handler - kid = klass.__name__.lower() - e_method = getattr(handler, 'visit_%s' % kid, - getattr(handler, 'visit_default', None)) - l_method = getattr(handler, 'leave_%s' % kid, - getattr(handler, 'leave_default', None)) - self._cache[klass] = (e_method, l_method) - else: - e_method, l_method = methods - return e_method, l_method - - def visit(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[0] - if method is not None: - method(node) - - def leave(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[1] - if method is not None: - method(node) - - -class LocalsVisitor(ASTWalker): - """visit a project by traversing the locals dictionary""" - def __init__(self): - ASTWalker.__init__(self, self) - self._visited = {} - - def visit(self, node): - """launch the visit starting from the given node""" - if node in self._visited: - return - self._visited[node] = 1 # FIXME: use set ? - methods = self.get_callbacks(node) - if methods[0] is not None: - methods[0](node) - if 'locals' in node.__dict__: # skip Instance and other proxy - for local_node in node.values(): - self.visit(local_node) - if methods[1] is not None: - return methods[1](node) - - -def _check_children(node): - """a helper function to check children - parent relations""" - for child in node.get_children(): - ok = False - if child is None: - print("Hm, child of %s is None" % node) - continue - if not hasattr(child, 'parent'): - print(" ERROR: %s has child %s %x with no parent" % ( - node, child, id(child))) - elif not child.parent: - print(" ERROR: %s has child %s %x with parent %r" % ( - node, child, id(child), child.parent)) - elif child.parent is not node: - print(" ERROR: %s %x has child %s %x with wrong parent %s" % ( - node, id(node), child, id(child), child.parent)) - else: - ok = True - if not ok: - print("lines;", node.lineno, child.lineno) - print("of module", node.root(), node.root().name) - raise AstroidBuildingException - _check_children(child) - - -class TreeTester(object): - '''A helper class to see _ast tree and compare with astroid tree - - indent: string for tree indent representation - lineno: bool to tell if we should print the line numbers - - >>> tester = TreeTester('print') - >>> print tester.native_tree_repr() - - - . body = [ - . - . . nl = True - . ] - >>> print tester.astroid_tree_repr() - Module() - body = [ - Print() - dest = - values = [ - ] - ] - ''' - - indent = '. ' - lineno = False - - def __init__(self, sourcecode): - self._string = '' - self.sourcecode = sourcecode - self._ast_node = None - self.build_ast() - - def build_ast(self): - """build the _ast tree from the source code""" - self._ast_node = parse(self.sourcecode) - - def native_tree_repr(self, node=None, indent=''): - """get a nice representation of the _ast tree""" - self._string = '' - if node is None: - node = self._ast_node - self._native_repr_tree(node, indent) - return self._string - - - def _native_repr_tree(self, node, indent, _done=None): - """recursive method for the native tree representation""" - from _ast import Load as _Load, Store as _Store, Del as _Del - from _ast import AST as Node - if _done is None: - _done = set() - if node in _done: - self._string += '\nloop in tree: %r (%s)' % ( - node, getattr(node, 'lineno', None)) - return - _done.add(node) - self._string += '\n' + indent + '<%s>' % node.__class__.__name__ - indent += self.indent - if not hasattr(node, '__dict__'): - self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node) - return - node_dict = node.__dict__ - if hasattr(node, '_attributes'): - for a in node._attributes: - attr = node_dict[a] - if attr is None: - continue - if a in ("lineno", "col_offset") and not self.lineno: - continue - self._string += '\n' + indent + a + " = " + repr(attr) - for field in node._fields or (): - attr = node_dict[field] - if attr is None: - continue - if isinstance(attr, list): - if not attr: - continue - self._string += '\n' + indent + field + ' = [' - for elt in attr: - self._native_repr_tree(elt, indent, _done) - self._string += '\n' + indent + ']' - continue - if isinstance(attr, (_Load, _Store, _Del)): - continue - if isinstance(attr, Node): - self._string += '\n' + indent + field + " = " - self._native_repr_tree(attr, indent, _done) - else: - self._string += '\n' + indent + field + " = " + repr(attr) - - - def build_astroid_tree(self): - """build astroid tree from the _ast tree - """ - from astroid.builder import AstroidBuilder - tree = AstroidBuilder().string_build(self.sourcecode) - return tree - - def astroid_tree_repr(self, ids=False): - """build the astroid tree and return a nice tree representation""" - mod = self.build_astroid_tree() - return mod.repr_tree(ids) - - -__all__ = ('LocalsVisitor', 'ASTWalker',) - 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/mccabe.py b/pymode/libs/mccabe.py deleted file mode 100644 index 90bf19cd..00000000 --- a/pymode/libs/mccabe.py +++ /dev/null @@ -1,311 +0,0 @@ -""" Meager code path measurement tool. - Ned Batchelder - http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html - MIT License. -""" -from __future__ import with_statement - -import optparse -import sys -from collections import defaultdict -try: - import ast - from ast import iter_child_nodes -except ImportError: # Python 2.5 - from flake8.util import ast, iter_child_nodes - -__version__ = '0.3.1' - - -class ASTVisitor(object): - """Performs a depth-first walk of the AST.""" - - def __init__(self): - self.node = None - self._cache = {} - - def default(self, node, *args): - for child in iter_child_nodes(node): - 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 preorder(self, tree, visitor, *args): - """Do preorder walk of tree using visitor""" - self.visitor = visitor - visitor.visit = self.dispatch - self.dispatch(tree, *args) # XXX *args make sense? - - -class PathNode(object): - def __init__(self, name, look="circle"): - self.name = name - self.look = look - - def to_dot(self): - print('node [shape=%s,label="%s"] %d;' % ( - self.look, self.name, self.dot_id())) - - def dot_id(self): - return id(self) - - -class PathGraph(object): - def __init__(self, name, entity, lineno): - self.name = name - self.entity = entity - self.lineno = lineno - self.nodes = defaultdict(list) - - def connect(self, n1, n2): - self.nodes[n1].append(n2) - # Ensure that the destination node is always counted. - self.nodes[n2] = [] - - def to_dot(self): - print('subgraph {') - for node in self.nodes: - node.to_dot() - for node, nexts in self.nodes.items(): - for next in nexts: - print('%s -- %s;' % (node.dot_id(), next.dot_id())) - print('}') - - def complexity(self): - """ Return the McCabe complexity for the graph. - V-E+2 - """ - num_edges = sum([len(n) for n in self.nodes.values()]) - num_nodes = len(self.nodes) - return num_edges - num_nodes + 2 - - -class PathGraphingAstVisitor(ASTVisitor): - """ A visitor for a parsed Abstract Syntax Tree which finds executable - statements. - """ - - def __init__(self): - super(PathGraphingAstVisitor, self).__init__() - self.classname = "" - self.graphs = {} - self.reset() - - def reset(self): - self.graph = None - self.tail = None - - def dispatch_list(self, node_list): - for node in node_list: - self.dispatch(node) - - def visitFunctionDef(self, node): - - if self.classname: - entity = '%s%s' % (self.classname, node.name) - else: - entity = node.name - - name = '%d:1: %r' % (node.lineno, entity) - - if self.graph is not None: - # closure - pathnode = self.appendPathNode(name) - self.tail = pathnode - self.dispatch_list(node.body) - bottom = PathNode("", look='point') - self.graph.connect(self.tail, bottom) - self.graph.connect(pathnode, bottom) - self.tail = bottom - else: - self.graph = PathGraph(name, entity, node.lineno) - pathnode = PathNode(name) - self.tail = pathnode - self.dispatch_list(node.body) - self.graphs["%s%s" % (self.classname, node.name)] = self.graph - self.reset() - - def visitClassDef(self, node): - old_classname = self.classname - self.classname += node.name + "." - self.dispatch_list(node.body) - self.classname = old_classname - - def appendPathNode(self, name): - if not self.tail: - return - pathnode = PathNode(name) - self.graph.connect(self.tail, pathnode) - self.tail = pathnode - return pathnode - - def visitSimpleStatement(self, node): - if node.lineno is None: - lineno = 0 - else: - lineno = node.lineno - name = "Stmt %d" % lineno - self.appendPathNode(name) - - visitAssert = visitAssign = visitAugAssign = visitDelete = visitPrint = \ - visitRaise = visitYield = visitImport = visitCall = visitSubscript = \ - visitPass = visitContinue = visitBreak = visitGlobal = visitReturn = \ - visitSimpleStatement - - def visitLoop(self, node): - name = "Loop %d" % node.lineno - self._subgraph(node, name) - - visitFor = visitWhile = visitLoop - - def visitIf(self, node): - name = "If %d" % node.lineno - self._subgraph(node, name) - - 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(name, name, node.lineno) - pathnode = PathNode(name) - self._subgraph_parse(node, pathnode, extra_blocks) - self.graphs["%s%s" % (self.classname, name)] = self.graph - self.reset() - else: - pathnode = self.appendPathNode(name) - self._subgraph_parse(node, pathnode, extra_blocks) - - def _subgraph_parse(self, node, pathnode, extra_blocks): - """parse the body and any `else` block of `if` and `for` statements""" - loose_ends = [] - self.tail = pathnode - self.dispatch_list(node.body) - loose_ends.append(self.tail) - for extra in extra_blocks: - self.tail = pathnode - self.dispatch_list(extra.body) - loose_ends.append(self.tail) - if node.orelse: - self.tail = pathnode - self.dispatch_list(node.orelse) - loose_ends.append(self.tail) - else: - loose_ends.append(pathnode) - if pathnode: - bottom = PathNode("", look='point') - for le in loose_ends: - self.graph.connect(le, bottom) - self.tail = bottom - - def visitTryExcept(self, node): - name = "TryExcept %d" % node.lineno - self._subgraph(node, name, extra_blocks=node.handlers) - - visitTry = visitTryExcept - - def visitWith(self, node): - name = "With %d" % node.lineno - self.appendPathNode(name) - self.dispatch_list(node.body) - - -class McCabeChecker(object): - """McCabe cyclomatic complexity checker.""" - name = 'mccabe' - version = __version__ - _code = 'C901' - _error_tmpl = "C901 %r is too complex (%d)" - max_complexity = 0 - - def __init__(self, tree, filename): - self.tree = tree - - @classmethod - def add_options(cls, parser): - parser.add_option('--max-complexity', default=-1, action='store', - type='int', help="McCabe complexity threshold") - parser.config_options.append('max-complexity') - - @classmethod - def parse_options(cls, options): - cls.max_complexity = int(options.max_complexity) - - def run(self): - if self.max_complexity < 0: - return - visitor = PathGraphingAstVisitor() - visitor.preorder(self.tree, visitor) - for graph in visitor.graphs.values(): - if graph.complexity() > self.max_complexity: - text = self._error_tmpl % (graph.entity, graph.complexity()) - yield graph.lineno, 0, text, type(self) - - -def get_code_complexity(code, threshold=7, filename='stdin'): - try: - tree = compile(code, filename, "exec", ast.PyCF_ONLY_AST) - except SyntaxError: - e = sys.exc_info()[1] - sys.stderr.write("Unable to parse %s: %s\n" % (filename, e)) - return 0 - - complx = [] - McCabeChecker.max_complexity = threshold - for lineno, offset, text, check in McCabeChecker(tree, filename).run(): - complx.append('%s:%d:1: %s' % (filename, lineno, text)) - - if len(complx) == 0: - return 0 - print('\n'.join(complx)) - return len(complx) - - -def get_module_complexity(module_path, threshold=7): - """Returns the complexity of a module""" - with open(module_path, "rU") as mod: - code = mod.read() - return get_code_complexity(code, threshold, filename=module_path) - - -def main(argv=None): - if argv is None: - argv = sys.argv[1:] - opar = optparse.OptionParser() - opar.add_option("-d", "--dot", dest="dot", - help="output a graphviz dot file", action="store_true") - opar.add_option("-m", "--min", dest="threshold", - help="minimum complexity for output", type="int", - default=1) - - options, args = opar.parse_args(argv) - - with open(args[0], "rU") as mod: - code = mod.read() - tree = compile(code, args[0], "exec", ast.PyCF_ONLY_AST) - visitor = PathGraphingAstVisitor() - visitor.preorder(tree, visitor) - - if options.dot: - print('graph {') - for graph in visitor.graphs.values(): - if (not options.threshold or - graph.complexity() >= options.threshold): - graph.to_dot() - print('}') - else: - for graph in visitor.graphs.values(): - if graph.complexity() >= options.threshold: - print(graph.name, graph.complexity()) - - -if __name__ == '__main__': - main(sys.argv[1:]) - diff --git a/pymode/libs/mccabe.py b/pymode/libs/mccabe.py new file mode 120000 index 00000000..02c3eab6 --- /dev/null +++ b/pymode/libs/mccabe.py @@ -0,0 +1 @@ +../../submodules/mccabe/mccabe.py \ No newline at end of file diff --git a/pymode/libs/pep257.py b/pymode/libs/pep257.py deleted file mode 100644 index 79d9eee1..00000000 --- a/pymode/libs/pep257.py +++ /dev/null @@ -1,1187 +0,0 @@ -#! /usr/bin/env python -"""Static analysis tool for checking docstring conventions and style. - -Implemented checks cover PEP257: -http://www.python.org/dev/peps/pep-0257/ - -Other checks can be added, e.g. NumPy docstring conventions: -https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt - -The repository is located at: -http://github.com/GreenSteam/pep257 - -""" -from __future__ import with_statement - -import os -import sys -import logging -import tokenize as tk -from itertools import takewhile, dropwhile, chain -from optparse import OptionParser -from re import compile as re -import itertools - -try: # Python 3.x - from ConfigParser import RawConfigParser -except ImportError: # Python 2.x - from configparser import RawConfigParser - -log = logging.getLogger(__name__) - - -try: - from StringIO import StringIO -except ImportError: # Python 3.0 and later - from io import StringIO - - -try: - next -except NameError: # Python 2.5 and earlier - nothing = object() - - def next(obj, default=nothing): - if default == nothing: - return obj.next() - else: - try: - return obj.next() - except StopIteration: - return default - - -# If possible (python >= 3.2) use tokenize.open to open files, so PEP 263 -# encoding markers are interpreted. -try: - tokenize_open = tk.open -except AttributeError: - tokenize_open = open - - -__version__ = '0.6.1-alpha' -__all__ = ('check', 'collect') - -PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep257') -NO_VIOLATIONS_RETURN_CODE = 0 -VIOLATIONS_RETURN_CODE = 1 -INVALID_OPTIONS_RETURN_CODE = 2 - - -def humanize(string): - return re(r'(.)([A-Z]+)').sub(r'\1 \2', string).lower() - - -def is_magic(name): - return name.startswith('__') and name.endswith('__') - - -def is_ascii(string): - return all(ord(char) < 128 for char in string) - - -def is_blank(string): - return not string.strip() - - -def leading_space(string): - return re('\s*').match(string).group() - - -class Value(object): - - def __init__(self, *args): - vars(self).update(zip(self._fields, args)) - - def __hash__(self): - return hash(repr(self)) - - def __eq__(self, other): - return other and vars(self) == vars(other) - - def __repr__(self): - kwargs = ', '.join('{}={!r}'.format(field, getattr(self, field)) - for field in self._fields) - return '{}({})'.format(self.__class__.__name__, kwargs) - - -class Definition(Value): - - _fields = ('name', '_source', 'start', 'end', 'decorators', 'docstring', - 'children', 'parent') - - _human = property(lambda self: humanize(type(self).__name__)) - kind = property(lambda self: self._human.split()[-1]) - module = property(lambda self: self.parent.module) - all = property(lambda self: self.module.all) - _slice = property(lambda self: slice(self.start - 1, self.end)) - source = property(lambda self: ''.join(self._source[self._slice])) - - def __iter__(self): - return chain([self], *self.children) - - @property - def _publicity(self): - return {True: 'public', False: 'private'}[self.is_public] - - def __str__(self): - return 'in %s %s `%s`' % (self._publicity, self._human, self.name) - - -class Module(Definition): - - _fields = ('name', '_source', 'start', 'end', 'decorators', 'docstring', - 'children', 'parent', '_all') - is_public = True - _nest = staticmethod(lambda s: {'def': Function, 'class': Class}[s]) - module = property(lambda self: self) - all = property(lambda self: self._all) - - def __str__(self): - return 'at module level' - - -class Package(Module): - - """A package is a __init__.py module.""" - - -class Function(Definition): - - _nest = staticmethod(lambda s: {'def': NestedFunction, - 'class': NestedClass}[s]) - - @property - def is_public(self): - if self.all is not None: - return self.name in self.all - else: # TODO: are there any magic functions? not methods - return not self.name.startswith('_') or is_magic(self.name) - - -class NestedFunction(Function): - - is_public = False - - -class Method(Function): - - @property - def is_public(self): - # Check if we are a setter/deleter method, and mark as private if so. - for decorator in self.decorators: - # Given 'foo', match 'foo.bar' but not 'foobar' or 'sfoo' - if re(r"^{0}\.".format(self.name)).match(decorator.name): - return False - name_is_public = not self.name.startswith('_') or is_magic(self.name) - return self.parent.is_public and name_is_public - - -class Class(Definition): - - _nest = staticmethod(lambda s: {'def': Method, 'class': NestedClass}[s]) - is_public = Function.is_public - - -class NestedClass(Class): - - is_public = False - - -class Decorator(Value): - - """A decorator for function, method or class.""" - - _fields = 'name arguments'.split() - - -class TokenKind(int): - def __repr__(self): - return "tk.{}".format(tk.tok_name[self]) - - -class Token(Value): - - _fields = 'kind value start end source'.split() - - def __init__(self, *args): - super(Token, self).__init__(*args) - self.kind = TokenKind(self.kind) - - -class TokenStream(object): - - def __init__(self, filelike): - self._generator = tk.generate_tokens(filelike.readline) - self.current = Token(*next(self._generator, None)) - self.line = self.current.start[0] - - def move(self): - previous = self.current - current = next(self._generator, None) - self.current = None if current is None else Token(*current) - self.line = self.current.start[0] if self.current else self.line - return previous - - def __iter__(self): - while True: - if self.current is not None: - yield self.current - else: - return - self.move() - - -class AllError(Exception): - - def __init__(self, message): - Exception.__init__( - self, message + - 'That means pep257 cannot decide which definitions are public. ' - 'Variable __all__ should be present at most once in each file, ' - "in form `__all__ = ('a_public_function', 'APublicClass', ...)`. " - 'More info on __all__: http://stackoverflow.com/q/44834/. ') - - -class Parser(object): - - def __call__(self, filelike, filename): - self.source = filelike.readlines() - src = ''.join(self.source) - self.stream = TokenStream(StringIO(src)) - self.filename = filename - self.all = None - self._accumulated_decorators = [] - return self.parse_module() - - current = property(lambda self: self.stream.current) - line = property(lambda self: self.stream.line) - - def consume(self, kind): - assert self.stream.move().kind == kind - - def leapfrog(self, kind, value=None): - """Skip tokens in the stream until a certain token kind is reached. - - If `value` is specified, tokens whose values are different will also - be skipped. - """ - while self.current is not None: - if (self.current.kind == kind and - (value is None or self.current.value == value)): - self.consume(kind) - return - self.stream.move() - - def parse_docstring(self): - """Parse a single docstring and return its value.""" - log.debug("parsing docstring, token is %r (%s)", - self.current.kind, self.current.value) - while self.current.kind in (tk.COMMENT, tk.NEWLINE, tk.NL): - self.stream.move() - log.debug("parsing docstring, token is %r (%s)", - self.current.kind, self.current.value) - if self.current.kind == tk.STRING: - docstring = self.current.value - self.stream.move() - return docstring - return None - - def parse_decorators(self): - """Called after first @ is found. - - Parse decorators into self._accumulated_decorators. - Continue to do so until encountering the 'def' or 'class' start token. - """ - name = [] - arguments = [] - at_arguments = False - - while self.current is not None: - if (self.current.kind == tk.NAME and - self.current.value in ['def', 'class']): - # Done with decorators - found function or class proper - break - elif self.current.kind == tk.OP and self.current.value == '@': - # New decorator found. Store the decorator accumulated so far: - self._accumulated_decorators.append( - Decorator(''.join(name), ''.join(arguments))) - # Now reset to begin accumulating the new decorator: - name = [] - arguments = [] - at_arguments = False - elif self.current.kind == tk.OP and self.current.value == '(': - at_arguments = True - elif self.current.kind == tk.OP and self.current.value == ')': - # Ignore close parenthesis - pass - elif self.current.kind == tk.NEWLINE or self.current.kind == tk.NL: - # Ignore newlines - pass - else: - # Keep accumulating current decorator's name or argument. - if not at_arguments: - name.append(self.current.value) - else: - arguments.append(self.current.value) - self.stream.move() - - # Add decorator accumulated so far - self._accumulated_decorators.append( - Decorator(''.join(name), ''.join(arguments))) - - def parse_definitions(self, class_, all=False): - """Parse multiple defintions and yield them.""" - while self.current is not None: - log.debug("parsing defintion list, current token is %r (%s)", - self.current.kind, self.current.value) - if all and self.current.value == '__all__': - self.parse_all() - elif self.current.kind == tk.OP and self.current.value == '@': - self.consume(tk.OP) - self.parse_decorators() - elif self.current.value in ['def', 'class']: - yield self.parse_definition(class_._nest(self.current.value)) - elif self.current.kind == tk.INDENT: - self.consume(tk.INDENT) - for definition in self.parse_definitions(class_): - yield definition - elif self.current.kind == tk.DEDENT: - self.consume(tk.DEDENT) - return - else: - self.stream.move() - - def parse_all(self): - """Parse the __all__ definition in a module.""" - assert self.current.value == '__all__' - self.consume(tk.NAME) - if self.current.value != '=': - raise AllError('Could not evaluate contents of __all__. ') - self.consume(tk.OP) - if self.current.value not in '([': - raise AllError('Could not evaluate contents of __all__. ') - if self.current.value == '[': - msg = ("%s WARNING: __all__ is defined as a list, this means " - "pep257 cannot reliably detect contents of the __all__ " - "variable, because it can be mutated. Change __all__ to be " - "an (immutable) tuple, to remove this warning. Note, " - "pep257 uses __all__ to detect which definitions are " - "public, to warn if public definitions are missing " - "docstrings. If __all__ is a (mutable) list, pep257 cannot " - "reliably assume its contents. pep257 will proceed " - "assuming __all__ is not mutated.\n" % self.filename) - sys.stderr.write(msg) - self.consume(tk.OP) - - self.all = [] - all_content = "(" - while self.current.kind != tk.OP or self.current.value not in ")]": - if self.current.kind in (tk.NL, tk.COMMENT): - pass - elif (self.current.kind == tk.STRING or - self.current.value == ','): - all_content += self.current.value - else: - kind = token.tok_name[self.current.kind] - raise AllError('Unexpected token kind in __all__: %s' % kind) - self.stream.move() - self.consume(tk.OP) - all_content += ")" - try: - self.all = eval(all_content, {}) - except BaseException as e: - raise AllError('Could not evaluate contents of __all__.' - '\bThe value was %s. The exception was:\n%s' - % (all_content, e)) - - def parse_module(self): - """Parse a module (and its children) and return a Module object.""" - log.debug("parsing module.") - start = self.line - docstring = self.parse_docstring() - children = list(self.parse_definitions(Module, all=True)) - assert self.current is None, self.current - end = self.line - cls = Module - if self.filename.endswith('__init__.py'): - cls = Package - module = cls(self.filename, self.source, start, end, - [], docstring, children, None, self.all) - for child in module.children: - child.parent = module - log.debug("finished parsing module.") - return module - - def parse_definition(self, class_): - """Parse a defintion and return its value in a `class_` object.""" - start = self.line - self.consume(tk.NAME) - name = self.current.value - log.debug("parsing %s '%s'", class_.__name__, name) - self.stream.move() - if self.current.kind == tk.OP and self.current.value == '(': - parenthesis_level = 0 - while True: - if self.current.kind == tk.OP: - if self.current.value == '(': - parenthesis_level += 1 - elif self.current.value == ')': - parenthesis_level -= 1 - if parenthesis_level == 0: - break - self.stream.move() - if self.current.kind != tk.OP or self.current.value != ':': - self.leapfrog(tk.OP, value=":") - else: - self.consume(tk.OP) - if self.current.kind in (tk.NEWLINE, tk.COMMENT): - self.leapfrog(tk.INDENT) - assert self.current.kind != tk.INDENT - docstring = self.parse_docstring() - decorators = self._accumulated_decorators - self._accumulated_decorators = [] - log.debug("parsing nested defintions.") - children = list(self.parse_definitions(class_)) - log.debug("finished parsing nested defintions for '%s'", name) - end = self.line - 1 - else: # one-liner definition - docstring = self.parse_docstring() - decorators = [] # TODO - children = [] - end = self.line - self.leapfrog(tk.NEWLINE) - definition = class_(name, self.source, start, end, - decorators, docstring, children, None) - for child in definition.children: - child.parent = definition - log.debug("finished parsing %s '%s'. Next token is %r (%s)", - class_.__name__, name, self.current.kind, - self.current.value) - return definition - - -class Error(object): - - """Error in docstring style.""" - - # should be overridden by inheriting classes - code = None - short_desc = None - context = None - - # Options that define how errors are printed: - explain = False - source = False - - def __init__(self, *parameters): - self.parameters = parameters - self.definition = None - self.explanation = None - - def set_context(self, definition, explanation): - self.definition = definition - self.explanation = explanation - - filename = property(lambda self: self.definition.module.name) - line = property(lambda self: self.definition.start) - - @property - def message(self): - ret = '%s: %s' % (self.code, self.short_desc) - if self.context is not None: - ret += ' (' + self.context % self.parameters + ')' - return ret - - @property - def lines(self): - source = '' - lines = self.definition._source[self.definition._slice] - offset = self.definition.start - lines_stripped = list(reversed(list(dropwhile(is_blank, - reversed(lines))))) - numbers_width = 0 - for n, line in enumerate(lines_stripped): - numbers_width = max(numbers_width, n + offset) - numbers_width = len(str(numbers_width)) - numbers_width = 6 - for n, line in enumerate(lines_stripped): - source += '%*d: %s' % (numbers_width, n + offset, line) - if n > 5: - source += ' ...\n' - break - return source - - def __str__(self): - self.explanation = '\n'.join(l for l in self.explanation.split('\n') - if not is_blank(l)) - template = '%(filename)s:%(line)s %(definition)s:\n %(message)s' - if self.source and self.explain: - template += '\n\n%(explanation)s\n\n%(lines)s\n' - elif self.source and not self.explain: - template += '\n\n%(lines)s\n' - elif self.explain and not self.source: - template += '\n\n%(explanation)s\n\n' - return template % dict((name, getattr(self, name)) for name in - ['filename', 'line', 'definition', 'message', - 'explanation', 'lines']) - - __repr__ = __str__ - - def __lt__(self, other): - return (self.filename, self.line) < (other.filename, other.line) - - -class ErrorRegistry(object): - groups = [] - - class ErrorGroup(object): - - def __init__(self, prefix, name): - self.prefix = prefix - self.name = name - self.errors = [] - - def create_error(self, error_code, error_desc, error_context=None): - # TODO: check prefix - - class _Error(Error): - code = error_code - short_desc = error_desc - context = error_context - - self.errors.append(_Error) - return _Error - - @classmethod - def create_group(cls, prefix, name): - group = cls.ErrorGroup(prefix, name) - cls.groups.append(group) - return group - - @classmethod - def get_error_codes(cls): - for group in cls.groups: - for error in group.errors: - yield error.code - - @classmethod - def to_rst(cls): - sep_line = '+' + 6 * '-' + '+' + '-' * 71 + '+\n' - blank_line = '|' + 78 * ' ' + '|\n' - table = '' - for group in cls.groups: - table += sep_line - table += blank_line - table += '|' + ('**%s**' % group.name).center(78) + '|\n' - table += blank_line - for error in group.errors: - table += sep_line - table += ('|' + error.code.center(6) + '| ' + - error.short_desc.ljust(70) + '|\n') - table += sep_line - return table - - -D1xx = ErrorRegistry.create_group('D1', 'Missing Docstrings') -D100 = D1xx.create_error('D100', 'Missing docstring in public module') -D101 = D1xx.create_error('D101', 'Missing docstring in public class') -D102 = D1xx.create_error('D102', 'Missing docstring in public method') -D103 = D1xx.create_error('D103', 'Missing docstring in public function') -D104 = D1xx.create_error('D104', 'Missing docstring in public package') - -D2xx = ErrorRegistry.create_group('D2', 'Whitespace Issues') -D200 = D2xx.create_error('D200', 'One-line docstring should fit on one line ' - 'with quotes', 'found %s') -D201 = D2xx.create_error('D201', 'No blank lines allowed before function ' - 'docstring', 'found %s') -D202 = D2xx.create_error('D202', 'No blank lines allowed after function ' - 'docstring', 'found %s') -D203 = D2xx.create_error('D203', '1 blank line required before class ' - 'docstring', 'found %s') -D204 = D2xx.create_error('D204', '1 blank line required after class ' - 'docstring', 'found %s') -D205 = D2xx.create_error('D205', '1 blank line required between summary line ' - 'and description', 'found %s') -D206 = D2xx.create_error('D206', 'Docstring should be indented with spaces, ' - 'not tabs') -D207 = D2xx.create_error('D207', 'Docstring is under-indented') -D208 = D2xx.create_error('D208', 'Docstring is over-indented') -D209 = D2xx.create_error('D209', 'Multi-line docstring closing quotes should ' - 'be on a separate line') -D210 = D2xx.create_error('D210', 'No whitespaces allowed surrounding ' - 'docstring text') - -D3xx = ErrorRegistry.create_group('D3', 'Quotes Issues') -D300 = D3xx.create_error('D300', 'Use """triple double quotes"""', - 'found %s-quotes') -D301 = D3xx.create_error('D301', 'Use r""" if any backslashes in a docstring') -D302 = D3xx.create_error('D302', 'Use u""" for Unicode docstrings') - -D4xx = ErrorRegistry.create_group('D4', 'Docstring Content Issues') -D400 = D4xx.create_error('D400', 'First line should end with a period', - 'not %r') -D401 = D4xx.create_error('D401', 'First line should be in imperative mood', - '%r, not %r') -D402 = D4xx.create_error('D402', 'First line should not be the function\'s ' - '"signature"') - - -class Conventions(object): - pep257 = set(ErrorRegistry.get_error_codes()) - - -def get_option_parser(): - parser = OptionParser(version=__version__, - usage='Usage: pep257 [options] [...]') - parser.config_options = ('explain', 'source', 'ignore', 'match', 'select', - 'match-dir', 'debug', 'verbose', 'count', - 'convention') - option = parser.add_option - option('-e', '--explain', action='store_true', - help='show explanation of each error') - option('-s', '--source', action='store_true', - help='show source for each error') - option('--select', metavar='', default='', - help='choose the basic list of checked errors by specifying which ' - 'errors to check for (with a list of comma-separated error ' - 'codes). for example: --select=D101,D202') - option('--ignore', metavar='', default='', - help='choose the basic list of checked errors by specifying which ' - 'errors to ignore (with a list of comma-separated error ' - 'codes). for example: --ignore=D101,D202') - option('--convention', metavar='', default='', - help='choose the basic list of checked errors by specifying an ' - 'existing convention. for example: --convention=pep257') - option('--add-select', metavar='', default='', - help='amend the list of errors to check for by specifying more ' - 'error codes to check.') - option('--add-ignore', metavar='', default='', - help='amend the list of errors to check for by specifying more ' - 'error codes to ignore.') - option('--match', metavar='', default='(?!test_).*\.py', - help="check only files that exactly match regular " - "expression; default is --match='(?!test_).*\.py' which " - "matches files that don't start with 'test_' but end with " - "'.py'") - option('--match-dir', metavar='', default='[^\.].*', - help="search only dirs that exactly match regular " - "expression; default is --match-dir='[^\.].*', which matches " - "all dirs that don't start with a dot") - option('-d', '--debug', action='store_true', - help='print debug information') - option('-v', '--verbose', action='store_true', - help='print status information') - option('--count', action='store_true', - help='print total number of errors to stdout') - return parser - - -def collect(names, match=lambda name: True, match_dir=lambda name: True): - """Walk dir trees under `names` and generate filnames that `match`. - - Example - ------- - >>> sorted(collect(['non-dir.txt', './'], - ... match=lambda name: name.endswith('.py'))) - ['non-dir.txt', './pep257.py', './setup.py', './test_pep257.py'] - - """ - for name in names: # map(expanduser, names): - if os.path.isdir(name): - for root, dirs, filenames in os.walk(name): - # Skip any dirs that do not match match_dir - dirs[:] = [dir for dir in dirs if match_dir(dir)] - for filename in filenames: - if match(filename): - yield os.path.join(root, filename) - else: - yield name - - -def check(filenames, select=None, ignore=None): - """Generate PEP 257 errors that exist in `filenames` iterable. - - Only returns errors with error-codes defined in `checked_codes` iterable. - - Example - ------- - >>> check(['pep257.py'], checked_codes=['D100']) - - - """ - if select and ignore: - raise ValueError('Cannot pass both select and ignore. They are ' - 'mutually exclusive.') - elif select or ignore: - checked_codes = (select or - set(ErrorRegistry.get_error_codes()) - set(ignore)) - else: - checked_codes = Conventions.pep257 - - for filename in filenames: - log.info('Checking file %s.', filename) - try: - with tokenize_open(filename) as file: - source = file.read() - for error in PEP257Checker().check_source(source, filename): - code = getattr(error, 'code', None) - if code in checked_codes: - yield error - except (EnvironmentError, AllError): - yield sys.exc_info()[1] - except tk.TokenError: - yield SyntaxError('invalid syntax in file %s' % filename) - - -def get_options(args, opt_parser): - config = RawConfigParser() - parent = tail = os.path.abspath(os.path.commonprefix(args)) - config_found = False - while tail and not config_found: - log.info(tail) - for fn in PROJECT_CONFIG: - full_path = os.path.join(parent, fn) - if config.read(full_path): - log.info('local configuration: in %s.', full_path) - config_found = True - break - parent, tail = os.path.split(parent) - - new_options = None - if config.has_section('pep257'): - option_list = dict([(o.dest, o.type or o.action) - for o in opt_parser.option_list]) - - # First, read the default values - new_options, _ = opt_parser.parse_args([]) - - # Second, parse the configuration - pep257_section = 'pep257' - for opt in config.options(pep257_section): - if opt.replace('_', '-') not in opt_parser.config_options: - log.warning("Unknown option '{}' ignored".format(opt)) - continue - normalized_opt = opt.replace('-', '_') - opt_type = option_list[normalized_opt] - if opt_type in ('int', 'count'): - value = config.getint(pep257_section, opt) - elif opt_type == 'string': - value = config.get(pep257_section, opt) - else: - assert opt_type in ('store_true', 'store_false') - value = config.getboolean(pep257_section, opt) - setattr(new_options, normalized_opt, value) - - # Third, overwrite with the command-line options - options, _ = opt_parser.parse_args(values=new_options) - log.debug("options: %s", options) - return options - - -def setup_stream_handlers(options): - """Setup logging stream handlers according to the options.""" - class StdoutFilter(logging.Filter): - def filter(self, record): - return record.levelno in (logging.DEBUG, logging.INFO) - - if log.handlers: - for handler in log.handlers: - log.removeHandler(handler) - - stdout_handler = logging.StreamHandler(sys.stdout) - stdout_handler.setLevel(logging.WARNING) - stdout_handler.addFilter(StdoutFilter()) - if options.debug: - stdout_handler.setLevel(logging.DEBUG) - elif options.verbose: - stdout_handler.setLevel(logging.INFO) - else: - stdout_handler.setLevel(logging.WARNING) - log.addHandler(stdout_handler) - - stderr_handler = logging.StreamHandler(sys.stderr) - stderr_handler.setLevel(logging.WARNING) - log.addHandler(stderr_handler) - - -def get_checked_error_codes(options): - codes = set(ErrorRegistry.get_error_codes()) - if options.ignore: - checked_codes = codes - set(options.ignore.split(',')) - elif options.select: - checked_codes = set(options.select.split(',')) - elif options.convention: - checked_codes = getattr(Conventions, options.convention) - else: - checked_codes = Conventions.pep257 - checked_codes -= set(options.add_ignore.split(',')) - checked_codes |= set(options.add_select.split(',')) - return checked_codes - set('') - - -def validate_options(options): - mutually_exclusive = ('ignore', 'select', 'convention') - for opt1, opt2 in itertools.permutations(mutually_exclusive, 2): - if getattr(options, opt1) and getattr(options, opt2): - log.error('Cannot pass both {0} and {1}. They are ' - 'mutually exclusive.'.format(opt1, opt2)) - return False - if options.convention and not hasattr(Conventions, options.convention): - return False - return True - - -def run_pep257(): - log.setLevel(logging.DEBUG) - opt_parser = get_option_parser() - # setup the logger before parsing the config file, so that command line - # arguments for debug / verbose will be printed. - options, arguments = opt_parser.parse_args() - setup_stream_handlers(options) - # We parse the files before opening the config file, since it changes where - # we look for the file. - options = get_options(arguments, opt_parser) - if not validate_options(options): - return INVALID_OPTIONS_RETURN_CODE - # Setup the handler again with values from the config file. - setup_stream_handlers(options) - - collected = collect(arguments or ['.'], - match=re(options.match + '$').match, - match_dir=re(options.match_dir + '$').match) - - log.debug("starting pep257 in debug mode.") - - Error.explain = options.explain - Error.source = options.source - collected = list(collected) - checked_codes = get_checked_error_codes(options) - errors = check(collected, select=checked_codes) - code = NO_VIOLATIONS_RETURN_CODE - count = 0 - for error in errors: - sys.stderr.write('%s\n' % error) - code = VIOLATIONS_RETURN_CODE - count += 1 - if options.count: - print(count) - return code - - -parse = Parser() - - -def check_for(kind, terminal=False): - def decorator(f): - f._check_for = kind - f._terminal = terminal - return f - return decorator - - -class PEP257Checker(object): - - """Checker for PEP 257. - - D10x: Missing docstrings - D20x: Whitespace issues - D30x: Docstring formatting - D40x: Docstring content issues - - """ - - def check_source(self, source, filename): - module = parse(StringIO(source), filename) - for definition in module: - for check in self.checks: - terminate = False - if isinstance(definition, check._check_for): - error = check(None, definition, definition.docstring) - errors = error if hasattr(error, '__iter__') else [error] - for error in errors: - if error is not None: - partition = check.__doc__.partition('.\n') - message, _, explanation = partition - error.set_context(explanation=explanation, - definition=definition) - yield error - if check._terminal: - terminate = True - break - if terminate: - break - - @property - def checks(self): - all = [check for check in vars(type(self)).values() - if hasattr(check, '_check_for')] - return sorted(all, key=lambda check: not check._terminal) - - @check_for(Definition, terminal=True) - def check_docstring_missing(self, definition, docstring): - """D10{0,1,2,3}: Public definitions should have docstrings. - - All modules should normally have docstrings. [...] all functions and - classes exported by a module should also have docstrings. Public - methods (including the __init__ constructor) should also have - docstrings. - - Note: Public (exported) definitions are either those with names listed - in __all__ variable (if present), or those that do not start - with a single underscore. - - """ - if (not docstring and definition.is_public or - docstring and is_blank(eval(docstring))): - codes = {Module: D100, Class: D101, NestedClass: D101, - Method: D102, Function: D103, NestedFunction: D103, - Package: D104} - return codes[type(definition)]() - - @check_for(Definition) - def check_one_liners(self, definition, docstring): - """D200: One-liner docstrings should fit on one line with quotes. - - The closing quotes are on the same line as the opening quotes. - This looks better for one-liners. - - """ - if docstring: - lines = eval(docstring).split('\n') - if len(lines) > 1: - non_empty_lines = sum(1 for l in lines if not is_blank(l)) - if non_empty_lines == 1: - return D200(len(lines)) - - @check_for(Function) - def check_no_blank_before(self, function, docstring): # def - """D20{1,2}: No blank lines allowed around function/method docstring. - - There's no blank line either before or after the docstring. - - """ - # NOTE: This does not take comments into account. - # NOTE: This does not take into account functions with groups of code. - if docstring: - before, _, after = function.source.partition(docstring) - blanks_before = list(map(is_blank, before.split('\n')[:-1])) - blanks_after = list(map(is_blank, after.split('\n')[1:])) - blanks_before_count = sum(takewhile(bool, reversed(blanks_before))) - blanks_after_count = sum(takewhile(bool, blanks_after)) - if blanks_before_count != 0: - yield D201(blanks_before_count) - if not all(blanks_after) and blanks_after_count != 0: - yield D202(blanks_after_count) - - @check_for(Class) - def check_blank_before_after_class(slef, class_, docstring): - """D20{3,4}: Class docstring should have 1 blank line around them. - - Insert a blank line before and after all docstrings (one-line or - multi-line) that document a class -- generally speaking, the class's - methods are separated from each other by a single blank line, and the - docstring needs to be offset from the first method by a blank line; - for symmetry, put a blank line between the class header and the - docstring. - - """ - # NOTE: this gives false-positive in this case - # class Foo: - # - # """Docstring.""" - # - # - # # comment here - # def foo(): pass - if docstring: - before, _, after = class_.source.partition(docstring) - blanks_before = list(map(is_blank, before.split('\n')[:-1])) - blanks_after = list(map(is_blank, after.split('\n')[1:])) - blanks_before_count = sum(takewhile(bool, reversed(blanks_before))) - blanks_after_count = sum(takewhile(bool, blanks_after)) - if blanks_before_count != 1: - yield D203(blanks_before_count) - if not all(blanks_after) and blanks_after_count != 1: - yield D204(blanks_after_count) - - @check_for(Definition) - def check_blank_after_summary(self, definition, docstring): - """D205: Put one blank line between summary line and description. - - Multi-line docstrings consist of a summary line just like a one-line - docstring, followed by a blank line, followed by a more elaborate - description. The summary line may be used by automatic indexing tools; - it is important that it fits on one line and is separated from the - rest of the docstring by a blank line. - - """ - if docstring: - lines = eval(docstring).strip().split('\n') - if len(lines) > 1: - post_summary_blanks = list(map(is_blank, lines[1:])) - blanks_count = sum(takewhile(bool, post_summary_blanks)) - if blanks_count != 1: - return D205(blanks_count) - - @check_for(Definition) - def check_indent(self, definition, docstring): - """D20{6,7,8}: The entire docstring should be indented same as code. - - The entire docstring is indented the same as the quotes at its - first line. - - """ - if docstring: - before_docstring, _, _ = definition.source.partition(docstring) - _, _, indent = before_docstring.rpartition('\n') - lines = docstring.split('\n') - if len(lines) > 1: - lines = lines[1:] # First line does not need indent. - indents = [leading_space(l) for l in lines if not is_blank(l)] - if set(' \t') == set(''.join(indents) + indent): - yield D206() - if (len(indents) > 1 and min(indents[:-1]) > indent or - indents[-1] > indent): - yield D208() - if min(indents) < indent: - yield D207() - - @check_for(Definition) - def check_newline_after_last_paragraph(self, definition, docstring): - """D209: Put multi-line docstring closing quotes on separate line. - - Unless the entire docstring fits on a line, place the closing - quotes on a line by themselves. - - """ - if docstring: - lines = [l for l in eval(docstring).split('\n') if not is_blank(l)] - if len(lines) > 1: - if docstring.split("\n")[-1].strip() not in ['"""', "'''"]: - return D209() - - @check_for(Definition) - def check_surrounding_whitespaces(self, definition, docstring): - """D210: No whitespaces allowed surrounding docstring text.""" - if docstring: - lines = eval(docstring).split('\n') - if lines[0].startswith(' ') or \ - len(lines) == 1 and lines[0].endswith(' '): - return D210() - - @check_for(Definition) - def check_triple_double_quotes(self, definition, docstring): - r'''D300: Use """triple double quotes""". - - For consistency, always use """triple double quotes""" around - docstrings. Use r"""raw triple double quotes""" if you use any - backslashes in your docstrings. For Unicode docstrings, use - u"""Unicode triple-quoted strings""". - - Note: Exception to this is made if the docstring contains - """ quotes in its body. - - ''' - if docstring and '"""' in eval(docstring) and docstring.startswith( - ("'''", "r'''", "u'''", "ur'''")): - # Allow ''' quotes if docstring contains """, because otherwise """ - # quotes could not be expressed inside docstring. Not in PEP 257. - return - if docstring and not docstring.startswith( - ('"""', 'r"""', 'u"""', 'ur"""')): - quotes = "'''" if "'''" in docstring[:4] else "'" - return D300(quotes) - - @check_for(Definition) - def check_backslashes(self, definition, docstring): - r'''D301: Use r""" if any backslashes in a docstring. - - Use r"""raw triple double quotes""" if you use any backslashes - (\) in your docstrings. - - ''' - # Just check that docstring is raw, check_triple_double_quotes - # ensures the correct quotes. - if docstring and '\\' in docstring and not docstring.startswith( - ('r', 'ur')): - return D301() - - @check_for(Definition) - def check_unicode_docstring(self, definition, docstring): - r'''D302: Use u""" for docstrings with Unicode. - - For Unicode docstrings, use u"""Unicode triple-quoted strings""". - - ''' - # Just check that docstring is unicode, check_triple_double_quotes - # ensures the correct quotes. - if docstring and sys.version_info[0] <= 2: - if not is_ascii(docstring) and not docstring.startswith( - ('u', 'ur')): - return D302() - - @check_for(Definition) - def check_ends_with_period(self, definition, docstring): - """D400: First line should end with a period. - - The [first line of a] docstring is a phrase ending in a period. - - """ - if docstring: - summary_line = eval(docstring).strip().split('\n')[0] - if not summary_line.endswith('.'): - return D400(summary_line[-1]) - - @check_for(Function) - def check_imperative_mood(self, function, docstring): # def context - """D401: First line should be in imperative mood: 'Do', not 'Does'. - - [Docstring] prescribes the function or method's effect as a command: - ("Do this", "Return that"), not as a description; e.g. don't write - "Returns the pathname ...". - - """ - if docstring: - stripped = eval(docstring).strip() - if stripped: - first_word = stripped.split()[0] - if first_word.endswith('s') and not first_word.endswith('ss'): - return D401(first_word[:-1], first_word) - - @check_for(Function) - def check_no_signature(self, function, docstring): # def context - """D402: First line should not be function's or method's "signature". - - The one-line docstring should NOT be a "signature" reiterating the - function/method parameters (which can be obtained by introspection). - - """ - if docstring: - first_line = eval(docstring).strip().split('\n')[0] - if function.name + '(' in first_line.replace(' ', ''): - return D402() - - # Somewhat hard to determine if return value is mentioned. - # @check(Function) - def SKIP_check_return_type(self, function, docstring): - """D40x: Return value type should be mentioned. - - [T]he nature of the return value cannot be determined by - introspection, so it should be mentioned. - - """ - if docstring and function.returns_value: - if 'return' not in docstring.lower(): - return Error() - - -def main(): - try: - sys.exit(run_pep257()) - except KeyboardInterrupt: - pass - - -if __name__ == '__main__': - main() diff --git a/pymode/libs/pep8.py b/pymode/libs/pep8.py deleted file mode 100644 index 34ce07ae..00000000 --- a/pymode/libs/pep8.py +++ /dev/null @@ -1,2127 +0,0 @@ -#!/usr/bin/env python -# pep8.py - Check Python source code formatting, according to PEP 8 -# Copyright (C) 2006-2009 Johann C. Rocholl -# Copyright (C) 2009-2014 Florent Xicluna -# Copyright (C) 2014-2015 Ian Lee -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -r""" -Check Python source code formatting, according to PEP 8. - -For usage and a list of options, try this: -$ python pep8.py -h - -This program and its regression test suite live here: -http://github.com/jcrocholl/pep8 - -Groups of errors and warnings: -E errors -W warnings -100 indentation -200 whitespace -300 blank lines -400 imports -500 line length -600 deprecation -700 statements -900 syntax error -""" -from __future__ import with_statement - -import os -import sys -import re -import time -import inspect -import keyword -import tokenize -from optparse import OptionParser -from fnmatch import fnmatch -try: - from configparser import RawConfigParser - from io import TextIOWrapper -except ImportError: - from ConfigParser import RawConfigParser - -__version__ = '1.6.3a0' - -DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox' -DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704' -try: - if sys.platform == 'win32': - USER_CONFIG = os.path.expanduser(r'~\.pep8') - else: - USER_CONFIG = os.path.join( - os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'), - 'pep8' - ) -except ImportError: - USER_CONFIG = None - -PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8') -TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite') -MAX_LINE_LENGTH = 79 -REPORT_FORMAT = { - 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s', - 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s', -} - -PyCF_ONLY_AST = 1024 -SINGLETONS = frozenset(['False', 'None', 'True']) -KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS -UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-']) -ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-']) -WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%']) -WS_NEEDED_OPERATORS = frozenset([ - '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>', - '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=']) -WHITESPACE = frozenset(' \t') -NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE]) -SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT]) -# ERRORTOKEN is triggered by backticks in Python 3 -SKIP_COMMENTS = SKIP_TOKENS.union([tokenize.COMMENT, tokenize.ERRORTOKEN]) -BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines'] - -INDENT_REGEX = re.compile(r'([ \t]*)') -RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,') -RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$') -ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b') -DOCSTRING_REGEX = re.compile(r'u?r?["\']') -EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]') -WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') -COMPARE_SINGLETON_REGEX = re.compile(r'\b(None|False|True)?\s*([=!]=)' - r'\s*(?(1)|(None|False|True))\b') -COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+[^][)(}{ ]+\s+(in|is)\s') -COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type' - r'|\s*\(\s*([^)]*[^ )])\s*\))') -KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS)) -OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)') -LAMBDA_REGEX = re.compile(r'\blambda\b') -HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$') - -# Work around Python < 2.6 behaviour, which does not generate NL after -# a comment which is on a line by itself. -COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n' - - -############################################################################## -# Plugins (check functions) for physical lines -############################################################################## - - -def tabs_or_spaces(physical_line, indent_char): - r"""Never mix tabs and spaces. - - The most popular way of indenting Python is with spaces only. The - second-most popular way is with tabs only. Code indented with a mixture - of tabs and spaces should be converted to using spaces exclusively. When - invoking the Python command line interpreter with the -t option, it issues - warnings about code that illegally mixes tabs and spaces. When using -tt - these warnings become errors. These options are highly recommended! - - Okay: if a == 0:\n a = 1\n b = 1 - E101: if a == 0:\n a = 1\n\tb = 1 - """ - indent = INDENT_REGEX.match(physical_line).group(1) - for offset, char in enumerate(indent): - if char != indent_char: - return offset, "E101 indentation contains mixed spaces and tabs" - - -def tabs_obsolete(physical_line): - r"""For new projects, spaces-only are strongly recommended over tabs. - - Okay: if True:\n return - W191: if True:\n\treturn - """ - indent = INDENT_REGEX.match(physical_line).group(1) - if '\t' in indent: - return indent.index('\t'), "W191 indentation contains tabs" - - -def trailing_whitespace(physical_line): - r"""Trailing whitespace is superfluous. - - The warning returned varies on whether the line itself is blank, for easier - filtering for those who want to indent their blank lines. - - Okay: spam(1)\n# - W291: spam(1) \n# - W293: class Foo(object):\n \n bang = 12 - """ - physical_line = physical_line.rstrip('\n') # chr(10), newline - physical_line = physical_line.rstrip('\r') # chr(13), carriage return - physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L - stripped = physical_line.rstrip(' \t\v') - if physical_line != stripped: - if stripped: - return len(stripped), "W291 trailing whitespace" - else: - return 0, "W293 blank line contains whitespace" - - -def trailing_blank_lines(physical_line, lines, line_number, total_lines): - r"""Trailing blank lines are superfluous. - - Okay: spam(1) - W391: spam(1)\n - - However the last line should end with a new line (warning W292). - """ - if line_number == total_lines: - stripped_last_line = physical_line.rstrip() - if not stripped_last_line: - return 0, "W391 blank line at end of file" - if stripped_last_line == physical_line: - return len(physical_line), "W292 no newline at end of file" - - -def maximum_line_length(physical_line, max_line_length, multiline): - r"""Limit all lines to a maximum of 79 characters. - - There are still many devices around that are limited to 80 character - lines; plus, limiting windows to 80 characters makes it possible to have - several windows side-by-side. The default wrapping on such devices looks - ugly. Therefore, please limit all lines to a maximum of 79 characters. - For flowing long blocks of text (docstrings or comments), limiting the - length to 72 characters is recommended. - - Reports error E501. - """ - line = physical_line.rstrip() - length = len(line) - if length > max_line_length and not noqa(line): - # Special case for long URLs in multi-line docstrings or comments, - # but still report the error when the 72 first chars are whitespaces. - chunks = line.split() - if ((len(chunks) == 1 and multiline) or - (len(chunks) == 2 and chunks[0] == '#')) and \ - len(line) - len(chunks[-1]) < max_line_length - 7: - return - if hasattr(line, 'decode'): # Python 2 - # The line could contain multi-byte characters - try: - length = len(line.decode('utf-8')) - except UnicodeError: - pass - if length > max_line_length: - return (max_line_length, "E501 line too long " - "(%d > %d characters)" % (length, max_line_length)) - - -############################################################################## -# Plugins (check functions) for logical lines -############################################################################## - - -def blank_lines(logical_line, blank_lines, indent_level, line_number, - blank_before, previous_logical, previous_indent_level): - r"""Separate top-level function and class definitions with two blank lines. - - Method definitions inside a class are separated by a single blank line. - - Extra blank lines may be used (sparingly) to separate groups of related - functions. Blank lines may be omitted between a bunch of related - one-liners (e.g. a set of dummy implementations). - - Use blank lines in functions, sparingly, to indicate logical sections. - - Okay: def a():\n pass\n\n\ndef b():\n pass - Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass - - E301: class Foo:\n b = 0\n def bar():\n pass - E302: def a():\n pass\n\ndef b(n):\n pass - E303: def a():\n pass\n\n\n\ndef b(n):\n pass - E303: def a():\n\n\n\n pass - E304: @decorator\n\ndef a():\n pass - """ - if line_number < 3 and not previous_logical: - return # Don't expect blank lines before the first line - if previous_logical.startswith('@'): - if blank_lines: - yield 0, "E304 blank lines found after function decorator" - elif blank_lines > 2 or (indent_level and blank_lines == 2): - yield 0, "E303 too many blank lines (%d)" % blank_lines - elif logical_line.startswith(('def ', 'class ', '@')): - if indent_level: - if not (blank_before or previous_indent_level < indent_level or - DOCSTRING_REGEX.match(previous_logical)): - yield 0, "E301 expected 1 blank line, found 0" - elif blank_before != 2: - yield 0, "E302 expected 2 blank lines, found %d" % blank_before - - -def extraneous_whitespace(logical_line): - r"""Avoid extraneous whitespace. - - Avoid extraneous whitespace in these situations: - - Immediately inside parentheses, brackets or braces. - - Immediately before a comma, semicolon, or colon. - - Okay: spam(ham[1], {eggs: 2}) - E201: spam( ham[1], {eggs: 2}) - E201: spam(ham[ 1], {eggs: 2}) - E201: spam(ham[1], { eggs: 2}) - E202: spam(ham[1], {eggs: 2} ) - E202: spam(ham[1 ], {eggs: 2}) - E202: spam(ham[1], {eggs: 2 }) - - E203: if x == 4: print x, y; x, y = y , x - E203: if x == 4: print x, y ; x, y = y, x - E203: if x == 4 : print x, y; x, y = y, x - """ - line = logical_line - for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): - text = match.group() - char = text.strip() - found = match.start() - if text == char + ' ': - # assert char in '([{' - yield found + 1, "E201 whitespace after '%s'" % char - elif line[found - 1] != ',': - code = ('E202' if char in '}])' else 'E203') # if char in ',;:' - yield found, "%s whitespace before '%s'" % (code, char) - - -def whitespace_around_keywords(logical_line): - r"""Avoid extraneous whitespace around keywords. - - Okay: True and False - E271: True and False - E272: True and False - E273: True and\tFalse - E274: True\tand False - """ - for match in KEYWORD_REGEX.finditer(logical_line): - before, after = match.groups() - - if '\t' in before: - yield match.start(1), "E274 tab before keyword" - elif len(before) > 1: - yield match.start(1), "E272 multiple spaces before keyword" - - if '\t' in after: - yield match.start(2), "E273 tab after keyword" - elif len(after) > 1: - yield match.start(2), "E271 multiple spaces after keyword" - - -def missing_whitespace(logical_line): - r"""Each comma, semicolon or colon should be followed by whitespace. - - Okay: [a, b] - Okay: (3,) - Okay: a[1:4] - Okay: a[:4] - Okay: a[1:] - Okay: a[1:4:2] - E231: ['a','b'] - E231: foo(bar,baz) - E231: [{'a':'b'}] - """ - line = logical_line - for index in range(len(line) - 1): - char = line[index] - if char in ',;:' and line[index + 1] not in WHITESPACE: - before = line[:index] - if char == ':' and before.count('[') > before.count(']') and \ - before.rfind('{') < before.rfind('['): - continue # Slice syntax, no space required - if char == ',' and line[index + 1] == ')': - continue # Allow tuple with only one element: (3,) - yield index, "E231 missing whitespace after '%s'" % char - - -def indentation(logical_line, previous_logical, indent_char, - indent_level, previous_indent_level): - r"""Use 4 spaces per indentation level. - - For really old code that you don't want to mess up, you can continue to - use 8-space tabs. - - Okay: a = 1 - Okay: if a == 0:\n a = 1 - E111: a = 1 - E114: # a = 1 - - Okay: for item in items:\n pass - E112: for item in items:\npass - E115: for item in items:\n# Hi\n pass - - Okay: a = 1\nb = 2 - E113: a = 1\n b = 2 - E116: a = 1\n # b = 2 - """ - c = 0 if logical_line else 3 - tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)" - if indent_level % 4: - yield 0, tmpl % (1 + c, "indentation is not a multiple of four") - indent_expect = previous_logical.endswith(':') - if indent_expect and indent_level <= previous_indent_level: - yield 0, tmpl % (2 + c, "expected an indented block") - elif not indent_expect and indent_level > previous_indent_level: - yield 0, tmpl % (3 + c, "unexpected indentation") - - -def continued_indentation(logical_line, tokens, indent_level, hang_closing, - indent_char, noqa, verbose): - r"""Continuation lines indentation. - - Continuation lines should align wrapped elements either vertically - using Python's implicit line joining inside parentheses, brackets - and braces, or using a hanging indent. - - When using a hanging indent these considerations should be applied: - - there should be no arguments on the first line, and - - further indentation should be used to clearly distinguish itself as a - continuation line. - - Okay: a = (\n) - E123: a = (\n ) - - Okay: a = (\n 42) - E121: a = (\n 42) - E122: a = (\n42) - E123: a = (\n 42\n ) - E124: a = (24,\n 42\n) - E125: if (\n b):\n pass - E126: a = (\n 42) - E127: a = (24,\n 42) - E128: a = (24,\n 42) - E129: if (a or\n b):\n pass - E131: a = (\n 42\n 24) - """ - first_row = tokens[0][2][0] - nrows = 1 + tokens[-1][2][0] - first_row - if noqa or nrows == 1: - return - - # indent_next tells us whether the next block is indented; assuming - # that it is indented by 4 spaces, then we should not allow 4-space - # indents on the final continuation line; in turn, some other - # indents are allowed to have an extra 4 spaces. - indent_next = logical_line.endswith(':') - - row = depth = 0 - valid_hangs = (4,) if indent_char != '\t' else (4, 8) - # remember how many brackets were opened on each line - parens = [0] * nrows - # relative indents of physical lines - rel_indent = [0] * nrows - # for each depth, collect a list of opening rows - open_rows = [[0]] - # for each depth, memorize the hanging indentation - hangs = [None] - # visual indents - indent_chances = {} - last_indent = tokens[0][2] - visual_indent = None - last_token_multiline = False - # for each depth, memorize the visual indent column - indent = [last_indent[1]] - if verbose >= 3: - print(">>> " + tokens[0][4].rstrip()) - - for token_type, text, start, end, line in tokens: - - newline = row < start[0] - first_row - if newline: - row = start[0] - first_row - newline = not last_token_multiline and token_type not in NEWLINE - - if newline: - # this is the beginning of a continuation line. - last_indent = start - if verbose >= 3: - print("... " + line.rstrip()) - - # record the initial indent. - rel_indent[row] = expand_indent(line) - indent_level - - # identify closing bracket - close_bracket = (token_type == tokenize.OP and text in ']})') - - # is the indent relative to an opening bracket line? - for open_row in reversed(open_rows[depth]): - hang = rel_indent[row] - rel_indent[open_row] - hanging_indent = hang in valid_hangs - if hanging_indent: - break - if hangs[depth]: - hanging_indent = (hang == hangs[depth]) - # is there any chance of visual indent? - visual_indent = (not close_bracket and hang > 0 and - indent_chances.get(start[1])) - - if close_bracket and indent[depth]: - # closing bracket for visual indent - if start[1] != indent[depth]: - yield (start, "E124 closing bracket does not match " - "visual indentation") - elif close_bracket and not hang: - # closing bracket matches indentation of opening bracket's line - if hang_closing: - yield start, "E133 closing bracket is missing indentation" - elif indent[depth] and start[1] < indent[depth]: - if visual_indent is not True: - # visual indent is broken - yield (start, "E128 continuation line " - "under-indented for visual indent") - elif hanging_indent or (indent_next and rel_indent[row] == 8): - # hanging indent is verified - if close_bracket and not hang_closing: - yield (start, "E123 closing bracket does not match " - "indentation of opening bracket's line") - hangs[depth] = hang - elif visual_indent is True: - # visual indent is verified - indent[depth] = start[1] - elif visual_indent in (text, str): - # ignore token lined up with matching one from a previous line - pass - else: - # indent is broken - if hang <= 0: - error = "E122", "missing indentation or outdented" - elif indent[depth]: - error = "E127", "over-indented for visual indent" - elif not close_bracket and hangs[depth]: - error = "E131", "unaligned for hanging indent" - else: - hangs[depth] = hang - if hang > 4: - error = "E126", "over-indented for hanging indent" - else: - error = "E121", "under-indented for hanging indent" - yield start, "%s continuation line %s" % error - - # look for visual indenting - if (parens[row] and - token_type not in (tokenize.NL, tokenize.COMMENT) and - not indent[depth]): - indent[depth] = start[1] - indent_chances[start[1]] = True - if verbose >= 4: - print("bracket depth %s indent to %s" % (depth, start[1])) - # deal with implicit string concatenation - elif (token_type in (tokenize.STRING, tokenize.COMMENT) or - text in ('u', 'ur', 'b', 'br')): - indent_chances[start[1]] = str - # special case for the "if" statement because len("if (") == 4 - elif not indent_chances and not row and not depth and text == 'if': - indent_chances[end[1] + 1] = True - elif text == ':' and line[end[1]:].isspace(): - open_rows[depth].append(row) - - # keep track of bracket depth - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - hangs.append(None) - if len(open_rows) == depth: - open_rows.append([]) - open_rows[depth].append(row) - parens[row] += 1 - if verbose >= 4: - print("bracket depth %s seen, col %s, visual min = %s" % - (depth, start[1], indent[depth])) - elif text in ')]}' and depth > 0: - # parent indents should not be more than this one - prev_indent = indent.pop() or last_indent[1] - hangs.pop() - for d in range(depth): - if indent[d] > prev_indent: - indent[d] = 0 - for ind in list(indent_chances): - if ind >= prev_indent: - del indent_chances[ind] - del open_rows[depth + 1:] - depth -= 1 - if depth: - indent_chances[indent[depth]] = True - for idx in range(row, -1, -1): - if parens[idx]: - parens[idx] -= 1 - break - assert len(indent) == depth + 1 - if start[1] not in indent_chances: - # allow to line up tokens - indent_chances[start[1]] = text - - last_token_multiline = (start[0] != end[0]) - if last_token_multiline: - rel_indent[end[0] - first_row] = rel_indent[row] - - if indent_next and expand_indent(line) == indent_level + 4: - pos = (start[0], indent[0] + 4) - if visual_indent: - code = "E129 visually indented line" - else: - code = "E125 continuation line" - yield pos, "%s with same indent as next logical line" % code - - -def whitespace_before_parameters(logical_line, tokens): - r"""Avoid extraneous whitespace. - - Avoid extraneous whitespace in the following situations: - - before the open parenthesis that starts the argument list of a - function call. - - before the open parenthesis that starts an indexing or slicing. - - Okay: spam(1) - E211: spam (1) - - Okay: dict['key'] = list[index] - E211: dict ['key'] = list[index] - E211: dict['key'] = list [index] - """ - prev_type, prev_text, __, prev_end, __ = tokens[0] - for index in range(1, len(tokens)): - token_type, text, start, end, __ = tokens[index] - if (token_type == tokenize.OP and - text in '([' and - start != prev_end and - (prev_type == tokenize.NAME or prev_text in '}])') and - # Syntax "class A (B):" is allowed, but avoid it - (index < 2 or tokens[index - 2][1] != 'class') and - # Allow "return (a.foo for a in range(5))" - not keyword.iskeyword(prev_text)): - yield prev_end, "E211 whitespace before '%s'" % text - prev_type = token_type - prev_text = text - prev_end = end - - -def whitespace_around_operator(logical_line): - r"""Avoid extraneous whitespace around an operator. - - Okay: a = 12 + 3 - E221: a = 4 + 5 - E222: a = 4 + 5 - E223: a = 4\t+ 5 - E224: a = 4 +\t5 - """ - for match in OPERATOR_REGEX.finditer(logical_line): - before, after = match.groups() - - if '\t' in before: - yield match.start(1), "E223 tab before operator" - elif len(before) > 1: - yield match.start(1), "E221 multiple spaces before operator" - - if '\t' in after: - yield match.start(2), "E224 tab after operator" - elif len(after) > 1: - yield match.start(2), "E222 multiple spaces after operator" - - -def missing_whitespace_around_operator(logical_line, tokens): - r"""Surround operators with a single space on either side. - - - Always surround these binary operators with a single space on - either side: assignment (=), augmented assignment (+=, -= etc.), - comparisons (==, <, >, !=, <=, >=, in, not in, is, is not), - Booleans (and, or, not). - - - If operators with different priorities are used, consider adding - whitespace around the operators with the lowest priorities. - - Okay: i = i + 1 - Okay: submitted += 1 - Okay: x = x * 2 - 1 - Okay: hypot2 = x * x + y * y - Okay: c = (a + b) * (a - b) - Okay: foo(bar, key='word', *args, **kwargs) - Okay: alpha[:-i] - - E225: i=i+1 - E225: submitted +=1 - E225: x = x /2 - 1 - E225: z = x **y - E226: c = (a+b) * (a-b) - E226: hypot2 = x*x + y*y - E227: c = a|b - E228: msg = fmt%(errno, errmsg) - """ - parens = 0 - need_space = False - prev_type = tokenize.OP - prev_text = prev_end = None - for token_type, text, start, end, line in tokens: - if token_type in SKIP_COMMENTS: - continue - if text in ('(', 'lambda'): - parens += 1 - elif text == ')': - parens -= 1 - if need_space: - if start != prev_end: - # Found a (probably) needed space - if need_space is not True and not need_space[1]: - yield (need_space[0], - "E225 missing whitespace around operator") - need_space = False - elif text == '>' and prev_text in ('<', '-'): - # Tolerate the "<>" operator, even if running Python 3 - # Deal with Python 3's annotated return value "->" - pass - else: - if need_space is True or need_space[1]: - # A needed trailing space was not found - yield prev_end, "E225 missing whitespace around operator" - elif prev_text != '**': - code, optype = 'E226', 'arithmetic' - if prev_text == '%': - code, optype = 'E228', 'modulo' - elif prev_text not in ARITHMETIC_OP: - code, optype = 'E227', 'bitwise or shift' - yield (need_space[0], "%s missing whitespace " - "around %s operator" % (code, optype)) - need_space = False - elif token_type == tokenize.OP and prev_end is not None: - if text == '=' and parens: - # Allow keyword args or defaults: foo(bar=None). - pass - elif text in WS_NEEDED_OPERATORS: - need_space = True - elif text in UNARY_OPERATORS: - # Check if the operator is being used as a binary operator - # Allow unary operators: -123, -x, +1. - # Allow argument unpacking: foo(*args, **kwargs). - if (prev_text in '}])' if prev_type == tokenize.OP - else prev_text not in KEYWORDS): - need_space = None - elif text in WS_OPTIONAL_OPERATORS: - need_space = None - - if need_space is None: - # Surrounding space is optional, but ensure that - # trailing space matches opening space - need_space = (prev_end, start != prev_end) - elif need_space and start == prev_end: - # A needed opening space was not found - yield prev_end, "E225 missing whitespace around operator" - need_space = False - prev_type = token_type - prev_text = text - prev_end = end - - -def whitespace_around_comma(logical_line): - r"""Avoid extraneous whitespace after a comma or a colon. - - Note: these checks are disabled by default - - Okay: a = (1, 2) - E241: a = (1, 2) - E242: a = (1,\t2) - """ - line = logical_line - for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line): - found = m.start() + 1 - if '\t' in m.group(): - yield found, "E242 tab after '%s'" % m.group()[0] - else: - yield found, "E241 multiple spaces after '%s'" % m.group()[0] - - -def whitespace_around_named_parameter_equals(logical_line, tokens): - r"""Don't use spaces around the '=' sign in function arguments. - - Don't use spaces around the '=' sign when used to indicate a - keyword argument or a default parameter value. - - Okay: def complex(real, imag=0.0): - Okay: return magic(r=real, i=imag) - Okay: boolean(a == b) - Okay: boolean(a != b) - Okay: boolean(a <= b) - Okay: boolean(a >= b) - Okay: def foo(arg: int = 42): - - E251: def complex(real, imag = 0.0): - E251: return magic(r = real, i = imag) - """ - parens = 0 - no_space = False - prev_end = None - annotated_func_arg = False - in_def = logical_line.startswith('def') - message = "E251 unexpected spaces around keyword / parameter equals" - for token_type, text, start, end, line in tokens: - if token_type == tokenize.NL: - continue - if no_space: - no_space = False - if start != prev_end: - yield (prev_end, message) - if token_type == tokenize.OP: - if text == '(': - parens += 1 - elif text == ')': - parens -= 1 - elif in_def and text == ':' and parens == 1: - annotated_func_arg = True - elif parens and text == ',' and parens == 1: - annotated_func_arg = False - elif parens and text == '=' and not annotated_func_arg: - no_space = True - if start != prev_end: - yield (prev_end, message) - if not parens: - annotated_func_arg = False - - prev_end = end - - -def whitespace_before_comment(logical_line, tokens): - r"""Separate inline comments by at least two spaces. - - An inline comment is a comment on the same line as a statement. Inline - comments should be separated by at least two spaces from the statement. - They should start with a # and a single space. - - Each line of a block comment starts with a # and a single space - (unless it is indented text inside the comment). - - Okay: x = x + 1 # Increment x - Okay: x = x + 1 # Increment x - Okay: # Block comment - E261: x = x + 1 # Increment x - E262: x = x + 1 #Increment x - E262: x = x + 1 # Increment x - E265: #Block comment - E266: ### Block comment - """ - prev_end = (0, 0) - for token_type, text, start, end, line in tokens: - if token_type == tokenize.COMMENT: - inline_comment = line[:start[1]].strip() - if inline_comment: - if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: - yield (prev_end, - "E261 at least two spaces before inline comment") - symbol, sp, comment = text.partition(' ') - bad_prefix = symbol not in '#:' and (symbol.lstrip('#')[:1] or '#') - if inline_comment: - if bad_prefix or comment[:1] in WHITESPACE: - yield start, "E262 inline comment should start with '# '" - elif bad_prefix and (bad_prefix != '!' or start[0] > 1): - if bad_prefix != '#': - yield start, "E265 block comment should start with '# '" - elif comment: - yield start, "E266 too many leading '#' for block comment" - elif token_type != tokenize.NL: - prev_end = end - - -def imports_on_separate_lines(logical_line): - r"""Imports should usually be on separate lines. - - Okay: import os\nimport sys - E401: import sys, os - - Okay: from subprocess import Popen, PIPE - Okay: from myclas import MyClass - Okay: from foo.bar.yourclass import YourClass - Okay: import myclass - Okay: import foo.bar.yourclass - """ - line = logical_line - if line.startswith('import '): - found = line.find(',') - if -1 < found and ';' not in line[:found]: - yield found, "E401 multiple imports on one line" - - -def module_imports_on_top_of_file( - logical_line, indent_level, checker_state, noqa): - r"""Imports are always put at the top of the file, just after any module - comments and docstrings, and before module globals and constants. - - Okay: import os - Okay: # this is a comment\nimport os - Okay: '''this is a module docstring'''\nimport os - Okay: r'''this is a module docstring'''\nimport os - Okay: try:\n import x\nexcept:\n pass\nelse:\n pass\nimport y - Okay: try:\n import x\nexcept:\n pass\nfinally:\n pass\nimport y - E402: a=1\nimport os - E402: 'One string'\n"Two string"\nimport os - E402: a=1\nfrom sys import x - - Okay: if x:\n import os - """ - def is_string_literal(line): - if line[0] in 'uUbB': - line = line[1:] - if line and line[0] in 'rR': - line = line[1:] - return line and (line[0] == '"' or line[0] == "'") - - allowed_try_keywords = ('try', 'except', 'else', 'finally') - - if indent_level: # Allow imports in conditional statements or functions - return - if not logical_line: # Allow empty lines or comments - return - if noqa: - return - line = logical_line - if line.startswith('import ') or line.startswith('from '): - if checker_state.get('seen_non_imports', False): - yield 0, "E402 module level import not at top of file" - elif any(line.startswith(kw) for kw in allowed_try_keywords): - # Allow try, except, else, finally keywords intermixed with imports in - # order to support conditional importing - return - elif is_string_literal(line): - # The first literal is a docstring, allow it. Otherwise, report error. - if checker_state.get('seen_docstring', False): - checker_state['seen_non_imports'] = True - else: - checker_state['seen_docstring'] = True - else: - checker_state['seen_non_imports'] = True - - -def compound_statements(logical_line): - r"""Compound statements (on the same line) are generally discouraged. - - While sometimes it's okay to put an if/for/while with a small body - on the same line, never do this for multi-clause statements. - Also avoid folding such long lines! - - Always use a def statement instead of an assignment statement that - binds a lambda expression directly to a name. - - Okay: if foo == 'blah':\n do_blah_thing() - Okay: do_one() - Okay: do_two() - Okay: do_three() - - E701: if foo == 'blah': do_blah_thing() - E701: for x in lst: total += x - E701: while t < 10: t = delay() - E701: if foo == 'blah': do_blah_thing() - E701: else: do_non_blah_thing() - E701: try: something() - E701: finally: cleanup() - E701: if foo == 'blah': one(); two(); three() - E702: do_one(); do_two(); do_three() - E703: do_four(); # useless semicolon - E704: def f(x): return 2*x - E731: f = lambda x: 2*x - """ - line = logical_line - last_char = len(line) - 1 - found = line.find(':') - while -1 < found < last_char: - before = line[:found] - if ((before.count('{') <= before.count('}') and # {'a': 1} (dict) - before.count('[') <= before.count(']') and # [1:2] (slice) - before.count('(') <= before.count(')'))): # (annotation) - lambda_kw = LAMBDA_REGEX.search(before) - if lambda_kw: - before = line[:lambda_kw.start()].rstrip() - if before[-1:] == '=' and isidentifier(before[:-1].strip()): - yield 0, ("E731 do not assign a lambda expression, use a " - "def") - break - if before.startswith('def '): - yield 0, "E704 multiple statements on one line (def)" - else: - yield found, "E701 multiple statements on one line (colon)" - found = line.find(':', found + 1) - found = line.find(';') - while -1 < found: - if found < last_char: - yield found, "E702 multiple statements on one line (semicolon)" - else: - yield found, "E703 statement ends with a semicolon" - found = line.find(';', found + 1) - - -def explicit_line_join(logical_line, tokens): - r"""Avoid explicit line join between brackets. - - The preferred way of wrapping long lines is by using Python's implied line - continuation inside parentheses, brackets and braces. Long lines can be - broken over multiple lines by wrapping expressions in parentheses. These - should be used in preference to using a backslash for line continuation. - - E502: aaa = [123, \\n 123] - E502: aaa = ("bbb " \\n "ccc") - - Okay: aaa = [123,\n 123] - Okay: aaa = ("bbb "\n "ccc") - Okay: aaa = "bbb " \\n "ccc" - Okay: aaa = 123 # \\ - """ - prev_start = prev_end = parens = 0 - comment = False - backslash = None - for token_type, text, start, end, line in tokens: - if token_type == tokenize.COMMENT: - comment = True - if start[0] != prev_start and parens and backslash and not comment: - yield backslash, "E502 the backslash is redundant between brackets" - if end[0] != prev_end: - if line.rstrip('\r\n').endswith('\\'): - backslash = (end[0], len(line.splitlines()[-1]) - 1) - else: - backslash = None - prev_start = prev_end = end[0] - else: - prev_start = start[0] - if token_type == tokenize.OP: - if text in '([{': - parens += 1 - elif text in ')]}': - parens -= 1 - - -def break_around_binary_operator(logical_line, tokens): - r""" - Avoid breaks before binary operators. - - The preferred place to break around a binary operator is after the - operator, not before it. - - W503: (width == 0\n + height == 0) - W503: (width == 0\n and height == 0) - - Okay: (width == 0 +\n height == 0) - Okay: foo(\n -x) - Okay: foo(x\n []) - Okay: x = '''\n''' + '' - Okay: foo(x,\n -y) - Okay: foo(x, # comment\n -y) - """ - def is_binary_operator(token_type, text): - # The % character is strictly speaking a binary operator, but the - # common usage seems to be to put it next to the format parameters, - # after a line break. - return ((token_type == tokenize.OP or text in ['and', 'or']) and - text not in "()[]{},:.;@=%") - - line_break = False - unary_context = True - for token_type, text, start, end, line in tokens: - if token_type == tokenize.COMMENT: - continue - if ('\n' in text or '\r' in text) and token_type != tokenize.STRING: - line_break = True - else: - if (is_binary_operator(token_type, text) and line_break and - not unary_context): - yield start, "W503 line break before binary operator" - unary_context = text in '([{,;' - line_break = False - - -def comparison_to_singleton(logical_line, noqa): - r"""Comparison to singletons should use "is" or "is not". - - Comparisons to singletons like None should always be done - with "is" or "is not", never the equality operators. - - Okay: if arg is not None: - E711: if arg != None: - E711: if None == arg: - E712: if arg == True: - E712: if False == arg: - - Also, beware of writing if x when you really mean if x is not None -- - e.g. when testing whether a variable or argument that defaults to None was - set to some other value. The other value might have a type (such as a - container) that could be false in a boolean context! - """ - match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line) - if match: - singleton = match.group(1) or match.group(3) - same = (match.group(2) == '==') - - msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton) - if singleton in ('None',): - code = 'E711' - else: - code = 'E712' - nonzero = ((singleton == 'True' and same) or - (singleton == 'False' and not same)) - msg += " or 'if %scond:'" % ('' if nonzero else 'not ') - yield match.start(2), ("%s comparison to %s should be %s" % - (code, singleton, msg)) - - -def comparison_negative(logical_line): - r"""Negative comparison should be done using "not in" and "is not". - - Okay: if x not in y:\n pass - Okay: assert (X in Y or X is Z) - Okay: if not (X in Y):\n pass - Okay: zz = x is not y - E713: Z = not X in Y - E713: if not X.B in Y:\n pass - E714: if not X is Y:\n pass - E714: Z = not X.B is Y - """ - match = COMPARE_NEGATIVE_REGEX.search(logical_line) - if match: - pos = match.start(1) - if match.group(2) == 'in': - yield pos, "E713 test for membership should be 'not in'" - else: - yield pos, "E714 test for object identity should be 'is not'" - - -def comparison_type(logical_line, noqa): - r"""Object type comparisons should always use isinstance(). - - Do not compare types directly. - - Okay: if isinstance(obj, int): - E721: if type(obj) is type(1): - - When checking if an object is a string, keep in mind that it might be a - unicode string too! In Python 2.3, str and unicode have a common base - class, basestring, so you can do: - - Okay: if isinstance(obj, basestring): - Okay: if type(a1) is type(b1): - """ - match = COMPARE_TYPE_REGEX.search(logical_line) - if match and not noqa: - inst = match.group(1) - if inst and isidentifier(inst) and inst not in SINGLETONS: - return # Allow comparison for types which are not obvious - yield match.start(), "E721 do not compare types, use 'isinstance()'" - - -def python_3000_has_key(logical_line, noqa): - r"""The {}.has_key() method is removed in Python 3: use the 'in' operator. - - Okay: if "alph" in d:\n print d["alph"] - W601: assert d.has_key('alph') - """ - pos = logical_line.find('.has_key(') - if pos > -1 and not noqa: - yield pos, "W601 .has_key() is deprecated, use 'in'" - - -def python_3000_raise_comma(logical_line): - r"""When raising an exception, use "raise ValueError('message')". - - The older form is removed in Python 3. - - Okay: raise DummyError("Message") - W602: raise DummyError, "Message" - """ - match = RAISE_COMMA_REGEX.match(logical_line) - if match and not RERAISE_COMMA_REGEX.match(logical_line): - yield match.end() - 1, "W602 deprecated form of raising exception" - - -def python_3000_not_equal(logical_line): - r"""New code should always use != instead of <>. - - The older syntax is removed in Python 3. - - Okay: if a != 'no': - W603: if a <> 'no': - """ - pos = logical_line.find('<>') - if pos > -1: - yield pos, "W603 '<>' is deprecated, use '!='" - - -def python_3000_backticks(logical_line): - r"""Backticks are removed in Python 3: use repr() instead. - - Okay: val = repr(1 + 2) - W604: val = `1 + 2` - """ - pos = logical_line.find('`') - if pos > -1: - yield pos, "W604 backticks are deprecated, use 'repr()'" - - -############################################################################## -# Helper functions -############################################################################## - - -if '' == ''.encode(): - # Python 2: implicit encoding. - def readlines(filename): - """Read the source code.""" - with open(filename, 'rU') as f: - return f.readlines() - isidentifier = re.compile(r'[a-zA-Z_]\w*$').match - stdin_get_value = sys.stdin.read -else: - # Python 3 - def readlines(filename): - """Read the source code.""" - try: - with open(filename, 'rb') as f: - (coding, lines) = tokenize.detect_encoding(f.readline) - f = TextIOWrapper(f, coding, line_buffering=True) - return [l.decode(coding) for l in lines] + f.readlines() - except (LookupError, SyntaxError, UnicodeError): - # Fall back if file encoding is improperly declared - with open(filename, encoding='latin-1') as f: - return f.readlines() - isidentifier = str.isidentifier - - def stdin_get_value(): - return TextIOWrapper(sys.stdin.buffer, errors='ignore').read() -noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search - - -def expand_indent(line): - r"""Return the amount of indentation. - - Tabs are expanded to the next multiple of 8. - - >>> expand_indent(' ') - 4 - >>> expand_indent('\t') - 8 - >>> expand_indent(' \t') - 8 - >>> expand_indent(' \t') - 16 - """ - if '\t' not in line: - return len(line) - len(line.lstrip()) - result = 0 - for char in line: - if char == '\t': - result = result // 8 * 8 + 8 - elif char == ' ': - result += 1 - else: - break - return result - - -def mute_string(text): - """Replace contents with 'xxx' to prevent syntax matching. - - >>> mute_string('"abc"') - '"xxx"' - >>> mute_string("'''abc'''") - "'''xxx'''" - >>> mute_string("r'abc'") - "r'xxx'" - """ - # String modifiers (e.g. u or r) - start = text.index(text[-1]) + 1 - end = len(text) - 1 - # Triple quotes - if text[-3:] in ('"""', "'''"): - start += 2 - end -= 2 - return text[:start] + 'x' * (end - start) + text[end:] - - -def parse_udiff(diff, patterns=None, parent='.'): - """Return a dictionary of matching lines.""" - # For each file of the diff, the entry key is the filename, - # and the value is a set of row numbers to consider. - rv = {} - path = nrows = None - for line in diff.splitlines(): - if nrows: - if line[:1] != '-': - nrows -= 1 - continue - if line[:3] == '@@ ': - hunk_match = HUNK_REGEX.match(line) - (row, nrows) = [int(g or '1') for g in hunk_match.groups()] - rv[path].update(range(row, row + nrows)) - elif line[:3] == '+++': - path = line[4:].split('\t', 1)[0] - if path[:2] == 'b/': - path = path[2:] - rv[path] = set() - return dict([(os.path.join(parent, path), rows) - for (path, rows) in rv.items() - if rows and filename_match(path, patterns)]) - - -def normalize_paths(value, parent=os.curdir): - """Parse a comma-separated list of paths. - - Return a list of absolute paths. - """ - if not value: - return [] - if isinstance(value, list): - return value - paths = [] - for path in value.split(','): - path = path.strip() - if '/' in path: - path = os.path.abspath(os.path.join(parent, path)) - paths.append(path.rstrip('/')) - return paths - - -def filename_match(filename, patterns, default=True): - """Check if patterns contains a pattern that matches filename. - - If patterns is unspecified, this always returns True. - """ - if not patterns: - return default - return any(fnmatch(filename, pattern) for pattern in patterns) - - -def _is_eol_token(token): - return token[0] in NEWLINE or token[4][token[3][1]:].lstrip() == '\\\n' -if COMMENT_WITH_NL: - def _is_eol_token(token, _eol_token=_is_eol_token): - return _eol_token(token) or (token[0] == tokenize.COMMENT and - token[1] == token[4]) - -############################################################################## -# Framework to run all checks -############################################################################## - - -_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} - - -def _get_parameters(function): - if sys.version_info >= (3, 3): - return list(inspect.signature(function).parameters) - else: - return inspect.getargspec(function)[0] - - -def register_check(check, codes=None): - """Register a new check object.""" - def _add_check(check, kind, codes, args): - if check in _checks[kind]: - _checks[kind][check][0].extend(codes or []) - else: - _checks[kind][check] = (codes or [''], args) - if inspect.isfunction(check): - args = _get_parameters(check) - if args and args[0] in ('physical_line', 'logical_line'): - if codes is None: - codes = ERRORCODE_REGEX.findall(check.__doc__ or '') - _add_check(check, args[0], codes, args) - elif inspect.isclass(check): - if _get_parameters(check.__init__)[:2] == ['self', 'tree']: - _add_check(check, 'tree', codes, None) - - -def init_checks_registry(): - """Register all globally visible functions. - - The first argument name is either 'physical_line' or 'logical_line'. - """ - mod = inspect.getmodule(register_check) - for (name, function) in inspect.getmembers(mod, inspect.isfunction): - register_check(function) -init_checks_registry() - - -class Checker(object): - """Load a Python source file, tokenize it, check coding style.""" - - def __init__(self, filename=None, lines=None, - options=None, report=None, **kwargs): - if options is None: - options = StyleGuide(kwargs).options - else: - assert not kwargs - self._io_error = None - self._physical_checks = options.physical_checks - self._logical_checks = options.logical_checks - self._ast_checks = options.ast_checks - self.max_line_length = options.max_line_length - self.multiline = False # in a multiline string? - self.hang_closing = options.hang_closing - self.verbose = options.verbose - self.filename = filename - # Dictionary where a checker can store its custom state. - self._checker_states = {} - if filename is None: - self.filename = 'stdin' - self.lines = lines or [] - elif filename == '-': - self.filename = 'stdin' - self.lines = stdin_get_value().splitlines(True) - elif lines is None: - try: - self.lines = readlines(filename) - except IOError: - (exc_type, exc) = sys.exc_info()[:2] - self._io_error = '%s: %s' % (exc_type.__name__, exc) - self.lines = [] - else: - self.lines = lines - if self.lines: - ord0 = ord(self.lines[0][0]) - if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM - if ord0 == 0xfeff: - self.lines[0] = self.lines[0][1:] - elif self.lines[0][:3] == '\xef\xbb\xbf': - self.lines[0] = self.lines[0][3:] - self.report = report or options.report - self.report_error = self.report.error - - def report_invalid_syntax(self): - """Check if the syntax is valid.""" - (exc_type, exc) = sys.exc_info()[:2] - if len(exc.args) > 1: - offset = exc.args[1] - if len(offset) > 2: - offset = offset[1:3] - else: - offset = (1, 0) - self.report_error(offset[0], offset[1] or 0, - 'E901 %s: %s' % (exc_type.__name__, exc.args[0]), - self.report_invalid_syntax) - - def readline(self): - """Get the next line from the input buffer.""" - if self.line_number >= self.total_lines: - return '' - line = self.lines[self.line_number] - self.line_number += 1 - if self.indent_char is None and line[:1] in WHITESPACE: - self.indent_char = line[0] - return line - - def run_check(self, check, argument_names): - """Run a check plugin.""" - arguments = [] - for name in argument_names: - arguments.append(getattr(self, name)) - return check(*arguments) - - def init_checker_state(self, name, argument_names): - """ Prepares a custom state for the specific checker plugin.""" - if 'checker_state' in argument_names: - self.checker_state = self._checker_states.setdefault(name, {}) - - def check_physical(self, line): - """Run all physical checks on a raw input line.""" - self.physical_line = line - for name, check, argument_names in self._physical_checks: - self.init_checker_state(name, argument_names) - result = self.run_check(check, argument_names) - if result is not None: - (offset, text) = result - self.report_error(self.line_number, offset, text, check) - if text[:4] == 'E101': - self.indent_char = line[0] - - def build_tokens_line(self): - """Build a logical line from tokens.""" - logical = [] - comments = [] - length = 0 - prev_row = prev_col = mapping = None - for token_type, text, start, end, line in self.tokens: - if token_type in SKIP_TOKENS: - continue - if not mapping: - mapping = [(0, start)] - if token_type == tokenize.COMMENT: - comments.append(text) - continue - if token_type == tokenize.STRING: - text = mute_string(text) - if prev_row: - (start_row, start_col) = start - if prev_row != start_row: # different row - prev_text = self.lines[prev_row - 1][prev_col - 1] - if prev_text == ',' or (prev_text not in '{[(' and - text not in '}])'): - text = ' ' + text - elif prev_col != start_col: # different column - text = line[prev_col:start_col] + text - logical.append(text) - length += len(text) - mapping.append((length, end)) - (prev_row, prev_col) = end - self.logical_line = ''.join(logical) - self.noqa = comments and noqa(''.join(comments)) - return mapping - - def check_logical(self): - """Build a line from tokens and run all logical checks on it.""" - self.report.increment_logical_line() - mapping = self.build_tokens_line() - - if not mapping: - return - - (start_row, start_col) = mapping[0][1] - start_line = self.lines[start_row - 1] - self.indent_level = expand_indent(start_line[:start_col]) - if self.blank_before < self.blank_lines: - self.blank_before = self.blank_lines - if self.verbose >= 2: - print(self.logical_line[:80].rstrip()) - for name, check, argument_names in self._logical_checks: - if self.verbose >= 4: - print(' ' + name) - self.init_checker_state(name, argument_names) - for offset, text in self.run_check(check, argument_names) or (): - if not isinstance(offset, tuple): - for token_offset, pos in mapping: - if offset <= token_offset: - break - offset = (pos[0], pos[1] + offset - token_offset) - self.report_error(offset[0], offset[1], text, check) - if self.logical_line: - self.previous_indent_level = self.indent_level - self.previous_logical = self.logical_line - self.blank_lines = 0 - self.tokens = [] - - def check_ast(self): - """Build the file's AST and run all AST checks.""" - try: - tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST) - except (ValueError, SyntaxError, TypeError): - return self.report_invalid_syntax() - for name, cls, __ in self._ast_checks: - checker = cls(tree, self.filename) - for lineno, offset, text, check in checker.run(): - if not self.lines or not noqa(self.lines[lineno - 1]): - self.report_error(lineno, offset, text, check) - - def generate_tokens(self): - """Tokenize the file, run physical line checks and yield tokens.""" - if self._io_error: - self.report_error(1, 0, 'E902 %s' % self._io_error, readlines) - tokengen = tokenize.generate_tokens(self.readline) - try: - for token in tokengen: - if token[2][0] > self.total_lines: - return - self.maybe_check_physical(token) - yield token - except (SyntaxError, tokenize.TokenError): - self.report_invalid_syntax() - - def maybe_check_physical(self, token): - """If appropriate (based on token), check current physical line(s).""" - # Called after every token, but act only on end of line. - if _is_eol_token(token): - # Obviously, a newline token ends a single physical line. - self.check_physical(token[4]) - elif token[0] == tokenize.STRING and '\n' in token[1]: - # Less obviously, a string that contains newlines is a - # multiline string, either triple-quoted or with internal - # newlines backslash-escaped. Check every physical line in the - # string *except* for the last one: its newline is outside of - # the multiline string, so we consider it a regular physical - # line, and will check it like any other physical line. - # - # Subtleties: - # - we don't *completely* ignore the last line; if it contains - # the magical "# noqa" comment, we disable all physical - # checks for the entire multiline string - # - have to wind self.line_number back because initially it - # points to the last line of the string, and we want - # check_physical() to give accurate feedback - if noqa(token[4]): - return - self.multiline = True - self.line_number = token[2][0] - for line in token[1].split('\n')[:-1]: - self.check_physical(line + '\n') - self.line_number += 1 - self.multiline = False - - def check_all(self, expected=None, line_offset=0): - """Run all checks on the input file.""" - self.report.init_file(self.filename, self.lines, expected, line_offset) - self.total_lines = len(self.lines) - if self._ast_checks: - self.check_ast() - self.line_number = 0 - self.indent_char = None - self.indent_level = self.previous_indent_level = 0 - self.previous_logical = '' - self.tokens = [] - self.blank_lines = self.blank_before = 0 - parens = 0 - for token in self.generate_tokens(): - self.tokens.append(token) - token_type, text = token[0:2] - if self.verbose >= 3: - if token[2][0] == token[3][0]: - pos = '[%s:%s]' % (token[2][1] or '', token[3][1]) - else: - pos = 'l.%s' % token[3][0] - print('l.%s\t%s\t%s\t%r' % - (token[2][0], pos, tokenize.tok_name[token[0]], text)) - if token_type == tokenize.OP: - if text in '([{': - parens += 1 - elif text in '}])': - parens -= 1 - elif not parens: - if token_type in NEWLINE: - if token_type == tokenize.NEWLINE: - self.check_logical() - self.blank_before = 0 - elif len(self.tokens) == 1: - # The physical line contains only this token. - self.blank_lines += 1 - del self.tokens[0] - else: - self.check_logical() - elif COMMENT_WITH_NL and token_type == tokenize.COMMENT: - if len(self.tokens) == 1: - # The comment also ends a physical line - token = list(token) - token[1] = text.rstrip('\r\n') - token[3] = (token[2][0], token[2][1] + len(token[1])) - self.tokens = [tuple(token)] - self.check_logical() - if self.tokens: - self.check_physical(self.lines[-1]) - self.check_logical() - return self.report.get_file_results() - - -class BaseReport(object): - """Collect the results of the checks.""" - - print_filename = False - - def __init__(self, options): - self._benchmark_keys = options.benchmark_keys - self._ignore_code = options.ignore_code - # Results - self.elapsed = 0 - self.total_errors = 0 - self.counters = dict.fromkeys(self._benchmark_keys, 0) - self.messages = {} - - def start(self): - """Start the timer.""" - self._start_time = time.time() - - def stop(self): - """Stop the timer.""" - self.elapsed = time.time() - self._start_time - - def init_file(self, filename, lines, expected, line_offset): - """Signal a new file.""" - self.filename = filename - self.lines = lines - self.expected = expected or () - self.line_offset = line_offset - self.file_errors = 0 - self.counters['files'] += 1 - self.counters['physical lines'] += len(lines) - - def increment_logical_line(self): - """Signal a new logical line.""" - self.counters['logical lines'] += 1 - - def error(self, line_number, offset, text, check): - """Report an error, according to options.""" - code = text[:4] - if self._ignore_code(code): - return - if code in self.counters: - self.counters[code] += 1 - else: - self.counters[code] = 1 - self.messages[code] = text[5:] - # Don't care about expected errors or warnings - if code in self.expected: - return - if self.print_filename and not self.file_errors: - print(self.filename) - self.file_errors += 1 - self.total_errors += 1 - return code - - def get_file_results(self): - """Return the count of errors and warnings for this file.""" - return self.file_errors - - def get_count(self, prefix=''): - """Return the total count of errors and warnings.""" - return sum([self.counters[key] - for key in self.messages if key.startswith(prefix)]) - - def get_statistics(self, prefix=''): - """Get statistics for message codes that start with the prefix. - - prefix='' matches all errors and warnings - prefix='E' matches all errors - prefix='W' matches all warnings - prefix='E4' matches all errors that have to do with imports - """ - return ['%-7s %s %s' % (self.counters[key], key, self.messages[key]) - for key in sorted(self.messages) if key.startswith(prefix)] - - def print_statistics(self, prefix=''): - """Print overall statistics (number of errors and warnings).""" - for line in self.get_statistics(prefix): - print(line) - - def print_benchmark(self): - """Print benchmark numbers.""" - print('%-7.2f %s' % (self.elapsed, 'seconds elapsed')) - if self.elapsed: - for key in self._benchmark_keys: - print('%-7d %s per second (%d total)' % - (self.counters[key] / self.elapsed, key, - self.counters[key])) - - -class FileReport(BaseReport): - """Collect the results of the checks and print only the filenames.""" - print_filename = True - - -class StandardReport(BaseReport): - """Collect and print the results of the checks.""" - - def __init__(self, options): - super(StandardReport, self).__init__(options) - self._fmt = REPORT_FORMAT.get(options.format.lower(), - options.format) - self._repeat = options.repeat - self._show_source = options.show_source - self._show_pep8 = options.show_pep8 - - def init_file(self, filename, lines, expected, line_offset): - """Signal a new file.""" - self._deferred_print = [] - return super(StandardReport, self).init_file( - filename, lines, expected, line_offset) - - def error(self, line_number, offset, text, check): - """Report an error, according to options.""" - code = super(StandardReport, self).error(line_number, offset, - text, check) - if code and (self.counters[code] == 1 or self._repeat): - self._deferred_print.append( - (line_number, offset, code, text[5:], check.__doc__)) - return code - - def get_file_results(self): - """Print the result and return the overall count for this file.""" - self._deferred_print.sort() - for line_number, offset, code, text, doc in self._deferred_print: - print(self._fmt % { - 'path': self.filename, - 'row': self.line_offset + line_number, 'col': offset + 1, - 'code': code, 'text': text, - }) - if self._show_source: - if line_number > len(self.lines): - line = '' - else: - line = self.lines[line_number - 1] - print(line.rstrip()) - print(re.sub(r'\S', ' ', line[:offset]) + '^') - if self._show_pep8 and doc: - print(' ' + doc.strip()) - - # stdout is block buffered when not stdout.isatty(). - # line can be broken where buffer boundary since other processes - # write to same file. - # flush() after print() to avoid buffer boundary. - # Typical buffer size is 8192. line written safely when - # len(line) < 8192. - sys.stdout.flush() - return self.file_errors - - -class DiffReport(StandardReport): - """Collect and print the results for the changed lines only.""" - - def __init__(self, options): - super(DiffReport, self).__init__(options) - self._selected = options.selected_lines - - def error(self, line_number, offset, text, check): - if line_number not in self._selected[self.filename]: - return - return super(DiffReport, self).error(line_number, offset, text, check) - - -class StyleGuide(object): - """Initialize a PEP-8 instance with few options.""" - - def __init__(self, *args, **kwargs): - # build options from the command line - self.checker_class = kwargs.pop('checker_class', Checker) - parse_argv = kwargs.pop('parse_argv', False) - config_file = kwargs.pop('config_file', False) - parser = kwargs.pop('parser', None) - # build options from dict - options_dict = dict(*args, **kwargs) - arglist = None if parse_argv else options_dict.get('paths', None) - options, self.paths = process_options( - arglist, parse_argv, config_file, parser) - if options_dict: - options.__dict__.update(options_dict) - if 'paths' in options_dict: - self.paths = options_dict['paths'] - - self.runner = self.input_file - self.options = options - - if not options.reporter: - options.reporter = BaseReport if options.quiet else StandardReport - - options.select = tuple(options.select or ()) - if not (options.select or options.ignore or - options.testsuite or options.doctest) and DEFAULT_IGNORE: - # The default choice: ignore controversial checks - options.ignore = tuple(DEFAULT_IGNORE.split(',')) - else: - # Ignore all checks which are not explicitly selected - options.ignore = ('',) if options.select else tuple(options.ignore) - options.benchmark_keys = BENCHMARK_KEYS[:] - options.ignore_code = self.ignore_code - options.physical_checks = self.get_checks('physical_line') - options.logical_checks = self.get_checks('logical_line') - options.ast_checks = self.get_checks('tree') - self.init_report() - - def init_report(self, reporter=None): - """Initialize the report instance.""" - self.options.report = (reporter or self.options.reporter)(self.options) - return self.options.report - - def check_files(self, paths=None): - """Run all checks on the paths.""" - if paths is None: - paths = self.paths - report = self.options.report - runner = self.runner - report.start() - try: - for path in paths: - if os.path.isdir(path): - self.input_dir(path) - elif not self.excluded(path): - runner(path) - except KeyboardInterrupt: - print('... stopped') - report.stop() - return report - - def input_file(self, filename, lines=None, expected=None, line_offset=0): - """Run all checks on a Python source file.""" - if self.options.verbose: - print('checking %s' % filename) - fchecker = self.checker_class( - filename, lines=lines, options=self.options) - return fchecker.check_all(expected=expected, line_offset=line_offset) - - def input_dir(self, dirname): - """Check all files in this directory and all subdirectories.""" - dirname = dirname.rstrip('/') - if self.excluded(dirname): - return 0 - counters = self.options.report.counters - verbose = self.options.verbose - filepatterns = self.options.filename - runner = self.runner - for root, dirs, files in os.walk(dirname): - if verbose: - print('directory ' + root) - counters['directories'] += 1 - for subdir in sorted(dirs): - if self.excluded(subdir, root): - dirs.remove(subdir) - for filename in sorted(files): - # contain a pattern that matches? - if ((filename_match(filename, filepatterns) and - not self.excluded(filename, root))): - runner(os.path.join(root, filename)) - - def excluded(self, filename, parent=None): - """Check if the file should be excluded. - - Check if 'options.exclude' contains a pattern that matches filename. - """ - if not self.options.exclude: - return False - basename = os.path.basename(filename) - if filename_match(basename, self.options.exclude): - return True - if parent: - filename = os.path.join(parent, filename) - filename = os.path.abspath(filename) - return filename_match(filename, self.options.exclude) - - def ignore_code(self, code): - """Check if the error code should be ignored. - - If 'options.select' contains a prefix of the error code, - return False. Else, if 'options.ignore' contains a prefix of - the error code, return True. - """ - if len(code) < 4 and any(s.startswith(code) - for s in self.options.select): - return False - return (code.startswith(self.options.ignore) and - not code.startswith(self.options.select)) - - def get_checks(self, argument_name): - """Get all the checks for this category. - - Find all globally visible functions where the first argument name - starts with argument_name and which contain selected tests. - """ - checks = [] - for check, attrs in _checks[argument_name].items(): - (codes, args) = attrs - if any(not (code and self.ignore_code(code)) for code in codes): - checks.append((check.__name__, check, args)) - return sorted(checks) - - -def get_parser(prog='pep8', version=__version__): - parser = OptionParser(prog=prog, version=version, - usage="%prog [options] input ...") - parser.config_options = [ - 'exclude', 'filename', 'select', 'ignore', 'max-line-length', - 'hang-closing', 'count', 'format', 'quiet', 'show-pep8', - 'show-source', 'statistics', 'verbose'] - parser.add_option('-v', '--verbose', default=0, action='count', - help="print status messages, or debug with -vv") - parser.add_option('-q', '--quiet', default=0, action='count', - help="report only file names, or nothing with -qq") - parser.add_option('-r', '--repeat', default=True, action='store_true', - help="(obsolete) show all occurrences of the same error") - parser.add_option('--first', action='store_false', dest='repeat', - help="show first occurrence of each error") - parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, - help="exclude files or directories which match these " - "comma separated patterns (default: %default)") - parser.add_option('--filename', metavar='patterns', default='*.py', - help="when parsing directories, only check filenames " - "matching these comma separated patterns " - "(default: %default)") - parser.add_option('--select', metavar='errors', default='', - help="select errors and warnings (e.g. E,W6)") - parser.add_option('--ignore', metavar='errors', default='', - help="skip errors and warnings (e.g. E4,W) " - "(default: %s)" % DEFAULT_IGNORE) - parser.add_option('--show-source', action='store_true', - help="show source code for each error") - parser.add_option('--show-pep8', action='store_true', - help="show text of PEP 8 for each error " - "(implies --first)") - parser.add_option('--statistics', action='store_true', - help="count errors and warnings") - parser.add_option('--count', action='store_true', - help="print total number of errors and warnings " - "to standard error and set exit code to 1 if " - "total is not null") - parser.add_option('--max-line-length', type='int', metavar='n', - default=MAX_LINE_LENGTH, - help="set maximum allowed line length " - "(default: %default)") - parser.add_option('--hang-closing', action='store_true', - help="hang closing bracket instead of matching " - "indentation of opening bracket's line") - parser.add_option('--format', metavar='format', default='default', - help="set the error format [default|pylint|]") - parser.add_option('--diff', action='store_true', - help="report only lines changed according to the " - "unified diff received on STDIN") - group = parser.add_option_group("Testing Options") - if os.path.exists(TESTSUITE_PATH): - group.add_option('--testsuite', metavar='dir', - help="run regression tests from dir") - group.add_option('--doctest', action='store_true', - help="run doctest on myself") - group.add_option('--benchmark', action='store_true', - help="measure processing speed") - return parser - - -def read_config(options, args, arglist, parser): - """Read and parse configurations - - If a config file is specified on the command line with the "--config" - option, then only it is used for configuration. - - Otherwise, the user configuration (~/.config/pep8) and any local - configurations in the current directory or above will be merged together - (in that order) using the read method of ConfigParser. - """ - config = RawConfigParser() - - cli_conf = options.config - - local_dir = os.curdir - - if USER_CONFIG and os.path.isfile(USER_CONFIG): - if options.verbose: - print('user configuration: %s' % USER_CONFIG) - config.read(USER_CONFIG) - - parent = tail = args and os.path.abspath(os.path.commonprefix(args)) - while tail: - if config.read(os.path.join(parent, fn) for fn in PROJECT_CONFIG): - local_dir = parent - if options.verbose: - print('local configuration: in %s' % parent) - break - (parent, tail) = os.path.split(parent) - - if cli_conf and os.path.isfile(cli_conf): - if options.verbose: - print('cli configuration: %s' % cli_conf) - config.read(cli_conf) - - pep8_section = parser.prog - if config.has_section(pep8_section): - option_list = dict([(o.dest, o.type or o.action) - for o in parser.option_list]) - - # First, read the default values - (new_options, __) = parser.parse_args([]) - - # Second, parse the configuration - for opt in config.options(pep8_section): - if opt.replace('_', '-') not in parser.config_options: - print(" unknown option '%s' ignored" % opt) - continue - if options.verbose > 1: - print(" %s = %s" % (opt, config.get(pep8_section, opt))) - normalized_opt = opt.replace('-', '_') - opt_type = option_list[normalized_opt] - if opt_type in ('int', 'count'): - value = config.getint(pep8_section, opt) - elif opt_type == 'string': - value = config.get(pep8_section, opt) - if normalized_opt == 'exclude': - value = normalize_paths(value, local_dir) - else: - assert opt_type in ('store_true', 'store_false') - value = config.getboolean(pep8_section, opt) - setattr(new_options, normalized_opt, value) - - # Third, overwrite with the command-line options - (options, __) = parser.parse_args(arglist, values=new_options) - options.doctest = options.testsuite = False - return options - - -def process_options(arglist=None, parse_argv=False, config_file=None, - parser=None): - """Process options passed either via arglist or via command line args. - - Passing in the ``config_file`` parameter allows other tools, such as flake8 - to specify their own options to be processed in pep8. - """ - if not parser: - parser = get_parser() - if not parser.has_option('--config'): - group = parser.add_option_group("Configuration", description=( - "The project options are read from the [%s] section of the " - "tox.ini file or the setup.cfg file located in any parent folder " - "of the path(s) being processed. Allowed options are: %s." % - (parser.prog, ', '.join(parser.config_options)))) - group.add_option('--config', metavar='path', default=config_file, - help="user config file location") - # Don't read the command line if the module is used as a library. - if not arglist and not parse_argv: - arglist = [] - # If parse_argv is True and arglist is None, arguments are - # parsed from the command line (sys.argv) - (options, args) = parser.parse_args(arglist) - options.reporter = None - - if options.ensure_value('testsuite', False): - args.append(options.testsuite) - elif not options.ensure_value('doctest', False): - if parse_argv and not args: - if options.diff or any(os.path.exists(name) - for name in PROJECT_CONFIG): - args = ['.'] - else: - parser.error('input not specified') - options = read_config(options, args, arglist, parser) - options.reporter = parse_argv and options.quiet == 1 and FileReport - - options.filename = options.filename and options.filename.split(',') - options.exclude = normalize_paths(options.exclude) - options.select = options.select and options.select.split(',') - options.ignore = options.ignore and options.ignore.split(',') - - if options.diff: - options.reporter = DiffReport - stdin = stdin_get_value() - options.selected_lines = parse_udiff(stdin, options.filename, args[0]) - args = sorted(options.selected_lines) - - return options, args - - -def _main(): - """Parse options and run checks on Python source.""" - import signal - - # Handle "Broken pipe" gracefully - try: - signal.signal(signal.SIGPIPE, lambda signum, frame: sys.exit(1)) - except AttributeError: - pass # not supported on Windows - - pep8style = StyleGuide(parse_argv=True) - options = pep8style.options - if options.doctest or options.testsuite: - from testsuite.support import run_tests - report = run_tests(pep8style) - else: - report = pep8style.check_files() - if options.statistics: - report.print_statistics() - if options.benchmark: - report.print_benchmark() - if options.testsuite and not options.quiet: - report.print_results() - if report.total_errors: - if options.count: - sys.stderr.write(str(report.total_errors) + '\n') - sys.exit(1) - -if __name__ == '__main__': - _main() 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/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/pycodestyle.py b/pymode/libs/pycodestyle.py
    new file mode 120000
    index 00000000..ef2127d7
    --- /dev/null
    +++ b/pymode/libs/pycodestyle.py
    @@ -0,0 +1 @@
    +../../submodules/pycodestyle/pycodestyle.py
    \ No newline at end of file
    diff --git a/pymode/libs/pydocstyle b/pymode/libs/pydocstyle
    new file mode 120000
    index 00000000..3a6e93e6
    --- /dev/null
    +++ b/pymode/libs/pydocstyle
    @@ -0,0 +1 @@
    +../../submodules/pydocstyle/src/pydocstyle/
    \ No newline at end of file
    diff --git a/pymode/libs/pyflakes b/pymode/libs/pyflakes
    new file mode 120000
    index 00000000..e123a7ee
    --- /dev/null
    +++ b/pymode/libs/pyflakes
    @@ -0,0 +1 @@
    +../../submodules/pyflakes/pyflakes/
    \ No newline at end of file
    diff --git a/pymode/libs/pyflakes/__init__.py b/pymode/libs/pyflakes/__init__.py
    deleted file mode 100644
    index 1f047803..00000000
    --- a/pymode/libs/pyflakes/__init__.py
    +++ /dev/null
    @@ -1 +0,0 @@
    -__version__ = '0.9.2'
    diff --git a/pymode/libs/pyflakes/__main__.py b/pymode/libs/pyflakes/__main__.py
    deleted file mode 100644
    index a69e6891..00000000
    --- a/pymode/libs/pyflakes/__main__.py
    +++ /dev/null
    @@ -1,5 +0,0 @@
    -from pyflakes.api import main
    -
    -# python -m pyflakes (with Python >= 2.7)
    -if __name__ == '__main__':
    -    main(prog='pyflakes')
    diff --git a/pymode/libs/pyflakes/api.py b/pymode/libs/pyflakes/api.py
    deleted file mode 100644
    index 3bc23306..00000000
    --- a/pymode/libs/pyflakes/api.py
    +++ /dev/null
    @@ -1,175 +0,0 @@
    -"""
    -API for the command-line I{pyflakes} tool.
    -"""
    -from __future__ import with_statement
    -
    -import sys
    -import os
    -import _ast
    -
    -from pyflakes import checker, __version__
    -from pyflakes import reporter as modReporter
    -
    -__all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
    -
    -
    -def check(codeString, filename, reporter=None):
    -    """
    -    Check the Python source given by C{codeString} for flakes.
    -
    -    @param codeString: The Python source to check.
    -    @type codeString: C{str}
    -
    -    @param filename: The name of the file the source came from, used to report
    -        errors.
    -    @type filename: C{str}
    -
    -    @param reporter: A L{Reporter} instance, where errors and warnings will be
    -        reported.
    -
    -    @return: The number of warnings emitted.
    -    @rtype: C{int}
    -    """
    -    if reporter is None:
    -        reporter = modReporter._makeDefaultReporter()
    -    # First, compile into an AST and handle syntax errors.
    -    try:
    -        tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
    -    except SyntaxError:
    -        value = sys.exc_info()[1]
    -        msg = value.args[0]
    -
    -        (lineno, offset, text) = value.lineno, value.offset, value.text
    -
    -        # If there's an encoding problem with the file, the text is None.
    -        if text is None:
    -            # Avoid using msg, since for the only known case, it contains a
    -            # bogus message that claims the encoding the file declared was
    -            # unknown.
    -            reporter.unexpectedError(filename, 'problem decoding source')
    -        else:
    -            reporter.syntaxError(filename, msg, lineno, offset, text)
    -        return 1
    -    except Exception:
    -        reporter.unexpectedError(filename, 'problem decoding source')
    -        return 1
    -    # Okay, it's syntactically valid.  Now check it.
    -    w = checker.Checker(tree, filename)
    -    w.messages.sort(key=lambda m: m.lineno)
    -    for warning in w.messages:
    -        reporter.flake(warning)
    -    return len(w.messages)
    -
    -
    -def checkPath(filename, reporter=None):
    -    """
    -    Check the given path, printing out any warnings detected.
    -
    -    @param reporter: A L{Reporter} instance, where errors and warnings will be
    -        reported.
    -
    -    @return: the number of warnings printed
    -    """
    -    if reporter is None:
    -        reporter = modReporter._makeDefaultReporter()
    -    try:
    -        # in Python 2.6, compile() will choke on \r\n line endings. In later
    -        # versions of python it's smarter, and we want binary mode to give
    -        # compile() the best opportunity to do the right thing WRT text
    -        # encodings.
    -        if sys.version_info < (2, 7):
    -            mode = 'rU'
    -        else:
    -            mode = 'rb'
    -
    -        with open(filename, mode) as f:
    -            codestr = f.read()
    -        if sys.version_info < (2, 7):
    -            codestr += '\n'     # Work around for Python <= 2.6
    -    except UnicodeError:
    -        reporter.unexpectedError(filename, 'problem decoding source')
    -        return 1
    -    except IOError:
    -        msg = sys.exc_info()[1]
    -        reporter.unexpectedError(filename, msg.args[1])
    -        return 1
    -    return check(codestr, filename, reporter)
    -
    -
    -def iterSourceCode(paths):
    -    """
    -    Iterate over all Python source files in C{paths}.
    -
    -    @param paths: A list of paths.  Directories will be recursed into and
    -        any .py files found will be yielded.  Any non-directories will be
    -        yielded as-is.
    -    """
    -    for path in paths:
    -        if os.path.isdir(path):
    -            for dirpath, dirnames, filenames in os.walk(path):
    -                for filename in filenames:
    -                    if filename.endswith('.py'):
    -                        yield os.path.join(dirpath, filename)
    -        else:
    -            yield path
    -
    -
    -def checkRecursive(paths, reporter):
    -    """
    -    Recursively check all source files in C{paths}.
    -
    -    @param paths: A list of paths to Python source files and directories
    -        containing Python source files.
    -    @param reporter: A L{Reporter} where all of the warnings and errors
    -        will be reported to.
    -    @return: The number of warnings found.
    -    """
    -    warnings = 0
    -    for sourcePath in iterSourceCode(paths):
    -        warnings += checkPath(sourcePath, reporter)
    -    return warnings
    -
    -
    -def _exitOnSignal(sigName, message):
    -    """Handles a signal with sys.exit.
    -
    -    Some of these signals (SIGPIPE, for example) don't exist or are invalid on
    -    Windows. So, ignore errors that might arise.
    -    """
    -    import signal
    -
    -    try:
    -        sigNumber = getattr(signal, sigName)
    -    except AttributeError:
    -        # the signal constants defined in the signal module are defined by
    -        # whether the C library supports them or not. So, SIGPIPE might not
    -        # even be defined.
    -        return
    -
    -    def handler(sig, f):
    -        sys.exit(message)
    -
    -    try:
    -        signal.signal(sigNumber, handler)
    -    except ValueError:
    -        # It's also possible the signal is defined, but then it's invalid. In
    -        # this case, signal.signal raises ValueError.
    -        pass
    -
    -
    -def main(prog=None):
    -    """Entry point for the script "pyflakes"."""
    -    import optparse
    -
    -    # Handle "Keyboard Interrupt" and "Broken pipe" gracefully
    -    _exitOnSignal('SIGINT', '... stopped')
    -    _exitOnSignal('SIGPIPE', 1)
    -
    -    parser = optparse.OptionParser(prog=prog, version=__version__)
    -    (__, args) = parser.parse_args()
    -    reporter = modReporter._makeDefaultReporter()
    -    if args:
    -        warnings = checkRecursive(args, reporter)
    -    else:
    -        warnings = check(sys.stdin.read(), '', reporter)
    -    raise SystemExit(warnings > 0)
    diff --git a/pymode/libs/pyflakes/checker.py b/pymode/libs/pyflakes/checker.py
    deleted file mode 100644
    index e6e19427..00000000
    --- a/pymode/libs/pyflakes/checker.py
    +++ /dev/null
    @@ -1,925 +0,0 @@
    -"""
    -Main module.
    -
    -Implement the central Checker class.
    -Also, it models the Bindings and Scopes.
    -"""
    -import doctest
    -import os
    -import sys
    -
    -PY2 = sys.version_info < (3, 0)
    -PY32 = sys.version_info < (3, 3)    # Python 2.5 to 3.2
    -PY33 = sys.version_info < (3, 4)    # Python 2.5 to 3.3
    -builtin_vars = dir(__import__('__builtin__' if PY2 else 'builtins'))
    -
    -try:
    -    import ast
    -except ImportError:     # Python 2.5
    -    import _ast as ast
    -
    -    if 'decorator_list' not in ast.ClassDef._fields:
    -        # Patch the missing attribute 'decorator_list'
    -        ast.ClassDef.decorator_list = ()
    -        ast.FunctionDef.decorator_list = property(lambda s: s.decorators)
    -
    -from pyflakes import messages
    -
    -
    -if PY2:
    -    def getNodeType(node_class):
    -        # workaround str.upper() which is locale-dependent
    -        return str(unicode(node_class.__name__).upper())
    -else:
    -    def getNodeType(node_class):
    -        return node_class.__name__.upper()
    -
    -# Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally)
    -if PY32:
    -    def getAlternatives(n):
    -        if isinstance(n, (ast.If, ast.TryFinally)):
    -            return [n.body]
    -        if isinstance(n, ast.TryExcept):
    -            return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
    -else:
    -    def getAlternatives(n):
    -        if isinstance(n, ast.If):
    -            return [n.body]
    -        if isinstance(n, ast.Try):
    -            return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
    -
    -
    -class _FieldsOrder(dict):
    -    """Fix order of AST node fields."""
    -
    -    def _get_fields(self, node_class):
    -        # handle iter before target, and generators before element
    -        fields = node_class._fields
    -        if 'iter' in fields:
    -            key_first = 'iter'.find
    -        elif 'generators' in fields:
    -            key_first = 'generators'.find
    -        else:
    -            key_first = 'value'.find
    -        return tuple(sorted(fields, key=key_first, reverse=True))
    -
    -    def __missing__(self, node_class):
    -        self[node_class] = fields = self._get_fields(node_class)
    -        return fields
    -
    -
    -def iter_child_nodes(node, omit=None, _fields_order=_FieldsOrder()):
    -    """
    -    Yield all direct child nodes of *node*, that is, all fields that
    -    are nodes and all items of fields that are lists of nodes.
    -    """
    -    for name in _fields_order[node.__class__]:
    -        if name == omit:
    -            continue
    -        field = getattr(node, name, None)
    -        if isinstance(field, ast.AST):
    -            yield field
    -        elif isinstance(field, list):
    -            for item in field:
    -                yield item
    -
    -
    -class Binding(object):
    -    """
    -    Represents the binding of a value to a name.
    -
    -    The checker uses this to keep track of which names have been bound and
    -    which names have not. See L{Assignment} for a special type of binding that
    -    is checked with stricter rules.
    -
    -    @ivar used: pair of (L{Scope}, line-number) indicating the scope and
    -                line number that this binding was last used
    -    """
    -
    -    def __init__(self, name, source):
    -        self.name = name
    -        self.source = source
    -        self.used = False
    -
    -    def __str__(self):
    -        return self.name
    -
    -    def __repr__(self):
    -        return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
    -                                                        self.name,
    -                                                        self.source.lineno,
    -                                                        id(self))
    -
    -    def redefines(self, other):
    -        return isinstance(other, Definition) and self.name == other.name
    -
    -
    -class Definition(Binding):
    -    """
    -    A binding that defines a function or a class.
    -    """
    -
    -
    -class Importation(Definition):
    -    """
    -    A binding created by an import statement.
    -
    -    @ivar fullName: The complete name given to the import statement,
    -        possibly including multiple dotted components.
    -    @type fullName: C{str}
    -    """
    -
    -    def __init__(self, name, source):
    -        self.fullName = name
    -        self.redefined = []
    -        name = name.split('.')[0]
    -        super(Importation, self).__init__(name, source)
    -
    -    def redefines(self, other):
    -        if isinstance(other, Importation):
    -            return self.fullName == other.fullName
    -        return isinstance(other, Definition) and self.name == other.name
    -
    -
    -class Argument(Binding):
    -    """
    -    Represents binding a name as an argument.
    -    """
    -
    -
    -class Assignment(Binding):
    -    """
    -    Represents binding a name with an explicit assignment.
    -
    -    The checker will raise warnings for any Assignment that isn't used. Also,
    -    the checker does not consider assignments in tuple/list unpacking to be
    -    Assignments, rather it treats them as simple Bindings.
    -    """
    -
    -
    -class FunctionDefinition(Definition):
    -    pass
    -
    -
    -class ClassDefinition(Definition):
    -    pass
    -
    -
    -class ExportBinding(Binding):
    -    """
    -    A binding created by an C{__all__} assignment.  If the names in the list
    -    can be determined statically, they will be treated as names for export and
    -    additional checking applied to them.
    -
    -    The only C{__all__} assignment that can be recognized is one which takes
    -    the value of a literal list containing literal strings.  For example::
    -
    -        __all__ = ["foo", "bar"]
    -
    -    Names which are imported and not otherwise used but appear in the value of
    -    C{__all__} will not have an unused import warning reported for them.
    -    """
    -
    -    def __init__(self, name, source, scope):
    -        if '__all__' in scope and isinstance(source, ast.AugAssign):
    -            self.names = list(scope['__all__'].names)
    -        else:
    -            self.names = []
    -        if isinstance(source.value, (ast.List, ast.Tuple)):
    -            for node in source.value.elts:
    -                if isinstance(node, ast.Str):
    -                    self.names.append(node.s)
    -        super(ExportBinding, self).__init__(name, source)
    -
    -
    -class Scope(dict):
    -    importStarred = False       # set to True when import * is found
    -
    -    def __repr__(self):
    -        scope_cls = self.__class__.__name__
    -        return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
    -
    -
    -class ClassScope(Scope):
    -    pass
    -
    -
    -class FunctionScope(Scope):
    -    """
    -    I represent a name scope for a function.
    -
    -    @ivar globals: Names declared 'global' in this function.
    -    """
    -    usesLocals = False
    -    alwaysUsed = set(['__tracebackhide__',
    -                      '__traceback_info__', '__traceback_supplement__'])
    -
    -    def __init__(self):
    -        super(FunctionScope, self).__init__()
    -        # Simplify: manage the special locals as globals
    -        self.globals = self.alwaysUsed.copy()
    -        self.returnValue = None     # First non-empty return
    -        self.isGenerator = False    # Detect a generator
    -
    -    def unusedAssignments(self):
    -        """
    -        Return a generator for the assignments which have not been used.
    -        """
    -        for name, binding in self.items():
    -            if (not binding.used and name not in self.globals
    -                    and not self.usesLocals
    -                    and isinstance(binding, Assignment)):
    -                yield name, binding
    -
    -
    -class GeneratorScope(Scope):
    -    pass
    -
    -
    -class ModuleScope(Scope):
    -    pass
    -
    -
    -# Globally defined names which are not attributes of the builtins module, or
    -# are only present on some platforms.
    -_MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
    -
    -
    -def getNodeName(node):
    -    # Returns node.id, or node.name, or None
    -    if hasattr(node, 'id'):     # One of the many nodes with an id
    -        return node.id
    -    if hasattr(node, 'name'):   # a ExceptHandler node
    -        return node.name
    -
    -
    -class Checker(object):
    -    """
    -    I check the cleanliness and sanity of Python code.
    -
    -    @ivar _deferredFunctions: Tracking list used by L{deferFunction}.  Elements
    -        of the list are two-tuples.  The first element is the callable passed
    -        to L{deferFunction}.  The second element is a copy of the scope stack
    -        at the time L{deferFunction} was called.
    -
    -    @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
    -        callables which are deferred assignment checks.
    -    """
    -
    -    nodeDepth = 0
    -    offset = None
    -    traceTree = False
    -
    -    builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
    -    _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
    -    if _customBuiltIns:
    -        builtIns.update(_customBuiltIns.split(','))
    -    del _customBuiltIns
    -
    -    def __init__(self, tree, filename='(none)', builtins=None,
    -                 withDoctest='PYFLAKES_DOCTEST' in os.environ):
    -        self._nodeHandlers = {}
    -        self._deferredFunctions = []
    -        self._deferredAssignments = []
    -        self.deadScopes = []
    -        self.messages = []
    -        self.filename = filename
    -        if builtins:
    -            self.builtIns = self.builtIns.union(builtins)
    -        self.withDoctest = withDoctest
    -        self.scopeStack = [ModuleScope()]
    -        self.exceptHandlers = [()]
    -        self.futuresAllowed = True
    -        self.root = tree
    -        self.handleChildren(tree)
    -        self.runDeferred(self._deferredFunctions)
    -        # Set _deferredFunctions to None so that deferFunction will fail
    -        # noisily if called after we've run through the deferred functions.
    -        self._deferredFunctions = None
    -        self.runDeferred(self._deferredAssignments)
    -        # Set _deferredAssignments to None so that deferAssignment will fail
    -        # noisily if called after we've run through the deferred assignments.
    -        self._deferredAssignments = None
    -        del self.scopeStack[1:]
    -        self.popScope()
    -        self.checkDeadScopes()
    -
    -    def deferFunction(self, callable):
    -        """
    -        Schedule a function handler to be called just before completion.
    -
    -        This is used for handling function bodies, which must be deferred
    -        because code later in the file might modify the global scope. When
    -        `callable` is called, the scope at the time this is called will be
    -        restored, however it will contain any new bindings added to it.
    -        """
    -        self._deferredFunctions.append((callable, self.scopeStack[:], self.offset))
    -
    -    def deferAssignment(self, callable):
    -        """
    -        Schedule an assignment handler to be called just after deferred
    -        function handlers.
    -        """
    -        self._deferredAssignments.append((callable, self.scopeStack[:], self.offset))
    -
    -    def runDeferred(self, deferred):
    -        """
    -        Run the callables in C{deferred} using their associated scope stack.
    -        """
    -        for handler, scope, offset in deferred:
    -            self.scopeStack = scope
    -            self.offset = offset
    -            handler()
    -
    -    @property
    -    def scope(self):
    -        return self.scopeStack[-1]
    -
    -    def popScope(self):
    -        self.deadScopes.append(self.scopeStack.pop())
    -
    -    def checkDeadScopes(self):
    -        """
    -        Look at scopes which have been fully examined and report names in them
    -        which were imported but unused.
    -        """
    -        for scope in self.deadScopes:
    -            if isinstance(scope.get('__all__'), ExportBinding):
    -                all_names = set(scope['__all__'].names)
    -                if not scope.importStarred and \
    -                   os.path.basename(self.filename) != '__init__.py':
    -                    # Look for possible mistakes in the export list
    -                    undefined = all_names.difference(scope)
    -                    for name in undefined:
    -                        self.report(messages.UndefinedExport,
    -                                    scope['__all__'].source, name)
    -            else:
    -                all_names = []
    -
    -            # Look for imported names that aren't used.
    -            for value in scope.values():
    -                if isinstance(value, Importation):
    -                    used = value.used or value.name in all_names
    -                    if not used:
    -                        messg = messages.UnusedImport
    -                        self.report(messg, value.source, value.name)
    -                    for node in value.redefined:
    -                        if isinstance(self.getParent(node), ast.For):
    -                            messg = messages.ImportShadowedByLoopVar
    -                        elif used:
    -                            continue
    -                        else:
    -                            messg = messages.RedefinedWhileUnused
    -                        self.report(messg, node, value.name, value.source)
    -
    -    def pushScope(self, scopeClass=FunctionScope):
    -        self.scopeStack.append(scopeClass())
    -
    -    def report(self, messageClass, *args, **kwargs):
    -        self.messages.append(messageClass(self.filename, *args, **kwargs))
    -
    -    def getParent(self, node):
    -        # Lookup the first parent which is not Tuple, List or Starred
    -        while True:
    -            node = node.parent
    -            if not hasattr(node, 'elts') and not hasattr(node, 'ctx'):
    -                return node
    -
    -    def getCommonAncestor(self, lnode, rnode, stop):
    -        if stop in (lnode, rnode) or not (hasattr(lnode, 'parent') and
    -                                          hasattr(rnode, 'parent')):
    -            return None
    -        if lnode is rnode:
    -            return lnode
    -
    -        if (lnode.depth > rnode.depth):
    -            return self.getCommonAncestor(lnode.parent, rnode, stop)
    -        if (lnode.depth < rnode.depth):
    -            return self.getCommonAncestor(lnode, rnode.parent, stop)
    -        return self.getCommonAncestor(lnode.parent, rnode.parent, stop)
    -
    -    def descendantOf(self, node, ancestors, stop):
    -        for a in ancestors:
    -            if self.getCommonAncestor(node, a, stop):
    -                return True
    -        return False
    -
    -    def differentForks(self, lnode, rnode):
    -        """True, if lnode and rnode are located on different forks of IF/TRY"""
    -        ancestor = self.getCommonAncestor(lnode, rnode, self.root)
    -        parts = getAlternatives(ancestor)
    -        if parts:
    -            for items in parts:
    -                if self.descendantOf(lnode, items, ancestor) ^ \
    -                   self.descendantOf(rnode, items, ancestor):
    -                    return True
    -        return False
    -
    -    def addBinding(self, node, value):
    -        """
    -        Called when a binding is altered.
    -
    -        - `node` is the statement responsible for the change
    -        - `value` is the new value, a Binding instance
    -        """
    -        # assert value.source in (node, node.parent):
    -        for scope in self.scopeStack[::-1]:
    -            if value.name in scope:
    -                break
    -        existing = scope.get(value.name)
    -
    -        if existing and not self.differentForks(node, existing.source):
    -
    -            parent_stmt = self.getParent(value.source)
    -            if isinstance(existing, Importation) and isinstance(parent_stmt, ast.For):
    -                self.report(messages.ImportShadowedByLoopVar,
    -                            node, value.name, existing.source)
    -
    -            elif scope is self.scope:
    -                if (isinstance(parent_stmt, ast.comprehension) and
    -                        not isinstance(self.getParent(existing.source),
    -                                       (ast.For, ast.comprehension))):
    -                    self.report(messages.RedefinedInListComp,
    -                                node, value.name, existing.source)
    -                elif not existing.used and value.redefines(existing):
    -                    self.report(messages.RedefinedWhileUnused,
    -                                node, value.name, existing.source)
    -
    -            elif isinstance(existing, Importation) and value.redefines(existing):
    -                existing.redefined.append(node)
    -
    -        if value.name in self.scope:
    -            # then assume the rebound name is used as a global or within a loop
    -            value.used = self.scope[value.name].used
    -
    -        self.scope[value.name] = value
    -
    -    def getNodeHandler(self, node_class):
    -        try:
    -            return self._nodeHandlers[node_class]
    -        except KeyError:
    -            nodeType = getNodeType(node_class)
    -        self._nodeHandlers[node_class] = handler = getattr(self, nodeType)
    -        return handler
    -
    -    def handleNodeLoad(self, node):
    -        name = getNodeName(node)
    -        if not name:
    -            return
    -        # try local scope
    -        try:
    -            self.scope[name].used = (self.scope, node)
    -        except KeyError:
    -            pass
    -        else:
    -            return
    -
    -        scopes = [scope for scope in self.scopeStack[:-1]
    -                  if isinstance(scope, (FunctionScope, ModuleScope, GeneratorScope))]
    -        if isinstance(self.scope, GeneratorScope) and scopes[-1] != self.scopeStack[-2]:
    -            scopes.append(self.scopeStack[-2])
    -
    -        # try enclosing function scopes and global scope
    -        importStarred = self.scope.importStarred
    -        for scope in reversed(scopes):
    -            importStarred = importStarred or scope.importStarred
    -            try:
    -                scope[name].used = (self.scope, node)
    -            except KeyError:
    -                pass
    -            else:
    -                return
    -
    -        # look in the built-ins
    -        if importStarred or name in self.builtIns:
    -            return
    -        if name == '__path__' and os.path.basename(self.filename) == '__init__.py':
    -            # the special name __path__ is valid only in packages
    -            return
    -
    -        # protected with a NameError handler?
    -        if 'NameError' not in self.exceptHandlers[-1]:
    -            self.report(messages.UndefinedName, node, name)
    -
    -    def handleNodeStore(self, node):
    -        name = getNodeName(node)
    -        if not name:
    -            return
    -        # if the name hasn't already been defined in the current scope
    -        if isinstance(self.scope, FunctionScope) and name not in self.scope:
    -            # for each function or module scope above us
    -            for scope in self.scopeStack[:-1]:
    -                if not isinstance(scope, (FunctionScope, ModuleScope)):
    -                    continue
    -                # if the name was defined in that scope, and the name has
    -                # been accessed already in the current scope, and hasn't
    -                # been declared global
    -                used = name in scope and scope[name].used
    -                if used and used[0] is self.scope and name not in self.scope.globals:
    -                    # then it's probably a mistake
    -                    self.report(messages.UndefinedLocal,
    -                                scope[name].used[1], name, scope[name].source)
    -                    break
    -
    -        parent_stmt = self.getParent(node)
    -        if isinstance(parent_stmt, (ast.For, ast.comprehension)) or (
    -                parent_stmt != node.parent and
    -                not self.isLiteralTupleUnpacking(parent_stmt)):
    -            binding = Binding(name, node)
    -        elif name == '__all__' and isinstance(self.scope, ModuleScope):
    -            binding = ExportBinding(name, node.parent, self.scope)
    -        else:
    -            binding = Assignment(name, node)
    -        self.addBinding(node, binding)
    -
    -    def handleNodeDelete(self, node):
    -
    -        def on_conditional_branch():
    -            """
    -            Return `True` if node is part of a conditional body.
    -            """
    -            current = getattr(node, 'parent', None)
    -            while current:
    -                if isinstance(current, (ast.If, ast.While, ast.IfExp)):
    -                    return True
    -                current = getattr(current, 'parent', None)
    -            return False
    -
    -        name = getNodeName(node)
    -        if not name:
    -            return
    -
    -        if on_conditional_branch():
    -            # We can not predict if this conditional branch is going to
    -            # be executed.
    -            return
    -
    -        if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
    -            self.scope.globals.remove(name)
    -        else:
    -            try:
    -                del self.scope[name]
    -            except KeyError:
    -                self.report(messages.UndefinedName, node, name)
    -
    -    def handleChildren(self, tree, omit=None):
    -        for node in iter_child_nodes(tree, omit=omit):
    -            self.handleNode(node, tree)
    -
    -    def isLiteralTupleUnpacking(self, node):
    -        if isinstance(node, ast.Assign):
    -            for child in node.targets + [node.value]:
    -                if not hasattr(child, 'elts'):
    -                    return False
    -            return True
    -
    -    def isDocstring(self, node):
    -        """
    -        Determine if the given node is a docstring, as long as it is at the
    -        correct place in the node tree.
    -        """
    -        return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and
    -                                             isinstance(node.value, ast.Str))
    -
    -    def getDocstring(self, node):
    -        if isinstance(node, ast.Expr):
    -            node = node.value
    -        if not isinstance(node, ast.Str):
    -            return (None, None)
    -        # Computed incorrectly if the docstring has backslash
    -        doctest_lineno = node.lineno - node.s.count('\n') - 1
    -        return (node.s, doctest_lineno)
    -
    -    def handleNode(self, node, parent):
    -        if node is None:
    -            return
    -        if self.offset and getattr(node, 'lineno', None) is not None:
    -            node.lineno += self.offset[0]
    -            node.col_offset += self.offset[1]
    -        if self.traceTree:
    -            print('  ' * self.nodeDepth + node.__class__.__name__)
    -        if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
    -                                        self.isDocstring(node)):
    -            self.futuresAllowed = False
    -        self.nodeDepth += 1
    -        node.depth = self.nodeDepth
    -        node.parent = parent
    -        try:
    -            handler = self.getNodeHandler(node.__class__)
    -            handler(node)
    -        finally:
    -            self.nodeDepth -= 1
    -        if self.traceTree:
    -            print('  ' * self.nodeDepth + 'end ' + node.__class__.__name__)
    -
    -    _getDoctestExamples = doctest.DocTestParser().get_examples
    -
    -    def handleDoctests(self, node):
    -        try:
    -            (docstring, node_lineno) = self.getDocstring(node.body[0])
    -            examples = docstring and self._getDoctestExamples(docstring)
    -        except (ValueError, IndexError):
    -            # e.g. line 6 of the docstring for  has inconsistent
    -            # leading whitespace: ...
    -            return
    -        if not examples:
    -            return
    -        node_offset = self.offset or (0, 0)
    -        self.pushScope()
    -        underscore_in_builtins = '_' in self.builtIns
    -        if not underscore_in_builtins:
    -            self.builtIns.add('_')
    -        for example in examples:
    -            try:
    -                tree = compile(example.source, "", "exec", ast.PyCF_ONLY_AST)
    -            except SyntaxError:
    -                e = sys.exc_info()[1]
    -                position = (node_lineno + example.lineno + e.lineno,
    -                            example.indent + 4 + (e.offset or 0))
    -                self.report(messages.DoctestSyntaxError, node, position)
    -            else:
    -                self.offset = (node_offset[0] + node_lineno + example.lineno,
    -                               node_offset[1] + example.indent + 4)
    -                self.handleChildren(tree)
    -                self.offset = node_offset
    -        if not underscore_in_builtins:
    -            self.builtIns.remove('_')
    -        self.popScope()
    -
    -    def ignore(self, node):
    -        pass
    -
    -    # "stmt" type nodes
    -    DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \
    -        ASYNCWITH = ASYNCWITHITEM = RAISE = TRYFINALLY = ASSERT = EXEC = \
    -        EXPR = ASSIGN = handleChildren
    -
    -    CONTINUE = BREAK = PASS = ignore
    -
    -    # "expr" type nodes
    -    BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = \
    -        COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \
    -        STARRED = NAMECONSTANT = handleChildren
    -
    -    NUM = STR = BYTES = ELLIPSIS = ignore
    -
    -    # "slice" type nodes
    -    SLICE = EXTSLICE = INDEX = handleChildren
    -
    -    # expression contexts are node instances too, though being constants
    -    LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
    -
    -    # same for operators
    -    AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
    -        BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
    -        EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
    -
    -    # additional node types
    -    COMPREHENSION = KEYWORD = handleChildren
    -
    -    def GLOBAL(self, node):
    -        """
    -        Keep track of globals declarations.
    -        """
    -        # In doctests, the global scope is an anonymous function at index 1.
    -        global_scope_index = 1 if self.withDoctest else 0
    -        global_scope = self.scopeStack[global_scope_index]
    -
    -        # Ignore 'global' statement in global scope.
    -        if self.scope is not global_scope:
    -
    -            # One 'global' statement can bind multiple (comma-delimited) names.
    -            for node_name in node.names:
    -                node_value = Assignment(node_name, node)
    -
    -                # Remove UndefinedName messages already reported for this name.
    -                self.messages = [
    -                    m for m in self.messages if not
    -                    isinstance(m, messages.UndefinedName) and not
    -                    m.message_args[0] == node_name]
    -
    -                # Bind name to global scope if it doesn't exist already.
    -                global_scope.setdefault(node_name, node_value)
    -
    -                # Bind name to non-global scopes, but as already "used".
    -                node_value.used = (global_scope, node)
    -                for scope in self.scopeStack[global_scope_index + 1:]:
    -                    scope[node_name] = node_value
    -
    -    NONLOCAL = GLOBAL
    -
    -    def GENERATOREXP(self, node):
    -        self.pushScope(GeneratorScope)
    -        self.handleChildren(node)
    -        self.popScope()
    -
    -    LISTCOMP = handleChildren if PY2 else GENERATOREXP
    -
    -    DICTCOMP = SETCOMP = GENERATOREXP
    -
    -    def NAME(self, node):
    -        """
    -        Handle occurrence of Name (which can be a load/store/delete access.)
    -        """
    -        # Locate the name in locals / function / globals scopes.
    -        if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
    -            self.handleNodeLoad(node)
    -            if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
    -                    and isinstance(node.parent, ast.Call)):
    -                # we are doing locals() call in current scope
    -                self.scope.usesLocals = True
    -        elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
    -            self.handleNodeStore(node)
    -        elif isinstance(node.ctx, ast.Del):
    -            self.handleNodeDelete(node)
    -        else:
    -            # must be a Param context -- this only happens for names in function
    -            # arguments, but these aren't dispatched through here
    -            raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
    -
    -    def RETURN(self, node):
    -        if isinstance(self.scope, ClassScope):
    -            self.report(messages.ReturnOutsideFunction, node)
    -            return
    -
    -        if (
    -            node.value and
    -            hasattr(self.scope, 'returnValue') and
    -            not self.scope.returnValue
    -        ):
    -            self.scope.returnValue = node.value
    -        self.handleNode(node.value, node)
    -
    -    def YIELD(self, node):
    -        self.scope.isGenerator = True
    -        self.handleNode(node.value, node)
    -
    -    AWAIT = YIELDFROM = YIELD
    -
    -    def FUNCTIONDEF(self, node):
    -        for deco in node.decorator_list:
    -            self.handleNode(deco, node)
    -        self.LAMBDA(node)
    -        self.addBinding(node, FunctionDefinition(node.name, node))
    -        if self.withDoctest:
    -            self.deferFunction(lambda: self.handleDoctests(node))
    -
    -    ASYNCFUNCTIONDEF = FUNCTIONDEF
    -
    -    def LAMBDA(self, node):
    -        args = []
    -        annotations = []
    -
    -        if PY2:
    -            def addArgs(arglist):
    -                for arg in arglist:
    -                    if isinstance(arg, ast.Tuple):
    -                        addArgs(arg.elts)
    -                    else:
    -                        args.append(arg.id)
    -            addArgs(node.args.args)
    -            defaults = node.args.defaults
    -        else:
    -            for arg in node.args.args + node.args.kwonlyargs:
    -                args.append(arg.arg)
    -                annotations.append(arg.annotation)
    -            defaults = node.args.defaults + node.args.kw_defaults
    -
    -        # Only for Python3 FunctionDefs
    -        is_py3_func = hasattr(node, 'returns')
    -
    -        for arg_name in ('vararg', 'kwarg'):
    -            wildcard = getattr(node.args, arg_name)
    -            if not wildcard:
    -                continue
    -            args.append(wildcard if PY33 else wildcard.arg)
    -            if is_py3_func:
    -                if PY33:  # Python 2.5 to 3.3
    -                    argannotation = arg_name + 'annotation'
    -                    annotations.append(getattr(node.args, argannotation))
    -                else:     # Python >= 3.4
    -                    annotations.append(wildcard.annotation)
    -
    -        if is_py3_func:
    -            annotations.append(node.returns)
    -
    -        if len(set(args)) < len(args):
    -            for (idx, arg) in enumerate(args):
    -                if arg in args[:idx]:
    -                    self.report(messages.DuplicateArgument, node, arg)
    -
    -        for child in annotations + defaults:
    -            if child:
    -                self.handleNode(child, node)
    -
    -        def runFunction():
    -
    -            self.pushScope()
    -            for name in args:
    -                self.addBinding(node, Argument(name, node))
    -            if isinstance(node.body, list):
    -                # case for FunctionDefs
    -                for stmt in node.body:
    -                    self.handleNode(stmt, node)
    -            else:
    -                # case for Lambdas
    -                self.handleNode(node.body, node)
    -
    -            def checkUnusedAssignments():
    -                """
    -                Check to see if any assignments have not been used.
    -                """
    -                for name, binding in self.scope.unusedAssignments():
    -                    self.report(messages.UnusedVariable, binding.source, name)
    -            self.deferAssignment(checkUnusedAssignments)
    -
    -            if PY32:
    -                def checkReturnWithArgumentInsideGenerator():
    -                    """
    -                    Check to see if there is any return statement with
    -                    arguments but the function is a generator.
    -                    """
    -                    if self.scope.isGenerator and self.scope.returnValue:
    -                        self.report(messages.ReturnWithArgsInsideGenerator,
    -                                    self.scope.returnValue)
    -                self.deferAssignment(checkReturnWithArgumentInsideGenerator)
    -            self.popScope()
    -
    -        self.deferFunction(runFunction)
    -
    -    def CLASSDEF(self, node):
    -        """
    -        Check names used in a class definition, including its decorators, base
    -        classes, and the body of its definition.  Additionally, add its name to
    -        the current scope.
    -        """
    -        for deco in node.decorator_list:
    -            self.handleNode(deco, node)
    -        for baseNode in node.bases:
    -            self.handleNode(baseNode, node)
    -        if not PY2:
    -            for keywordNode in node.keywords:
    -                self.handleNode(keywordNode, node)
    -        self.pushScope(ClassScope)
    -        if self.withDoctest:
    -            self.deferFunction(lambda: self.handleDoctests(node))
    -        for stmt in node.body:
    -            self.handleNode(stmt, node)
    -        self.popScope()
    -        self.addBinding(node, ClassDefinition(node.name, node))
    -
    -    def AUGASSIGN(self, node):
    -        self.handleNodeLoad(node.target)
    -        self.handleNode(node.value, node)
    -        self.handleNode(node.target, node)
    -
    -    def IMPORT(self, node):
    -        for alias in node.names:
    -            name = alias.asname or alias.name
    -            importation = Importation(name, node)
    -            self.addBinding(node, importation)
    -
    -    def IMPORTFROM(self, node):
    -        if node.module == '__future__':
    -            if not self.futuresAllowed:
    -                self.report(messages.LateFutureImport,
    -                            node, [n.name for n in node.names])
    -        else:
    -            self.futuresAllowed = False
    -
    -        for alias in node.names:
    -            if alias.name == '*':
    -                self.scope.importStarred = True
    -                self.report(messages.ImportStarUsed, node, node.module)
    -                continue
    -            name = alias.asname or alias.name
    -            importation = Importation(name, node)
    -            if node.module == '__future__':
    -                importation.used = (self.scope, node)
    -            self.addBinding(node, importation)
    -
    -    def TRY(self, node):
    -        handler_names = []
    -        # List the exception handlers
    -        for handler in node.handlers:
    -            if isinstance(handler.type, ast.Tuple):
    -                for exc_type in handler.type.elts:
    -                    handler_names.append(getNodeName(exc_type))
    -            elif handler.type:
    -                handler_names.append(getNodeName(handler.type))
    -        # Memorize the except handlers and process the body
    -        self.exceptHandlers.append(handler_names)
    -        for child in node.body:
    -            self.handleNode(child, node)
    -        self.exceptHandlers.pop()
    -        # Process the other nodes: "except:", "else:", "finally:"
    -        self.handleChildren(node, omit='body')
    -
    -    TRYEXCEPT = TRY
    -
    -    def EXCEPTHANDLER(self, node):
    -        # 3.x: in addition to handling children, we must handle the name of
    -        # the exception, which is not a Name node, but a simple string.
    -        if isinstance(node.name, str):
    -            self.handleNodeStore(node)
    -        self.handleChildren(node)
    diff --git a/pymode/libs/pyflakes/messages.py b/pymode/libs/pyflakes/messages.py
    deleted file mode 100644
    index 8899b7b0..00000000
    --- a/pymode/libs/pyflakes/messages.py
    +++ /dev/null
    @@ -1,134 +0,0 @@
    -"""
    -Provide the class Message and its subclasses.
    -"""
    -
    -
    -class Message(object):
    -    message = ''
    -    message_args = ()
    -
    -    def __init__(self, filename, loc):
    -        self.filename = filename
    -        self.lineno = loc.lineno
    -        self.col = getattr(loc, 'col_offset', 0)
    -
    -    def __str__(self):
    -        return '%s:%s: %s' % (self.filename, self.lineno,
    -                              self.message % self.message_args)
    -
    -
    -class UnusedImport(Message):
    -    message = '%r imported but unused'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class RedefinedWhileUnused(Message):
    -    message = 'redefinition of unused %r from line %r'
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class RedefinedInListComp(Message):
    -    message = 'list comprehension redefines %r from line %r'
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class ImportShadowedByLoopVar(Message):
    -    message = 'import %r from line %r shadowed by loop variable'
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class ImportStarUsed(Message):
    -    message = "'from %s import *' used; unable to detect undefined names"
    -
    -    def __init__(self, filename, loc, modname):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (modname,)
    -
    -
    -class UndefinedName(Message):
    -    message = 'undefined name %r'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class DoctestSyntaxError(Message):
    -    message = 'syntax error in doctest'
    -
    -    def __init__(self, filename, loc, position=None):
    -        Message.__init__(self, filename, loc)
    -        if position:
    -            (self.lineno, self.col) = position
    -        self.message_args = ()
    -
    -
    -class UndefinedExport(Message):
    -    message = 'undefined name %r in __all__'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class UndefinedLocal(Message):
    -    message = ('local variable %r (defined in enclosing scope on line %r) '
    -               'referenced before assignment')
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class DuplicateArgument(Message):
    -    message = 'duplicate argument %r in function definition'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class LateFutureImport(Message):
    -    message = 'future import(s) %r after other statements'
    -
    -    def __init__(self, filename, loc, names):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (names,)
    -
    -
    -class UnusedVariable(Message):
    -    """
    -    Indicates that a variable has been explicity assigned to but not actually
    -    used.
    -    """
    -    message = 'local variable %r is assigned to but never used'
    -
    -    def __init__(self, filename, loc, names):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (names,)
    -
    -
    -class ReturnWithArgsInsideGenerator(Message):
    -    """
    -    Indicates a return statement with arguments inside a generator.
    -    """
    -    message = '\'return\' with argument inside generator'
    -
    -
    -class ReturnOutsideFunction(Message):
    -    """
    -    Indicates a return statement outside of a function/method.
    -    """
    -    message = '\'return\' outside function'
    diff --git a/pymode/libs/pyflakes/reporter.py b/pymode/libs/pyflakes/reporter.py
    deleted file mode 100644
    index ae645bdf..00000000
    --- a/pymode/libs/pyflakes/reporter.py
    +++ /dev/null
    @@ -1,81 +0,0 @@
    -"""
    -Provide the Reporter class.
    -"""
    -
    -import re
    -import sys
    -
    -
    -class Reporter(object):
    -    """
    -    Formats the results of pyflakes checks to users.
    -    """
    -
    -    def __init__(self, warningStream, errorStream):
    -        """
    -        Construct a L{Reporter}.
    -
    -        @param warningStream: A file-like object where warnings will be
    -            written to.  The stream's C{write} method must accept unicode.
    -            C{sys.stdout} is a good value.
    -        @param errorStream: A file-like object where error output will be
    -            written to.  The stream's C{write} method must accept unicode.
    -            C{sys.stderr} is a good value.
    -        """
    -        self._stdout = warningStream
    -        self._stderr = errorStream
    -
    -    def unexpectedError(self, filename, msg):
    -        """
    -        An unexpected error occurred trying to process C{filename}.
    -
    -        @param filename: The path to a file that we could not process.
    -        @ptype filename: C{unicode}
    -        @param msg: A message explaining the problem.
    -        @ptype msg: C{unicode}
    -        """
    -        self._stderr.write("%s: %s\n" % (filename, msg))
    -
    -    def syntaxError(self, filename, msg, lineno, offset, text):
    -        """
    -        There was a syntax errror in C{filename}.
    -
    -        @param filename: The path to the file with the syntax error.
    -        @ptype filename: C{unicode}
    -        @param msg: An explanation of the syntax error.
    -        @ptype msg: C{unicode}
    -        @param lineno: The line number where the syntax error occurred.
    -        @ptype lineno: C{int}
    -        @param offset: The column on which the syntax error occurred, or None.
    -        @ptype offset: C{int}
    -        @param text: The source code containing the syntax error.
    -        @ptype text: C{unicode}
    -        """
    -        line = text.splitlines()[-1]
    -        if offset is not None:
    -            offset = offset - (len(text) - len(line))
    -            self._stderr.write('%s:%d:%d: %s\n' %
    -                               (filename, lineno, offset + 1, msg))
    -        else:
    -            self._stderr.write('%s:%d: %s\n' % (filename, lineno, msg))
    -        self._stderr.write(line)
    -        self._stderr.write('\n')
    -        if offset is not None:
    -            self._stderr.write(re.sub(r'\S', ' ', line[:offset]) +
    -                               "^\n")
    -
    -    def flake(self, message):
    -        """
    -        pyflakes found something wrong with the code.
    -
    -        @param: A L{pyflakes.messages.Message}.
    -        """
    -        self._stdout.write(str(message))
    -        self._stdout.write('\n')
    -
    -
    -def _makeDefaultReporter():
    -    """
    -    Make a reporter that can be used when no reporter is specified.
    -    """
    -    return Reporter(sys.stdout, sys.stderr)
    diff --git a/pymode/libs/pylama b/pymode/libs/pylama
    new file mode 120000
    index 00000000..0ebc41a2
    --- /dev/null
    +++ b/pymode/libs/pylama
    @@ -0,0 +1 @@
    +../../submodules/pylama/pylama/
    \ No newline at end of file
    diff --git a/pymode/libs/pylama/__init__.py b/pymode/libs/pylama/__init__.py
    deleted file mode 100644
    index 3fb8051b..00000000
    --- a/pymode/libs/pylama/__init__.py
    +++ /dev/null
    @@ -1,11 +0,0 @@
    -"""
    -Code audit tool for python.
    -
    -:copyright: 2013 by Kirill Klenov.
    -:license: BSD, see LICENSE for more details.
    -"""
    -
    -__version__ = "7.0.6"
    -__project__ = "pylama"
    -__author__ = "Kirill Klenov "
    -__license__ = "GNU LGPL"
    diff --git a/pymode/libs/pylama/__main__.py b/pymode/libs/pylama/__main__.py
    deleted file mode 100644
    index 64994e75..00000000
    --- a/pymode/libs/pylama/__main__.py
    +++ /dev/null
    @@ -1,6 +0,0 @@
    -"""Support the module execution."""
    -
    -from .main import shell
    -
    -if __name__ == '__main__':
    -    shell()
    diff --git a/pymode/libs/pylama/async.py b/pymode/libs/pylama/async.py
    deleted file mode 100644
    index 12f929fa..00000000
    --- a/pymode/libs/pylama/async.py
    +++ /dev/null
    @@ -1,76 +0,0 @@
    -""" Support for asyncronious checking. """
    -
    -import logging
    -import threading
    -
    -from .core import run
    -
    -
    -try:
    -    import Queue
    -except ImportError:
    -    import queue as Queue
    -
    -
    -try:
    -    import multiprocessing
    -
    -    CPU_COUNT = multiprocessing.cpu_count()
    -
    -except (ImportError, NotImplementedError):
    -    CPU_COUNT = 1
    -
    -LOGGER = logging.getLogger('pylama')
    -
    -
    -class Worker(threading.Thread):
    -
    -    """ Get tasks from queue and run. """
    -
    -    def __init__(self, path_queue, result_queue):
    -        """ Init worker. """
    -        threading.Thread.__init__(self)
    -        self.path_queue = path_queue
    -        self.result_queue = result_queue
    -
    -    def run(self):
    -        """ Run tasks from queue. """
    -        while True:
    -            path, params = self.path_queue.get()
    -            errors = run(path, **params)
    -            self.result_queue.put(errors)
    -            self.path_queue.task_done()
    -
    -
    -def check_async(paths, options, rootdir=None):
    -    """ Check given paths asynchronously.
    -
    -    :return list: list of errors
    -
    -    """
    -    LOGGER.info('Async code checking is enabled.')
    -    path_queue = Queue.Queue()
    -    result_queue = Queue.Queue()
    -
    -    for num in range(CPU_COUNT):
    -        worker = Worker(path_queue, result_queue)
    -        worker.setDaemon(True)
    -        LOGGER.info('Start worker #%s', (num + 1))
    -        worker.start()
    -
    -    for path in paths:
    -        path_queue.put((path, dict(options=options, rootdir=rootdir)))
    -
    -    path_queue.join()
    -
    -    errors = []
    -    while True:
    -        try:
    -            errors += result_queue.get(False)
    -        except Queue.Empty:
    -            break
    -
    -    return errors
    -
    -
    -# pylama:ignore=W0212,D210,F0001
    diff --git a/pymode/libs/pylama/config.py b/pymode/libs/pylama/config.py
    deleted file mode 100644
    index 3df38829..00000000
    --- a/pymode/libs/pylama/config.py
    +++ /dev/null
    @@ -1,241 +0,0 @@
    -""" Parse arguments from command line and configuration files. """
    -import fnmatch
    -import os
    -import sys
    -import re
    -
    -import logging
    -from argparse import ArgumentParser
    -
    -from . import __version__
    -from .libs.inirama import Namespace
    -from .lint.extensions import LINTERS
    -
    -#: A default checkers
    -DEFAULT_LINTERS = 'pep8', 'pyflakes', 'mccabe'
    -
    -CURDIR = os.getcwd()
    -CONFIG_FILES = 'pylama.ini', 'setup.cfg', 'tox.ini', 'pytest.ini'
    -
    -#: The skip pattern
    -SKIP_PATTERN = re.compile(r'# *noqa\b', re.I).search
    -
    -# Parse a modelines
    -MODELINE_RE = re.compile(r'^\s*#\s+(?:pylama:)\s*((?:[\w_]*=[^:\n\s]+:?)+)', re.I | re.M)
    -
    -# Setup a logger
    -LOGGER = logging.getLogger('pylama')
    -LOGGER.propagate = False
    -STREAM = logging.StreamHandler(sys.stdout)
    -LOGGER.addHandler(STREAM)
    -
    -
    -class _Default(object):
    -
    -    def __init__(self, value=None):
    -        self.value = value
    -
    -    def __str__(self):
    -        return str(self.value)
    -
    -    def __repr__(self):
    -        return "<_Default [%s]>" % self.value
    -
    -
    -def split_csp_str(s):
    -    """ Split commaseparated string.
    -
    -    :returns: list of splitted values
    -
    -    """
    -    if isinstance(s, (list, tuple)):
    -        return s
    -    return list(set(i for i in s.strip().split(',') if i))
    -
    -
    -def parse_linters(linters):
    -    """ Initialize choosen linters.
    -
    -    :returns: list of inited linters
    -
    -    """
    -    result = list()
    -    for name in split_csp_str(linters):
    -        linter = LINTERS.get(name)
    -        if linter:
    -            result.append((name, linter))
    -        else:
    -            logging.warn("Linter `%s` not found.", name)
    -    return result
    -
    -
    -PARSER = ArgumentParser(description="Code audit tool for python.")
    -PARSER.add_argument(
    -    "paths", nargs='*', default=_Default([CURDIR]),
    -    help="Paths to files or directories for code check.")
    -
    -PARSER.add_argument(
    -    "--verbose", "-v", action='store_true', help="Verbose mode.")
    -
    -PARSER.add_argument('--version', action='version',
    -                    version='%(prog)s ' + __version__)
    -
    -PARSER.add_argument(
    -    "--format", "-f", default=_Default('pep8'), choices=['pep8', 'pylint'],
    -    help="Choose errors format (pep8, pylint).")
    -
    -PARSER.add_argument(
    -    "--select", "-s", default=_Default(''), type=split_csp_str,
    -    help="Select errors and warnings. (comma-separated list)")
    -
    -PARSER.add_argument(
    -    "--sort", default=_Default(''), type=split_csp_str,
    -    help="Sort result by error types. Ex. E,W,D")
    -
    -PARSER.add_argument(
    -    "--linters", "-l", default=_Default(','.join(DEFAULT_LINTERS)),
    -    type=parse_linters, help=(
    -        "Select linters. (comma-separated). Choices are %s."
    -        % ','.join(s for s in LINTERS.keys())
    -    ))
    -
    -PARSER.add_argument(
    -    "--ignore", "-i", default=_Default(''), type=split_csp_str,
    -    help="Ignore errors and warnings. (comma-separated)")
    -
    -PARSER.add_argument(
    -    "--skip", default=_Default(''),
    -    type=lambda s: [re.compile(fnmatch.translate(p)) for p in s.split(',') if p],
    -    help="Skip files by masks (comma-separated, Ex. */messages.py)")
    -
    -PARSER.add_argument("--report", "-r", help="Send report to file [REPORT]")
    -PARSER.add_argument(
    -    "--hook", action="store_true", help="Install Git (Mercurial) hook.")
    -
    -PARSER.add_argument(
    -    "--async", action="store_true",
    -    help="Enable async mode. Usefull for checking a lot of files. "
    -    "Dont supported with pylint.")
    -
    -PARSER.add_argument(
    -    "--options", "-o", default="",
    -    help="Select configuration file. By default is '/pylama.ini'")
    -
    -PARSER.add_argument(
    -    "--force", "-F", action='store_true', default=_Default(False),
    -    help="Force code checking (if linter doesnt allow)")
    -
    -PARSER.add_argument(
    -    "--abspath", "-a", action='store_true', default=_Default(False),
    -    help="Use absolute paths in output.")
    -
    -
    -ACTIONS = dict((a.dest, a) for a in PARSER._actions)
    -
    -
    -def parse_options(args=None, config=True, rootdir=CURDIR, **overrides): # noqa
    -    """ Parse options from command line and configuration files.
    -
    -    :return argparse.Namespace:
    -
    -    """
    -    if args is None:
    -        args = []
    -
    -    # Parse args from command string
    -    options = PARSER.parse_args(args)
    -    options.file_params = dict()
    -    options.linters_params = dict()
    -
    -    # Override options
    -    for k, v in overrides.items():
    -        passed_value = getattr(options, k, _Default())
    -        if isinstance(passed_value, _Default):
    -            setattr(options, k, _Default(v))
    -
    -    # Compile options from ini
    -    if config:
    -        cfg = get_config(str(options.options), rootdir=rootdir)
    -        for k, v in cfg.default.items():
    -            LOGGER.info('Find option %s (%s)', k, v)
    -            passed_value = getattr(options, k, _Default())
    -            if isinstance(passed_value, _Default):
    -                if k == 'paths':
    -                    v = v.split()
    -                setattr(options, k, _Default(v))
    -
    -        # Parse file related options
    -        for name, opts in cfg.sections.items():
    -
    -            if not name.startswith('pylama'):
    -                continue
    -
    -            if name == cfg.default_section:
    -                continue
    -
    -            name = name[7:]
    -
    -            if name in LINTERS:
    -                options.linters_params[name] = dict(opts)
    -                continue
    -
    -            mask = re.compile(fnmatch.translate(name))
    -            options.file_params[mask] = dict(opts)
    -
    -    # Postprocess options
    -    opts = dict(options.__dict__.items())
    -    for name, value in opts.items():
    -        if isinstance(value, _Default):
    -            setattr(options, name, process_value(name, value.value))
    -
    -    if options.async and 'pylint' in options.linters:
    -        LOGGER.warn('Cant parse code asynchronously while pylint is enabled.')
    -        options.async = False
    -
    -    return options
    -
    -
    -def process_value(name, value):
    -    """ Compile option value. """
    -    action = ACTIONS.get(name)
    -    if not action:
    -        return value
    -
    -    if callable(action.type):
    -        return action.type(value)
    -
    -    if action.const:
    -        return bool(int(value))
    -
    -    return value
    -
    -
    -def get_config(ini_path=None, rootdir=CURDIR):
    -    """ Load configuration from INI.
    -
    -    :return Namespace:
    -
    -    """
    -    config = Namespace()
    -    config.default_section = 'pylama'
    -
    -    if not ini_path:
    -        for path in CONFIG_FILES:
    -            path = os.path.join(rootdir, path)
    -            if os.path.isfile(path) and os.access(path, os.R_OK):
    -                config.read(path)
    -    else:
    -        config.read(ini_path)
    -
    -    return config
    -
    -
    -def setup_logger(options):
    -    """ Setup logger with options. """
    -    LOGGER.setLevel(logging.INFO if options.verbose else logging.WARN)
    -    if options.report:
    -        LOGGER.removeHandler(STREAM)
    -        LOGGER.addHandler(logging.FileHandler(options.report, mode='w'))
    -    LOGGER.info('Try to read configuration from: ' + options.options)
    -
    -# pylama:ignore=W0212,D210,F0001
    diff --git a/pymode/libs/pylama/core.py b/pymode/libs/pylama/core.py
    deleted file mode 100644
    index 69d3c59e..00000000
    --- a/pymode/libs/pylama/core.py
    +++ /dev/null
    @@ -1,200 +0,0 @@
    -""" Pylama's core functionality.
    -
    -Prepare params, check a modeline and run the checkers.
    -
    -"""
    -import logging
    -
    -import os.path as op
    -from .config import process_value, LOGGER, MODELINE_RE, SKIP_PATTERN, CURDIR
    -from .errors import Error, remove_duplicates
    -from .lint.extensions import LINTERS
    -
    -
    -def run(path='', code=None, rootdir=CURDIR, options=None):
    -    """ Run code checkers with given params.
    -
    -    :param path: (str) A file's path.
    -    :param code: (str) A code source
    -    :return errors: list of dictionaries with error's information
    -
    -    """
    -    errors = []
    -    fileconfig = dict()
    -    linters = LINTERS
    -    linters_params = dict()
    -    lname = 'undefined'
    -    params = dict()
    -    path = op.relpath(path, rootdir)
    -
    -    if options:
    -        linters = options.linters
    -        linters_params = options.linters_params
    -        for mask in options.file_params:
    -            if mask.match(path):
    -                fileconfig.update(options.file_params[mask])
    -
    -        if options.skip and any(p.match(path) for p in options.skip):
    -            LOGGER.info('Skip checking for path: %s', path)
    -            return []
    -
    -    try:
    -        with CodeContext(code, path) as ctx:
    -            code = ctx.code
    -            params = prepare_params(parse_modeline(code), fileconfig, options)
    -            LOGGER.debug('Checking params: %s', params)
    -
    -            if params.get('skip'):
    -                return errors
    -
    -            for item in params.get('linters') or linters:
    -
    -                if not isinstance(item, tuple):
    -                    item = (item, LINTERS.get(item))
    -
    -                lname, linter = item
    -
    -                if not linter:
    -                    continue
    -
    -                lparams = linters_params.get(lname, dict())
    -                LOGGER.info("Run %s %s", lname, lparams)
    -
    -                for er in linter.run(
    -                        path, code=code, ignore=params.get("ignore", set()),
    -                        select=params.get("select", set()), params=lparams):
    -                    errors.append(Error(filename=path, linter=lname, **er))
    -
    -    except IOError as e:
    -        LOGGER.debug("IOError %s", e)
    -        errors.append(Error(text=str(e), filename=path, linter=lname))
    -
    -    except SyntaxError as e:
    -        LOGGER.debug("SyntaxError %s", e)
    -        errors.append(
    -            Error(linter=lname, lnum=e.lineno, col=e.offset, text=e.args[0],
    -                  filename=path))
    -
    -    except Exception as e: # noqa
    -        import traceback
    -        LOGGER.info(traceback.format_exc())
    -
    -    errors = filter_errors(errors, **params)
    -
    -    errors = list(remove_duplicates(errors))
    -
    -    if code and errors:
    -        errors = filter_skiplines(code, errors)
    -
    -    key = lambda e: e.lnum
    -    if options and options.sort:
    -        sort = dict((v, n) for n, v in enumerate(options.sort, 1))
    -        key = lambda e: (sort.get(e.type, 999), e.lnum)
    -    return sorted(errors, key=key)
    -
    -
    -def parse_modeline(code):
    -    """ Parse params from file's modeline.
    -
    -    :return dict: Linter params.
    -
    -    """
    -    seek = MODELINE_RE.search(code)
    -    if seek:
    -        return dict(v.split('=') for v in seek.group(1).split(':'))
    -
    -    return dict()
    -
    -
    -def prepare_params(modeline, fileconfig, options):
    -    """ Prepare and merge a params from modelines and configs.
    -
    -    :return dict:
    -
    -    """
    -    params = dict(skip=False, ignore=[], select=[], linters=[])
    -    if options:
    -        params['ignore'] = list(options.ignore)
    -        params['select'] = list(options.select)
    -
    -    for config in filter(None, [modeline, fileconfig]):
    -        for key in ('ignore', 'select', 'linters'):
    -            params[key] += process_value(key, config.get(key, []))
    -        params['skip'] = bool(int(config.get('skip', False)))
    -
    -    params['ignore'] = set(params['ignore'])
    -    params['select'] = set(params['select'])
    -
    -    return params
    -
    -
    -def filter_errors(errors, select=None, ignore=None, **params):
    -    """ Filter a erros by select and ignore options.
    -
    -    :return bool:
    -
    -    """
    -    select = select or []
    -    ignore = ignore or []
    -
    -    for e in errors:
    -        for s in select:
    -            if e.number.startswith(s):
    -                yield e
    -                break
    -        else:
    -            for s in ignore:
    -                if e.number.startswith(s):
    -                    break
    -            else:
    -                yield e
    -
    -
    -def filter_skiplines(code, errors):
    -    """ Filter lines by `noqa`.
    -
    -    :return list: A filtered errors
    -
    -    """
    -    if not errors:
    -        return errors
    -
    -    enums = set(er.lnum for er in errors)
    -    removed = set([
    -        num for num, l in enumerate(code.split('\n'), 1)
    -        if num in enums and SKIP_PATTERN(l)
    -    ])
    -
    -    if removed:
    -        errors = [er for er in errors if er.lnum not in removed]
    -
    -    return errors
    -
    -
    -class CodeContext(object):
    -
    -    """ Read file if code is None. """
    -
    -    def __init__(self, code, path):
    -        """ Init context. """
    -        self.code = code
    -        self.path = path
    -        self._file = None
    -
    -    def __enter__(self):
    -        """ Open a file and read it. """
    -        if self.code is None:
    -            LOGGER.info("File is reading: %s", self.path)
    -            self._file = open(self.path, 'rU')
    -            self.code = self._file.read()
    -        return self
    -
    -    def __exit__(self, t, value, traceback):
    -        """ Close the file which was opened. """
    -        if self._file is not None:
    -            self._file.close()
    -
    -        if t and LOGGER.level == logging.DEBUG:
    -            LOGGER.debug(traceback)
    -
    -# pylama:ignore=R0912,D210,F0001
    diff --git a/pymode/libs/pylama/errors.py b/pymode/libs/pylama/errors.py
    deleted file mode 100644
    index 7f6c0a11..00000000
    --- a/pymode/libs/pylama/errors.py
    +++ /dev/null
    @@ -1,97 +0,0 @@
    -""" Don't duplicate same errors from different linters. """
    -
    -from collections import defaultdict
    -
    -
    -DUPLICATES = (
    -
    -    # multiple statements on one line
    -    [('pep8', 'E701'), ('pylint', 'C0321')],
    -
    -    # unused variable
    -    [('pylint', 'W0612'), ('pyflakes', 'W0612')],
    -
    -    # undefined variable
    -    [('pylint', 'E0602'), ('pyflakes', 'E0602')],
    -
    -    # unused import
    -    [('pylint', 'W0611'), ('pyflakes', 'W0611')],
    -
    -    # whitespace before ')'
    -    [('pylint', 'C0326'), ('pep8', 'E202')],
    -
    -    # whitespace before '('
    -    [('pylint', 'C0326'), ('pep8', 'E211')],
    -
    -    # multiple spaces after operator
    -    [('pylint', 'C0326'), ('pep8', 'E222')],
    -
    -    # missing whitespace around operator
    -    [('pylint', 'C0326'), ('pep8', 'E225')],
    -
    -    # unexpected spaces
    -    [('pylint', 'C0326'), ('pep8', 'E251')],
    -
    -    # long lines
    -    [('pylint', 'C0301'), ('pep8', 'E501')],
    -
    -    # statement ends with a semicolon
    -    [('pylint', 'W0301'), ('pep8', 'E703')],
    -
    -    # multiple statements on one line
    -    [('pylint', 'C0321'), ('pep8', 'E702')],
    -
    -    # bad indentation
    -    [('pylint', 'W0311'), ('pep8', 'E111')],
    -
    -    # wildcart import
    -    [('pylint', 'W00401'), ('pyflakes', 'W0401')],
    -
    -    # module docstring
    -    [('pep257', 'D100'), ('pylint', 'C0111')],
    -
    -)
    -
    -DUPLICATES = dict((key, values) for values in DUPLICATES for key in values)
    -
    -
    -def remove_duplicates(errors):
    -    """ Filter duplicates from given error's list. """
    -    passed = defaultdict(list)
    -    for error in errors:
    -        key = error.linter, error.number
    -        if key in DUPLICATES:
    -            if key in passed[error.lnum]:
    -                continue
    -            passed[error.lnum] = DUPLICATES[key]
    -        yield error
    -
    -
    -class Error(object):
    -
    -    """ Store an error's information. """
    -
    -    def __init__(self, linter="", col=1, lnum=1, type="E",
    -                 text="unknown error", filename="", **kwargs):
    -        """ Init error information with default values. """
    -        text = ' '.join(str(text).strip().split('\n'))
    -        if linter:
    -            text = "%s [%s]" % (text, linter)
    -        number = text.split(' ', 1)[0]
    -        self._info = dict(linter=linter, col=col, lnum=lnum, type=type[:1],
    -                          text=text, filename=filename, number=number)
    -
    -    def __getattr__(self, name):
    -        return self._info[name]
    -
    -    def __getitem__(self, name):
    -        return self._info[name]
    -
    -    def get(self, name, default=None):
    -        """ Implement dictionary `get` method. """
    -        return self._info.get(name, default)
    -
    -    def __repr__(self):
    -        return "" % (self.number, self.linter)
    -
    -# pylama:ignore=W0622,D,R0924
    diff --git a/pymode/libs/pylama/hook.py b/pymode/libs/pylama/hook.py
    deleted file mode 100644
    index f65ef46f..00000000
    --- a/pymode/libs/pylama/hook.py
    +++ /dev/null
    @@ -1,111 +0,0 @@
    -""" SCM hooks. Integration with git and mercurial. """
    -
    -from __future__ import absolute_import
    -
    -import sys
    -from os import path as op, chmod
    -from subprocess import Popen, PIPE
    -
    -from .main import LOGGER, process_paths
    -from .config import parse_options, setup_logger
    -
    -
    -try:
    -    from configparser import ConfigParser  # noqa
    -except ImportError:   # Python 2
    -    from ConfigParser import ConfigParser
    -
    -
    -def run(command):
    -    """ Run a shell command.
    -
    -    :return str: Stdout
    -
    -    """
    -    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    -    (stdout, stderr) = p.communicate()
    -    return (p.returncode, [line.strip() for line in stdout.splitlines()],
    -            [line.strip() for line in stderr.splitlines()])
    -
    -
    -def git_hook():
    -    """ Run pylama after git commit. """
    -    _, files_modified, _ = run("git diff-index --cached --name-only HEAD")
    -
    -    options = parse_options()
    -    setup_logger(options)
    -    candidates = list(map(str, files_modified))
    -    if candidates:
    -        process_paths(options, candidates=candidates)
    -
    -
    -def hg_hook(ui, repo, node=None, **kwargs):
    -    """ Run pylama after mercurial commit. """
    -    seen = set()
    -    paths = []
    -    if len(repo):
    -        for rev in range(repo[node], len(repo)):
    -            for file_ in repo[rev].files():
    -                file_ = op.join(repo.root, file_)
    -                if file_ in seen or not op.exists(file_):
    -                    continue
    -                seen.add(file_)
    -                paths.append(file_)
    -
    -    options = parse_options()
    -    setup_logger(options)
    -    if paths:
    -        process_paths(options, candidates=paths)
    -
    -
    -def install_git(path):
    -    """ Install hook in Git repository. """
    -    hook = op.join(path, 'pre-commit')
    -    with open(hook, 'w') as fd:
    -        fd.write("""#!/usr/bin/env python
    -import sys
    -from pylama.hook import git_hook
    -
    -if __name__ == '__main__':
    -    sys.exit(git_hook())
    -""")
    -    chmod(hook, 484)
    -
    -
    -def install_hg(path):
    -    """ Install hook in Mercurial repository. """
    -    hook = op.join(path, 'hgrc')
    -    if not op.isfile(hook):
    -        open(hook, 'w+').close()
    -
    -    c = ConfigParser()
    -    c.readfp(open(hook, 'r'))
    -    if not c.has_section('hooks'):
    -        c.add_section('hooks')
    -
    -    if not c.has_option('hooks', 'commit'):
    -        c.set('hooks', 'commit', 'python:pylama.hooks.hg_hook')
    -
    -    if not c.has_option('hooks', 'qrefresh'):
    -        c.set('hooks', 'qrefresh', 'python:pylama.hooks.hg_hook')
    -
    -    c.write(open(hook, 'w+'))
    -
    -
    -def install_hook(path):
    -    """ Auto definition of SCM and hook installation. """
    -    git = op.join(path, '.git', 'hooks')
    -    hg = op.join(path, '.hg')
    -    if op.exists(git):
    -        install_git(git)
    -        LOGGER.warn('Git hook has been installed.')
    -
    -    elif op.exists(hg):
    -        install_hg(hg)
    -        LOGGER.warn('Mercurial hook has been installed.')
    -
    -    else:
    -        LOGGER.error('VCS has not found. Check your path.')
    -        sys.exit(1)
    -
    -# pylama:ignore=F0401,E1103,D210,F0001
    diff --git a/pymode/libs/pylama/libs/__init__.py b/pymode/libs/pylama/libs/__init__.py
    deleted file mode 100644
    index 95fec137..00000000
    --- a/pymode/libs/pylama/libs/__init__.py
    +++ /dev/null
    @@ -1 +0,0 @@
    -""" Support libs. """
    diff --git a/pymode/libs/pylama/libs/importlib.py b/pymode/libs/pylama/libs/importlib.py
    deleted file mode 100644
    index ad31a1ac..00000000
    --- a/pymode/libs/pylama/libs/importlib.py
    +++ /dev/null
    @@ -1,38 +0,0 @@
    -"""Backport of importlib.import_module from 3.x."""
    -# While not critical (and in no way guaranteed!), it would be nice to keep this
    -# code compatible with Python 2.3.
    -import sys
    -
    -def _resolve_name(name, package, level):
    -    """Return the absolute name of the module to be imported."""
    -    if not hasattr(package, 'rindex'):
    -        raise ValueError("'package' not set to a string")
    -    dot = len(package)
    -    for x in xrange(level, 1, -1):
    -        try:
    -            dot = package.rindex('.', 0, dot)
    -        except ValueError:
    -            raise ValueError("attempted relative import beyond top-level "
    -                              "package")
    -    return "%s.%s" % (package[:dot], name)
    -
    -
    -def import_module(name, package=None):
    -    """Import a module.
    -
    -    The 'package' argument is required when performing a relative import. It
    -    specifies the package to use as the anchor point from which to resolve the
    -    relative import to an absolute import.
    -
    -    """
    -    if name.startswith('.'):
    -        if not package:
    -            raise TypeError("relative imports require the 'package' argument")
    -        level = 0
    -        for character in name:
    -            if character != '.':
    -                break
    -            level += 1
    -        name = _resolve_name(name[level:], package, level)
    -    __import__(name)
    -    return sys.modules[name]
    diff --git a/pymode/libs/pylama/libs/inirama.py b/pymode/libs/pylama/libs/inirama.py
    deleted file mode 100644
    index 2437fd3c..00000000
    --- a/pymode/libs/pylama/libs/inirama.py
    +++ /dev/null
    @@ -1,405 +0,0 @@
    -"""
    -    Inirama is a python module that parses INI files.
    -
    -    .. _badges:
    -    .. include:: ../README.rst
    -        :start-after: .. _badges:
    -        :end-before: .. _contents:
    -
    -    .. _description:
    -    .. include:: ../README.rst
    -        :start-after: .. _description:
    -        :end-before: .. _badges:
    -
    -    :copyright: 2013 by Kirill Klenov.
    -    :license: BSD, see LICENSE for more details.
    -"""
    -from __future__ import unicode_literals, print_function
    -
    -
    -__version__ = "0.8.0"
    -__project__ = "Inirama"
    -__author__ = "Kirill Klenov "
    -__license__ = "BSD"
    -
    -
    -import io
    -import re
    -import logging
    -try:
    -    from collections import OrderedDict
    -except ImportError:
    -    from UserDict import DictMixin
    -
    -    class OrderedDict(dict, DictMixin):
    -
    -        null = object()
    -
    -        def __init__(self, *args, **kwargs):
    -            self.clear()
    -            self.update(*args, **kwargs)
    -
    -        def clear(self):
    -            self.__map = dict()
    -            self.__order = list()
    -            dict.clear(self)
    -
    -    def __setitem__(self, key, value):
    -        if key not in self:
    -            self.__map[key] = len(self.__order)
    -            self.__order.append(key)
    -        dict.__setitem__(self, key, value)
    -
    -    def __delitem__(self, key):
    -        dict.__delitem__(self, key)
    -        self.__map.pop(key)
    -        self.__order = self.null
    -
    -    def __iter__(self):
    -        for key in self.__order:
    -            if key is not self.null:
    -                yield key
    -
    -    def keys(self):
    -        return list(self)
    -
    -    setdefault = DictMixin.setdefault
    -    update = DictMixin.update
    -    pop = DictMixin.pop
    -    values = DictMixin.values
    -    items = DictMixin.items
    -    iterkeys = DictMixin.iterkeys
    -    itervalues = DictMixin.itervalues
    -    iteritems = DictMixin.iteritems
    -
    -
    -NS_LOGGER = logging.getLogger('inirama')
    -
    -
    -class Scanner(object):
    -
    -    """ Split a code string on tokens. """
    -
    -    def __init__(self, source, ignore=None, patterns=None):
    -        """ Init Scanner instance.
    -
    -        :param patterns: List of token patterns [(token, regexp)]
    -        :param ignore: List of ignored tokens
    -
    -        """
    -        self.reset(source)
    -        if patterns:
    -            self.patterns = []
    -            for k, r in patterns:
    -                self.patterns.append((k, re.compile(r)))
    -
    -        if ignore:
    -            self.ignore = ignore
    -
    -    def reset(self, source):
    -        """ Reset scanner's state.
    -
    -        :param source: Source for parsing
    -
    -        """
    -        self.tokens = []
    -        self.source = source
    -        self.pos = 0
    -
    -    def scan(self):
    -        """ Scan source and grab tokens. """
    -
    -        self.pre_scan()
    -
    -        token = None
    -        end = len(self.source)
    -
    -        while self.pos < end:
    -
    -            best_pat = None
    -            best_pat_len = 0
    -
    -            # Check patterns
    -            for p, regexp in self.patterns:
    -                m = regexp.match(self.source, self.pos)
    -                if m:
    -                    best_pat = p
    -                    best_pat_len = len(m.group(0))
    -                    break
    -
    -            if best_pat is None:
    -                raise SyntaxError(
    -                    "SyntaxError[@char {0}: {1}]".format(
    -                        self.pos, "Bad token."))
    -
    -            # Ignore patterns
    -            if best_pat in self.ignore:
    -                self.pos += best_pat_len
    -                continue
    -
    -            # Create token
    -            token = (
    -                best_pat,
    -                self.source[self.pos:self.pos + best_pat_len],
    -                self.pos,
    -                self.pos + best_pat_len,
    -            )
    -
    -            self.pos = token[-1]
    -            self.tokens.append(token)
    -
    -    def pre_scan(self):
    -        """ Prepare source. """
    -        pass
    -
    -    def __repr__(self):
    -        """ Print the last 5 tokens that have been scanned in.
    -
    -        :return str:
    -
    -        """
    -        return '"
    -
    -
    -class INIScanner(Scanner):
    -
    -    """ Get tokens for INI. """
    -
    -    patterns = [
    -        ('SECTION', re.compile(r'\[[^]]+\]')),
    -        ('IGNORE', re.compile(r'[ \r\t\n]+')),
    -        ('COMMENT', re.compile(r'[;#].*')),
    -        ('KEY_VALUE', re.compile(r'[^=\s]+\s*[:=].*')),
    -        ('CONTINUATION', re.compile(r'.*'))
    -    ]
    -
    -    ignore = ['IGNORE']
    -
    -    def pre_scan(self):
    -        """ Prepare string for scaning. """
    -        escape_re = re.compile(r'\\\n[\t ]+')
    -        self.source = escape_re.sub('', self.source)
    -
    -
    -undefined = object()
    -
    -
    -class Section(OrderedDict):
    -
    -    """ Representation of INI section. """
    -
    -    def __init__(self, namespace, *args, **kwargs):
    -        super(Section, self).__init__(*args, **kwargs)
    -        self.namespace = namespace
    -
    -    def __setitem__(self, name, value):
    -        value = str(value)
    -        if value.isdigit():
    -            value = int(value)
    -
    -        super(Section, self).__setitem__(name, value)
    -
    -
    -class InterpolationSection(Section):
    -
    -    """ INI section with interpolation support. """
    -
    -    var_re = re.compile('{([^}]+)}')
    -
    -    def get(self, name, default=None):
    -        """ Get item by name.
    -
    -        :return object: value or None if name not exists
    -
    -        """
    -
    -        if name in self:
    -            return self[name]
    -        return default
    -
    -    def __interpolate__(self, math):
    -        try:
    -            key = math.group(1).strip()
    -            return self.namespace.default.get(key) or self[key]
    -        except KeyError:
    -            return ''
    -
    -    def __getitem__(self, name, raw=False):
    -        value = super(InterpolationSection, self).__getitem__(name)
    -        if not raw:
    -            sample = undefined
    -            while sample != value:
    -                try:
    -                    sample, value = value, self.var_re.sub(
    -                        self.__interpolate__, value)
    -                except RuntimeError:
    -                    message = "Interpolation failed: {0}".format(name)
    -                    NS_LOGGER.error(message)
    -                    raise ValueError(message)
    -        return value
    -
    -    def iteritems(self, raw=False):
    -        """ Iterate self items. """
    -
    -        for key in self:
    -            yield key, self.__getitem__(key, raw=raw)
    -
    -    items = iteritems
    -
    -
    -class Namespace(object):
    -
    -    """ Default class for parsing INI.
    -
    -    :param **default_items: Default items for default section.
    -
    -    Usage
    -    -----
    -
    -    ::
    -
    -        from inirama import Namespace
    -
    -        ns = Namespace()
    -        ns.read('config.ini')
    -
    -        print ns['section']['key']
    -
    -        ns['other']['new'] = 'value'
    -        ns.write('new_config.ini')
    -
    -    """
    -
    -    #: Name of default section (:attr:`~inirama.Namespace.default`)
    -    default_section = 'DEFAULT'
    -
    -    #: Dont raise any exception on file reading erorrs
    -    silent_read = True
    -
    -    #: Class for generating sections
    -    section_type = Section
    -
    -    def __init__(self, **default_items):
    -        self.sections = OrderedDict()
    -        for k, v in default_items.items():
    -            self[self.default_section][k] = v
    -
    -    @property
    -    def default(self):
    -        """ Return default section or empty dict.
    -
    -        :return :class:`inirama.Section`: section
    -
    -        """
    -        return self.sections.get(self.default_section, dict())
    -
    -    def read(self, *files, **params):
    -        """ Read and parse INI files.
    -
    -        :param *files: Files for reading
    -        :param **params: Params for parsing
    -
    -        Set `update=False` for prevent values redefinition.
    -
    -        """
    -        for f in files:
    -            try:
    -                with io.open(f, encoding='utf-8') as ff:
    -                    NS_LOGGER.info('Read from `{0}`'.format(ff.name))
    -                    self.parse(ff.read(), **params)
    -            except (IOError, TypeError, SyntaxError, io.UnsupportedOperation):
    -                if not self.silent_read:
    -                    NS_LOGGER.error('Reading error `{0}`'.format(ff.name))
    -                    raise
    -
    -    def write(self, f):
    -        """ Write namespace as INI file.
    -
    -        :param f: File object or path to file.
    -
    -        """
    -        if isinstance(f, str):
    -            f = io.open(f, 'w', encoding='utf-8')
    -
    -        if not hasattr(f, 'read'):
    -            raise AttributeError("Wrong type of file: {0}".format(type(f)))
    -
    -        NS_LOGGER.info('Write to `{0}`'.format(f.name))
    -        for section in self.sections.keys():
    -            f.write('[{0}]\n'.format(section))
    -            for k, v in self[section].items():
    -                f.write('{0:15}= {1}\n'.format(k, v))
    -            f.write('\n')
    -        f.close()
    -
    -    def parse(self, source, update=True, **params):
    -        """ Parse INI source as string.
    -
    -        :param source: Source of INI
    -        :param update: Replace alredy defined items
    -
    -        """
    -        scanner = INIScanner(source)
    -        scanner.scan()
    -
    -        section = self.default_section
    -        name = None
    -
    -        for token in scanner.tokens:
    -            if token[0] == 'KEY_VALUE':
    -                name, value = re.split('[=:]', token[1], 1)
    -                name, value = name.strip(), value.strip()
    -                if not update and name in self[section]:
    -                    continue
    -                self[section][name] = value
    -
    -            elif token[0] == 'SECTION':
    -                section = token[1].strip('[]')
    -
    -            elif token[0] == 'CONTINUATION':
    -                if not name:
    -                    raise SyntaxError(
    -                        "SyntaxError[@char {0}: {1}]".format(
    -                            token[2], "Bad continuation."))
    -                self[section][name] += '\n' + token[1].strip()
    -
    -    def __getitem__(self, name):
    -        """ Look name in self sections.
    -
    -        :return :class:`inirama.Section`: section
    -
    -        """
    -        if name not in self.sections:
    -            self.sections[name] = self.section_type(self)
    -        return self.sections[name]
    -
    -    def __contains__(self, name):
    -        return name in self.sections
    -
    -    def __repr__(self):
    -        return "".format(self.sections)
    -
    -
    -class InterpolationNamespace(Namespace):
    -
    -    """ That implements the interpolation feature.
    -
    -    ::
    -
    -        from inirama import InterpolationNamespace
    -
    -        ns = InterpolationNamespace()
    -        ns.parse('''
    -            [main]
    -            test = value
    -            foo = bar {test}
    -            more_deep = wow {foo}
    -        ''')
    -        print ns['main']['more_deep']  # wow bar value
    -
    -    """
    -
    -    section_type = InterpolationSection
    -
    -# pylama:ignore=D,W02,E731,W0621
    diff --git a/pymode/libs/pylama/lint/__init__.py b/pymode/libs/pylama/lint/__init__.py
    deleted file mode 100644
    index bd8e8da7..00000000
    --- a/pymode/libs/pylama/lint/__init__.py
    +++ /dev/null
    @@ -1,19 +0,0 @@
    -"""Custom module loader."""
    -
    -
    -class Linter(object):
    -
    -    """Abstract class for linter plugin."""
    -
    -    @staticmethod
    -    def allow(path):
    -        """Check path is relevant for linter.
    -
    -        :return bool:
    -        """
    -        return path.endswith('.py')
    -
    -    @staticmethod
    -    def run(path, **meta):
    -        """Method 'run' should be defined."""
    -        raise NotImplementedError(__doc__)
    diff --git a/pymode/libs/pylama/lint/extensions.py b/pymode/libs/pylama/lint/extensions.py
    deleted file mode 100644
    index bfd83d88..00000000
    --- a/pymode/libs/pylama/lint/extensions.py
    +++ /dev/null
    @@ -1,39 +0,0 @@
    -"""Load extensions."""
    -
    -LINTERS = {}
    -
    -try:
    -    from pylama.lint.pylama_mccabe import Linter
    -    LINTERS['mccabe'] = Linter()
    -except ImportError:
    -    pass
    -
    -try:
    -    from pylama.lint.pylama_pep257 import Linter
    -    LINTERS['pep257'] = Linter()
    -except ImportError:
    -    pass
    -
    -try:
    -    from pylama.lint.pylama_pep8 import Linter
    -    LINTERS['pep8'] = Linter()
    -except ImportError:
    -    pass
    -
    -try:
    -    from pylama.lint.pylama_pyflakes import Linter
    -    LINTERS['pyflakes'] = Linter()
    -except ImportError:
    -    pass
    -
    -
    -from pkg_resources import iter_entry_points
    -
    -for entry in iter_entry_points('pylama.linter'):
    -    if entry.name not in LINTERS:
    -        try:
    -            LINTERS[entry.name] = entry.load()()
    -        except ImportError:
    -            pass
    -
    -#  pylama:ignore=E0611
    diff --git a/pymode/libs/pylama/lint/pylama_mccabe.py b/pymode/libs/pylama/lint/pylama_mccabe.py
    deleted file mode 100644
    index fc191004..00000000
    --- a/pymode/libs/pylama/lint/pylama_mccabe.py
    +++ /dev/null
    @@ -1,29 +0,0 @@
    -"""Code complexity checking."""
    -from mccabe import McCabeChecker
    -
    -from pylama.lint import Linter as Abstract
    -import ast
    -
    -
    -class Linter(Abstract):
    -
    -    """Run complexity checking."""
    -
    -    @staticmethod
    -    def run(path, code=None, params=None, **meta):
    -        """MCCabe code checking.
    -
    -        :return list: List of errors.
    -        """
    -        try:
    -            tree = compile(code, path, "exec", ast.PyCF_ONLY_AST)
    -        except SyntaxError as exc:
    -            return [{'lnum': exc.lineno, 'text': 'Invalid syntax: %s' % exc.text.strip()}]
    -
    -        McCabeChecker.max_complexity = int(params.get('complexity', 10))
    -        return [
    -            {'lnum': lineno, 'offset': offset, 'text': text, 'type': McCabeChecker._code}
    -            for lineno, offset, text, _ in McCabeChecker(tree, path).run()
    -        ]
    -
    -#  pylama:ignore=W0212
    diff --git a/pymode/libs/pylama/lint/pylama_pep257.py b/pymode/libs/pylama/lint/pylama_pep257.py
    deleted file mode 100644
    index 5e1f785c..00000000
    --- a/pymode/libs/pylama/lint/pylama_pep257.py
    +++ /dev/null
    @@ -1,21 +0,0 @@
    -"""PEP257 support."""
    -
    -from pep257 import PEP257Checker
    -
    -from pylama.lint import Linter as Abstract
    -
    -
    -class Linter(Abstract):
    -
    -    """Check PEP257 errors."""
    -
    -    @staticmethod
    -    def run(path, code=None, **meta):
    -        """PEP257 code checking.
    -
    -        :return list: List of errors.
    -        """
    -        return [
    -            {'lnum': e.line, 'text': e.message, 'type': 'D'}
    -            for e in PEP257Checker().check_source(code, path)
    -        ]
    diff --git a/pymode/libs/pylama/lint/pylama_pep8.py b/pymode/libs/pylama/lint/pylama_pep8.py
    deleted file mode 100644
    index 30329d80..00000000
    --- a/pymode/libs/pylama/lint/pylama_pep8.py
    +++ /dev/null
    @@ -1,66 +0,0 @@
    -"""PEP8 support."""
    -from pep8 import BaseReport, StyleGuide, get_parser
    -
    -from pylama.lint import Linter as Abstract
    -
    -
    -try:
    -    from StringIO import StringIO
    -except ImportError:
    -    from io import StringIO
    -
    -
    -class Linter(Abstract):
    -
    -    """PEP8 runner."""
    -
    -    @staticmethod
    -    def run(path, code=None, params=None, **meta):
    -        """Check code with PEP8.
    -
    -        :return list: List of errors.
    -        """
    -        parser = get_parser()
    -        for option in parser.option_list:
    -            if option.dest and option.dest in params:
    -                value = params[option.dest]
    -                if not isinstance(value, str):
    -                    continue
    -                params[option.dest] = option.convert_value(option, params[option.dest])
    -        P8Style = StyleGuide(reporter=_PEP8Report, **params)
    -        buf = StringIO(code)
    -        return P8Style.input_file(path, lines=buf.readlines())
    -
    -
    -class _PEP8Report(BaseReport):
    -
    -    def __init__(self, *args, **kwargs):
    -        super(_PEP8Report, self).__init__(*args, **kwargs)
    -        self.errors = []
    -
    -    def init_file(self, filename, lines, expected, line_offset):
    -        """Prepare storage for errors."""
    -        super(_PEP8Report, self).init_file(
    -            filename, lines, expected, line_offset)
    -        self.errors = []
    -
    -    def error(self, line_number, offset, text, check):
    -        """Save errors."""
    -        code = super(_PEP8Report, self).error(
    -            line_number, offset, text, check)
    -
    -        if code:
    -            self.errors.append(dict(
    -                text=text,
    -                type=code.replace('E', 'C'),
    -                col=offset + 1,
    -                lnum=line_number,
    -            ))
    -
    -    def get_file_results(self):
    -        """Get errors.
    -
    -        :return list: List of errors.
    -
    -        """
    -        return self.errors
    diff --git a/pymode/libs/pylama/lint/pylama_pyflakes.py b/pymode/libs/pylama/lint/pylama_pyflakes.py
    deleted file mode 100644
    index 184d969f..00000000
    --- a/pymode/libs/pylama/lint/pylama_pyflakes.py
    +++ /dev/null
    @@ -1,49 +0,0 @@
    -"""Pyflakes support."""
    -
    -from pyflakes import checker
    -
    -from pylama.lint import Linter as Abstract
    -
    -
    -checker.messages.UnusedImport.message = "W0611 %r imported but unused"
    -checker.messages.RedefinedWhileUnused.message = "W0404 redefinition of unused %r from line %r"
    -checker.messages.RedefinedInListComp.message = "W0621 list comprehension redefines %r from line %r"
    -checker.messages.ImportShadowedByLoopVar.message = "W0621 import %r from line %r shadowed by loop variable"
    -checker.messages.ImportStarUsed.message = "W0401 'from %s import *' used; unable to detect undefined names"
    -checker.messages.UndefinedName.message = "E0602 undefined name %r"
    -checker.messages.DoctestSyntaxError.message = "W0511 syntax error in doctest"
    -checker.messages.UndefinedExport.message = "E0603 undefined name %r in __all__"
    -checker.messages.UndefinedLocal.message = "E0602 local variable %r (defined in enclosing scope on line %r) referenced before assignment"
    -checker.messages.DuplicateArgument.message = "E1122 duplicate argument %r in function definition"
    -checker.messages.LateFutureImport.message = "W0410 future import(s) %r after other statements"
    -checker.messages.UnusedVariable.message = "W0612 local variable %r is assigned to but never used"
    -checker.messages.ReturnWithArgsInsideGenerator.message = "E0106 'return' with argument inside generator"
    -checker.messages.ReturnOutsideFunction.message = "E0104 'return' outside function"
    -
    -
    -class Linter(Abstract):
    -
    -    """Pyflakes runner."""
    -
    -    @staticmethod
    -    def run(path, code=None, params=None, **meta):
    -        """Check code with pyflakes.
    -
    -        :return list: List of errors.
    -        """
    -        import _ast
    -
    -        builtins = params.get("builtins", "")
    -
    -        if builtins:
    -            builtins = builtins.split(",")
    -
    -        tree = compile(code, path, "exec", _ast.PyCF_ONLY_AST)
    -        w = checker.Checker(tree, path, builtins=builtins)
    -        w.messages = sorted(w.messages, key=lambda m: m.lineno)
    -        return [
    -            {'lnum': m.lineno, 'text': m.message % m.message_args}
    -            for m in sorted(w.messages, key=lambda m: m.lineno)
    -        ]
    -
    -#  pylama:ignore=E501,C0301
    diff --git a/pymode/libs/pylama/lint/pylama_pylint/__init__.py b/pymode/libs/pylama/lint/pylama_pylint/__init__.py
    deleted file mode 100644
    index 74e6bc22..00000000
    --- a/pymode/libs/pylama/lint/pylama_pylint/__init__.py
    +++ /dev/null
    @@ -1,12 +0,0 @@
    -""" Description. """
    -
    -# Module information
    -# ==================
    -
    -
    -__version__ = "2.1.1"
    -__project__ = "pylama_pylint"
    -__author__ = "horneds "
    -__license__ = "BSD"
    -
    -from .main import Linter  # noqa
    diff --git a/pymode/libs/pylama/lint/pylama_pylint/main.py b/pymode/libs/pylama/lint/pylama_pylint/main.py
    deleted file mode 100644
    index f50b6647..00000000
    --- a/pymode/libs/pylama/lint/pylama_pylint/main.py
    +++ /dev/null
    @@ -1,111 +0,0 @@
    -""" Pylint support. """
    -from os import path as op, environ
    -import logging
    -
    -from pylama.lint import Linter as BaseLinter
    -
    -CURDIR = op.abspath(op.dirname(__file__))
    -
    -from astroid import MANAGER
    -from pylint.lint import Run
    -from pylint.reporters import BaseReporter
    -
    -HOME_RCFILE = op.abspath(op.join(environ.get('HOME', ''), '.pylintrc'))
    -LAMA_RCFILE = op.abspath(op.join(CURDIR, 'pylint.rc'))
    -
    -
    -logger = logging.getLogger('pylama')
    -
    -
    -class Linter(BaseLinter):
    -
    -    """ Check code with pylint. """
    -
    -    @staticmethod
    -    def run(path, code, params=None, ignore=None, select=None, **meta):
    -        """ Pylint code checking.
    -
    -        :return list: List of errors.
    -
    -        """
    -        logger.debug('Start pylint')
    -
    -        MANAGER.astroid_cache.clear()
    -
    -        class Reporter(BaseReporter):
    -
    -            def __init__(self):
    -                self.errors = []
    -                super(Reporter, self).__init__()
    -
    -            def _display(self, layout):
    -                pass
    -
    -            def add_message(self, msg_id, location, msg):
    -                _, _, line, col = location[1:]
    -                self.errors.append(dict(
    -                    lnum=line,
    -                    col=col,
    -                    text="%s %s" % (msg_id, msg),
    -                    type=msg_id[0]
    -                ))
    -
    -        params = _Params(ignore=ignore, select=select, params=params)
    -        logger.debug(params)
    -
    -        runner = Run(
    -            [path] + params.to_attrs(), reporter=Reporter(), exit=False)
    -
    -        return runner.linter.reporter.errors
    -
    -
    -class _Params(object):
    -
    -    """ Store pylint params. """
    -
    -    def __init__(self, select=None, ignore=None, params=None):
    -
    -        params = dict(params.items())
    -        rcfile = params.get('rcfile', LAMA_RCFILE)
    -        enable = params.get('enable', None)
    -        disable = params.get('disable', None)
    -
    -        if op.exists(HOME_RCFILE):
    -            rcfile = HOME_RCFILE
    -
    -        if select:
    -            enable = select | set(enable.split(",") if enable else [])
    -
    -        if ignore:
    -            disable = ignore | set(disable.split(",") if disable else [])
    -
    -        params.update(dict(
    -            report=params.get('report', False), rcfile=rcfile,
    -            enable=enable, disable=disable))
    -
    -        self.params = dict(
    -            (name.replace('_', '-'), self.prepare_value(value))
    -            for name, value in params.items() if value is not None)
    -
    -    @staticmethod
    -    def prepare_value(value):
    -        """ Prepare value to pylint. """
    -        if isinstance(value, (list, tuple, set)):
    -            return ",".join(value)
    -
    -        if isinstance(value, bool):
    -            return "y" if value else "n"
    -
    -        return str(value)
    -
    -    def to_attrs(self):
    -        """ Convert to argument list. """
    -        return ["--%s=%s" % item for item in self.params.items()]
    -
    -    def __str__(self):
    -        return " ".join(self.to_attrs())
    -
    -    def __repr__(self):
    -        return "" % self
    -
    -# pylama:ignore=W0403
    diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint.rc b/pymode/libs/pylama/lint/pylama_pylint/pylint.rc
    deleted file mode 100644
    index 799c62f6..00000000
    --- a/pymode/libs/pylama/lint/pylama_pylint/pylint.rc
    +++ /dev/null
    @@ -1,23 +0,0 @@
    -[MESSAGES CONTROL]
    -# Disable the message(s) with the given id(s).
    -# http://pylint-messages.wikidot.com/all-codes
    -#
    -# C0103: Invalid name "%s" (should match %s)
    -# E1101: %s %r has no %r member
    -# R0901: Too many ancestors (%s/%s)
    -# R0902: Too many instance attributes (%s/%s)
    -# R0903: Too few public methods (%s/%s)
    -# R0904: Too many public methods (%s/%s)
    -# R0913: Too many arguments (%s/%s)
    -# R0915: Too many statements (%s/%s)
    -# W0141: Used builtin function %r
    -# W0142: Used * or ** magic
    -# W0221: Arguments number differs from %s method
    -# W0232: Class has no __init__ method
    -# W0613: Unused argument %r
    -# W0631: Using possibly undefined loop variable %r
    -#
    -disable = C0103,E1101,R0901,R0902,R0903,R0904,R0913,R0915,W0141,W0142,W0221,W0232,W0613,W0631
    -
    -[TYPECHECK]
    -generated-members = REQUEST,acl_users,aq_parent,objects,DoesNotExist,_meta,status_code,content,context
    diff --git a/pymode/libs/pylama/main.py b/pymode/libs/pylama/main.py
    deleted file mode 100644
    index 2926ccdb..00000000
    --- a/pymode/libs/pylama/main.py
    +++ /dev/null
    @@ -1,101 +0,0 @@
    -"""Pylama's shell support."""
    -
    -from __future__ import absolute_import, with_statement
    -
    -import sys
    -from os import walk, path as op
    -
    -from .config import parse_options, CURDIR, setup_logger
    -from .core import LOGGER, run
    -from .async import check_async
    -
    -
    -def check_path(options, rootdir=None, candidates=None, code=None):
    -    """Check path.
    -
    -    :param rootdir: Root directory (for making relative file paths)
    -    :param options: Parsed pylama options (from pylama.config.parse_options)
    -
    -    :returns: (list) Errors list
    -
    -    """
    -    if not candidates:
    -        candidates = []
    -        for path_ in options.paths:
    -            path = op.abspath(path_)
    -            if op.isdir(path):
    -                for root, _, files in walk(path):
    -                    candidates += [op.relpath(op.join(root, f), CURDIR) for f in files]
    -            else:
    -                candidates.append(path)
    -
    -    if rootdir is None:
    -        rootdir = path if op.isdir(path) else op.dirname(path)
    -
    -    paths = []
    -    for path in candidates:
    -
    -        if not options.force and not any(l.allow(path) for _, l in options.linters):
    -            continue
    -
    -        if not op.exists(path):
    -            continue
    -
    -        paths.append(path)
    -
    -    if options.async:
    -        return check_async(paths, options, rootdir)
    -
    -    errors = []
    -    for path in paths:
    -        errors += run(path=path, code=code, rootdir=rootdir, options=options)
    -    return errors
    -
    -
    -def shell(args=None, error=True):
    -    """Endpoint for console.
    -
    -    Parse a command arguments, configuration files and run a checkers.
    -
    -    :return list: list of errors
    -    :raise SystemExit:
    -
    -    """
    -    if args is None:
    -        args = sys.argv[1:]
    -
    -    options = parse_options(args)
    -    setup_logger(options)
    -    LOGGER.info(options)
    -
    -    # Install VSC hook
    -    if options.hook:
    -        from .hook import install_hook
    -        return install_hook(options.path)
    -
    -    return process_paths(options, error=error)
    -
    -
    -def process_paths(options, candidates=None, error=True):
    -    """Process files and log errors."""
    -    errors = check_path(options, rootdir=CURDIR, candidates=candidates)
    -
    -    pattern = "%(filename)s:%(lnum)s:%(col)s: %(text)s"
    -    if options.format == 'pylint':
    -        pattern = "%(filename)s:%(lnum)s: [%(type)s] %(text)s"
    -
    -    for er in errors:
    -        if options.abspath:
    -            er._info['filename'] = op.abspath(er.filename)
    -        LOGGER.warning(pattern, er._info)
    -
    -    if error:
    -        sys.exit(int(bool(errors)))
    -
    -    return errors
    -
    -
    -if __name__ == '__main__':
    -    shell()
    -
    -# pylama:ignore=F0001
    diff --git a/pymode/libs/pylama/pytest.py b/pymode/libs/pylama/pytest.py
    deleted file mode 100644
    index eeaa58ce..00000000
    --- a/pymode/libs/pylama/pytest.py
    +++ /dev/null
    @@ -1,87 +0,0 @@
    -""" py.test plugin for checking files with pylama. """
    -from __future__ import absolute_import
    -
    -from os import path as op
    -
    -import py # noqa
    -import pytest
    -
    -
    -HISTKEY = "pylama/mtimes"
    -
    -
    -def pytest_addoption(parser):
    -    group = parser.getgroup("general")
    -    group.addoption(
    -        '--pylama', action='store_true',
    -        help="perform some pylama code checks on .py files")
    -
    -
    -def pytest_sessionstart(session):
    -    config = session.config
    -    if config.option.pylama and getattr(config, 'cache', None):
    -        config._pylamamtimes = config.cache.get(HISTKEY, {})
    -
    -
    -def pytest_sessionfinish(session):
    -    config = session.config
    -    if hasattr(config, "_pylamamtimes"):
    -        config.cache.set(HISTKEY, config._pylamamtimes)
    -
    -
    -def pytest_collect_file(path, parent):
    -    config = parent.config
    -    if config.option.pylama and path.ext == '.py':
    -        return PylamaItem(path, parent)
    -
    -
    -class PylamaError(Exception):
    -    """ indicates an error during pylama checks. """
    -
    -
    -class PylamaItem(pytest.Item, pytest.File):
    -
    -    def __init__(self, path, parent):
    -        super(PylamaItem, self).__init__(path, parent)
    -        self.add_marker("pep8")
    -        self.cache = None
    -        self._pylamamtimes = None
    -
    -    def setup(self):
    -        if not getattr(self.config, 'cache', None):
    -            return False
    -
    -        self.cache = True
    -        self._pylamamtimes = self.fspath.mtime()
    -        pylamamtimes = self.config._pylamamtimes
    -        old = pylamamtimes.get(str(self.fspath), 0)
    -        if old == self._pylamamtimes:
    -            pytest.skip("file(s) previously passed Pylama checks")
    -
    -    def runtest(self):
    -        errors = check_file(self.fspath)
    -        if errors:
    -            pattern = "%(filename)s:%(lnum)s:%(col)s: %(text)s"
    -            out = "\n".join([pattern % e._info for e in errors])
    -            raise PylamaError(out)
    -
    -        # update mtime only if test passed
    -        # otherwise failures would not be re-run next time
    -        if self.cache:
    -            self.config._pylamamtimes[str(self.fspath)] = self._pylamamtimes
    -
    -    def repr_failure(self, excinfo):
    -        if excinfo.errisinstance(PylamaError):
    -            return excinfo.value.args[0]
    -        return super(PylamaItem, self).repr_failure(excinfo)
    -
    -
    -def check_file(path):
    -    from pylama.main import parse_options, process_paths
    -    from pylama.config import CURDIR
    -
    -    options = parse_options()
    -    path = op.relpath(str(path), CURDIR)
    -    return process_paths(options, candidates=[path], error=False)
    -
    -# pylama:ignore=D,E1002,W0212,F0001
    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 82e557dc..00000000
    --- a/pymode/libs/pylint/__init__.py
    +++ /dev/null
    @@ -1,46 +0,0 @@
    -# Copyright (c) 2003-2012 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -import sys
    -
    -from .__pkginfo__ import version as __version__
    -
    -def run_pylint():
    -    """run pylint"""
    -    from pylint.lint import Run
    -    Run(sys.argv[1:])
    -
    -def run_pylint_gui():
    -    """run pylint-gui"""
    -    try:
    -        from pylint.gui import Run
    -        Run(sys.argv[1:])
    -    except ImportError:
    -        sys.exit('tkinter is not available')
    -
    -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 7716361d..00000000
    --- a/pymode/libs/pylint/__main__.py
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -#!/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 33ae5b64..00000000
    --- a/pymode/libs/pylint/__pkginfo__.py
    +++ /dev/null
    @@ -1,70 +0,0 @@
    -# pylint: disable=W0622,C0103
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""pylint packaging information"""
    -from __future__ import absolute_import
    -
    -modname = distname = 'pylint'
    -
    -numversion = (1, 4, 4)
    -version = '.'.join([str(num) for num in numversion])
    -
    -install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.3.6', 'six']
    -
    -license = 'GPL'
    -description = "python code static checker"
    -web = 'http://www.pylint.org'
    -mailinglist = "mailto://code-quality@python.org"
    -author = 'Logilab'
    -author_email = 'python-projects@lists.logilab.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 "pylint-gui", "pyreverse" (UML diagram generator)
    - and "symilar" (an independent similarities checker)."""
    -
    -from os.path import join
    -scripts = [join('bin', filename)
    -           for filename in ('pylint', 'pylint-gui', "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 51adb4d0..00000000
    --- a/pymode/libs/pylint/checkers/__init__.py
    +++ /dev/null
    @@ -1,124 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""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-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 logilab.common.configuration 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/base.py b/pymode/libs/pylint/checkers/base.py
    deleted file mode 100644
    index 6ce88251..00000000
    --- a/pymode/libs/pylint/checkers/base.py
    +++ /dev/null
    @@ -1,1236 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# Copyright (c) 2009-2010 Arista Networks, Inc.
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""basic checker for Python code"""
    -
    -import collections
    -import itertools
    -import sys
    -import re
    -
    -import six
    -from six.moves import zip  # pylint: disable=redefined-builtin
    -
    -from logilab.common.ureports import Table
    -
    -import astroid
    -import astroid.bases
    -from astroid import are_exclusive, InferenceError
    -
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
    -from pylint.utils import EmptyReport
    -from pylint.reporters import diff_string
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    check_messages,
    -    clobber_in_except,
    -    is_builtin_object,
    -    is_inside_except,
    -    overrides_a_method,
    -    safe_infer,
    -    get_argument_from_call,
    -    has_known_bases,
    -    NoSuchArgumentError,
    -    is_import_error,
    -    unimplemented_abstract_methods,
    -    )
    -
    -
    -# 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}$')
    -CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$')
    -# do not require a doc string on system methods
    -NO_REQUIRED_DOC_RGX = re.compile('__.*__')
    -REVERSED_METHODS = (('__getitem__', '__len__'),
    -                    ('__reversed__', ))
    -
    -PY33 = sys.version_info >= (3, 3)
    -PY3K = sys.version_info >= (3, 0)
    -BAD_FUNCTIONS = ['map', 'filter']
    -if sys.version_info < (3, 0):
    -    BAD_FUNCTIONS.append('input')
    -
    -# Name categories that are always consistent with all naming conventions.
    -EXEMPT_NAME_CATEGORIES = set(('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([astroid.bases.BUILTINS, x]) for x in ('set', 'dict', 'list')],
    -        ['set()', '{}', '[]'])
    -)
    -
    -del re
    -
    -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 is_import_error(current.parent):
    -        return False
    -    try_block = current.parent.parent
    -    for import_node in try_block.nodes_of_class((astroid.From, 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.GenExpr)):
    -            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 != INFERENCE_FAILURE))
    -
    -
    -if sys.version_info < (3, 0):
    -    PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty'))
    -else:
    -    PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty'))
    -
    -
    -def _determine_function_name_type(node):
    -    """Determine the name type whose regex the a function's name should match.
    -
    -    :param node: A function node.
    -    :returns: One of ('function', 'method', 'attr')
    -    """
    -    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.Getattr) and
    -                 decorator.attrname == 'abstractproperty')):
    -            infered = 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.Getattr) 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(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 EmptyReport()
    -        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 = 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(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.Getattr) and
    -                    getattr(decorator.expr, 'name', None) == node.name):
    -                return True
    -    return False
    -
    -class _BasicChecker(BaseChecker):
    -    __implements__ = 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.'),
    -        }
    -
    -    @check_messages('function-redefined')
    -    def visit_class(self, node):
    -        self._check_redefinition('class', node)
    -
    -    @check_messages('init-is-generator', 'return-in-init',
    -                    'function-redefined', 'return-arg-in-generator',
    -                    'duplicate-argument-name')
    -    def visit_function(self, node):
    -        if not redefined_by_decorator(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.Function, astroid.Class))
    -        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 [v for v in values
    -                        if not (v is None or
    -                                (isinstance(v, astroid.Const) and v.value is None) or
    -                                (isinstance(v, astroid.Name)  and v.name == 'None')
    -                               )]:
    -                    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)
    -
    -
    -    @check_messages('return-outside-function')
    -    def visit_return(self, node):
    -        if not isinstance(node.frame(), astroid.Function):
    -            self.add_message('return-outside-function', node=node)
    -
    -    @check_messages('yield-outside-function')
    -    def visit_yield(self, node):
    -        if not isinstance(node.frame(), (astroid.Function, astroid.Lambda)):
    -            self.add_message('yield-outside-function', node=node)
    -
    -    @check_messages('not-in-loop')
    -    def visit_continue(self, node):
    -        self._check_in_loop(node, 'continue')
    -
    -    @check_messages('not-in-loop')
    -    def visit_break(self, node):
    -        self._check_in_loop(node, 'break')
    -
    -    @check_messages('useless-else-on-loop')
    -    def visit_for(self, node):
    -        self._check_else_on_loop(node)
    -
    -    @check_messages('useless-else-on-loop')
    -    def visit_while(self, node):
    -        self._check_else_on_loop(node)
    -
    -    @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)
    -
    -    @check_messages('abstract-class-instantiated')
    -    def visit_callfunc(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.Class):
    -            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_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)):
    -                break
    -            _node = _node.parent
    -        else:
    -            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 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__ = 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 can not 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. '),
    -        'W0141': ('Used builtin function %r',
    -                  '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.'),
    -        '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.'),
    -        'C0121': ('Missing required attribute "%s"', # W0103
    -                  'missing-module-attribute',
    -                  'Used when an attribute required for modules is missing.'),
    -
    -        'E0109': ('Missing argument to reversed()',
    -                  'missing-reversed-argument',
    -                  'Used when reversed() builtin didn\'t receive an argument.'),
    -        '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__'),
    -
    -    }
    -
    -    options = (('required-attributes',
    -                {'default' : (), 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'Required attributes for module, separated by a '
    -                          'comma'}
    -               ),
    -               ('bad-functions',
    -                {'default' : BAD_FUNCTIONS,
    -                 'type' :'csv', 'metavar' : '',
    -                 'help' : 'List of builtins function names that should not be '
    -                          'used, separated by a comma'}
    -               ),
    -              )
    -    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)
    -
    -    @check_messages('missing-module-attribute')
    -    def visit_module(self, node):
    -        """check module name, docstring and required arguments
    -        """
    -        self.stats['module'] += 1
    -        for attr in self.config.required_attributes:
    -            if attr not in node:
    -                self.add_message('missing-module-attribute', node=node, args=attr)
    -
    -    def visit_class(self, node): # pylint: disable=unused-argument
    -        """check module name, docstring and redefinition
    -        increment branch counter
    -        """
    -        self.stats['class'] += 1
    -
    -    @check_messages('pointless-statement', 'pointless-string-statement',
    -                    'expression-not-assigned')
    -    def visit_discard(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.Class, astroid.Module, astroid.Function)):
    -                if isinstance(scope, astroid.Function) 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.CallFunc)) or
    -                (isinstance(node.parent, astroid.TryExcept) and
    -                 node.parent.body == [node])):
    -            return
    -        if any(expr.nodes_of_class(astroid.CallFunc)):
    -            self.add_message('expression-not-assigned', node=node,
    -                             args=expr.as_string())
    -        else:
    -            self.add_message('pointless-statement', node=node)
    -
    -    @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.CallFunc):
    -            # The body of the lambda must be a function call expression
    -            # for the lambda to be unnecessary.
    -            return
    -        # XXX are lambda still different with astroid >= 0.18 ?
    -        # *args and **kwargs need to be treated specially, since they
    -        # are structured differently between the lambda and the function
    -        # call (in the lambda they appear in the args.args list and are
    -        # indicated as * and ** by two bits in the lambda's flags, but
    -        # in the function call they are omitted from the args list and
    -        # are indicated by separate attributes on the function call node).
    -        ordinary_args = list(node.args.args)
    -        if node.args.kwarg:
    -            if (not call.kwargs
    -                    or not isinstance(call.kwargs, astroid.Name)
    -                    or node.args.kwarg != call.kwargs.name):
    -                return
    -        elif call.kwargs:
    -            return
    -        if node.args.vararg:
    -            if (not call.starargs
    -                    or not isinstance(call.starargs, astroid.Name)
    -                    or node.args.vararg != call.starargs.name):
    -                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(call.args):
    -            return
    -        for i in range(len(ordinary_args)):
    -            if not isinstance(call.args[i], astroid.Name):
    -                return
    -            if node.args.args[i].name != call.args[i].name:
    -                return
    -        if (isinstance(node.body.func, astroid.Getattr) and
    -                isinstance(node.body.func.expr, astroid.CallFunc)):
    -            # Chained call, the intermediate call might
    -            # return something else (but we don't check that, yet).
    -            return
    -        self.add_message('unnecessary-lambda', line=node.fromlineno, node=node)
    -
    -    @check_messages('dangerous-default-value')
    -    def visit_function(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)
    -
    -    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 type(value) is 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.CallFunc):
    -                        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, ))
    -
    -    @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.Function,))
    -
    -    @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)
    -
    -    @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,))
    -
    -    @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)
    -
    -    @check_messages('exec-used')
    -    def visit_exec(self, node):
    -        """just print a warning on exec statements"""
    -        self.add_message('exec-used', node=node)
    -
    -    @check_messages('bad-builtin', 'eval-used',
    -                    'exec-used', 'missing-reversed-argument',
    -                    'bad-reversed-sequence')
    -    def visit_callfunc(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)
    -                if name in self.config.bad_functions:
    -                    self.add_message('bad-builtin', node=node, args=name)
    -
    -    @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)
    -
    -    @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 = safe_infer(get_argument_from_call(node, position=0))
    -        except NoSuchArgumentError:
    -            self.add_message('missing-reversed-argument', node=node)
    -        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.CallFunc):
    -                    try:
    -                        func = next(node.args[0].func.infer())
    -                    except InferenceError:
    -                        return
    -                    if (getattr(func, 'name', None) == 'iter' and
    -                            is_builtin_object(func)):
    -                        self.add_message('bad-reversed-sequence', node=node)
    -                return
    -
    -            if isinstance(argument, astroid.Instance):
    -                if (argument._proxied.name == 'dict' and
    -                        is_builtin_object(argument._proxied)):
    -                    self.add_message('bad-reversed-sequence', node=node)
    -                    return
    -                elif any(ancestor.name == 'dict' and is_builtin_object(ancestor)
    -                         for ancestor in argument._proxied.ancestors()):
    -                    # mappings aren't accepted by reversed()
    -                    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:
    -                    # Check if it is a .deque. It doesn't seem that
    -                    # we can retrieve special methods
    -                    # from C implemented constructs.
    -                    if argument._proxied.qname().endswith(".deque"):
    -                        return
    -                    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)
    -
    -_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...).'),
    -    }
    -
    -    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'}
    -               ),
    -              ) + _create_naming_options()
    -
    -
    -    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,)
    -
    -    @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)
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_class(self, node):
    -        self._check_name('class', node.name, node)
    -        for attr, anodes in six.iteritems(node.instance_attrs):
    -            if not list(node.instance_attr_ancestors(attr)):
    -                self._check_name('attr', attr, anodes[0])
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_function(self, node):
    -        # Do not emit any warnings if the method is just an implementation
    -        # of a base class method.
    -        confidence = HIGH
    -        if node.is_method():
    -            if overrides_a_method(node.parent.frame(), node.name):
    -                return
    -            confidence = (INFERENCE if has_known_bases(node.parent.frame())
    -                          else INFERENCE_FAILURE)
    -
    -        self._check_name(_determine_function_name_type(node),
    -                         node.name, node, confidence)
    -        # Check argument names
    -        args = node.args.args
    -        if args is not None:
    -            self._recursive_check_names(args, node)
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_global(self, node):
    -        for name in node.names:
    -            self._check_name('const', name, node)
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_assname(self, node):
    -        """check module level assigned names"""
    -        frame = node.frame()
    -        ass_type = node.ass_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(safe_infer(ass_type.value), astroid.Class):
    -                    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.Function):
    -            # 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.Class):
    -            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.AssName):
    -                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=HIGH):
    -        """check for a name using the type's regexp"""
    -        if is_inside_except(node):
    -            clobbering, _ = 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)
    -
    -
    -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)
    -    @check_messages('missing-docstring', 'empty-docstring')
    -    def visit_module(self, node):
    -        self._check_docstring('module', node)
    -
    -    @check_messages('missing-docstring', 'empty-docstring')
    -    def visit_class(self, node):
    -        if self.config.no_docstring_rgx.match(node.name) is None:
    -            self._check_docstring('class', node)
    -
    -    @check_messages('missing-docstring', 'empty-docstring')
    -    def visit_function(self, node):
    -        if self.config.no_docstring_rgx.match(node.name) is None:
    -            ftype = node.is_method() and 'method' or 'function'
    -            if isinstance(node.parent.frame(), astroid.Class):
    -                overridden = False
    -                confidence = (INFERENCE if has_known_bases(node.parent.frame())
    -                              else 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.Function):
    -                        overridden = True
    -                        break
    -                self._check_docstring(ftype, node,
    -                                      report_missing=not overridden,
    -                                      confidence=confidence)
    -            else:
    -                self._check_docstring(ftype, node)
    -
    -    def _check_docstring(self, node_type, node, report_missing=True,
    -                         confidence=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.Discard) and
    -                    isinstance(node.body[0].value, astroid.CallFunc)):
    -                # Most likely a string with a format call. Let's see.
    -                func = 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.'),
    -           }
    -    @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)}),
    -           }
    -
    -    @check_messages('deprecated-lambda')
    -    def visit_callfunc(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 = safe_infer(node.func)
    -        if (is_builtin_object(infered)
    -                and infered.name in ['map', 'filter']):
    -            self.add_message('deprecated-lambda', 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))
    diff --git a/pymode/libs/pylint/checkers/classes.py b/pymode/libs/pylint/checkers/classes.py
    deleted file mode 100644
    index 87e3bcfe..00000000
    --- a/pymode/libs/pylint/checkers/classes.py
    +++ /dev/null
    @@ -1,982 +0,0 @@
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""classes checker for Python code
    -"""
    -from __future__ import generators
    -
    -import sys
    -from collections import defaultdict
    -
    -import astroid
    -from astroid import YES, Instance, are_exclusive, AssAttr, Class
    -from astroid.bases import Generator, BUILTINS
    -from astroid.inference import InferenceContext
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    PYMETHODS, overrides_a_method, check_messages, is_attr_private,
    -    is_attr_protected, node_frame_class, safe_infer, is_builtin_object,
    -    decorated_with_property, unimplemented_abstract_methods)
    -import six
    -
    -if sys.version_info >= (3, 0):
    -    NEXT_METHOD = '__next__'
    -else:
    -    NEXT_METHOD = 'next'
    -ITER_METHODS = ('__iter__', '__getitem__')
    -
    -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.Function):
    -        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.CallFunc):
    -                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 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 _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.Function) and
    -                decorated_with_property(infered)):
    -            return True
    -        if infered.pytype() == property_name:
    -            return True
    -    return False
    -
    -
    -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 agument 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.'
    -             ),
    -
    -    'E0221': ('Interface resolved to %s is not a class',
    -              'interface-is-not-class',
    -              'Used when a class claims to implement an interface which is not '
    -              'a class.'),
    -    'E0222': ('Missing method %r from %s interface',
    -              'missing-interface-method',
    -              'Used when a method declared in an interface is missing from a '
    -              'class implementing this interface'),
    -    'W0221': ('Arguments number differs 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.'
    -             ),
    -    'F0220': ('failed to resolve interfaces implemented by %s (%s)',
    -              'unresolved-interface',
    -              'Used when a Pylint as failed to find interfaces implemented by '
    -              ' a 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.'),
    -    'W0234': ('__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),
    -    'E0235': ('__exit__ must accept 3 arguments: type, value, traceback',
    -              'bad-context-manager',
    -              'Used when the __exit__ special method, belonging to a '
    -              'context manager, does not accept 3 arguments '
    -              '(type, value, traceback).'),
    -    '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.'),
    -
    -
    -    }
    -
    -
    -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
    -    * supported interfaces implementation
    -    * unreachable code
    -    """
    -
    -    __implements__ = (IAstroidChecker,)
    -
    -    # configuration section name
    -    name = 'classes'
    -    # messages
    -    msgs = MSGS
    -    priority = -2
    -    # configuration options
    -    options = (('ignore-iface-methods',
    -                {'default' : (#zope interface
    -                    'isImplementedBy', 'deferred', 'extends', 'names',
    -                    'namesAndDescriptions', 'queryDescriptionFor', 'getBases',
    -                    'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue',
    -                    'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue',
    -                    'isImplementedByInstancesOf',
    -                    # twisted
    -                    'adaptWith',
    -                    # logilab.common interface
    -                    'is_implemented_by'),
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of interface methods to ignore, \
    -separated by a comma. This is used for instance to not check methods defines \
    -in Zope\'s Interface base class.'}
    -               ),
    -               ('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 = []
    -        self._first_attrs = []
    -        self._meth_could_be_func = None
    -
    -    def visit_class(self, node):
    -        """init visit variable _accessed and check interfaces
    -        """
    -        self._accessed.append(defaultdict(list))
    -        self._check_bases_classes(node)
    -        self._check_interfaces(node)
    -        # if not an interface, exception, metaclass
    -        if node.type == 'class':
    -            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)
    -
    -    @check_messages('inherit-non-class')
    -    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 (YES, None):
    -                continue
    -            if (isinstance(ancestor, astroid.Instance) and
    -                    ancestor.is_subtype_of('%s.type' % (BUILTINS,))):
    -                continue
    -            if not isinstance(ancestor, astroid.Class):
    -                self.add_message('inherit-non-class',
    -                                 args=base.as_string(), node=node)
    -
    -    @check_messages('access-member-before-definition',
    -                    'attribute-defined-outside-init')
    -    def leave_class(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
    -        accessed = self._accessed.pop()
    -        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_function(self, node):
    -        """check method arguments, overriding"""
    -        # ignore actual functions
    -        if not node.is_method():
    -            return
    -        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.Function):
    -                continue
    -            self._check_signature(node, meth_node, 'overridden')
    -            break
    -        if node.decorators:
    -            for decorator in node.decorators.nodes:
    -                if isinstance(decorator, astroid.Getattr) 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.Function)
    -                    and overridden_frame.type == 'method'):
    -                overridden_frame = overridden_frame.parent.frame()
    -            if (isinstance(overridden_frame, Class)
    -                    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
    -
    -        # check non-iterators in __iter__
    -        if node.name == '__iter__':
    -            self._check_iter(node)
    -        elif node.name == '__exit__':
    -            self._check_exit(node)
    -
    -    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
    -            for meth in ITER_METHODS:
    -                try:
    -                    slots.getattr(meth)
    -                    break
    -                except astroid.NotFoundError:
    -                    continue
    -            else:
    -                self.add_message('invalid-slots', node=node)
    -                continue
    -
    -            if isinstance(slots, astroid.Const):
    -                # a string, ignore the following checks
    -                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 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 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 _check_iter(self, node):
    -        try:
    -            infered = node.infer_call_result(node)
    -        except astroid.InferenceError:
    -            return
    -
    -        for infered_node in infered:
    -            if (infered_node is YES
    -                    or isinstance(infered_node, Generator)):
    -                continue
    -            if isinstance(infered_node, astroid.Instance):
    -                try:
    -                    infered_node.local_attr(NEXT_METHOD)
    -                except astroid.NotFoundError:
    -                    self.add_message('non-iterator-returned',
    -                                     node=node)
    -                    break
    -
    -    def _check_exit(self, node):
    -        positional = sum(1 for arg in node.args.args if arg.name != 'self')
    -        if positional < 3 and not node.args.vararg:
    -            self.add_message('bad-context-manager',
    -                             node=node)
    -        elif positional > 3:
    -            self.add_message('bad-context-manager',
    -                             node=node)
    -
    -    def leave_function(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 and any
    -        kind of method defined in an interface for this warning
    -        """
    -        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 not node.name in PYMETHODS
    -                    and not (node.is_abstract() or
    -                             overrides_a_method(class_node, node.name))
    -                    and class_node.type != 'interface'):
    -                self.add_message('no-self-use', node=node)
    -
    -    def visit_getattr(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)
    -        """
    -        attrname = node.attrname
    -        # Check self
    -        if self.is_first_attr(node):
    -            self._accessed[-1][attrname].append(node)
    -            return
    -        if not self.linter.is_message_enabled('protected-access'):
    -            return
    -
    -        self._check_protected_attribute_access(node)
    -
    -    def visit_assattr(self, node):
    -        if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr(node):
    -            self._accessed[-1][node.attrname].append(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, 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
    -                    self.add_message('assigning-non-slot',
    -                                     args=(node.attrname, ), node=node)
    -
    -    @check_messages('protected-access')
    -    def visit_assign(self, assign_node):
    -        node = assign_node.targets[0]
    -        if not isinstance(node, AssAttr):
    -            return
    -
    -        if self.is_first_attr(node):
    -            return
    -
    -        self._check_protected_attribute_access(node)
    -
    -    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.CallFunc) and \
    -               isinstance(node.expr.func, astroid.Name) and \
    -               node.expr.func.name == 'super':
    -                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()
    -                try:
    -                    if (isinstance(stmt, astroid.Assign) and
    -                            (stmt in klass.body or klass.parent_of(stmt)) and
    -                            isinstance(stmt.value, astroid.CallFunc) and
    -                            isinstance(stmt.value.func, astroid.Name) and
    -                            stmt.value.func.name == 'property' and
    -                            is_builtin_object(next(stmt.value.func.infer(), None))):
    -                        return
    -                except astroid.InferenceError:
    -                    pass
    -                self.add_message('protected-access', node=node, args=attrname)
    -
    -    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
    -        for attr, nodes in six.iteritems(accessed):
    -            # deactivate "except doesn't do anything", that's expected
    -            # pylint: disable=W0704
    -            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 are_exclusive(_node.statement(), defstmt,
    -                                                 ('AttributeError', 'Exception', 'BaseException')):
    -                            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_interfaces(self, node):
    -        """check that the given class node really implements declared
    -        interfaces
    -        """
    -        e0221_hack = [False]
    -        def iface_handler(obj):
    -            """filter interface objects, it should be classes"""
    -            if not isinstance(obj, astroid.Class):
    -                e0221_hack[0] = True
    -                self.add_message('interface-is-not-class', node=node,
    -                                 args=(obj.as_string(),))
    -                return False
    -            return True
    -        ignore_iface_methods = self.config.ignore_iface_methods
    -        try:
    -            for iface in node.interfaces(handler_func=iface_handler):
    -                for imethod in iface.methods():
    -                    name = imethod.name
    -                    if name.startswith('_') or name in ignore_iface_methods:
    -                        # don't check method beginning with an underscore,
    -                        # usually belonging to the interface implementation
    -                        continue
    -                    # get class method astroid
    -                    try:
    -                        method = node_method(node, name)
    -                    except astroid.NotFoundError:
    -                        self.add_message('missing-interface-method',
    -                                         args=(name, iface.name),
    -                                         node=node)
    -                        continue
    -                    # ignore inherited methods
    -                    if method.parent.frame() is not node:
    -                        continue
    -                    # check signature
    -                    self._check_signature(method, imethod,
    -                                          '%s interface' % iface.name)
    -        except astroid.InferenceError:
    -            if e0221_hack[0]:
    -                return
    -            implements = Instance(node).getattr('__implements__')[0]
    -            assignment = implements.parent
    -            assert isinstance(assignment, astroid.Assign)
    -            # assignment.expr can be a Name or a Tuple or whatever.
    -            # Use as_string() for the message
    -            # FIXME: in case of multiple interfaces, find which one could not
    -            #        be resolved
    -            self.add_message('unresolved-interface', node=implements,
    -                             args=(node.name, assignment.value.as_string()))
    -
    -    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.CallFunc):
    -            expr = stmt.func
    -            if not isinstance(expr, astroid.Getattr) \
    -                   or expr.attrname != '__init__':
    -                continue
    -            # skip the test if using super
    -            if isinstance(expr.expr, astroid.CallFunc) and \
    -                   isinstance(expr.expr.func, astroid.Name) and \
    -               expr.expr.func.name == 'super':
    -                return
    -            try:
    -                for klass in expr.expr.infer():
    -                    if klass is 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.Class) and
    -                            is_builtin_object(klass._proxied) and
    -                            klass._proxied.name == '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):
    -            if klass.name == 'object' or method.parent.name == 'object':
    -                continue
    -            self.add_message('super-init-not-called', args=klass.name, node=node)
    -
    -    def _check_signature(self, method1, refmethod, class_type):
    -        """check that the signature of the two given methods match
    -
    -        class_type is in 'class', 'interface'
    -        """
    -        if not (isinstance(method1, astroid.Function)
    -                and isinstance(refmethod, astroid.Function)):
    -            self.add_message('method-check-failed',
    -                             args=(method1, refmethod), node=method1)
    -            return
    -        # don't care about functions with unknown argument (builtins)
    -        if method1.args.args is None or refmethod.args.args is None:
    -            return
    -        # if we use *args, **kwargs, skip the below checks
    -        if method1.args.vararg or method1.args.kwarg:
    -            return
    -        if is_attr_private(method1.name):
    -            return
    -        if len(method1.args.args) != len(refmethod.args.args):
    -            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 is_first_attr(self, node):
    -        """Check that attribute lookup name use first attribute variable name
    -        (self for method, cls for classmethod and mcs for metaclass).
    -        """
    -        return self._first_attrs and isinstance(node.expr, astroid.Name) and \
    -                   node.expr.name == self._first_attrs[-1]
    -
    -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 n in node.local_attr(method_name):
    -        if isinstance(n, astroid.Function):
    -            return n
    -    raise astroid.NotFoundError(method_name)
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(ClassChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/design_analysis.py b/pymode/libs/pylint/checkers/design_analysis.py
    deleted file mode 100644
    index 9ff10bf3..00000000
    --- a/pymode/libs/pylint/checkers/design_analysis.py
    +++ /dev/null
    @@ -1,331 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""check for signs of poor design"""
    -
    -import re
    -from collections import defaultdict
    -
    -from astroid import If, InferenceError
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import check_messages
    -
    -# regexp for ignored argument name
    -IGNORED_ARGUMENT_NAMES = re.compile('_.*')
    -
    -
    -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.'),
    -    'R0923': ('Interface not implemented',
    -              'interface-not-implemented',
    -              'Used when an interface class is not implemented anywhere.'),
    -    }
    -
    -
    -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'}
    -               ),
    -               ('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'}
    -               ),
    -               ('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).'}
    -               ),
    -              )
    -
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        self.stats = None
    -        self._returns = None
    -        self._branches = None
    -        self._used_ifaces = None
    -        self._ifaces = None
    -        self._stmts = 0
    -
    -    def open(self):
    -        """initialize visit variables"""
    -        self.stats = self.linter.add_stats()
    -        self._returns = []
    -        self._branches = defaultdict(int)
    -        self._used_ifaces = {}
    -        self._ifaces = []
    -
    -    def close(self):
    -        """check that interface classes are used"""
    -        for iface in self._ifaces:
    -            if not iface in self._used_ifaces:
    -                self.add_message('interface-not-implemented', node=iface)
    -
    -    @check_messages('too-many-ancestors', 'too-many-instance-attributes',
    -                    'too-few-public-methods', 'too-many-public-methods',
    -                    'interface-not-implemented')
    -    def visit_class(self, node):
    -        """check size of inheritance hierarchy and number of instance attributes
    -        """
    -        # Is the total inheritance hierarchy is 7 or less?
    -        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))
    -        # Does the class contain less than 20 attributes for
    -        # non-GUI classes (40 for GUI)?
    -        # FIXME detect gui classes
    -        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))
    -        # update interface classes structures
    -        if node.type == 'interface' and node.name != 'Interface':
    -            self._ifaces.append(node)
    -            for parent in node.ancestors(False):
    -                if parent.name == 'Interface':
    -                    continue
    -                self._used_ifaces[parent] = 1
    -        try:
    -            for iface in node.interfaces():
    -                self._used_ifaces[iface] = 1
    -        except InferenceError:
    -            # XXX log ?
    -            pass
    -
    -    @check_messages('too-few-public-methods', 'too-many-public-methods')
    -    def leave_class(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_function(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
    -        if args is not None:
    -            ignored_args_num = len(
    -                [arg for arg in args
    -                 if self.config.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
    -
    -    @check_messages('too-many-return-statements', 'too-many-branches',
    -                    'too-many-arguments', 'too-many-locals',
    -                    'too-many-statements')
    -    def leave_function(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))
    -
    -    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
    -
    -    def visit_if(self, node):
    -        """increments the branches counter"""
    -        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 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 88a8f225..00000000
    --- a/pymode/libs/pylint/checkers/exceptions.py
    +++ /dev/null
    @@ -1,332 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""exceptions handling (raising, catching, exceptions classes) checker
    -"""
    -import sys
    -
    -import astroid
    -from astroid import YES, Instance, unpack_infer, List, Tuple
    -from logilab.common.compat import builtins
    -
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    is_empty,
    -    is_raising,
    -    check_messages,
    -    inherit_from_std_ex,
    -    EXCEPTIONS_MODULE,
    -    has_known_bases,
    -    safe_infer)
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
    -
    -
    -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, (List, Tuple)):
    -        for elt in stmt.elts:
    -            inferred = safe_infer(elt)
    -            if inferred and inferred is not YES:
    -                yield elt, inferred
    -        return
    -    for infered in stmt.infer(context):
    -        if infered is 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 catched 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)}),
    -    '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 BaseException: %s',
    -              'catching-non-exception',
    -              'Used when a class which doesn\'t inherit from \
    -               BaseException 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.'),
    -    'W0704': ('Except doesn\'t do anything',
    -              'pointless-except',
    -              'Used when an except clause does nothing but "pass" and there is\
    -              no "else" clause.'),
    -    '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 ExceptionsChecker(BaseChecker):
    -    """checks for
    -    * excepts without exception filter
    -    * type of raise argument : string, Exceptions, other values
    -    """
    -
    -    __implements__ = 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),)}
    -               ),
    -              )
    -
    -    @check_messages('nonstandard-exception',
    -                    'raising-bad-type', 'raising-non-exception',
    -                    'notimplemented-raised', 'bad-exception-context')
    -    def visit_raise(self, node):
    -        """visit raise possibly inferring value"""
    -        # ignore empty raise
    -        if node.exc is None:
    -            return
    -        if PY3K and node.cause:
    -            self._check_bad_exception_context(node)
    -
    -        expr = node.exc
    -        if self._check_raise_value(node, expr):
    -            return
    -        else:
    -            try:
    -                value = next(unpack_infer(expr))
    -            except astroid.InferenceError:
    -                return
    -            self._check_raise_value(node, value)
    -
    -    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 = safe_infer(node.cause)
    -        if cause in (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.Class) and
    -              not inherit_from_std_ex(cause)):
    -            self.add_message('bad-exception-context',
    -                             node=node)
    -
    -    def _check_raise_value(self, node, expr):
    -        """check for bad values, string exception and class inheritance
    -        """
    -        value_found = True
    -        if isinstance(expr, astroid.Const):
    -            value = expr.value
    -            if not isinstance(value, str):
    -                # raising-string will be emitted from python3 porting checker.
    -                self.add_message('raising-bad-type', node=node,
    -                                 args=value.__class__.__name__)
    -        elif ((isinstance(expr, astroid.Name) and
    -               expr.name in ('None', 'True', 'False')) or
    -              isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple,
    -                                astroid.Module, astroid.Function))):
    -            emit = True
    -            if not PY3K and isinstance(expr, astroid.Tuple):
    -                # 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 = expr.elts[0]
    -                inferred = safe_infer(first)
    -                if isinstance(inferred, Instance):
    -                    # pylint: disable=protected-access
    -                    inferred = inferred._proxied
    -                if (inferred is YES or
    -                        isinstance(inferred, astroid.Class)
    -                        and inherit_from_std_ex(inferred)):
    -                    emit = False
    -            if emit:
    -                self.add_message('raising-bad-type',
    -                                 node=node,
    -                                 args=expr.name)
    -        elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented')
    -              or (isinstance(expr, astroid.CallFunc) and
    -                  isinstance(expr.func, astroid.Name) and
    -                  expr.func.name == 'NotImplemented')):
    -            self.add_message('notimplemented-raised', node=node)
    -        elif isinstance(expr, (Instance, astroid.Class)):
    -            if isinstance(expr, Instance):
    -                # pylint: disable=protected-access
    -                expr = expr._proxied
    -            if (isinstance(expr, astroid.Class) and
    -                    not inherit_from_std_ex(expr)):
    -                if expr.newstyle:
    -                    self.add_message('raising-non-exception', node=node)
    -                else:
    -                    if has_known_bases(expr):
    -                        confidence = INFERENCE
    -                    else:
    -                        confidence = INFERENCE_FAILURE
    -                    self.add_message(
    -                        'nonstandard-exception', node=node,
    -                        confidence=confidence)
    -            else:
    -                value_found = False
    -        else:
    -            value_found = False
    -        return value_found
    -
    -    def _check_catching_non_exception(self, handler, exc, part):
    -        if isinstance(exc, astroid.Tuple):
    -            # Check if it is a tuple of exceptions.
    -            inferred = [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 inherit_from_std_ex(node)
    -                   for node in inferred):
    -                return
    -
    -        if not isinstance(exc, astroid.Class):
    -            # 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 inherit_from_std_ex(exc) and
    -                exc.root().name != BUILTINS_NAME):
    -            if has_known_bases(exc):
    -                self.add_message('catching-non-exception',
    -                                 node=handler.type,
    -                                 args=(exc.name, ))
    -
    -    @check_messages('bare-except', 'broad-except', 'pointless-except',
    -                    'binary-op-exception', 'bad-except-order',
    -                    'catching-non-exception')
    -    def visit_tryexcept(self, node):
    -        """check for empty except"""
    -        exceptions_classes = []
    -        nb_handlers = len(node.handlers)
    -        for index, handler in enumerate(node.handlers):
    -            # single except doing nothing but "pass" without else clause
    -            if is_empty(handler.body) and not node.orelse:
    -                self.add_message('pointless-except',
    -                                 node=handler.type or handler.body[0])
    -            if handler.type is None:
    -                if not 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 YES:
    -                        continue
    -                    if (isinstance(exc, astroid.Instance)
    -                            and 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.Class):
    -                        continue
    -
    -                    exc_ancestors = [anc for anc in exc.ancestors()
    -                                     if isinstance(anc, astroid.Class)]
    -                    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 == EXCEPTIONS_MODULE
    -                            and not is_raising(handler.body)):
    -                        self.add_message('broad-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 8c496ac1..00000000
    --- a/pymode/libs/pylint/checkers/format.py
    +++ /dev/null
    @@ -1,968 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""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
    -
    -_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'
    -_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.'),
    -    '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.\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]
    -    return token[4] + (' ' * 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 ''
    -    markers = [(pos, '|') for pos in bar_positions]
    -    markers.append((bad_position, '^'))
    -    markers.sort()
    -    line = [' '] * (markers[-1][0] + 1)
    -    for position, marker in markers:
    -        line[position] = marker
    -    return ''.join(line)
    -
    -
    -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'),
    -}
    -
    -
    -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 single: Valid offset for statements on a single logical line.
    -    :param with_body: Valid offset for statements on several lines.
    -    """
    -    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
    -        self._is_block_opener = self._tokens.token(pos) 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))
    -        elif 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)
    -        else:
    -            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))
    -        else:
    -            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 token: The concrete token
    -        :param 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.')}),
    -               ('no-space-check',
    -                {'default': ','.join(_NO_SPACE_CHECK_CHOICES),
    -                 'type': 'multiple_choice',
    -                 'choices': _NO_SPACE_CHECK_CHOICES,
    -                 'help': ('List of optional constructs for which whitespace '
    -                          'checking is disabled')}),
    -               ('max-module-lines',
    -                {'default' : 1000, 'type' : 'int', 'metavar' : '',
    -                 'help': 'Maximum number of lines in a module'}
    -               ),
    -               ('indent-string',
    -                {'default' : '    ', 'type' : "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 not depth:
    -                    # ')' 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 _check_equals_spacing(self, tokens, i):
    -        """Check the spacing of a single equals sign."""
    -        if 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'
    -            else:
    -                return 'No', 'allowed'
    -
    -        def _name_construct(token):
    -            if token[1] == ',':
    -                return 'comma'
    -            elif token[1] == ':':
    -                return ':'
    -            elif token[1] in '()[]{}':
    -                return 'bracket'
    -            elif token[1] in ('<', '>', '<=', '>=', '!=', '=='):
    -                return 'comparison'
    -            else:
    -                if self._inside_brackets('('):
    -                    return 'keyword argument assignment'
    -                else:
    -                    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
    -
    -        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:
    -                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)
    -
    -    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 = _get_indent_hint_line(offsets, tokens.start_col(position))
    -        self.add_message(
    -            'bad-continuation',
    -            line=tokens.start_line(position),
    -            args=(readable_type, readable_position, 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
    -        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
    -
    -        for line in lines.splitlines(True):
    -            if not line.endswith('\n'):
    -                self.add_message('missing-final-newline', line=i)
    -            else:
    -                stripped_line = line.rstrip()
    -                if 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))
    -            i += 1
    -
    -    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 1969eeb1..00000000
    --- a/pymode/libs/pylint/checkers/imports.py
    +++ /dev/null
    @@ -1,413 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""imports checkers for Python code"""
    -
    -import sys
    -from collections import defaultdict
    -
    -import six
    -from six.moves import map # pylint: disable=redefined-builtin
    -
    -from logilab.common.graph import get_cycles, DotBackend
    -from logilab.common.ureports import VerbatimText, Paragraph
    -
    -import astroid
    -from astroid import are_exclusive
    -from astroid.modutils import get_module_part, is_standard_module
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.utils import EmptyReport
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import check_messages, is_import_error
    -
    -def _except_import_error(node):
    -    """
    -    Check if the try-except node has an ImportError handler.
    -    Return True if an ImportError handler was infered, False otherwise.
    -    """
    -    if not isinstance(node, astroid.TryExcept):
    -        return
    -    return any(map(is_import_error, node.handlers))
    -
    -def get_first_import(node, context, name, base, level):
    -    """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.From):
    -            if level == first.level and any(
    -                    fullname == '%s.%s' % (first.modname, iname[0])
    -                    for iname in first.names):
    -                found = True
    -                break
    -    if found and not are_exclusive(first, node):
    -        return first
    -
    -# 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(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 modname in dependencies:
    -            if modname not in done:
    -                done[modname] = 1
    -                printer.emit_node(modname)
    -    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 = {
    -    'F0401': ('Unable to import %s',
    -              'import-error',
    -              'Used when pylint has been unable to import a module.'),
    -    '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.',
    -              {'maxversion': (3, 0)}),
    -    }
    -
    -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 sys.version_info < (3,):
    -        deprecated_modules = ('regsub', 'TERMIOS', 'Bastion', 'rexec')
    -    else:
    -        deprecated_modules = ('stringprep', 'optparse')
    -    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)'}
    -               ),
    -              )
    -
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        self.stats = None
    -        self.import_graph = 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),
    -                       )
    -
    -    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 = defaultdict(set)
    -
    -    def close(self):
    -        """called before visiting project (i.e set of modules)"""
    -        # don't try to compute cycles if the associated message is disabled
    -        if self.linter.is_message_enabled('cyclic-import'):
    -            vertices = list(self.import_graph)
    -            for cycle in get_cycles(self.import_graph, vertices=vertices):
    -                self.add_message('cyclic-import', args=' -> '.join(cycle))
    -
    -    def visit_import(self, node):
    -        """triggered when an import statement is seen"""
    -        modnode = node.root()
    -        for name, _ in node.names:
    -            importedmodnode = self.get_imported_module(node, name)
    -            if importedmodnode is None:
    -                continue
    -            self._check_relative_import(modnode, node, importedmodnode, name)
    -            self._add_imported_module(node, importedmodnode.name)
    -            self._check_deprecated_module(node, name)
    -            self._check_reimport(node, name)
    -
    -    # TODO This appears to be the list of all messages of the checker...
    -    # @check_messages('W0410', 'W0401', 'W0403', 'W0402', 'W0404', 'W0406', 'F0401')
    -    @check_messages(*(MSGS.keys()))
    -    def visit_from(self, node):
    -        """triggered when a from statement is seen"""
    -        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.From)
    -                        and prev.modname == '__future__'):
    -                    self.add_message('misplaced-future', node=node)
    -            return
    -        for name, _ in node.names:
    -            if name == '*':
    -                self.add_message('wildcard-import', args=basename, node=node)
    -        modnode = node.root()
    -        importedmodnode = self.get_imported_module(node, basename)
    -        if importedmodnode is None:
    -            return
    -        self._check_relative_import(modnode, node, importedmodnode, basename)
    -        self._check_deprecated_module(node, basename)
    -        for name, _ in node.names:
    -            if name != '*':
    -                self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name))
    -                self._check_reimport(node, name, basename, node.level)
    -
    -    def get_imported_module(self, importnode, modname):
    -        try:
    -            return importnode.do_import_module(modname)
    -        except astroid.InferenceError as ex:
    -            if str(ex) != modname:
    -                args = '%r (%s)' % (modname, ex)
    -            else:
    -                args = repr(modname)
    -            if not _except_import_error(importnode.parent):
    -                self.add_message("import-error", args=args, 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"""
    -        try:
    -            importedmodname = get_module_part(importedmodname)
    -        except ImportError:
    -            pass
    -        context_name = node.root().name
    -        if context_name == importedmodname:
    -            # module importing itself !
    -            self.add_message('import-self', node=node)
    -        elif not is_standard_module(importedmodname):
    -            # handle dependencies
    -            importedmodnames = self.stats['dependencies'].setdefault(
    -                importedmodname, set())
    -            if not context_name in importedmodnames:
    -                importedmodnames.add(context_name)
    -            # update import graph
    -            mgraph = self.import_graph[context_name]
    -            if importedmodname not in mgraph:
    -                mgraph.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, name, 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 context, level in contexts:
    -            first = get_first_import(node, context, name, basename, level)
    -            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 EmptyReport()
    -        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 EmptyReport()
    -        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 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 897c1c7f..00000000
    --- a/pymode/libs/pylint/checkers/logging.py
    +++ /dev/null
    @@ -1,256 +0,0 @@
    -# Copyright (c) 2009-2010 Google, Inc.
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""checker for use of Python logging
    -"""
    -
    -import astroid
    -from pylint import checkers
    -from pylint import interfaces
    -from pylint.checkers import utils
    -from pylint.checkers.utils import check_messages
    -
    -import six
    -
    -
    -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 but 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 few arguments.'),
    -    'E1206': ('Not enough arguments for logging format string',
    -              'logging-too-few-args',
    -              'Used when a logging format string is given too many arguments'),
    -    }
    -
    -
    -CHECKED_CONVENIENCE_FUNCTIONS = set([
    -    'critical', 'debug', 'error', 'exception', 'fatal', 'info', 'warn',
    -    'warning'])
    -
    -def is_method_call(callfunc_node, types=(), methods=()):
    -    """Determines if a CallFunc node represents a method call.
    -
    -    Args:
    -      callfunc_node: The CallFunc AST node to check.
    -      types: Optional sequence of caller type names to restrict check.
    -      methods: Optional sequence of method names to restrict check.
    -
    -    Returns:
    -      True, if the node represents a method call for the given type and
    -      method names, False otherwise.
    -    """
    -    if not isinstance(callfunc_node, astroid.CallFunc):
    -        return False
    -    func = utils.safe_infer(callfunc_node.func)
    -    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_from(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_callfunc(self, node):
    -        """Checks calls to logging methods."""
    -        def is_logging_name():
    -            return (isinstance(node.func, astroid.Getattr) 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.Class) 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.CallFunc):
    -            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, callfunc_node):
    -        """Checks that function call is not format_string.format().
    -
    -        Args:
    -          callfunc_node: CallFunc AST node to be checked.
    -        """
    -        if is_method_call(callfunc_node, ('str', 'unicode'), ('format',)):
    -            self.add_message('logging-format-interpolation', node=callfunc_node)
    -
    -    def _check_format_string(self, node, format_arg):
    -        """Checks that format string tokens match the supplied arguments.
    -
    -        Args:
    -          node: AST node to be checked.
    -          format_arg: 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 _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 of AST nodes that are arguments for a log format string.
    -
    -    Returns:
    -      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 7fbe70bf..00000000
    --- a/pymode/libs/pylint/checkers/misc.py
    +++ /dev/null
    @@ -1,104 +0,0 @@
    -# pylint: disable=W0511
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -""" Copyright (c) 2000-2010 LOGILAB S.A. (Paris, FRANCE).
    - http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -
    -Check source code is ascii only or has an encoding declaration (PEP 263)
    -"""
    -
    -import re
    -
    -from pylint.interfaces import IRawChecker
    -from pylint.checkers import BaseChecker
    -import six
    -
    -
    -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):-1], 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 f74e7f15..00000000
    --- a/pymode/libs/pylint/checkers/newstyle.py
    +++ /dev/null
    @@ -1,172 +0,0 @@
    -# Copyright (c) 2005-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""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,
    -    has_known_bases,
    -    node_frame_class,
    -)
    -
    -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_class(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_callfunc(self, node):
    -        """check property usage"""
    -        parent = node.parent.frame()
    -        if (isinstance(parent, astroid.Class) 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_function(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.CallFunc):
    -            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.Getattr):
    -                continue
    -            call = expr.expr
    -            # skip the test if using super
    -            if isinstance(call, astroid.CallFunc) and \
    -               isinstance(call.func, astroid.Name) and \
    -               call.func.name == 'super':
    -                confidence = (INFERENCE if has_known_bases(klass)
    -                              else INFERENCE_FAILURE)
    -                if not klass.newstyle:
    -                    # super should not be used on an old style class
    -                    self.add_message('super-on-old-class', node=node,
    -                                     confidence=confidence)
    -                else:
    -                    # super first arg should be the class
    -                    if not call.args and sys.version_info[0] == 3:
    -                        # unless Python 3
    -                        continue
    -
    -                    try:
    -                        supcls = (call.args and next(call.args[0].infer())
    -                                  or None)
    -                    except astroid.InferenceError:
    -                        continue
    -
    -                    if supcls is None:
    -                        self.add_message('missing-super-argument', node=call,
    -                                         confidence=confidence)
    -                        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 is not astroid.YES:
    -                            name = supcls.name
    -                        else:
    -                            if hasattr(call.args[0], 'name'):
    -                                name = call.args[0].name
    -                        if name is not None:
    -                            self.add_message('bad-super-call',
    -                                             node=call,
    -                                             args=(name, ),
    -                                             confidence=confidence)
    -
    -
    -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 837cbef1..00000000
    --- a/pymode/libs/pylint/checkers/python3.py
    +++ /dev/null
    @@ -1,581 +0,0 @@
    -# Copyright 2014 Google Inc.
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Check Python 2 code for Python 2/3 source-compatible issues."""
    -from __future__ import absolute_import, print_function
    -
    -import re
    -import tokenize
    -
    -import astroid
    -from astroid import bases
    -from pylint import checkers, interfaces
    -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('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, astroid.UnresolvableName):
    -        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.CallFunc):
    -        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.Getattr):
    -            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
    -
    -
    -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')]}),
    -        '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' __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)}),
    -    }
    -
    -    _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__',
    -    ])
    -
    -    def __init__(self, *args, **kwargs):
    -        self._future_division = False
    -        self._future_absolute_import = False
    -        super(Python3Checker, self).__init__(*args, **kwargs)
    -
    -    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_function(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)
    -
    -    @utils.check_messages('no-absolute-import')
    -    def visit_from(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
    -        elif not self._future_absolute_import:
    -            self.add_message('no-absolute-import', node=node)
    -
    -    @utils.check_messages('no-absolute-import')
    -    def visit_import(self, node):
    -        if not self._future_absolute_import:
    -            self.add_message('no-absolute-import', node=node)
    -
    -    @utils.check_messages('metaclass-assignment')
    -    def visit_class(self, node):
    -        if '__metaclass__' in node.locals:
    -            self.add_message('metaclass-assignment', 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
    -        args = []
    -        if (isinstance(node.func, astroid.Getattr)
    -                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):
    -                args = node.args
    -
    -        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:
    -                args = node.args
    -
    -        for arg in args:
    -            if isinstance(arg, astroid.Keyword) and arg.arg == 'cmp':
    -                self.add_message('using-cmp-argument', node=node)
    -                return
    -
    -    def visit_callfunc(self, node):
    -        self._check_cmp_argument(node)
    -
    -        if isinstance(node.func, astroid.Getattr):
    -            if any([node.args, node.starargs, node.kwargs]):
    -                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)
    -
    -
    -    @utils.check_messages('indexing-exception')
    -    def visit_subscript(self, node):
    -        """ Look for indexing exceptions. """
    -        try:
    -            for infered in node.value.infer():
    -                if not isinstance(infered, astroid.Instance):
    -                    continue
    -                if utils.inherit_from_std_ex(infered):
    -                    self.add_message('indexing-exception', node=node)
    -        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_backquote(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',
    -                  'Usen 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 71fecf68..00000000
    --- a/pymode/libs/pylint/checkers/raw_metrics.py
    +++ /dev/null
    @@ -1,129 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
    - http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -
    -Raw metrics checker
    -"""
    -
    -import tokenize
    -
    -# pylint now requires pylint >= 2.2, so this is no longer necessary
    -#if not hasattr(tokenize, 'NL'):
    -#    raise ValueError("tokenize.NL doesn't exist -- tokenize module too old")
    -
    -from logilab.common.ureports import Table
    -
    -from pylint.interfaces import ITokenChecker
    -from pylint.utils import EmptyReport
    -from pylint.checkers import BaseTokenChecker
    -from pylint.reporters import diff_string
    -
    -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 EmptyReport()
    -    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 tok_type == 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/similar.py b/pymode/libs/pylint/checkers/similar.py
    deleted file mode 100644
    index 95420776..00000000
    --- a/pymode/libs/pylint/checkers/similar.py
    +++ /dev/null
    @@ -1,372 +0,0 @@
    -# pylint: disable=W0622
    -# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""a similarities / code duplication command line tool and pylint checker
    -"""
    -from __future__ import print_function
    -import sys
    -from collections import defaultdict
    -
    -from logilab.common.ureports import Table
    -
    -from pylint.interfaces import IRawChecker
    -from pylint.checkers import BaseChecker, table_lines_from_stats
    -
    -import six
    -from six.moves import zip
    -
    -
    -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 f6edd5db..00000000
    --- a/pymode/libs/pylint/checkers/spelling.py
    +++ /dev/null
    @@ -1,250 +0,0 @@
    -# Copyright 2014 Michal Nowikowski.
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Checker for spelling errors in comments and docstrings.
    -"""
    -
    -import sys
    -import tokenize
    -import string
    -import re
    -
    -if sys.version_info[0] >= 3:
    -    maketrans = str.maketrans
    -else:
    -    maketrans = string.maketrans
    -
    -from pylint.interfaces import ITokenChecker, IAstroidChecker
    -from pylint.checkers import BaseTokenChecker
    -from pylint.checkers.utils import check_messages
    -
    -try:
    -    import enchant
    -except ImportError:
    -    enchant = None
    -
    -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 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"])
    -
    -        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.initialized = True
    -
    -    def close(self):
    -        if self.private_dict_file:
    -            self.private_dict_file.close()
    -
    -    def _check_spelling(self, msgid, line, line_num):
    -        line2 = line.strip()
    -        # Replace ['afadf with afadf (but preserve don't)
    -        line2 = re.sub("'([^a-zA-Z]|$)", " ", line2)
    -        # Replace afadf'] with afadf (but preserve don't)
    -        line2 = re.sub("([^a-zA-Z]|^)'", " ", line2)
    -        # Replace punctuation signs with space e.g. and/or -> and or
    -        line2 = self.punctuation_regex.sub(' ', line2)
    -
    -        words = []
    -        for word in line2.split():
    -            # Skip words with digits.
    -            if len(re.findall(r"\d", word)) > 0:
    -                continue
    -
    -            # Skip words with mixed big and small letters,
    -            # they are probaly class names.
    -            if (len(re.findall("[A-Z]", word)) > 0 and
    -                    len(re.findall("[a-z]", word)) > 0 and
    -                    len(word) > 2):
    -                continue
    -
    -            # Skip words with _ - they are probably function parameter names.
    -            if word.count('_') > 0:
    -                continue
    -
    -            words.append(word)
    -
    -        # Go through words and check them.
    -        for word in words:
    -            # 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,
    -                                       "' 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:
    -                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_class(self, node):
    -        if not self.initialized:
    -            return
    -        self._check_docstring(node)
    -
    -    @check_messages('wrong-spelling-in-docstring')
    -    def visit_function(self, node):
    -        if not self.initialized:
    -            return
    -        self._check_docstring(node)
    -
    -    def _check_docstring(self, node):
    -        """check the node has any spelling errors"""
    -        docstring = node.doc
    -        if not docstring:
    -            return
    -
    -        start_line = node.lineno + 1
    -
    -        # 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 a3a61063..00000000
    --- a/pymode/libs/pylint/checkers/stdlib.py
    +++ /dev/null
    @@ -1,216 +0,0 @@
    -# Copyright 2012 Google Inc.
    -#
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Checkers for various standard library functions."""
    -
    -import six
    -import sys
    -
    -import astroid
    -from astroid.bases import Instance
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.checkers import BaseChecker
    -from pylint.checkers import utils
    -
    -
    -TYPECHECK_COMPARISON_OPERATORS = frozenset(('is', 'is not', '==', '!=', 'in', 'not in'))
    -LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set)
    -
    -if sys.version_info >= (3, 0):
    -    OPEN_MODULE = '_io'
    -    TYPE_QNAME = 'builtins.type'
    -else:
    -    OPEN_MODULE = '__builtin__'
    -    TYPE_QNAME = '__builtin__.type'
    -
    -
    -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
    -
    -
    -def _is_one_arg_pos_call(call):
    -    """Is this a call with exactly 1 argument,
    -    where that argument is positional?
    -    """
    -    return (isinstance(call, astroid.CallFunc)
    -            and len(call.args) == 1
    -            and not isinstance(call.args[0], astroid.Keyword))
    -
    -
    -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.'),
    -        'W1504': ('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.')
    -    }
    -
    -    @utils.check_messages('bad-open-mode', 'redundant-unittest-assert')
    -    def visit_callfunc(self, node):
    -        """Visit a CallFunc node."""
    -        if hasattr(node, 'func'):
    -            infer = utils.safe_infer(node.func)
    -            if infer:
    -                if infer.root().name == OPEN_MODULE:
    -                    if getattr(node.func, 'name', None) in ('open', 'file'):
    -                        self._check_open_mode(node)
    -                if infer.root().name == 'unittest.case':
    -                    self._check_redundant_assert(node, infer)
    -
    -    @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)
    -
    -    @utils.check_messages('unidiomatic-typecheck')
    -    def visit_compare(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_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 _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.Class)
    -                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.Class)
    -                    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(StdlibChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/strings.py b/pymode/libs/pylint/checkers/strings.py
    deleted file mode 100644
    index 8892c2cc..00000000
    --- a/pymode/libs/pylint/checkers/strings.py
    +++ /dev/null
    @@ -1,615 +0,0 @@
    -# Copyright (c) 2009-2010 Arista Networks, Inc. - James Lingard
    -# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE).
    -# Copyright 2012 Google Inc.
    -#
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Checker for string formatting operations.
    -"""
    -
    -import sys
    -import tokenize
    -import string
    -import numbers
    -
    -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
    -
    -import six
    -
    -
    -_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 conWtains 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"),
    -
    -    '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",
    -              "Usen 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.Backquote,
    -               astroid.Lambda, astroid.Function,
    -               astroid.ListComp, astroid.SetComp, astroid.GenExpr)
    -
    -if _PY3K:
    -    import _string
    -
    -    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):
    -        keyname, fielditerator = format_string._formatter_field_name_split()
    -        # 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:
    -        # probably the format string is invalid
    -        # should we check the argument of the ValueError?
    -        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)
    -            keys.append((keyname, list(fielditerator)))
    -        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.
    -    """
    -    positional = 0
    -    named = {}
    -
    -    for arg in callfunc.args:
    -        if isinstance(arg, astroid.Keyword):
    -            named[arg.arg] = utils.safe_infer(arg.value)
    -        else:
    -            positional += 1
    -    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,
    -                        # supress 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):
    -                num_args = len(args.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)
    -
    -
    -class StringMethodsChecker(BaseChecker):
    -    __implements__ = (IAstroidChecker,)
    -    name = 'string'
    -    msgs = {
    -        'E1310': ("Suspicious argument in %s.%s call",
    -                  "bad-str-strip-call",
    -                  "The argument to a str.{l,r,}strip call contains a"
    -                  " duplicate character, "),
    -        }
    -
    -    @check_messages(*(MSGS.keys()))
    -    def visit_callfunc(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.Getattr)
    -                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 node.starargs or node.kwargs:
    -            # TODO: Don't complicate the logic, skip these for now.
    -            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(specifier)
    -                        except (IndexError, TypeError):
    -                            warn_error = True
    -                    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(StringMethodsChecker(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 9f074ae0..00000000
    --- a/pymode/libs/pylint/checkers/typecheck.py
    +++ /dev/null
    @@ -1,627 +0,0 @@
    -# Copyright (c) 2006-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""try to find more bugs in the code using astroid inference capabilities
    -"""
    -
    -import re
    -import shlex
    -
    -import astroid
    -from astroid import InferenceError, NotFoundError, YES, Instance
    -from astroid.bases import BUILTINS
    -
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    safe_infer, is_super,
    -    check_messages, decorated_with_property)
    -
    -MSGS = {
    -    'E1101': ('%s %r has no %r member',
    -              '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.'),
    -    'W1111': ('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.'),
    -
    -    '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.'),
    -    }
    -
    -# builtin sequence types in Python 2 and 3.
    -SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray',
    -                      'xrange', 'range', 'bytes', 'memoryview'])
    -
    -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.Function):
    -        return callable_obj, 0, callable_obj.type
    -    elif isinstance(callable_obj, astroid.Lambda):
    -        return callable_obj, 0, 'lambda'
    -    elif isinstance(callable_obj, astroid.Class):
    -        # Class instantiation, lookup __new__ instead.
    -        # If we only find object.__new__, we can safely check __init__
    -        # instead.
    -        try:
    -            # Use the last definition of __new__.
    -            new = callable_obj.local_attr('__new__')[-1]
    -        except astroid.NotFoundError:
    -            new = None
    -
    -        if not new or new.parent.scope().name == 'object':
    -            try:
    -                # Use the last definition of __init__.
    -                callable_obj = callable_obj.local_attr('__init__')[-1]
    -            except astroid.NotFoundError:
    -                # do nothing, covered by no-init.
    -                raise ValueError
    -        else:
    -            callable_obj = new
    -
    -        if not isinstance(callable_obj, astroid.Function):
    -            raise ValueError
    -        # both have an extra implicit 'cls'/'self' argument.
    -        return callable_obj, 1, 'constructor'
    -    else:
    -        raise ValueError
    -
    -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-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'},
    -               ),
    -               ('ignored-classes',
    -                {'default' : ('SQLObject',),
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of classes names for which member attributes \
    -should not be checked (useful for classes with attributes dynamically set).'}
    -               ),
    -
    -               ('zope',
    -                {'default' : False, 'type' : 'yn', 'metavar': '',
    -                 'help' : 'When zope mode is activated, add a predefined set \
    -of Zope acquired attributes to generated-members.'}
    -               ),
    -               ('generated-members',
    -                {'default' : ('REQUEST', 'acl_users', 'aq_parent'),
    -                 'type' : 'string',
    -                 'metavar' : '',
    -                 'help' : 'List of members which are set dynamically and \
    -missed by pylint inference system, and so shouldn\'t trigger E0201 when \
    -accessed. Python regular expressions are accepted.'}
    -               ),
    -              )
    -
    -    def open(self):
    -        # do this in open since config not fully initialized in __init__
    -        self.generated_members = list(self.config.generated_members)
    -        if self.config.zope:
    -            self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent'))
    -
    -    def visit_assattr(self, node):
    -        if isinstance(node.ass_type(), astroid.AugAssign):
    -            self.visit_getattr(node)
    -
    -    def visit_delattr(self, node):
    -        self.visit_getattr(node)
    -
    -    @check_messages('no-member')
    -    def visit_getattr(self, node):
    -        """check that the accessed attribute exists
    -
    -        to avoid to 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
    -        """
    -        # generated_members may containt 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, str):
    -            gen = shlex.shlex(self.config.generated_members)
    -            gen.whitespace += ','
    -            gen.wordchars += '[]-+'
    -            self.config.generated_members = tuple(tok.strip('"') for tok in gen)
    -        for pattern in self.config.generated_members:
    -            # attribute is marked as generated, stop here
    -            if re.match(pattern, node.attrname):
    -                return
    -        try:
    -            infered = list(node.expr.infer())
    -        except InferenceError:
    -            return
    -        # list of (node, nodename) which are missing the attribute
    -        missingattr = set()
    -        ignoremim = self.config.ignore_mixin_members
    -        inference_failure = False
    -        for owner in infered:
    -            # skip yes object
    -            if owner is YES:
    -                inference_failure = True
    -                continue
    -            # skip None anyway
    -            if isinstance(owner, astroid.Const) and owner.value is None:
    -                continue
    -            # XXX "super" / metaclass call
    -            if is_super(owner) or getattr(owner, 'type', None) == 'metaclass':
    -                continue
    -            name = getattr(owner, 'name', 'None')
    -            if name in self.config.ignored_classes:
    -                continue
    -            if ignoremim and name[-5:].lower() == 'mixin':
    -                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 NotFoundError:
    -                if isinstance(owner, astroid.Function) and owner.decorators:
    -                    continue
    -                if isinstance(owner, Instance) and owner.has_dynamic_getattr():
    -                    continue
    -                # explicit skipping of module member access
    -                if owner.root().name in self.config.ignored_modules:
    -                    continue
    -                if isinstance(owner, astroid.Class):
    -                    # Look up in the metaclass only if the owner is itself
    -                    # a class.
    -                    # TODO: getattr doesn't return by default members
    -                    # from the metaclass, because handling various cases
    -                    # of methods accessible from the metaclass itself
    -                    # and/or subclasses only is too complicated for little to
    -                    # no benefit.
    -                    metaclass = owner.metaclass()
    -                    try:
    -                        if metaclass and metaclass.getattr(node.attrname):
    -                            continue
    -                    except NotFoundError:
    -                        pass
    -                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, Instance):
    -                    actual = owner._proxied
    -                else:
    -                    actual = owner
    -                if actual in done:
    -                    continue
    -                done.add(actual)
    -                confidence = INFERENCE if not inference_failure else INFERENCE_FAILURE
    -                self.add_message('no-member', node=node,
    -                                 args=(owner.display_type(), name,
    -                                       node.attrname),
    -                                 confidence=confidence)
    -
    -    @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.CallFunc):
    -            return
    -        function_node = safe_infer(node.value.func)
    -        # skip class, generator and incomplete function definition
    -        if not (isinstance(function_node, astroid.Function) 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.Function))
    -        if len(returns) == 0:
    -            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.Getattr):
    -            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 astroid.NotFoundError:
    -            return
    -
    -        for attr in attrs:
    -            if attr is astroid.YES:
    -                continue
    -            if not isinstance(attr, astroid.Function):
    -                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_callfunc(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.
    -        keyword_args = set()
    -        num_positional_args = 0
    -        for arg in node.args:
    -            if isinstance(arg, astroid.Keyword):
    -                keyword_args.add(arg.arg)
    -            else:
    -                num_positional_args += 1
    -
    -        called = safe_infer(node.func)
    -        # only function, generator and object defining __call__ are allowed
    -        if called is not None and not called.callable():
    -            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 E9801).  We can't really make sense
    -            # of the function call in this case, so just return.
    -            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:
    -                if isinstance(arg, astroid.Keyword):
    -                    name = arg.arg
    -                else:
    -                    assert isinstance(arg, astroid.AssName)
    -                    # 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.AssName)
    -                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.
    -                    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 *args, if any.  Note that Python actually processes
    -        #    *args _before_ any keyword arguments, but we wait until after
    -        #    looking at the keyword arguments so as to make a more conservative
    -        #    guess at how many values are in the *args sequence.
    -        if node.starargs is not None:
    -            for i in range(num_positional_args, len(parameters)):
    -                [(name, defval), assigned] = parameters[i]
    -                # Assume that *args provides just enough values for all
    -                # non-default parameters after the last parameter assigned by
    -                # the positional arguments but before the first parameter
    -                # assigned by the keyword arguments.  This is the best we can
    -                # get without generating any false positives.
    -                if (defval is not None) or assigned:
    -                    break
    -                parameters[i][1] = True
    -
    -        # 4. Match the **kwargs, if any.
    -        if node.kwargs is not None:
    -            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)
    -                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:
    -                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.Class, astroid.Instance)):
    -            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.
    -        operation = node.parent.parent
    -        if isinstance(operation, astroid.Assign):
    -            methodname = '__setitem__'
    -        elif isinstance(operation, astroid.Delete):
    -            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 = parent_type.getattr(methodname)
    -            if methods is astroid.YES:
    -                return
    -            itemmethod = methods[0]
    -        except (astroid.NotFoundError, IndexError):
    -            return
    -
    -        if not isinstance(itemmethod, astroid.Function):
    -            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 astroid.NotFoundError:
    -                pass
    -
    -        # 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 astroid.NotFoundError:
    -                    pass
    -
    -            # Anything else is an error
    -            self.add_message('invalid-slice-index', node=node)
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(TypeChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/utils.py b/pymode/libs/pylint/checkers/utils.py
    deleted file mode 100644
    index 2cb01d55..00000000
    --- a/pymode/libs/pylint/checkers/utils.py
    +++ /dev/null
    @@ -1,564 +0,0 @@
    -# pylint: disable=W0611
    -#
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""some functions that may be useful for various checkers
    -"""
    -
    -import re
    -import sys
    -import string
    -
    -import astroid
    -from astroid import scoped_nodes
    -from logilab.common.compat import builtins
    -
    -BUILTINS_NAME = builtins.__name__
    -COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.GenExpr
    -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'))
    -
    -
    -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.AssAttr):
    -        return (True, (node.attrname, 'object %r' % (node.expr.as_string(),)))
    -    elif isinstance(node, astroid.AssName):
    -        name = node.name
    -        if is_builtin(name):
    -            return (True, (name, 'builtins'))
    -        else:
    -            stmts = node.lookup(name)[1]
    -            if (stmts and not isinstance(stmts[0].ass_type(),
    -                                         (astroid.Assign, astroid.AugAssign,
    -                                          astroid.ExceptHandler))):
    -                return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno))
    -    return (False, None)
    -
    -
    -def safe_infer(node):
    -    """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()
    -        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 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
    -
    -def is_empty(body):
    -    """return true if the given node does nothing but 'pass'"""
    -    return len(body) == 1 and isinstance(body[0], astroid.Pass)
    -
    -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): # was is_native_builtin
    -    """return true if  could be considered as a builtin defined by python
    -    """
    -    if name in builtins:
    -        return True
    -    if name in SPECIAL_BUILTINS:
    -        return True
    -    return False
    -
    -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.AssName):
    -                if ass_node.name == varname:
    -                    return True
    -        elif isinstance(_node, astroid.For):
    -            for ass_node in _node.target.nodes_of_class(astroid.AssName):
    -                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.AssName) and
    -                        ids.name == varname):
    -                    return True
    -        elif isinstance(_node, (astroid.Lambda, astroid.Function)):
    -            if _node.args.is_argument(varname):
    -                return True
    -            if getattr(_node, 'name', None) == varname:
    -                return True
    -            break
    -        elif isinstance(_node, astroid.ExceptHandler):
    -            if isinstance(_node.name, astroid.AssName):
    -                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.AssName):
    -            if ass_node.name == varname:
    -                return True
    -        for imp_node in _node.nodes_of_class((astroid.From, 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.Function):
    -        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) or
    -                isinstance(parent, (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.AssName,
    -                                     astroid.Tuple,
    -                                     astroid.List)):
    -        node = node.parent
    -    return node
    -
    -def overrides_an_abstract_method(class_node, name):
    -    """return True if pnode is a parent of node"""
    -    for ancestor in class_node.ancestors():
    -        if name in ancestor and isinstance(ancestor[name], astroid.Function) and \
    -               ancestor[name].is_abstract(pass_is_abstract=False):
    -            return True
    -    return False
    -
    -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.Function):
    -            return True
    -    return False
    -
    -PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__',
    -                 '__str__', '__repr__',
    -                 '__len__', '__iter__',
    -                 '__delete__', '__get__', '__set__',
    -                 '__getitem__', '__setitem__', '__delitem__', '__contains__',
    -                 '__getattribute__', '__getattr__', '__setattr__', '__delattr__',
    -                 '__call__',
    -                 '__enter__', '__exit__',
    -                 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__',
    -                 '__nonzero__', '__neg__', '__invert__',
    -                 '__mul__', '__imul__', '__rmul__',
    -                 '__div__', '__idiv__', '__rdiv__',
    -                 '__add__', '__iadd__', '__radd__',
    -                 '__sub__', '__isub__', '__rsub__',
    -                 '__pow__', '__ipow__', '__rpow__',
    -                 '__mod__', '__imod__', '__rmod__',
    -                 '__and__', '__iand__', '__rand__',
    -                 '__or__', '__ior__', '__ror__',
    -                 '__xor__', '__ixor__', '__rxor__',
    -                 # XXX To be continued
    -                ))
    -
    -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 not 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.Class):
    -        if klass.parent is None:
    -            klass = None
    -        else:
    -            klass = klass.parent.frame()
    -
    -    return klass
    -
    -def is_super_call(expr):
    -    """return True if expression node is a function call and if function name
    -    is super. Check before that you're in a method.
    -    """
    -    return (isinstance(expr, astroid.CallFunc) and
    -            isinstance(expr.func, astroid.Name) and
    -            expr.func.name == 'super')
    -
    -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 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.
    -    :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.')
    -    try:
    -        if position is not None and not isinstance(callfunc_node.args[position], astroid.Keyword):
    -            return callfunc_node.args[position]
    -    except IndexError as error:
    -        raise NoSuchArgumentError(error)
    -    if keyword:
    -        for arg in callfunc_node.args:
    -            if isinstance(arg, astroid.Keyword) and 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=False))
    -
    -def is_import_error(handler):
    -    """
    -    Check if the given exception handler catches
    -    ImportError.
    -
    -    :param handler: A node, representing an ExceptHandler node.
    -    :returns: True if the handler catches ImportError, False otherwise.
    -    """
    -    names = None
    -    if isinstance(handler.type, astroid.Tuple):
    -        names = [name for name in handler.type.elts
    -                 if isinstance(name, astroid.Name)]
    -    elif isinstance(handler.type, astroid.Name):
    -        names = [handler.type]
    -    else:
    -        # Don't try to infer that.
    -        return
    -    for name in names:
    -        try:
    -            for infered in name.infer():
    -                if (isinstance(infered, astroid.Class) and
    -                        inherit_from_std_ex(infered) and
    -                        infered.name == 'ImportError'):
    -                    return True
    -        except astroid.InferenceError:
    -            continue
    -
    -def has_known_bases(klass):
    -    """Returns 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)
    -        # TODO: check for A->B->A->B pattern in class structure too?
    -        if (not isinstance(result, astroid.Class) or
    -                result is klass or
    -                not has_known_bases(result)):
    -            klass._all_bases_known = False
    -            return False
    -    klass._all_bases_known = True
    -    return True
    -
    -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:
    -            for infered in decorator.infer():
    -                if isinstance(infered, astroid.Class):
    -                    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
    -        except astroid.InferenceError:
    -            pass
    -
    -
    -def decorated_with_abc(func):
    -    """Determine if the `func` node is decorated with `abc` decorators."""
    -    if func.decorators:
    -        for node in func.decorators.nodes:
    -            try:
    -                infered = next(node.infer())
    -            except astroid.InferenceError:
    -                continue
    -            if infered and infered.qname() in ABC_METHODS:
    -                return True
    -
    -
    -def unimplemented_abstract_methods(node, is_abstract_cb=decorated_with_abc):
    -    """
    -    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.
    -    """
    -    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.AssName):
    -                infered = safe_infer(obj)
    -                if not infered:
    -                    continue
    -                if not isinstance(infered, astroid.Function):
    -                    if obj.name in visited:
    -                        del visited[obj.name]
    -            if isinstance(infered, astroid.Function):
    -                # 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
    diff --git a/pymode/libs/pylint/checkers/variables.py b/pymode/libs/pylint/checkers/variables.py
    deleted file mode 100644
    index 8f6f9574..00000000
    --- a/pymode/libs/pylint/checkers/variables.py
    +++ /dev/null
    @@ -1,1069 +0,0 @@
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""variables checkers for Python code
    -"""
    -import os
    -import sys
    -import re
    -from copy import copy
    -
    -import astroid
    -from astroid import are_exclusive, builtin_lookup
    -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.utils import (
    -    PYMETHODS, is_ancestor_name, is_builtin,
    -    is_defined_before, is_error, is_func_default, is_func_decorator,
    -    assign_parent, check_messages, is_inside_except, clobber_in_except,
    -    get_all_elements, has_known_bases)
    -import six
    -
    -SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
    -
    -PY3K = sys.version_info >= (3, 0)
    -
    -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) for else_stmt in parent.orelse))
    -
    -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.Function):
    -        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.Function):
    -        # 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.Function, astroid.Arguments)):
    -            return False
    -    elif any(not isinstance(f, (astroid.Class, 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.Class, 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.AssName)
    -               and isinstance(stmt.ass_type(), astroid.AugAssign)
    -               for stmt in stmts):
    -            continue
    -        for stmt in stmts:
    -            if not isinstance(stmt, (astroid.From, 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.From))
    -    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
    -
    -
    -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 can not 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.'),
    -
    -    'W0632': ('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'),
    -
    -    'W0633': ('Attempting to unpack a non-sequence%s',
    -              'unpacking-non-sequence',
    -              'Used when something which is not '
    -              'a sequence is used in an unpack assignment'),
    -
    -    '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': ('_$|dummy'),
    -                 '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.'}
    -               )
    -              )
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        self._to_consume = None
    -        self._checking_mod_attr = None
    -
    -    def visit_module(self, node):
    -        """visit module : update consumption analysis variable
    -        checks globals doesn't overrides builtins
    -        """
    -        self._to_consume = [(copy(node.locals), {}, 'module')]
    -        for name, stmts in six.iteritems(node.locals):
    -            if is_builtin(name) and not is_inside_except(stmts[0]):
    -                # do not print Redefining builtin for additional builtins
    -                self.add_message('redefined-builtin', args=name, node=stmts[0])
    -
    -    @check_messages('unused-import', 'unused-wildcard-import',
    -                    'redefined-builtin', 'undefined-all-variable',
    -                    'invalid-all-object')
    -    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:
    -            assigned = next(node.igetattr('__all__'))
    -            if assigned is not astroid.YES:
    -                for elt in getattr(assigned, 'elts', ()):
    -                    try:
    -                        elt_name = next(elt.infer())
    -                    except astroid.InferenceError:
    -                        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
    -        # 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_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.From) and
    -                         not stmt.modname)):
    -                    if (isinstance(stmt, astroid.From) and
    -                            SPECIAL_OBJ.search(imported_name)):
    -                        # Filter special objects (__doc__, __all__) etc.,
    -                        # because they can be imported for exporting.
    -                        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.From) 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 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_class(self, node):
    -        """visit class: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'class'))
    -
    -    def leave_class(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(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_genexpr(self, node):
    -        """visit genexpr: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
    -
    -    def leave_genexpr(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(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(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_function(self, node):
    -        """visit function: update consumption analysis variable and check locals
    -        """
    -        self._to_consume.append((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 is_inside_except(stmt):
    -                continue
    -            if name in globs and not isinstance(stmt, astroid.Global):
    -                line = globs[name][0].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 is_builtin(name):
    -                # do not print Redefining builtin for additional builtins
    -                self.add_message('redefined-builtin', args=name, node=stmt)
    -
    -    def leave_function(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 is_error(node):
    -            return
    -        # don't check arguments of abstract methods or within an interface
    -        is_method = node.is_method()
    -        klass = node.parent.frame()
    -        if is_method and (klass.type == 'interface' or node.is_abstract()):
    -            return
    -        if is_method and isinstance(klass, astroid.Class):
    -            confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE
    -        else:
    -            confidence = HIGH
    -        authorized_rgx = self.config.dummy_variables_rgx
    -        called_overridden = False
    -        argnames = node.argnames()
    -        global_names = set()
    -        nonlocal_names = set()
    -        for global_stmt in node.nodes_of_class(astroid.Global):
    -            global_names.update(set(global_stmt.names))
    -        for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal):
    -            nonlocal_names.update(set(nonlocal_stmt.names))
    -
    -        for name, stmts in six.iteritems(not_consumed):
    -            # ignore some special names specified by user configuration
    -            if authorized_rgx.match(name):
    -                continue
    -            # ignore names imported by the global statement
    -            # FIXME: should only ignore them if it's assigned latter
    -            stmt = stmts[0]
    -            if isinstance(stmt, astroid.Global):
    -                continue
    -            if isinstance(stmt, (astroid.Import, astroid.From)):
    -                # Detect imports, assigned to global statements.
    -                if global_names:
    -                    skip = False
    -                    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:
    -                                skip = True
    -                                break
    -                        elif import_name in global_names:
    -                            skip = True
    -                            break
    -                    if skip:
    -                        continue
    -
    -            # 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]:
    -                        continue
    -                    # don't warn for argument of an overridden method
    -                    if not called_overridden:
    -                        overridden = overridden_method(klass, node.name)
    -                        called_overridden = True
    -                    if overridden is not None and name in overridden.argnames():
    -                        continue
    -                    if node.name in PYMETHODS and node.name not in ('__init__', '__new__'):
    -                        continue
    -                # don't check callback arguments
    -                if any(node.name.startswith(cb) or node.name.endswith(cb)
    -                       for cb in self.config.callbacks):
    -                    continue
    -                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:
    -                        continue
    -                self.add_message('unused-variable', args=name, node=stmt)
    -
    -    @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 = []
    -            for anode in assign_nodes:
    -                if anode.parent is None:
    -                    # node returned for builtin attribute such as __file__,
    -                    # __doc__, etc...
    -                    continue
    -                if anode.frame() is frame:
    -                    # same scope level assignment
    -                    break
    -            else:
    -                if not _find_frame_imports(name, frame):
    -                    self.add_message('global-variable-not-assigned',
    -                                     args=name, node=node)
    -                default_message = False
    -            if not assign_nodes:
    -                continue
    -            for anode in assign_nodes:
    -                if anode.parent is None:
    -                    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.CallFunc)
    -                    and node_scope.parent.func is node_scope)
    -
    -        node_scope = node.scope()
    -        if not isinstance(node_scope, (astroid.Lambda, astroid.Function)):
    -            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:
    -            ass = astmts[0].ass_type()
    -            if isinstance(ass, (astroid.For, astroid.Comprehension, astroid.GenExpr)) \
    -                   and not ass.statement() is node.statement():
    -                self.add_message('undefined-loop-variable', args=name, node=node)
    -
    -    @check_messages('redefine-in-handler')
    -    def visit_excepthandler(self, node):
    -        for name in get_all_elements(node.name):
    -            clobbering, args = clobber_in_except(name)
    -            if clobbering:
    -                self.add_message('redefine-in-handler', args=args, node=name)
    -
    -    def visit_assname(self, node):
    -        if isinstance(node.ass_type(), astroid.AugAssign):
    -            self.visit_name(node)
    -
    -    def visit_delname(self, node):
    -        self.visit_name(node)
    -
    -    @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 (is_func_default(node) or is_func_decorator(node)
    -                or 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]
    -        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):
    -                # 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):
    -                #        ...
    -
    -                in_annotation = (
    -                    PY3K and isinstance(frame, astroid.Function)
    -                    and node.statement() is frame and
    -                    (node in frame.args.annotations
    -                     or node is frame.args.varargannotation
    -                     or node is frame.args.kwargannotation))
    -                if in_annotation:
    -                    frame_locals = frame.parent.scope().locals
    -                else:
    -                    frame_locals = frame.locals
    -                if not ((isinstance(frame, astroid.Class) or in_annotation)
    -                        and name in frame_locals):
    -                    continue
    -            # the name has already been consumed, only check it's not a loop
    -            # variable used outside the loop
    -            if name in consumed:
    -                defnode = assign_parent(consumed[name][0])
    -                self._check_late_binding_closure(node, defnode)
    -                self._loopvar_name(node, name)
    -                break
    -            # mark the name as consumed if it's defined in this scope
    -            # (i.e. no KeyError is raised by "to_consume[name]")
    -            try:
    -                consumed[name] = to_consume[name]
    -            except KeyError:
    -                continue
    -            # checks for use before assignment
    -            defnode = assign_parent(to_consume[name][0])
    -            if defnode is not None:
    -                self._check_late_binding_closure(node, defnode)
    -                defstmt = defnode.statement()
    -                defframe = defstmt.frame()
    -                maybee0601 = True
    -                if not frame is 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 builtin_lookup(name)[1]:
    -                        maybee0601 = False
    -                else:
    -                    # we are in a local scope, check the name is not
    -                    # defined in global or builtin scope
    -                    if defframe.root().lookup(name)[1]:
    -                        maybee0601 = False
    -                    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())
    -
    -                # Handle a couple of class scoping issues.
    -                annotation_return = False
    -                # The class reuses itself in the class scope.
    -                recursive_klass = (frame is defframe and
    -                                   defframe.parent_of(node) and
    -                                   isinstance(defframe, astroid.Class) and
    -                                   node.name == defframe.name)
    -                if (self._to_consume[-1][-1] == 'lambda' and
    -                        isinstance(frame, astroid.Class)
    -                        and name in frame.locals):
    -                    maybee0601 = True
    -                elif (isinstance(defframe, astroid.Class) and
    -                      isinstance(frame, astroid.Function)):
    -                    # 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
    -                elif recursive_klass:
    -                    maybee0601 = True
    -                else:
    -                    maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno
    -
    -                if (maybee0601
    -                        and not is_defined_before(node)
    -                        and not are_exclusive(stmt, defstmt, ('NameError',
    -                                                              'Exception',
    -                                                              'BaseException'))):
    -                    if recursive_klass or (defstmt is stmt and
    -                                           isinstance(node, (astroid.DelName,
    -                                                             astroid.AssName))):
    -                        self.add_message('undefined-variable', args=name, node=node)
    -                    elif annotation_return:
    -                        self.add_message('undefined-variable', args=name, node=node)
    -                    elif self._to_consume[-1][-1] != 'lambda':
    -                        # E0601 may *not* occurs in lambda scope.
    -                        self.add_message('used-before-assignment', args=name, node=node)
    -                    elif self._to_consume[-1][-1] == '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.Class) and name in frame.locals:
    -                            if isinstance(node.parent, astroid.Arguments):
    -                                # Doing the following is fine:
    -                                #   class A:
    -                                #      x = 42
    -                                #      y = lambda attr=x: attr
    -                                if stmt.fromlineno <= defstmt.fromlineno:
    -                                    self.add_message('used-before-assignment',
    -                                                     args=name, node=node)
    -                            else:
    -                                self.add_message('undefined-variable',
    -                                                 args=name, node=node)
    -
    -            if isinstance(node, astroid.AssName): # Aug AssName
    -                del consumed[name]
    -            else:
    -                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 is_builtin(name)
    -                    or name in self.config.additional_builtins):
    -                self.add_message('undefined-variable', args=name, node=node)
    -
    -    @check_messages('no-name-in-module')
    -    def visit_import(self, node):
    -        """check modules attribute accesses"""
    -        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:])
    -
    -    @check_messages('no-name-in-module')
    -    def visit_from(self, node):
    -        """check modules attribute accesses"""
    -        name_parts = node.modname.split('.')
    -        level = getattr(node, 'level', None)
    -        try:
    -            module = node.root().import_module(name_parts[0], level=level)
    -        except Exception: # pylint: disable=broad-except
    -            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('.'))
    -
    -    @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:
    -            for infered in node.value.infer():
    -                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 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)
    -        elif isinstance(infered, astroid.Instance):
    -            for meth in ('__iter__', '__getitem__'):
    -                try:
    -                    infered.getattr(meth)
    -                    break
    -                except astroid.NotFoundError:
    -                    continue
    -            else:
    -                self.add_message('unpacking-non-sequence', node=node,
    -                                 args=(_get_unpacking_extra_info(node, infered),))
    -        else:
    -            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
    -        ignored_modules = get_global_option(self, 'ignored-modules',
    -                                            default=[])
    -        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 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 and module.name or '__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(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_module(self, node):
    -        """ Update consumption analysis variable
    -        for metaclasses.
    -        """
    -        module_locals = self._to_consume[0][0]
    -        module_imports = self._to_consume[0][1]
    -        consumed = {}
    -
    -        for klass in node.nodes_of_class(astroid.Class):
    -            found = metaclass = name = None
    -            if not klass._metaclass:
    -                # Skip if this class doesn't use
    -                # explictly a metaclass, but inherits it from ancestors
    -                continue
    -
    -            metaclass = klass.metaclass()
    -
    -            # Look the name in the already found locals.
    -            # If it's not found there, look in the module locals
    -            # and in the imported modules.
    -            if isinstance(klass._metaclass, astroid.Name):
    -                name = klass._metaclass.name
    -            elif metaclass:
    -                # if it uses a `metaclass=module.Class`
    -                name = metaclass.root().name
    -
    -            if name:
    -                found = consumed.setdefault(
    -                    name, module_locals.get(name, module_imports.get(name)))
    -
    -            if found is None and not metaclass:
    -                name = None
    -                if isinstance(klass._metaclass, astroid.Name):
    -                    name = klass._metaclass.name
    -                elif isinstance(klass._metaclass, astroid.Getattr):
    -                    name = klass._metaclass.as_string()
    -
    -                if name is not None:
    -                    if not (name in astroid.Module.scope_attrs or
    -                            is_builtin(name) or
    -                            name in self.config.additional_builtins or
    -                            name in node.locals):
    -                        self.add_message('undefined-variable',
    -                                         node=klass,
    -                                         args=(name, ))
    -        # Pop the consumed items, in order to
    -        # avoid having unused-import false positives
    -        for name in consumed:
    -            module_locals.pop(name, None)
    -        super(VariablesChecker3k, self).leave_module(node)
    -
    -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 ebfe5789..00000000
    --- a/pymode/libs/pylint/config.py
    +++ /dev/null
    @@ -1,157 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""utilities for Pylint configuration :
    -
    -* pylintrc
    -* pylint.d (PYLINTHOME)
    -"""
    -from __future__ import with_statement
    -from __future__ import print_function
    -
    -import pickle
    -import os
    -import sys
    -from os.path import exists, isfile, join, expanduser, abspath, dirname
    -
    -# pylint home is used to save old runs results ################################
    -
    -USER_HOME = expanduser('~')
    -if 'PYLINTHOME' in os.environ:
    -    PYLINT_HOME = os.environ['PYLINTHOME']
    -    if USER_HOME == '~':
    -        USER_HOME = dirname(PYLINT_HOME)
    -elif USER_HOME == '~':
    -    PYLINT_HOME = ".pylint.d"
    -else:
    -    PYLINT_HOME = join(USER_HOME, '.pylint.d')
    -
    -def get_pdata_path(base_name, recurs):
    -    """return the path of the file which should contain old search data for the
    -    given base_name with the given options values
    -    """
    -    base_name = base_name.replace(os.sep, '_')
    -    return join(PYLINT_HOME, "%s%s%s"%(base_name, recurs, '.stats'))
    -
    -def load_results(base):
    -    """try to unpickle and return data from file if it exists and is not
    -    corrupted
    -
    -    return an empty dictionary if it doesn't exists
    -    """
    -    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):
    -    """pickle results"""
    -    if not 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)
    -
    -# location of the configuration file ##########################################
    -
    -
    -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 exists('pylintrc'):
    -        return abspath('pylintrc')
    -    if isfile('__init__.py'):
    -        curdir = abspath(os.getcwd())
    -        while isfile(join(curdir, '__init__.py')):
    -            curdir = abspath(join(curdir, '..'))
    -            if isfile(join(curdir, 'pylintrc')):
    -                return join(curdir, 'pylintrc')
    -    if 'PYLINTRC' in os.environ and exists(os.environ['PYLINTRC']):
    -        pylintrc = os.environ['PYLINTRC']
    -    else:
    -        user_home = expanduser('~')
    -        if user_home == '~' or user_home == '/root':
    -            pylintrc = ".pylintrc"
    -        else:
    -            pylintrc = join(user_home, '.pylintrc')
    -            if not isfile(pylintrc):
    -                pylintrc = join(user_home, '.config', 'pylintrc')
    -    if not isfile(pylintrc):
    -        if 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()
    -
    -# evaluation messages #########################################################
    -
    -def get_note_message(note):
    -    """return a message according to note
    -    note is a float < 10  (10 is the highest note)
    -    """
    -    assert note <= 10, "Note is %.2f. Either you cheated, or pylint's \
    -broken!" % note
    -    if note < 0:
    -        msg = 'You have to do something quick !'
    -    elif note < 1:
    -        msg = 'Hey! This is really dreadful. Or maybe pylint is buggy?'
    -    elif note < 2:
    -        msg = "Come on! You can't be proud of this code"
    -    elif note < 3:
    -        msg = 'Hum... Needs work.'
    -    elif note < 4:
    -        msg = 'Wouldn\'t you be a bit lazy?'
    -    elif note < 5:
    -        msg = 'A little more work would make it acceptable.'
    -    elif note < 6:
    -        msg = 'Just the bare minimum. Give it a bit more polish. '
    -    elif note < 7:
    -        msg = 'This is okay-ish, but I\'m sure you can do better.'
    -    elif note < 8:
    -        msg = 'If you commit now, people should not be making nasty \
    -comments about you on c.l.py'
    -    elif note < 9:
    -        msg = 'That\'s pretty good. Good work mate.'
    -    elif note < 10:
    -        msg = 'So close to being perfect...'
    -    else:
    -        msg = 'Wow ! Now this deserves our uttermost respect.\nPlease send \
    -your code to python-projects@logilab.org'
    -    return msg
    diff --git a/pymode/libs/pylint/epylint.py b/pymode/libs/pylint/epylint.py
    deleted file mode 100644
    index 3d73ecd3..00000000
    --- a/pymode/libs/pylint/epylint.py
    +++ /dev/null
    @@ -1,177 +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) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""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 sys, os
    -import os.path as osp
    -from subprocess import Popen, PIPE
    -
    -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
    -    from pylint import lint as lint_mod
    -    lint_path = lint_mod.__file__
    -    options = options or ['--disable=C,R,I']
    -    cmd = [sys.executable, lint_path] + options + [
    -        '--msg-template', '{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}',
    -        '-r', 'n', child_path]
    -    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,
    -           script='epylint'):
    -    """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
    -    if os.name == 'nt':
    -        script += '.bat'
    -    command_line = script + ' ' + command_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
    -    p = Popen(command_line, shell=True, stdout=stdout, stderr=stderr,
    -              env=_get_env(), universal_newlines=True)
    -    p.wait()
    -    # Return standard output and error
    -    if return_std:
    -        return (p.stdout, p.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/gui.py b/pymode/libs/pylint/gui.py
    deleted file mode 100644
    index 8327e0ec..00000000
    --- a/pymode/libs/pylint/gui.py
    +++ /dev/null
    @@ -1,531 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Tkinker gui for pylint"""
    -from __future__ import print_function
    -
    -import os
    -import sys
    -import re
    -from threading import Thread
    -
    -import six
    -
    -from six.moves.tkinter import (
    -    Tk, Frame, Listbox, Entry, Label, Button, Scrollbar,
    -    Checkbutton, Radiobutton, IntVar, StringVar, PanedWindow,
    -    TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W,
    -    HORIZONTAL, DISABLED, NORMAL, W,
    -)
    -from six.moves.tkinter_tkfiledialog import (
    -    askopenfilename, askdirectory,
    -)
    -
    -import pylint.lint
    -from pylint.reporters.guireporter import GUIReporter
    -
    -HOME = os.path.expanduser('~/')
    -HISTORY = '.pylint-gui-history'
    -COLORS = {'(I)':'green',
    -          '(C)':'blue', '(R)':'darkblue',
    -          '(W)':'black', '(E)':'darkred',
    -          '(F)':'red'}
    -
    -
    -def convert_to_string(msg):
    -    """make a string representation of a message"""
    -    module_object = msg.module
    -    if msg.obj:
    -        module_object += ".%s" % msg.obj
    -    return "(%s) %s [%d]: %s" % (msg.C, module_object, msg.line, msg.msg)
    -
    -class BasicStream(object):
    -    '''
    -    used in gui reporter instead of writing to stdout, it is written to
    -    this stream and saved in contents
    -    '''
    -    def __init__(self, gui):
    -        """init"""
    -        self.curline = ""
    -        self.gui = gui
    -        self.contents = []
    -        self.outdict = {}
    -        self.currout = None
    -        self.next_title = None
    -
    -    def write(self, text):
    -        """write text to the stream"""
    -        if re.match('^--+$', text.strip()) or re.match('^==+$', text.strip()):
    -            if self.currout:
    -                self.outdict[self.currout].remove(self.next_title)
    -                self.outdict[self.currout].pop()
    -            self.currout = self.next_title
    -            self.outdict[self.currout] = ['']
    -
    -        if text.strip():
    -            self.next_title = text.strip()
    -
    -        if text.startswith(os.linesep):
    -            self.contents.append('')
    -            if self.currout:
    -                self.outdict[self.currout].append('')
    -        self.contents[-1] += text.strip(os.linesep)
    -        if self.currout:
    -            self.outdict[self.currout][-1] += text.strip(os.linesep)
    -        if text.endswith(os.linesep) and text.strip():
    -            self.contents.append('')
    -            if self.currout:
    -                self.outdict[self.currout].append('')
    -
    -    def fix_contents(self):
    -        """finalize what the contents of the dict should look like before output"""
    -        for item in self.outdict:
    -            num_empty = self.outdict[item].count('')
    -            for _ in range(num_empty):
    -                self.outdict[item].remove('')
    -            if self.outdict[item]:
    -                self.outdict[item].pop(0)
    -
    -    def output_contents(self):
    -        """output contents of dict to the gui, and set the rating"""
    -        self.fix_contents()
    -        self.gui.tabs = self.outdict
    -        try:
    -            self.gui.rating.set(self.outdict['Global evaluation'][0])
    -        except KeyError:
    -            self.gui.rating.set('Error')
    -        self.gui.refresh_results_window()
    -
    -        #reset stream variables for next run
    -        self.contents = []
    -        self.outdict = {}
    -        self.currout = None
    -        self.next_title = None
    -
    -
    -class LintGui(object):
    -    """Build and control a window to interact with pylint"""
    -
    -    def __init__(self, root=None):
    -        """init"""
    -        self.root = root or Tk()
    -        self.root.title('Pylint')
    -        #reporter
    -        self.reporter = None
    -        #message queue for output from reporter
    -        self.msg_queue = six.moves.queue.Queue()
    -        self.msgs = []
    -        self.visible_msgs = []
    -        self.filenames = []
    -        self.rating = StringVar()
    -        self.tabs = {}
    -        self.report_stream = BasicStream(self)
    -        #gui objects
    -        self.lb_messages = None
    -        self.showhistory = None
    -        self.results = None
    -        self.btnRun = None
    -        self.information_box = None
    -        self.convention_box = None
    -        self.refactor_box = None
    -        self.warning_box = None
    -        self.error_box = None
    -        self.fatal_box = None
    -        self.txtModule = None
    -        self.status = None
    -        self.msg_type_dict = None
    -        self.init_gui()
    -
    -    def init_gui(self):
    -        """init helper"""
    -
    -        window = PanedWindow(self.root, orient="vertical")
    -        window.pack(side=TOP, fill=BOTH, expand=True)
    -
    -        top_pane = Frame(window)
    -        window.add(top_pane)
    -        mid_pane = Frame(window)
    -        window.add(mid_pane)
    -        bottom_pane = Frame(window)
    -        window.add(bottom_pane)
    -
    -        #setting up frames
    -        top_frame = Frame(top_pane)
    -        mid_frame = Frame(top_pane)
    -        history_frame = Frame(top_pane)
    -        radio_frame = Frame(mid_pane)
    -        rating_frame = Frame(mid_pane)
    -        res_frame = Frame(mid_pane)
    -        check_frame = Frame(bottom_pane)
    -        msg_frame = Frame(bottom_pane)
    -        btn_frame = Frame(bottom_pane)
    -        top_frame.pack(side=TOP, fill=X)
    -        mid_frame.pack(side=TOP, fill=X)
    -        history_frame.pack(side=TOP, fill=BOTH, expand=True)
    -        radio_frame.pack(side=TOP, fill=X)
    -        rating_frame.pack(side=TOP, fill=X)
    -        res_frame.pack(side=TOP, fill=BOTH, expand=True)
    -        check_frame.pack(side=TOP, fill=X)
    -        msg_frame.pack(side=TOP, fill=BOTH, expand=True)
    -        btn_frame.pack(side=TOP, fill=X)
    -
    -        # Binding F5 application-wide to run lint
    -        self.root.bind('', self.run_lint)
    -
    -        #Message ListBox
    -        rightscrollbar = Scrollbar(msg_frame)
    -        rightscrollbar.pack(side=RIGHT, fill=Y)
    -        bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL)
    -        bottomscrollbar.pack(side=BOTTOM, fill=X)
    -        self.lb_messages = Listbox(
    -            msg_frame,
    -            yscrollcommand=rightscrollbar.set,
    -            xscrollcommand=bottomscrollbar.set,
    -            bg="white")
    -        self.lb_messages.bind("", self.show_sourcefile)
    -        self.lb_messages.pack(expand=True, fill=BOTH)
    -        rightscrollbar.config(command=self.lb_messages.yview)
    -        bottomscrollbar.config(command=self.lb_messages.xview)
    -
    -        #History ListBoxes
    -        rightscrollbar2 = Scrollbar(history_frame)
    -        rightscrollbar2.pack(side=RIGHT, fill=Y)
    -        bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL)
    -        bottomscrollbar2.pack(side=BOTTOM, fill=X)
    -        self.showhistory = Listbox(
    -            history_frame,
    -            yscrollcommand=rightscrollbar2.set,
    -            xscrollcommand=bottomscrollbar2.set,
    -            bg="white")
    -        self.showhistory.pack(expand=True, fill=BOTH)
    -        rightscrollbar2.config(command=self.showhistory.yview)
    -        bottomscrollbar2.config(command=self.showhistory.xview)
    -        self.showhistory.bind('', self.select_recent_file)
    -        self.set_history_window()
    -
    -        #status bar
    -        self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
    -        self.status.pack(side=BOTTOM, fill=X)
    -
    -        #labelbl_ratingls
    -        lbl_rating_label = Label(rating_frame, text='Rating:')
    -        lbl_rating_label.pack(side=LEFT)
    -        lbl_rating = Label(rating_frame, textvariable=self.rating)
    -        lbl_rating.pack(side=LEFT)
    -        Label(mid_frame, text='Recently Used:').pack(side=LEFT)
    -        Label(top_frame, text='Module or package').pack(side=LEFT)
    -
    -        #file textbox
    -        self.txt_module = Entry(top_frame, background='white')
    -        self.txt_module.bind('', self.run_lint)
    -        self.txt_module.pack(side=LEFT, expand=True, fill=X)
    -
    -        #results box
    -        rightscrollbar = Scrollbar(res_frame)
    -        rightscrollbar.pack(side=RIGHT, fill=Y)
    -        bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL)
    -        bottomscrollbar.pack(side=BOTTOM, fill=X)
    -        self.results = Listbox(
    -            res_frame,
    -            yscrollcommand=rightscrollbar.set,
    -            xscrollcommand=bottomscrollbar.set,
    -            bg="white", font="Courier")
    -        self.results.pack(expand=True, fill=BOTH, side=BOTTOM)
    -        rightscrollbar.config(command=self.results.yview)
    -        bottomscrollbar.config(command=self.results.xview)
    -
    -        #buttons
    -        Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT)
    -        Button(top_frame, text='Open Package',
    -               command=(lambda: self.file_open(package=True))).pack(side=LEFT)
    -
    -        self.btnRun = Button(top_frame, text='Run', command=self.run_lint)
    -        self.btnRun.pack(side=LEFT)
    -        Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM)
    -
    -        #radio buttons
    -        self.information_box = IntVar()
    -        self.convention_box = IntVar()
    -        self.refactor_box = IntVar()
    -        self.warning_box = IntVar()
    -        self.error_box = IntVar()
    -        self.fatal_box = IntVar()
    -        i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'],
    -                        variable=self.information_box, command=self.refresh_msg_window)
    -        c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'],
    -                        variable=self.convention_box, command=self.refresh_msg_window)
    -        r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'],
    -                        variable=self.refactor_box, command=self.refresh_msg_window)
    -        w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'],
    -                        variable=self.warning_box, command=self.refresh_msg_window)
    -        e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'],
    -                        variable=self.error_box, command=self.refresh_msg_window)
    -        f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'],
    -                        variable=self.fatal_box, command=self.refresh_msg_window)
    -        i.select()
    -        c.select()
    -        r.select()
    -        w.select()
    -        e.select()
    -        f.select()
    -        i.pack(side=LEFT)
    -        c.pack(side=LEFT)
    -        r.pack(side=LEFT)
    -        w.pack(side=LEFT)
    -        e.pack(side=LEFT)
    -        f.pack(side=LEFT)
    -
    -        #check boxes
    -        self.box = StringVar()
    -        # XXX should be generated
    -        report = Radiobutton(
    -            radio_frame, text="Report", variable=self.box,
    -            value="Report", command=self.refresh_results_window)
    -        raw_met = Radiobutton(
    -            radio_frame, text="Raw metrics", variable=self.box,
    -            value="Raw metrics", command=self.refresh_results_window)
    -        dup = Radiobutton(
    -            radio_frame, text="Duplication", variable=self.box,
    -            value="Duplication", command=self.refresh_results_window)
    -        ext = Radiobutton(
    -            radio_frame, text="External dependencies",
    -            variable=self.box, value="External dependencies",
    -            command=self.refresh_results_window)
    -        stat = Radiobutton(
    -            radio_frame, text="Statistics by type",
    -            variable=self.box, value="Statistics by type",
    -            command=self.refresh_results_window)
    -        msg_cat = Radiobutton(
    -            radio_frame, text="Messages by category",
    -            variable=self.box, value="Messages by category",
    -            command=self.refresh_results_window)
    -        msg = Radiobutton(
    -            radio_frame, text="Messages", variable=self.box,
    -            value="Messages", command=self.refresh_results_window)
    -        source_file = Radiobutton(
    -            radio_frame, text="Source File", variable=self.box,
    -            value="Source File", command=self.refresh_results_window)
    -        report.select()
    -        report.grid(column=0, row=0, sticky=W)
    -        raw_met.grid(column=1, row=0, sticky=W)
    -        dup.grid(column=2, row=0, sticky=W)
    -        msg.grid(column=3, row=0, sticky=W)
    -        stat.grid(column=0, row=1, sticky=W)
    -        msg_cat.grid(column=1, row=1, sticky=W)
    -        ext.grid(column=2, row=1, sticky=W)
    -        source_file.grid(column=3, row=1, sticky=W)
    -
    -        #dictionary for check boxes and associated error term
    -        self.msg_type_dict = {
    -            'I': lambda: self.information_box.get() == 1,
    -            'C': lambda: self.convention_box.get() == 1,
    -            'R': lambda: self.refactor_box.get() == 1,
    -            'E': lambda: self.error_box.get() == 1,
    -            'W': lambda: self.warning_box.get() == 1,
    -            'F': lambda: self.fatal_box.get() == 1
    -        }
    -        self.txt_module.focus_set()
    -
    -
    -    def select_recent_file(self, event): # pylint: disable=unused-argument
    -        """adds the selected file in the history listbox to the Module box"""
    -        if not self.showhistory.size():
    -            return
    -
    -        selected = self.showhistory.curselection()
    -        item = self.showhistory.get(selected)
    -        #update module
    -        self.txt_module.delete(0, END)
    -        self.txt_module.insert(0, item)
    -
    -    def refresh_msg_window(self):
    -        """refresh the message window with current output"""
    -        #clear the window
    -        self.lb_messages.delete(0, END)
    -        self.visible_msgs = []
    -        for msg in self.msgs:
    -            if self.msg_type_dict.get(msg.C)():
    -                self.visible_msgs.append(msg)
    -                msg_str = convert_to_string(msg)
    -                self.lb_messages.insert(END, msg_str)
    -                fg_color = COLORS.get(msg_str[:3], 'black')
    -                self.lb_messages.itemconfigure(END, fg=fg_color)
    -
    -    def refresh_results_window(self):
    -        """refresh the results window with current output"""
    -        #clear the window
    -        self.results.delete(0, END)
    -        try:
    -            for res in self.tabs[self.box.get()]:
    -                self.results.insert(END, res)
    -        except KeyError:
    -            pass
    -
    -    def process_incoming(self):
    -        """process the incoming messages from running pylint"""
    -        while self.msg_queue.qsize():
    -            try:
    -                msg = self.msg_queue.get(0)
    -                if msg == "DONE":
    -                    self.report_stream.output_contents()
    -                    return False
    -
    -                #adding message to list of msgs
    -                self.msgs.append(msg)
    -
    -                #displaying msg if message type is selected in check box
    -                if self.msg_type_dict.get(msg.C)():
    -                    self.visible_msgs.append(msg)
    -                    msg_str = convert_to_string(msg)
    -                    self.lb_messages.insert(END, msg_str)
    -                    fg_color = COLORS.get(msg_str[:3], 'black')
    -                    self.lb_messages.itemconfigure(END, fg=fg_color)
    -
    -            except six.moves.queue.Empty:
    -                pass
    -        return True
    -
    -    def periodic_call(self):
    -        """determine when to unlock the run button"""
    -        if self.process_incoming():
    -            self.root.after(100, self.periodic_call)
    -        else:
    -            #enabling button so it can be run again
    -            self.btnRun.config(state=NORMAL)
    -
    -    def mainloop(self):
    -        """launch the mainloop of the application"""
    -        self.root.mainloop()
    -
    -    def quit(self, _=None):
    -        """quit the application"""
    -        self.root.quit()
    -
    -    def halt(self): # pylint: disable=no-self-use
    -        """program halt placeholder"""
    -        return
    -
    -    def file_open(self, package=False, _=None):
    -        """launch a file browser"""
    -        if not package:
    -            filename = askopenfilename(parent=self.root,
    -                                       filetypes=[('pythonfiles', '*.py'),
    -                                                  ('allfiles', '*')],
    -                                       title='Select Module')
    -        else:
    -            filename = askdirectory(title="Select A Folder", mustexist=1)
    -
    -        if filename == ():
    -            return
    -
    -        self.txt_module.delete(0, END)
    -        self.txt_module.insert(0, filename)
    -
    -    def update_filenames(self):
    -        """update the list of recent filenames"""
    -        filename = self.txt_module.get()
    -        if not filename:
    -            filename = os.getcwd()
    -        if filename+'\n' in self.filenames:
    -            index = self.filenames.index(filename+'\n')
    -            self.filenames.pop(index)
    -
    -        #ensure only 10 most recent are stored
    -        if len(self.filenames) == 10:
    -            self.filenames.pop()
    -        self.filenames.insert(0, filename+'\n')
    -
    -    def set_history_window(self):
    -        """update the history window with info from the history file"""
    -        #clear the window
    -        self.showhistory.delete(0, END)
    -        # keep the last 10 most recent files
    -        try:
    -            view_history = open(HOME+HISTORY, 'r')
    -            for hist in view_history.readlines():
    -                if not hist in self.filenames:
    -                    self.filenames.append(hist)
    -                self.showhistory.insert(END, hist.split('\n')[0])
    -            view_history.close()
    -        except IOError:
    -            # do nothing since history file will be created later
    -            return
    -
    -    def run_lint(self, _=None):
    -        """launches pylint"""
    -        self.update_filenames()
    -        self.root.configure(cursor='watch')
    -        self.reporter = GUIReporter(self, output=self.report_stream)
    -        module = self.txt_module.get()
    -        if not module:
    -            module = os.getcwd()
    -
    -        #cleaning up msgs and windows
    -        self.msgs = []
    -        self.visible_msgs = []
    -        self.lb_messages.delete(0, END)
    -        self.tabs = {}
    -        self.results.delete(0, END)
    -        self.btnRun.config(state=DISABLED)
    -
    -        #setting up a worker thread to run pylint
    -        worker = Thread(target=lint_thread, args=(module, self.reporter, self,))
    -        self.periodic_call()
    -        worker.start()
    -
    -        # Overwrite the .pylint-gui-history file with all the new recently added files
    -        # in order from filenames but only save last 10 files
    -        write_history = open(HOME+HISTORY, 'w')
    -        write_history.writelines(self.filenames)
    -        write_history.close()
    -        self.set_history_window()
    -
    -        self.root.configure(cursor='')
    -
    -    def show_sourcefile(self, event=None):  # pylint: disable=unused-argument
    -        selected = self.lb_messages.curselection()
    -        if not selected:
    -            return
    -
    -        msg = self.visible_msgs[int(selected[0])]
    -        scroll = msg.line - 3
    -        if scroll < 0:
    -            scroll = 0
    -
    -        self.tabs["Source File"] = open(msg.path, "r").readlines()
    -        self.box.set("Source File")
    -        self.refresh_results_window()
    -        self.results.yview(scroll)
    -        self.results.select_set(msg.line - 1)
    -
    -
    -def lint_thread(module, reporter, gui):
    -    """thread for pylint"""
    -    gui.status.text = "processing module(s)"
    -    pylint.lint.Run(args=[module], reporter=reporter, exit=False)
    -    gui.msg_queue.put("DONE")
    -
    -
    -def Run(args):
    -    """launch pylint gui from args"""
    -    if args:
    -        print('USAGE: pylint-gui\n launch a simple pylint gui using Tk')
    -        sys.exit(1)
    -    gui = LintGui()
    -    gui.mainloop()
    -    sys.exit(0)
    -
    -if __name__ == '__main__':
    -    Run(sys.argv[1:])
    diff --git a/pymode/libs/pylint/interfaces.py b/pymode/libs/pylint/interfaces.py
    deleted file mode 100644
    index 64f5a956..00000000
    --- a/pymode/libs/pylint/interfaces.py
    +++ /dev/null
    @@ -1,84 +0,0 @@
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Interfaces for Pylint objects"""
    -from collections import namedtuple
    -
    -from logilab.common.interface import Interface
    -
    -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 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 add_message(self, msg_id, location, msg):
    -        """add a message of a given type
    -
    -        msg_id is a message identifier
    -        location is a 3-uple (module, object, line)
    -        msg is the actual message
    -        """
    -
    -    def display_results(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 01fc2f5d..00000000
    --- a/pymode/libs/pylint/lint.py
    +++ /dev/null
    @@ -1,1397 +0,0 @@
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -""" %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 itertools
    -import operator
    -import os
    -try:
    -    import multiprocessing
    -except ImportError:
    -    multiprocessing = None
    -import sys
    -import tokenize
    -import warnings
    -
    -import astroid
    -from astroid.__pkginfo__ import version as astroid_version
    -from astroid import modutils
    -from logilab.common import configuration
    -from logilab.common import optik_ext
    -from logilab.common import interface
    -from logilab.common import textutils
    -from logilab.common import ureports
    -from logilab.common import __version__ as common_version
    -import six
    -
    -from pylint import checkers
    -from pylint import interfaces
    -from pylint import reporters
    -from pylint import utils
    -from pylint import config
    -from pylint.__pkginfo__ import version
    -
    -
    -MANAGER = astroid.MANAGER
    -INCLUDE_IDS_HELP = ("Deprecated. It was used to include message\'s "
    -                    "id in output. Use --msg-template instead.")
    -SYMBOLS_HELP = ("Deprecated. It was used to include symbolic ids of "
    -                "messages in output. Use --msg-template instead.")
    -
    -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 = {}
    -    for stat in 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
    -    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 !'),
    -    'F0003': ('ignored builtin module %s',
    -              'ignored-builtin-module',
    -              'Used to indicate that the user asked to analyze a builtin '
    -              'module which has been skipped.'),
    -    'F0010': ('error while code parsing: %s',
    -              'parse-error',
    -              'Used when an exception occured 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.'),
    -    }
    -
    -
    -def _deprecated_option(shortname, opt_type, help_msg):
    -    def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument
    -        sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,))
    -    return {'short': shortname, 'help': help_msg, 'hide': True,
    -            'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated}
    -
    -
    -if multiprocessing is not None:
    -    class ChildLinter(multiprocessing.Process): # pylint: disable=no-member
    -        def run(self):
    -            tasks_queue, results_queue, self._config = self._args # pylint: disable=no-member
    -
    -            self._config["jobs"] = 1  # Child does not parallelize any further.
    -            self._python3_porting_mode = self._config.pop(
    -                'python3_porting_mode', 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.
    -            # TODO linter.load_plugin_modules(self._plugins)
    -
    -            linter.load_configuration(**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(configuration.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 plugins developpers: you may have to call
    -    `astroid.builder.MANAGER.astroid_cache.clear()` accross run 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.'}),
    -                ('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, msvs (visual studio) and html. You '
    -                           'can also give a reporter class, eg mypackage.mymodule.'
    -                           'MyReporterClass.'}),
    -
    -                ('files-output',
    -                 {'default': 0, 'type' : 'yn', 'metavar' : '',
    -                  'group': 'Reports', 'level': 1,
    -                  'help' : 'Put messages in a separate file for each module / '
    -                           'package specified on the command line instead of printing '
    -                           'them on stdout. Reports (if any) will be written in a file '
    -                           'name "pylint_global.[txt|html]".'}),
    -
    -                ('reports',
    -                 {'default': 1, '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).'}),
    -
    -                ('comment',
    -                 {'default': 0, 'type' : 'yn', 'metavar' : '',
    -                  'group': 'Reports', 'level': 1,
    -                  'help' : 'Add a comment according to your evaluation note. '
    -                           'This is used by the global evaluation report (RP0004).'}),
    -
    -                ('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. '
    -                           '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': '