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 c09fe72d..a4bcbf28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,49 +1,78 @@ -Maintainer: +Author: * Kirill Klenov +Maintainers: + +* Diego Rabatone Oliveira (https://github.com/diraol); Contributors: * Alvin Francis (http://github.com/alvinfrancis); +* 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) +* 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); -* Mel Boyce (http://github.com/syngin) +* 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); -* Ronald Andreu Kaiser (http://github.com/cathoderay); +* 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) -* Wayne Ye (https://github.com/WayneYe) -* bendavis78 (http://github.com/bendavis78) -* fwuzju (http://github.com/fwuzju) -* lawrenceakka; -* lee (loyalpartner); -* nixon; -* tramchamploo; +* 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); +* 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 ac48fccb..00000000 --- a/Changelog.rst +++ /dev/null @@ -1,348 +0,0 @@ -Changelog -========= - -* Pylama updated to version 5.0.5 -* 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' - -## 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 a4370605..00000000 --- a/Makefile +++ /dev/null @@ -1,59 +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 - -$(PYLAMA): - cp -r ~/Dropbox/projects/pylama/pylama $(PYLAMA) - -$(PYLAMA)/lint/pylama_pylint: - cp -r ~/Dropbox/projects/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 b6f9bb69..00000000 --- a/README.rst +++ /dev/null @@ -1,208 +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 - -.. image:: https://dl.dropboxusercontent.com/u/487440/reformal/donate.png - :target: https://www.gittip.com/klen/ - :alt: Donate - -Python-mode is a vim plugin that helps you to create python code very quickly -by utilizing libraries including pylint_, rope_, pydoc_, pyflakes_, pep8_, 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.txt`` - - -.. 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 git://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 git://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: http://klen.github.io/python-mode/deb/ -Install with commands: - -:: - - add-apt-repository http://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`. - - -Copyright -========= - -Copyright © 2013 Kirill Klenov (klen_) - -License -======= - -Licensed under a `GNU lesser general public license`_. - -If you like this plugin, you can send me postcard :) -My address is here: "Russia, 143401, Krasnogorsk, Shkolnaya 1-19" to "Kirill Klenov". -**Thanks for support!** - -.. _GNU lesser general public license: http://www.gnu.org/copyleft/lesser.html -.. _klen: http://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 c518c415..b637289a 100644 --- a/autoload/pymode.vim +++ b/autoload/pymode.vim @@ -12,13 +12,6 @@ endfunction "}}} " DESC: Import python libs fun! pymode#init(plugin_root, paths) "{{{ - if g:pymode_python == 'disable' - if g:pymode_warning - call pymode#error("Pymode requires vim compiled with +python. Most of features will be disabled.") - endif - return - endif - PymodePython import sys, vim PymodePython sys.path.insert(0, vim.eval('a:plugin_root')) PymodePython sys.path = vim.eval('a:paths') + sys.path @@ -61,7 +54,7 @@ fun! pymode#quickfix_open(onlyRecognized, maxHeight, minHeight, jumpError) "{{{ redraw if numOthers > 0 call pymode#wide_message(printf('Quickfix: %d(+%d)', numErrors, numOthers)) - else + elseif numErrors > 0 call pymode#wide_message(printf('Quickfix: %d', numErrors)) endif endfunction "}}} @@ -69,19 +62,20 @@ 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 "}}} " DESC: Remove unused whitespaces fun! pymode#trim_whitespaces() "{{{ - let cursor_pos = getpos('.') - silent! %s/\s\+$// - call setpos('.', cursor_pos) + if g:pymode_trim_whitespaces + let cursor_pos = getpos('.') + silent! %s/\s\+$//e + call setpos('.', cursor_pos) + endif endfunction "}}} - fun! pymode#save() "{{{ if &modifiable && &modified try @@ -107,11 +101,11 @@ endfunction "}}} fun! pymode#buffer_pre_write() "{{{ let b:pymode_modified = &modified -endfunction +endfunction "}}} fun! pymode#buffer_post_write() "{{{ if g:pymode_rope - if b:pymode_modified && g:pymode_rope_regenerate_on_write + if g:pymode_rope_regenerate_on_write && b:pymode_modified call pymode#debug('regenerate') call pymode#rope#regenerate() endif @@ -125,14 +119,30 @@ 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 "}}} fun! pymode#quit() "{{{ augroup pymode - au! + au! * augroup END endfunction "}}} diff --git a/autoload/pymode/breakpoint.vim b/autoload/pymode/breakpoint.vim index 18e1a95b..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 ('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 d29d5e9e..a7a753c5 100644 --- a/autoload/pymode/doc.vim +++ b/autoload/pymode/doc.vim @@ -29,6 +29,12 @@ fun! pymode#doc#show(word) "{{{ setlocal nomodifiable setlocal nomodified setlocal filetype=rst + 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 93f18b09..9cbb64d3 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -1,29 +1,36 @@ -" 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_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) - let nucolwidth = &fdc + &number * &numberwidth + let has_numbers = &number || &relativenumber + let nucolwidth = &fdc + has_numbers * &numberwidth let windowwidth = winwidth(0) - nucolwidth - 6 let foldedlinecount = v:foldend - v:foldstart @@ -32,23 +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 + + " 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 @@ -62,94 +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) - endif + " 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. - " 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 - endif - finally - call setpos('.', curpos) - endtry + " 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 "}}} - 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 + " 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 - return -1 - else - return '=' endif + call setpos('.', s:save_cursor) + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + else + call setpos('.', s:save_cursor) endif + " endif " }}} - if indent == 0 - return 0 - 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 + 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 + " 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] @@ -159,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 d8e9f148..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 @@ -110,7 +112,7 @@ function! s:SearchParensPair() " {{{ " Skip strings and comments and don't look too far let skip = "line('.') < " . (line - 50) . " ? dummy :" . \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? ' . - \ '"string\\|comment"' + \ '"string\\|comment\\|doctest"' " Search for parentheses call cursor(line, col) diff --git a/autoload/pymode/lint.vim b/autoload/pymode/lint.vim index b77bdba2..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,16 +61,17 @@ 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) - if g:pymode_lint_cwindow - call loclist.show() - endif + 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 714f3235..356409b6 100644 --- a/autoload/pymode/run.vim +++ b/autoload/pymode/run.vim @@ -43,8 +43,6 @@ let s:efm .= '%-G%.%#' PymodePython from pymode.run import run_code -call pymode#tools#loclist#init() - " DESC: Run python code fun! pymode#run#code_run(line1, line2) "{{{ @@ -76,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 @@ -88,10 +86,7 @@ fun! pymode#run#code_run(line1, line2) "{{{ call setqflist(qflist) endif - let loclist = g:PymodeLocList.current() - let loclist._loclist = getqflist() - let loclist._title = "Run errors" - call loclist.show() + call pymode#quickfix_open(0, g:pymode_quickfix_maxheight, g:pymode_quickfix_maxheight, 0) let &efm = l:_efm diff --git a/autoload/pymode/tools/loclist.vim b/autoload/pymode/tools/loclist.vim index 3a07fa3a..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,10 +83,10 @@ endfunction "}}} fun! g:PymodeLocList.show() "{{{ - call setloclist(0, self._loclist) + call setloclist(0, self.loclist()) if self.is_empty() lclose - else + elseif g:pymode_lint_cwindow let num = winnr() lopen setl nowrap 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 5cec03f9..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,41 +73,37 @@ 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 -Setup max line length *'g:pymode_options_max_line_length'* -> - let g:pymode_options_max_line_length = 79 - If this option is set to 1, pymode will enable the following options for python buffers: > @@ -115,137 +117,166 @@ python buffers: > setlocal commentstring=#%s setlocal define=^\s*\\(def\\\\|class\\) -Enable colorcolumn display at max_line_length *'g:pymode_options_colorcolumn'* +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'* > 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,31 +456,30 @@ 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 = "" The location of the `.ropeproject` folder may also be overridden if you wish to 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 proejct root (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 = 1 + 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,90 +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 '=' operator *'g:pymode_syntax_highlight_equal_operator'* +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'* > 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: -Rope completion is very slow *pymode-rope-slow* ----------------------------- +`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! + + + +2. Rope completion is very slow *pymode-rope-slow* +------------------------------- Rope creates a project-level service directory in |.ropeproject| @@ -711,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 @@ -722,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 @@ -736,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/ @@ -754,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 @@ -774,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, you can send me a postcard :) +If you like this plugin, I would very appreciated if you kindly send me a +postcard :) -My address is: "Russia, 143401, Krasnogorsk, Shkolnaya 1-19" to "Kirill +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 3a19d286..a1370669 100644 --- a/ftplugin/python/pymode.vim +++ b/ftplugin/python/pymode.vim @@ -2,15 +2,31 @@ if !g:pymode || pymode#default('b:pymode', 1) finish endif +if g:pymode_python == 'disable' + + if g:pymode_warning + call pymode#error("Pymode requires vim compiled with +python3 (exclusively). Most of features will be disabled.") + endif + + finish + +else + + let b:pymode_modified = &modified " Init paths if !pymode#default('g:pymode_init', 1) - call pymode#init(expand(':p:h:h:h'), g:pymode_paths) - call pymode#virtualenv#init() - call pymode#breakpoint#init() - PymodePython from pymode.utils import patch_paths - PymodePython patch_paths() + + call pymode#init(expand(':p:h:h:h'), g:pymode_paths) + call pymode#virtualenv#init() + call pymode#breakpoint#init() + + PymodePython from pymode.utils import patch_paths + PymodePython patch_paths() + + endif + endif command! -buffer -nargs=1 PymodeVirtualenv call pymode#virtualenv#activate() @@ -94,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 @@ -108,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 @@ -118,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() @@ -191,6 +207,52 @@ if g:pymode_rope if g:pymode_rope_autoimport command! -buffer PymodeRopeAutoImport call pymode#rope#autoimport(expand('')) - end + endif + +endif + -end +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 9bd4d95c..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,15 +60,24 @@ 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 +call pymode#default("g:pymode_doc_vertical", 0) + " Minimal height of pymode quickfix window 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 @@ -102,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) @@ -132,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", @@ -164,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', 1) + " 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 " }}} @@ -265,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 @@ -307,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 07c1ab7a..9579796e 100644 --- a/pylama.ini +++ b/pylama.ini @@ -1,3 +1,9 @@ -[main] -ignore = R0201,R0922,E1103 -skip = pymode/autopep8.py +[pylama] +ignore=D213 +linters=pep8,pyflakes,pylint + +[pylama:pymode/libs*] +skip=1 + +[pylama:pylint] +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 5f3ccf0b..00000000 --- a/pymode/autopep8.py +++ /dev/null @@ -1,3279 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2010-2011 Hideo Hattori -# Copyright (C) 2011-2013 Hideo Hattori, Steven Myint -# Copyright (C) 2013-2014 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 token -import tokenize - -from pylama.lint.pylama_pep8 import pep8 - - -try: - unicode -except NameError: - unicode = str - - -__version__ = '1.0' - - -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 = { - 'E721': ['idioms'], - 'W601': ['has_key'], - 'W603': ['ne'], - 'W604': ['repr'], - 'W690': ['apply', - 'except', - 'exitfunc', - 'import', - 'numliterals', - 'operator', - 'paren', - 'reduce', - 'renames', - 'standarderror', - 'sys_exc', - 'throw', - 'tuple_params', - 'xreadlines']} - - -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, - indent_level, - previous_logical): - """Check for missing blank lines after class declaration.""" - if previous_logical.startswith('class '): - if ( - logical_line.startswith(('def ', 'class ', '@')) or - pep8.DOCSTRING_REGEX.match(logical_line) - ): - if indent_level and not blank_lines: - 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 - 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, 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 - - # Remember how many brackets were opened on each line. - parens = [0] * nrows - - # Relative indents of physical lines. - rel_indent = [0] * nrows - - # 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 - - if depth: - # A bracket expression in a continuation line. - # Find the line that it was opened on. - for open_row in range(row - 1, -1, -1): - if parens[open_row]: - break - else: - # An unbracketed continuation line (ie, backslash). - open_row = 0 - hang = rel_indent[row] - rel_indent[open_row] - close_bracket = (token_type == tokenize.OP and text in ']})') - 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 visual_indent is True: - # Visual indent is verified. - if not indent[depth]: - indent[depth] = start[1] - elif visual_indent in (text, unicode): - # Ignore token lined up with matching one from a previous line. - pass - elif indent[depth] and start[1] < indent[depth]: - # Visual indent is broken. - yield (start, 'E128 {0}'.format(indent[depth])) - elif (hang == DEFAULT_INDENT_SIZE 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])) - 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 = ('E121', one_indented) - else: - error = ('E126', 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 - - # Keep track of bracket depth. - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - 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] - 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] - depth -= 1 - if depth: - indent_chances[indent[depth]] = True - for idx in range(row, -1, -1): - if parens[idx]: - parens[idx] -= 1 - rel_indent[row] = rel_indent[idx] - 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]) - last_line = line - - if ( - indent_next and - not last_line_begins_with_multiline and - pep8.expand_indent(line) == indent_level + DEFAULT_INDENT_SIZE - ): - yield (last_indent, '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) - - # method definition - 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._ws_comma_done = False - - 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: - results = [ - r for r in results - if self.options.line_range[0] <= r['line'] <= - self.options.line_range[1]] - - self._fix_source(filter_results(source=''.join(self.source), - results=results, - aggressive=self.options.aggressive, - indent_size=self.options.indent_size)) - 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_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.""" - # Optimize for comma case. This will fix all commas in the full source - # code in one pass. Don't do this more than once. If it fails the first - # time, there is no point in trying again. - if ',' in result['info'] and not self._ws_comma_done: - self._ws_comma_done = True - original = ''.join(self.source) - new = refactor(original, ['ws_comma']) - if original.strip() != new.strip(): - self.source = [new] - return range(1, 1 + len(original)) - - 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 = result['line'] - 1 - target = self.source[line_index] - 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()) - - self.source[line_index] = first + '\n' + second - return [line_index + 1] - - def fix_e711(self, result): - """Fix comparison with None.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - 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 comparison with boolean.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - # 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_w291(self, result): - """Remove trailing whitespace.""" - fixed_line = self.source[result['line'] - 1].rstrip() - self.source[result['line'] - 1] = fixed_line + '\n' - - -def get_fixed_long_line(target, previous_line, original, - indent_word=' ', max_line_length=79, - aggressive=False, experimental=False, verbose=False): - 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)) - - if verbose >= 4: - print(('-' * 79 + '\n').join([''] + candidates + ['']), - file=codecs.getwriter('utf-8')(sys.stderr.buffer - if hasattr(sys.stderr, - 'buffer') - else sys.stderr)) - - if candidates: - return candidates[0] - - -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 - - -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_e269(source, aggressive=False): - """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: - if ( - # Leave multiple spaces like '# ' alone. - (line.count('#') > 1 or line[1].isalnum()) - # Leave stylistic outlined blocks alone. - and 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): - """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) - 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): - """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)) - - -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 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, - indent_word=indent_word, - 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): - if isinstance(obj, Atom): - self._add_item(obj, indent_amt) - return - - self._add_container(obj, indent_amt) - - def add_comment(self, item): - self._lines.append(self._Space()) - self._lines.append(self._Space()) - 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 ('+', '-', '%', '*', '/', '//', '**'))))) - ): - self._lines.append(self._Space()) - - 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): - if ( - unicode(self._prev_item) != '=' and - not self.line_empty() and - not self.fits_on_current_line( - container.size + self._bracket_depth + 2) and - - # Don't split before the opening bracket of a call. - (unicode(container)[0] != '(' or not self._prev_item.is_name) - ): - # 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)) - - # Increase the continued indentation only if recursing on a - # container. - container.reflow(self, ' ' * (indent_amt + 1)) - - 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 isinstance(item, self._Space): - last_space = item - break - 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): - 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 - - if ( - not reflowed_lines.fits_on_current_line(total_size) and - not reflowed_lines.line_empty() - ): - # 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)) - - 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): - for (index, item) in enumerate(self._items): - if isinstance(item, Atom): - item.reflow(reflowed_lines, continued_indent, - self._get_extent(index)) - else: # isinstance(item, Container) - reflowed_lines.add(item, len(continued_indent)) - - next_item = get_item(self._items, index + 1) - 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 '.%' and next_item and - next_next_item and unicode(next_item) != ':' and - not isinstance(next_next_item, Atom) and - not reflowed_lines.line_empty() and - not reflowed_lines.fits_on_current_line( - next_item.size + next_next_item.size + 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 - while index < len(self._items): - item = get_item(self._items, index) - if unicode(item) not in '.=' and not item.is_name: - break - extent += len(item) - index += 1 - 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.""" - - -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 - - -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) - parsed_tokens.append(container) - else: - parsed_tokens.append(Atom(tok)) - - index += 1 - - return parsed_tokens - - -def _reflow_lines(parsed_tokens, indentation, indent_word, 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)) - - 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, indent_word, - 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, indent_word, - max_line_length, start_on_prefix_line=True) - if check_syntax(normalize_multiline(fixed.lstrip())): - yield fixed - - fixed = _reflow_lines(parsed_tokens, indentation, indent_word, - max_line_length, start_on_prefix_line=False) - if 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' - 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, _): - """Collect errors.""" - code = super(QuietReport, self).error(line_number, offset, text, _) - 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 - while lines and lines[-1] == '\n': - lines.pop() - # 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): - """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: - return unicode(tool.refactor_string(source_text, name='')) - 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, indent_size): - """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) - - 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', )): - continue - - if r['line'] in commented_out_code_line_numbers: - if issue_id.startswith(('e26', 'e501')): - 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): - import textwrap - 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): - """Return fixed source code.""" - if not options: - options = parse_args(['']) - - if not isinstance(source, unicode): - source = source.decode(locale.getpreferredencoding(False)) - - sio = io.StringIO(source) - return fix_lines(sio.readlines(), options=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: - fixed_source = tmp_source - else: - # Apply global fixes only once (for efficiency). - fixed_source = apply_global_fixes(tmp_source, options) - - 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): - if not options: - options = parse_args([filename]) - - original_source = readlines_from_file(filename) - - fixed_source = original_source - - if options.in_place or output: - encoding = detect_encoding(filename) - - if output: - output = codecs.getwriter(encoding)(output.buffer - if hasattr(output, 'buffer') - else output) - - output = LineEndingWrapper(output) - - 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 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): - """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 code_match('E101', select=options.select, ignore=options.ignore): - 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 global fix for {0}'.format(code.upper()), - file=sys.stderr) - source = function(source, - aggressive=options.aggressive) - - source = fix_2to3(source, - aggressive=options.aggressive, - select=options.select, - ignore=options.ignore) - - 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', dest='verbose', - default=0, - help='print verbose messages; ' - 'multiple -v result in more verbose messages') - parser.add_argument('-d', '--diff', action='store_true', dest='diff', - 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('-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('--range', metavar='line', dest='line_range', - 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): - """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 '-' 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.exclude and not args.recursive: - parser.error('--exclude is only relevant when used with --recursive') - - 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 = args.select.split(',') - - if args.ignore: - args.ignore = args.ignore.split(',') - elif not args.select: - if args.aggressive: - # Enable everything by default if aggressive. - args.select = ['E', 'W'] - else: - args.ignore = DEFAULT_IGNORE.split(',') - - if args.exclude: - args.exclude = args.exclude.split(',') - 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') - - return args - - -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): - """Return rank of candidate. - - This is for sorting candidates. - - """ - if not candidate.strip(): - return 0 - - rank = 0 - lines = candidate.split('\n') - - offset = 0 - if ( - not lines[0].lstrip().startswith('#') and - lines[0].rstrip()[-1] not in '([{' - ): - for symbol in '([{': - offset = max(offset, 1 + lines[0].find(symbol)) - - current_longest = max(offset + len(x.strip()) for x in lines) - - rank += 2 * 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(('(', '[', '{')): - # Avoid lonely opening. They result in longer lines. - if len(current_line) <= len(indent_word): - rank += 100 - - # Avoid ugliness of ", (\n". - if current_line.endswith(','): - rank += 100 - - if has_arithmetic_operator(current_line): - rank += 100 - - 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 += 1 - - # Prefer breaking at commas rather than colon. - if ',' in current_line and current_line.endswith(':'): - rank += 10 - - 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 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 main(): - """Tool main.""" - 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(sys.argv[1:]) - - 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 - - # LineEndingWrapper is unnecessary here due to the symmetry between - # standard in and standard out. - sys.stdout.write(fix_code(sys.stdin.read(), args)) - 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 43246cea..86527f56 100644 --- a/pymode/environment.py +++ b/pymode/environment.py @@ -1,40 +1,36 @@ -""" Define interfaces. """ +"""Define interfaces.""" -from __future__ import print_function - -import vim import json -import time import os.path - -from ._compat import PY2 +import time +import vim # noqa class VimPymodeEnviroment(object): - """ Vim User interface. """ + """Vim User interface.""" prefix = '[Pymode]' def __init__(self): - """ Init VIM environment. """ + """Init VIM environment.""" self.current = vim.current self.options = dict(encoding=vim.eval('&enc')) self.options['debug'] = self.var('g:pymode_debug', True) @property def curdir(self): - """ Return current working directory. """ + """Return current working directory.""" return self.var('getcwd()') @property def curbuf(self): - """ Return current buffer. """ + """Return current buffer.""" return self.current.buffer @property def cursor(self): - """ Return current window position. + """Return current window position. :return tuple: (row, col) @@ -43,24 +39,21 @@ def cursor(self): @property def source(self): - """ Return source of current buffer. """ + """Return source of current buffer.""" return "\n".join(self.lines) @property def lines(self): - """ Iterate by lines in current file. + """Iterate by lines in current file. :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): - """ Get vim variable. + 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: @@ -81,7 +74,7 @@ def var(name, to_bool=False, silence=False): @staticmethod def message(msg, history=False): - """ Show message to user. + """Show message to user. :return: :None @@ -91,28 +84,35 @@ def message(msg, history=False): return vim.command('call pymode#wide_message("%s")' % str(msg)) - def user_input(self, msg, default=''): - """ Return user input or 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): - """ Get user confirmation. + """Get user confirmation. :return bool: @@ -122,7 +122,7 @@ def user_confirm(self, msg, yes=False): return action and 'yes'.startswith(action) def user_input_choices(self, msg, *options): - """ Get one of many options. + """Get one of many options. :return str: A choosen option @@ -148,24 +148,24 @@ def user_input_choices(self, msg, *options): @staticmethod def error(msg): - """ Show error to user. """ + """Show error to user.""" vim.command('call pymode#error("%s")' % str(msg)) def debug(self, msg, *args): - """ Print debug information. """ + """Print debug information.""" if self.options.get('debug'): print("%s %s [%s]" % ( int(time.time()), msg, ', '.join([str(a) for a in args]))) def stop(self, value=None): - """ Break Vim function. """ + """Break Vim function.""" cmd = 'return' if value is not None: cmd += ' ' + self.prepare_value(value) vim.command(cmd) def catch_exceptions(self, func): - """ Decorator. Make execution more silence. + """Decorator. Make execution more silence. :return func: @@ -181,19 +181,19 @@ def _wrapper(*args, **kwargs): return _wrapper def run(self, name, *args): - """ Run vim function. """ + """Run vim function.""" vim.command('call %s(%s)' % (name, ", ".join([ self.prepare_value(a) for a in args ]))) def let(self, name, value): - """ Set variable. """ + """Set variable.""" cmd = 'let %s = %s' % (name, self.prepare_value(value)) self.debug(cmd) vim.command(cmd) def prepare_value(self, value, dumps=True): - """ Decode bstr to vim encoding. + """Decode bstr to vim encoding. :return unicode string: @@ -201,13 +201,10 @@ 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=""): - """ Calculate current offset. + """Calculate current offset. :return tuple: (source, offset) @@ -228,11 +225,11 @@ def get_offset_params(self, cursor=None, base=""): @staticmethod def goto_line(line): - """ Go to line. """ + """Go to line.""" vim.command('normal %sggzz' % line) def goto_file(self, path, cmd='e', force=False): - """ Function description. """ + """Open file by path.""" if force or os.path.abspath(path) != self.curbuf.name: self.debug('read', path) if ' ' in path and os.name == 'posix': @@ -241,9 +238,12 @@ def goto_file(self, path, cmd='e', force=False): @staticmethod def goto_buffer(bufnr): - """ Open buffer. """ + """Open buffer.""" 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/pylama/lint/pylama_pylint/logilab/__init__.py b/pymode/libs/__init__.py similarity index 100% rename from pymode/libs/pylama/lint/pylama_pylint/logilab/__init__.py rename to pymode/libs/__init__.py 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/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/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/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 1576bfd2..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__ = "5.0.5" -__project__ = "pylama" -__author__ = "Kirill Klenov " -__license__ = "GNU LGPL" diff --git a/pymode/libs/pylama/config.py b/pymode/libs/pylama/config.py deleted file mode 100644 index 881e930a..00000000 --- a/pymode/libs/pylama/config.py +++ /dev/null @@ -1,225 +0,0 @@ -""" Parse arguments from command line and configuration files. """ -import fnmatch -import sys -import os -from re import compile as re - -import logging -from argparse import ArgumentParser - -from . import __version__ -from .libs.inirama import Namespace -from .lint.extensions import LINTERS - - -# Setup a logger -LOGGER = logging.getLogger('pylama') -LOGGER.propagate = False -STREAM = logging.StreamHandler(sys.stdout) -LOGGER.addHandler(STREAM) - -#: A default checkers -DEFAULT_LINTERS = 'pep8', 'pyflakes', 'mccabe' - -CURDIR = os.getcwd() -CONFIG_FILES = [ - os.path.join(CURDIR, basename) for basename in - ('pylama.ini', 'setup.cfg', 'tox.ini', 'pytest.ini') -] - - -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( - "path", nargs='?', default=_Default(CURDIR), - help="Path on file or directory 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( - "--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(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)") - - -ACTIONS = dict((a.dest, a) for a in PARSER._actions) - - -def parse_options(args=None, config=True, **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)) - 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): - 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(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)) - - 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): - """ Load configuration from INI. - - :return Namespace: - - """ - config = Namespace() - config.default_section = 'pylama' - - if not ini_path: - for path in CONFIG_FILES: - 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 diff --git a/pymode/libs/pylama/core.py b/pymode/libs/pylama/core.py deleted file mode 100644 index 1283a662..00000000 --- a/pymode/libs/pylama/core.py +++ /dev/null @@ -1,208 +0,0 @@ -""" Pylama's core functionality. - -Prepare params, check a modeline and run the checkers. - -""" -import re - -import logging -from collections import defaultdict - -from .config import process_value, LOGGER -from .lint.extensions import LINTERS -from .errors import DUPLICATES, Error - - -#: 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) - - -def run(path='', code=None, options=None): - """ Run a code checkers with given params. - - :return errors: list of dictionaries with error's information - - """ - errors = [] - fileconfig = dict() - params = dict() - linters = LINTERS - linters_params = dict() - - 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]) - - 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 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: - 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) - - return sorted(errors, key=lambda e: e.lnum) - - -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=[]) - if options: - params['ignore'] = options.ignore - params['select'] = options.select - - for config in filter(None, [modeline, fileconfig]): - for key in ('ignore', 'select'): - 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 - - -def remove_duplicates(errors): - """ Remove same errors from others linters. """ - 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 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 file and read a code. """ - if self.code is None: - self._file = open(self.path, 'rU') - self.code = self._file.read() - return self - - def __exit__(self, t, value, traceback): - """ Close opened file. """ - if self._file is not None: - self._file.close() - - if t and LOGGER.level == logging.DEBUG: - LOGGER.debug(traceback) diff --git a/pymode/libs/pylama/errors.py b/pymode/libs/pylama/errors.py deleted file mode 100644 index 9e80d2a6..00000000 --- a/pymode/libs/pylama/errors.py +++ /dev/null @@ -1,70 +0,0 @@ -""" Dont duplicate errors same type. """ - -DUPLICATES = ( - - # multiple statements on one line - [('pep8', 'E701'), ('pylint', 'C0321')], - - # missing whitespace around operator - [('pep8', 'E225'), ('pylint', 'C0326')], - - # unused variable - [('pylint', 'W0612'), ('pyflakes', 'W0612')], - - # undefined variable - [('pylint', 'E0602'), ('pyflakes', 'E0602')], - - # unused import - [('pylint', 'W0611'), ('pyflakes', 'W0611')], - - # unexpected spaces - [('pylint', 'C0326'), ('pep8', 'E251')], - - # long lines - [('pylint', 'C0301'), ('pep8', 'E501')], - - # whitespace before '(' - [('pylint', 'C0326'), ('pep8', 'E211')], - - # statement ends with a semicolon - [('pylint', 'W0301'), ('pep8', 'E703')], - - # multiple statements on one line - [('pylint', 'C0321'), ('pep8', 'E702')], - - # bad indentation - [('pylint', 'W0311'), ('pep8', 'E111')], - -) - -DUPLICATES = dict((key, values) for values in DUPLICATES for key in values) - - -class Error(object): - - """ Store error 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, - 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 0dc34069..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 -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. """ - from .main import check_files - - _, files_modified, _ = run("git diff-index --cached --name-only HEAD") - - options = parse_options() - setup_logger(options) - check_files([f for f in map(str, files_modified)], options) - - -def hg_hook(ui, repo, node=None, **kwargs): - """ Run pylama after mercurial commit. """ - from .main import check_files - 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) - check_files(paths, options) - - -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(path, '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(path, '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(git) - LOGGER.warn('Mercurial hook has been installed.') - - else: - LOGGER.error('VCS has not found. Check your path.') - sys.exit(1) - -# lint_ignore=F0401,E1103 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 d5d75901..00000000 --- a/pymode/libs/pylama/lint/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -""" Custom module loader. """ - - -class Linter(object): # noqa - - """ 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 6e0bc3d2..00000000 --- a/pymode/libs/pylama/lint/extensions.py +++ /dev/null @@ -1,31 +0,0 @@ -""" Load extensions. """ - -from os import listdir, path as op - - -CURDIR = op.dirname(__file__) -LINTERS = dict() -PREFIX = 'pylama_' - -try: - from importlib import import_module -except ImportError: - from ..libs.importlib import import_module - -for p in listdir(CURDIR): - if p.startswith(PREFIX) and op.isdir(op.join(CURDIR, p)): - name = p[len(PREFIX):] - try: - module = import_module('.lint.%s%s' % (PREFIX, name), 'pylama') - LINTERS[name] = getattr(module, 'Linter')() - except ImportError: - continue - -try: - from pkg_resources import iter_entry_points - - for entry in iter_entry_points('pylama.linter'): - if entry.name not in LINTERS: - LINTERS[entry.name] = entry.load()() -except ImportError: - pass diff --git a/pymode/libs/pylama/lint/pylama_mccabe/__init__.py b/pymode/libs/pylama/lint/pylama_mccabe/__init__.py deleted file mode 100644 index da8b5f2a..00000000 --- a/pymode/libs/pylama/lint/pylama_mccabe/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -""" Check complexity. """ - -from .. import Linter as BaseLinter - - -class Linter(BaseLinter): - - """ Mccabe code complexity. """ - - @staticmethod - def run(path, code=None, params=None, **meta): - """ MCCabe code checking. - - :return list: List of errors. - - """ - from .mccabe import get_code_complexity - - complexity = int(params.get('complexity', 10)) - return get_code_complexity(code, complexity, filename=path) or [] diff --git a/pymode/libs/pylama/lint/pylama_mccabe/mccabe.py b/pymode/libs/pylama/lint/pylama_mccabe/mccabe.py deleted file mode 100644 index 82bdf5f0..00000000 --- a/pymode/libs/pylama/lint/pylama_mccabe/mccabe.py +++ /dev/null @@ -1,313 +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.2.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) - - 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 - - if self.graph is None: - # global loop - self.graph = PathGraph(name, name, node.lineno) - pathnode = PathNode(name) - self.tail = pathnode - self.dispatch_list(node.body) - self.graphs["%s%s" % (self.classname, name)] = self.graph - self.reset() - else: - 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 - - # TODO: else clause in node.orelse - - visitFor = visitWhile = visitLoop - - def visitIf(self, node): - name = "If %d" % node.lineno - pathnode = self.appendPathNode(name) - loose_ends = [] - self.dispatch_list(node.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 - pathnode = self.appendPathNode(name) - loose_ends = [] - self.dispatch_list(node.body) - loose_ends.append(self.tail) - for handler in node.handlers: - self.tail = pathnode - self.dispatch_list(handler.body) - loose_ends.append(self.tail) - if pathnode: - bottom = PathNode("", look='point') - for le in loose_ends: - self.graph.connect(le, bottom) - self.tail = bottom - - 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 = 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(dict( - type=McCabeChecker._code, - lnum=lineno, - text=text, - )) - - return 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): - 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=2) - - 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 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/pylama/lint/pylama_pep257/__init__.py b/pymode/libs/pylama/lint/pylama_pep257/__init__.py deleted file mode 100644 index 99474666..00000000 --- a/pymode/libs/pylama/lint/pylama_pep257/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -""" Check PEP257. """ - -from .. import Linter as BaseLinter - - -class Linter(BaseLinter): - - """ Mccabe code complexity. """ - - @staticmethod - def run(path, code=None, **meta): - """ PEP257 code checking. - - :return list: List of errors. - - """ - from .pep257 import PEP257Checker - - errors = [] - for er in PEP257Checker().check_source(code, path): - errors.append(dict( - lnum=er.line, - text=er.message, - type='D', - )) - return errors diff --git a/pymode/libs/pylama/lint/pylama_pep257/pep257.py b/pymode/libs/pylama/lint/pylama_pep257/pep257.py deleted file mode 100644 index c5df0f72..00000000 --- a/pymode/libs/pylama/lint/pylama_pep257/pep257.py +++ /dev/null @@ -1,728 +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 tokenize as tk -from itertools import takewhile, dropwhile, chain -from optparse import OptionParser -from re import compile as re - - -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 - - -__version__ = '0.3.3-alpha' -__all__ = ('check', 'collect') - - -humanize = lambda string: re(r'(.)([A-Z]+)').sub(r'\1 \2', string).lower() -is_magic = lambda name: name.startswith('__') and name.endswith('__') -is_ascii = lambda string: all(ord(char) < 128 for char in string) -is_blank = lambda string: not string.strip() -leading_space = lambda string: re('\s*').match(string).group() - - -class Value(object): - - __init__ = lambda self, *args: vars(self).update(zip(self._fields, args)) - __hash__ = lambda self: hash(repr(self)) - __eq__ = lambda self, other: other and vars(self) == vars(other) - - def __repr__(self): - args = [vars(self)[field] for field in self._fields] - return '%s(%s)' % (self.__class__.__name__, ', '.join(map(repr, args))) - - -class Definition(Value): - - _fields = 'name _source start end docstring children parent'.split() - - _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])) - __iter__ = lambda self: 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 docstring children parent _all'.split() - is_public = True - _nest = staticmethod(lambda s: {'def': Function, 'class': Class}[s]) - module = property(lambda self: self) - all = property(lambda self: self._all) - __str__ = lambda self: 'at module level' - - -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): - 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 Token(Value): - - _fields = 'kind value start end source'.split() - - -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 - 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): - for token in self.stream: - if token.kind == kind: - self.consume(kind) - return - - def parse_docstring(self): - for token in self.stream: - if token.kind in [tk.COMMENT, tk.NEWLINE, tk.NL]: - continue - elif token.kind == tk.STRING: - return token.value - else: - return None - - def parse_definitions(self, class_, all=False): - for token in self.stream: - if all and token.value == '__all__': - self.parse_all() - if token.value in ['def', 'class']: - yield self.parse_definition(class_._nest(token.value)) - if token.kind == tk.INDENT: - self.consume(tk.INDENT) - for definition in self.parse_definitions(class_): - yield definition - if token.kind == tk.DEDENT: - return - - def parse_all(self): - 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) - s = '(' - while self.current.kind in (tk.NL, tk.COMMENT): - self.stream.move() - if self.current.kind != tk.STRING: - raise AllError('Could not evaluate contents of __all__. ') - while self.current.value not in ')]': - s += self.current.value - self.stream.move() - s += ')' - try: - self.all = eval(s, {}) - except BaseException: - raise AllError('Could not evaluate contents of __all__: %s. ' % s) - - def parse_module(self): - start = self.line - docstring = self.parse_docstring() - children = list(self.parse_definitions(Module, all=True)) - assert self.current is None - end = self.line - module = Module(self.filename, self.source, start, end, - docstring, children, None, self.all) - for child in module.children: - child.parent = module - return module - - def parse_definition(self, class_): - start = self.line - self.consume(tk.NAME) - name = self.current.value - self.leapfrog(tk.INDENT) - assert self.current.kind != tk.INDENT - docstring = self.parse_docstring() - children = list(self.parse_definitions(class_)) - assert self.current.kind == tk.DEDENT - end = self.line - 1 - definition = class_(name, self.source, start, end, - docstring, children, None) - for child in definition.children: - child.parent = definition - return definition - - -class Error(object): - - """Error in docstring style.""" - - # Options that define how errors are printed: - explain = False - source = False - - def __init__(self, message=None, final=False): - self.message, self.is_final = message, final - self.definition, self.explanation = [None, None] - - code = property(lambda self: self.message.partition(':')[0]) - filename = property(lambda self: self.definition.module.name) - line = property(lambda self: self.definition.start) - - @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) - - -def parse_options(): - parser = OptionParser(version=__version__, - usage='Usage: pep257 [options] [...]') - 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('--ignore', metavar='', default='', - help='ignore a list comma-separated error codes, ' - 'for example: --ignore=D101,D202') - 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") - return parser.parse_args() - - -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): - for dir in dirs: - if not match_dir(dir): - dirs.remove(dir) # do not visit those dirs - for filename in filenames: - if match(filename): - yield os.path.join(root, filename) - else: - yield name - - -def check(filenames, ignore=()): - """Generate PEP 257 errors that exist in `filenames` iterable. - - Skips errors with error-codes defined in `ignore` iterable. - - Example - ------- - >>> check(['pep257.py'], ignore=['D100']) - - - """ - for filename in filenames: - try: - with open(filename) as file: - source = file.read() - for error in PEP257Checker().check_source(source, filename): - code = getattr(error, 'code', None) - if code is not None and code not in ignore: - yield error - except (EnvironmentError, AllError): - yield sys.exc_info()[1] - except tk.TokenError: - yield SyntaxError('invalid syntax in file %s' % filename) - - -def main(options, arguments): - Error.explain = options.explain - Error.source = options.source - collected = collect(arguments or ['.'], - match=re(options.match + '$').match, - match_dir=re(options.match_dir + '$').match) - code = 0 - for error in check(collected, ignore=options.ignore.split(',')): - sys.stderr.write('%s\n' % error) - code = 1 - 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 - if error.message is None: - error.message = message - error.explanation = explanation - error.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'} - return Error('%s: Docstring missing' % 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 Error('D200: One-line docstring should not occupy ' - '%s lines' % 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 Error('D201: No blank lines allowed *before* %s ' - 'docstring, found %s' - % (function.kind, blanks_before_count)) - if not all(blanks_after) and blanks_after_count != 0: - yield Error('D202: No blank lines allowed *after* %s ' - 'docstring, found %s' - % (function.kind, 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 flase-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 Error('D203: Expected 1 blank line *before* class ' - 'docstring, found %s' % blanks_before_count) - if not all(blanks_after) and blanks_after_count != 1: - yield Error('D204: Expected 1 blank line *after* class ' - 'docstring, found %s' % blanks_after_count) - - @check_for(Definition) - def check_blank_after_summary(self, definition, docstring): - """D205: Blank line missing between one-line summary 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 and not is_blank(lines[1]): - return Error() - - @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): - return Error('D206: Docstring indented with both tabs and ' - 'spaces') - if (len(indents) > 1 and min(indents[:-1]) > indent - or indents[-1] > indent): - return Error('D208: Docstring is over-indented') - if min(indents) < indent: - return Error('D207: Docstring is under-indented') - - @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 Error('D209: Put multi-line docstring closing ' - 'quotes on separate line') - - @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'''")): - # 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"""')): - quotes = "'''" if "'''" in docstring[:4] else "'" - return Error('D300: Expected """-quotes, got %s-quotes' % 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'): - return Error() - - @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'): - return Error() - - @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 Error("D400: First line should end with '.', not %r" - % 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 Error('D401: First line should be imperative: ' - '%r, not %r' % (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 Error("D402: First line should not be %s's signature" - % function.kind) - - # 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() - - -if __name__ == '__main__': - try: - sys.exit(main(*parse_options())) - except KeyboardInterrupt: - pass diff --git a/pymode/libs/pylama/lint/pylama_pep8/__init__.py b/pymode/libs/pylama/lint/pylama_pep8/__init__.py deleted file mode 100644 index a0a4ecb7..00000000 --- a/pymode/libs/pylama/lint/pylama_pep8/__init__.py +++ /dev/null @@ -1,58 +0,0 @@ -""" Check PEP8. """ -from .. import Linter as BaseLinter -from .pep8 import BaseReport, StyleGuide - -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - - -class Linter(BaseLinter): - - """ PEP8 code check. """ - - @staticmethod - def run(path, code=None, params=None, **meta): - """ PEP8 code checking. - - :return list: List of errors. - - """ - 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_pep8/pep8.py b/pymode/libs/pylama/lint/pylama_pep8/pep8.py deleted file mode 100644 index 10a3a155..00000000 --- a/pymode/libs/pylama/lint/pylama_pep8/pep8.py +++ /dev/null @@ -1,1960 +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 -# -# 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 - -__version__ = '1.6.0a0' - -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 - -DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__' -DEFAULT_IGNORE = 'E123,E226,E24,E704' -if sys.platform == 'win32': - 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') -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'([=!]=)\s*(None|False|True)') -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. - """ - max_line_length = int(max_line_length) - 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 - # 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" - else: - 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) - - E251: def complex(real, imag = 0.0): - E251: return magic(r = real, i = imag) - """ - parens = 0 - no_space = False - prev_end = None - 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) - elif token_type == tokenize.OP: - if text == '(': - parens += 1 - elif text == ')': - parens -= 1 - elif parens and text == '=': - no_space = True - if start != prev_end: - yield (prev_end, message) - 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 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) - if LAMBDA_REGEX.search(before): - 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" - """ - prev_start = prev_end = parens = 0 - for token_type, text, start, end, line in tokens: - if start[0] != prev_start and parens and backslash: - 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 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: - E712: if arg == True: - - 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: - same = (match.group(1) == '==') - singleton = 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(1), ("%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): - 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: - 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 or isinstance(value, list): - return value - paths = [] - for path in value.split(','): - 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) - - -if COMMENT_WITH_NL: - def _is_eol_token(token): - return (token[0] in NEWLINE or - (token[0] == tokenize.COMMENT and token[1] == token[4])) -else: - def _is_eol_token(token): - return token[0] in NEWLINE - - -############################################################################## -# Framework to run all checks -############################################################################## - - -_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} - - -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 = inspect.getargspec(check)[0] - 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 inspect.getargspec(check.__init__)[0][: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 - 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 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: - 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() - (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) - 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 (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()) - 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', None) - 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)") - 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 both user configuration and local configuration.""" - config = RawConfigParser() - - user_conf = options.config - if user_conf and os.path.isfile(user_conf): - if options.verbose: - print('user configuration: %s' % user_conf) - config.read(user_conf) - - local_dir = os.curdir - 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) - - 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.""" - if not parser: - parser = get_parser() - if not parser.has_option('--config'): - if config_file is True: - config_file = DEFAULT_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 (default: %default)") - # 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, config_file=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/pylama/lint/pylama_pyflakes/__init__.py b/pymode/libs/pylama/lint/pylama_pyflakes/__init__.py deleted file mode 100644 index 72fc26fe..00000000 --- a/pymode/libs/pylama/lint/pylama_pyflakes/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -""" Check Pyflakes. """ -import sys -from os import path as op - -from .. import Linter as BaseLinter - - -# Use local version of pyflakes -path = op.dirname(op.abspath(__file__)) -sys.path.insert(0, path) - -from pyflakes import checker - - -class Linter(BaseLinter): - - """ Pyflakes code check. """ - - def __init__(self): - if checker.messages.UndefinedName.message != "E0602 undefined name %r": - monkey_patch_messages(checker.messages) - - @staticmethod - def run(path, code=None, params=None, **meta): - """ Pyflake code checking. - - :return list: List of errors. - - """ - import _ast - - builtins = params.get("builtins", "") - - if builtins: - builtins = builtins.split(",") - - errors = [] - 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) - for w in w.messages: - errors.append(dict( - lnum=w.lineno, - text=w.message % w.message_args, - )) - return errors - - -def monkey_patch_messages(messages): - """ Patch pyflakes messages. """ - - messages.LateFutureImport.message = "W0410 future import(s) %r after other statements" - messages.ImportStarUsed.message = "W0401 'from %s import *' used; unable to detect undefined names" - messages.RedefinedWhileUnused.message = "W0404 redefinition of unused %r from line %r" - messages.DoctestSyntaxError.message = "W0511 syntax error in doctest" - messages.UnusedImport.message = "W0611 %r imported but unused" - messages.UnusedVariable.message = "W0612 local variable %r is assigned to but never used" - messages.RedefinedInListComp.message = "W0621 list comprehension redefines %r from line %r" - messages.Redefined.message = "W0621 redefinition of %r from line %r" - messages.ImportShadowedByLoopVar.message = "W0621 import %r from line %r shadowed by loop variable" - messages.ReturnWithArgsInsideGenerator.message = "E0106 'return' with argument inside generator" - messages.UndefinedName.message = "E0602 undefined name %r" - messages.UndefinedLocal.message = "E0602 local variable %r (defined in enclosing scope on line %r) referenced before assignment" - messages.UndefinedExport.message = "E0603 undefined name %r in __all__" - messages.DuplicateArgument.message = "E1122 duplicate argument %r in function definition" diff --git a/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/__init__.py b/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/__init__.py deleted file mode 100644 index cb2b136b..00000000 --- a/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - -__version__ = '0.8.2a0' diff --git a/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/checker.py b/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/checker.py deleted file mode 100644 index 70558324..00000000 --- a/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/checker.py +++ /dev/null @@ -1,874 +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) - - 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))] - 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) - if name in self.scope: - binding.used = self.scope[name].used - self.addBinding(node, binding) - - def handleNodeDelete(self, node): - name = getNodeName(node) - if not name: - 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 = WHILE = IF = WITH = WITHITEM = 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 - LISTCOMP = COMPREHENSION = KEYWORD = handleChildren - - def GLOBAL(self, node): - """ - Keep track of globals declarations. - """ - if isinstance(self.scope, FunctionScope): - self.scope.globals.update(node.names) - - NONLOCAL = GLOBAL - - def GENERATOREXP(self, node): - self.pushScope(GeneratorScope) - self.handleChildren(node) - self.popScope() - - 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 ( - 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) - - 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)) - - 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/pylama/lint/pylama_pyflakes/pyflakes/messages.py b/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/messages.py deleted file mode 100644 index 1f799ec5..00000000 --- a/pymode/libs/pylama/lint/pylama_pyflakes/pyflakes/messages.py +++ /dev/null @@ -1,135 +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 Redefined(Message): - message = 'redefinition of %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 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' 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 6ec4f3ba..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -""" Description. """ - -# Module information -# ================== - - -__version__ = "0.3.1" -__project__ = "pylama_pylint" -__author__ = "horneds " -__license__ = "BSD" - -import sys -if sys.version_info >= (3, 0, 0): - raise ImportError("pylama_pylint doesnt support python3") - -from .main import Linter -assert Linter diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/__init__.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/__init__.py deleted file mode 100644 index 19c80902..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/__init__.py +++ /dev/null @@ -1,118 +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. - """ - 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), - AsStringRegexpPredicate('namedtuple', 'func')) - """ - def transform(node, infer_function=infer_function): - node._explicit_inference = infer_function - return node - return transform - -# load brain plugins -from os import listdir -from os.path import join, dirname -BRAIN_MODULES_DIR = join(dirname(__file__), 'brain') -if BRAIN_MODULES_DIR not in sys.path: - # add it to the end of the list so user path take precedence - sys.path.append(BRAIN_MODULES_DIR) -# load modules in this directory -for module in listdir(BRAIN_MODULES_DIR): - if module.endswith('.py'): - __import__(module[:-3]) diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/__pkginfo__.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/__pkginfo__.py deleted file mode 100644 index 85398ff1..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/__pkginfo__.py +++ /dev/null @@ -1,48 +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, 1, 1) -version = '.'.join([str(num) for num in numversion]) - -install_requires = ['logilab-common >= 0.60.0'] - -license = 'LGPL' - -author = 'Logilab' -author_email = 'python-projects@lists.logilab.org' -mailinglist = "mailto://%s" % author_email -web = 'http://bitbucket.org/logilab/astroid' - -description = "rebuild a new abstract syntax tree from Python's ast" - -from os.path import join -include_dirs = ['brain', - join('test', 'regrtest_data'), - join('test', 'data'), join('test', 'data2')] - -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/pylama/lint/pylama_pylint/astroid/as_string.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/as_string.py deleted file mode 100644 index ace1c4e3..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/as_string.py +++ /dev/null @@ -1,496 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module renders Astroid nodes as string: - -* :func:`to_code` function return equivalent (hopefuly valid) python string - -* :func:`dump` function return an internal representation of nodes found - in the tree, useful for debugging or understanding the tree structure -""" - -import sys - -INDENT = ' ' # 4 spaces ; keep indentation variable - - -def dump(node, ids=False): - """print a nice astroid tree representation. - - :param ids: if true, we also print the ids (usefull for debugging) - """ - result = [] - _repr_tree(node, result, ids=ids) - return "\n".join(result) - -def _repr_tree(node, result, indent='', _done=None, ids=False): - """built a tree representation of a node as a list of lines""" - if _done is None: - _done = set() - if not hasattr(node, '_astroid_fields'): # not a astroid node - return - if node in _done: - result.append( indent + 'loop in tree: %s' % node ) - return - _done.add(node) - node_str = str(node) - if ids: - node_str += ' . \t%x' % id(node) - result.append( indent + node_str ) - indent += INDENT - for field in node._astroid_fields: - value = getattr(node, field) - if isinstance(value, (list, tuple) ): - result.append( indent + field + " = [" ) - for child in value: - if isinstance(child, (list, tuple) ): - # special case for Dict # FIXME - _repr_tree(child[0], result, indent, _done, ids) - _repr_tree(child[1], result, indent, _done, ids) - result.append(indent + ',') - else: - _repr_tree(child, result, indent, _done, ids) - result.append( indent + "]" ) - else: - result.append( indent + field + " = " ) - _repr_tree(value, result, indent, _done, ids) - - -class AsStringVisitor(object): - """Visitor to render an Astroid node as a valid python code string""" - - def __call__(self, node): - """Makes this visitor behave as a simple function""" - return node.accept(self) - - def _stmt_list(self, stmts): - """return a list of nodes to string""" - stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr]) - return INDENT + stmts.replace('\n', '\n'+INDENT) - - - ## visit_ methods ########################################### - - def visit_arguments(self, node): - """return an astroid.Function node as string""" - return node.format_args() - - def visit_assattr(self, node): - """return an astroid.AssAttr node as string""" - return self.visit_getattr(node) - - def visit_assert(self, node): - """return an astroid.Assert node as string""" - if node.fail: - return 'assert %s, %s' % (node.test.accept(self), - node.fail.accept(self)) - return 'assert %s' % node.test.accept(self) - - def visit_assname(self, node): - """return an astroid.AssName node as string""" - return node.name - - def visit_assign(self, node): - """return an astroid.Assign node as string""" - lhs = ' = '.join([n.accept(self) for n in node.targets]) - return '%s = %s' % (lhs, node.value.accept(self)) - - def visit_augassign(self, node): - """return an astroid.AugAssign node as string""" - return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) - - def visit_backquote(self, node): - """return an astroid.Backquote node as string""" - return '`%s`' % node.value.accept(self) - - def visit_binop(self, node): - """return an astroid.BinOp node as string""" - return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self)) - - def visit_boolop(self, node): - """return an astroid.BoolOp node as string""" - return (' %s ' % node.op).join(['(%s)' % n.accept(self) - for n in node.values]) - - def visit_break(self, node): - """return an astroid.Break node as string""" - return 'break' - - def visit_callfunc(self, node): - """return an astroid.CallFunc node as string""" - expr_str = node.func.accept(self) - args = [arg.accept(self) for arg in node.args] - if node.starargs: - args.append( '*' + node.starargs.accept(self)) - if node.kwargs: - args.append( '**' + node.kwargs.accept(self)) - return '%s(%s)' % (expr_str, ', '.join(args)) - - def visit_class(self, node): - """return an astroid.Class node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - bases = ', '.join([n.accept(self) for n in node.bases]) - if sys.version_info[0] == 2: - bases = bases and '(%s)' % bases or '' - else: - metaclass = node.metaclass() - if metaclass: - if bases: - bases = '(%s, metaclass=%s)' % (bases, metaclass.name) - else: - bases = '(metaclass=%s)' % metaclass.name - else: - bases = bases and '(%s)' % bases or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, - self._stmt_list( node.body)) - - def visit_compare(self, node): - """return an astroid.Compare node as string""" - rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) - for op, expr in node.ops]) - return '%s %s' % (node.left.accept(self), rhs_str) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - ifs = ''.join([ ' if %s' % n.accept(self) for n in node.ifs]) - return 'for %s in %s%s' % (node.target.accept(self), - node.iter.accept(self), ifs ) - - def visit_const(self, node): - """return an astroid.Const node as string""" - return repr(node.value) - - def visit_continue(self, node): - """return an astroid.Continue node as string""" - return 'continue' - - def visit_delete(self, node): # XXX check if correct - """return an astroid.Delete node as string""" - return 'del %s' % ', '.join([child.accept(self) - for child in node.targets]) - - def visit_delattr(self, node): - """return an astroid.DelAttr node as string""" - return self.visit_getattr(node) - - def visit_delname(self, node): - """return an astroid.DelName node as string""" - return node.name - - def visit_decorators(self, node): - """return an astroid.Decorators node as string""" - return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) - - def visit_dict(self, node): - """return an astroid.Dict node as string""" - return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), - value.accept(self)) for key, value in node.items]) - - def visit_dictcomp(self, node): - """return an astroid.DictComp node as string""" - return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_discard(self, node): - """return an astroid.Discard node as string""" - return node.value.accept(self) - - def visit_emptynode(self, node): - """dummy method for visiting an Empty node""" - return '' - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s, %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_ellipsis(self, node): - """return an astroid.Ellipsis node as string""" - return '...' - - def visit_empty(self, node): - """return an Empty node as string""" - return '' - - def visit_exec(self, node): - """return an astroid.Exec node as string""" - if node.locals: - return 'exec %s in %s, %s' % (node.expr.accept(self), - node.locals.accept(self), - node.globals.accept(self)) - if node.globals: - return 'exec %s in %s' % (node.expr.accept(self), - node.globals.accept(self)) - return 'exec %s' % node.expr.accept(self) - - def visit_extslice(self, node): - """return an astroid.ExtSlice node as string""" - return ','.join( [dim.accept(self) for dim in node.dims] ) - - def visit_for(self, node): - """return an astroid.For node as string""" - fors = 'for %s in %s:\n%s' % (node.target.accept(self), - node.iter.accept(self), - self._stmt_list( node.body)) - if node.orelse: - fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) - return fors - - def visit_from(self, node): - """return an astroid.From node as string""" - return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, - _import_string(node.names)) - - def visit_function(self, node): - """return an astroid.Function node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self), - docs, self._stmt_list(node.body)) - - def visit_genexpr(self, node): - """return an astroid.GenExpr node as string""" - return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self) - for n in node.generators])) - - def visit_getattr(self, node): - """return an astroid.Getattr node as string""" - return '%s.%s' % (node.expr.accept(self), node.attrname) - - def visit_global(self, node): - """return an astroid.Global node as string""" - return 'global %s' % ', '.join(node.names) - - def visit_if(self, node): - """return an astroid.If node as string""" - ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))] - if node.orelse:# XXX use elif ??? - ifs.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(ifs) - - def visit_ifexp(self, node): - """return an astroid.IfExp node as string""" - return '%s if %s else %s' % (node.body.accept(self), - node.test.accept(self), node.orelse.accept(self)) - - def visit_import(self, node): - """return an astroid.Import node as string""" - return 'import %s' % _import_string(node.names) - - def visit_keyword(self, node): - """return an astroid.Keyword node as string""" - return '%s=%s' % (node.arg, node.value.accept(self)) - - def visit_lambda(self, node): - """return an astroid.Lambda node as string""" - return 'lambda %s: %s' % (node.args.accept(self), node.body.accept(self)) - - def visit_list(self, node): - """return an astroid.List node as string""" - return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_listcomp(self, node): - """return an astroid.ListComp node as string""" - return '[%s %s]' % (node.elt.accept(self), ' '.join([n.accept(self) - for n in node.generators])) - - def visit_module(self, node): - """return an astroid.Module node as string""" - docs = node.doc and '"""%s"""\n\n' % node.doc or '' - return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' - - def visit_name(self, node): - """return an astroid.Name node as string""" - return node.name - - def visit_pass(self, node): - """return an astroid.Pass node as string""" - return 'pass' - - def visit_print(self, node): - """return an astroid.Print node as string""" - nodes = ', '.join([n.accept(self) for n in node.values]) - if not node.nl: - nodes = '%s,' % nodes - if node.dest: - return 'print >> %s, %s' % (node.dest.accept(self), nodes) - return 'print %s' % nodes - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.inst: - if node.tback: - return 'raise %s, %s, %s' % (node.exc.accept(self), - node.inst.accept(self), - node.tback.accept(self)) - return 'raise %s, %s' % (node.exc.accept(self), - node.inst.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_return(self, node): - """return an astroid.Return node as string""" - if node.value: - return 'return %s' % node.value.accept(self) - else: - return 'return' - - def visit_index(self, node): - """return a astroid.Index node as string""" - return node.value.accept(self) - - def visit_set(self, node): - """return an astroid.Set node as string""" - return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_setcomp(self, node): - """return an astroid.SetComp node as string""" - return '{%s %s}' % (node.elt.accept(self), ' '.join([n.accept(self) - for n in node.generators])) - - def visit_slice(self, node): - """return a astroid.Slice node as string""" - lower = node.lower and node.lower.accept(self) or '' - upper = node.upper and node.upper.accept(self) or '' - step = node.step and node.step.accept(self) or '' - if step: - return '%s:%s:%s' % (lower, upper, step) - return '%s:%s' % (lower, upper) - - def visit_subscript(self, node): - """return an astroid.Subscript node as string""" - return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) - - def visit_tryexcept(self, node): - """return an astroid.TryExcept node as string""" - trys = ['try:\n%s' % self._stmt_list( node.body)] - for handler in node.handlers: - trys.append(handler.accept(self)) - if node.orelse: - trys.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(trys) - - def visit_tryfinally(self, node): - """return an astroid.TryFinally node as string""" - return 'try:\n%s\nfinally:\n%s' % (self._stmt_list( node.body), - self._stmt_list(node.finalbody)) - - def visit_tuple(self, node): - """return an astroid.Tuple node as string""" - if len(node.elts) == 1: - return '(%s, )' % node.elts[0].accept(self) - return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_unaryop(self, node): - """return an astroid.UnaryOp node as string""" - if node.op == 'not': - operator = 'not ' - else: - operator = node.op - return '%s%s' % (operator, node.operand.accept(self)) - - def visit_while(self, node): - """return an astroid.While node as string""" - whiles = 'while %s:\n%s' % (node.test.accept(self), - self._stmt_list(node.body)) - if node.orelse: - whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) - return whiles - - def visit_with(self, node): # 'with' without 'as' is possible - """return an astroid.With node as string""" - items = ', '.join(('(%s)' % expr.accept(self)) + - (vars and ' as (%s)' % (vars.accept(self)) or '') - for expr, vars in node.items) - return 'with %s:\n%s' % (items, self._stmt_list( node.body)) - - def visit_yield(self, node): - """yield an ast.Yield node as string""" - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -class AsStringVisitor3k(AsStringVisitor): - """AsStringVisitor3k overwrites some AsStringVisitor methods""" - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s as %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_nonlocal(self, node): - """return an astroid.Nonlocal node as string""" - return 'nonlocal %s' % ', '.join(node.names) - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.cause: - return 'raise %s from %s' % (node.exc.accept(self), - node.cause.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_starred(self, node): - """return Starred node as string""" - return "*" + node.value.accept(self) - - def visit_yieldfrom(self, node): - """ Return an astroid.YieldFrom node as string. """ - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield from' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -def _import_string(names): - """return a list of (name, asname) formatted as a string""" - _names = [] - for name, asname in names: - if asname is not None: - _names.append('%s as %s' % (name, asname)) - else: - _names.append(name) - return ', '.join(_names) - - -if sys.version_info >= (3, 0): - AsStringVisitor = AsStringVisitor3k - -# this visitor is stateless, thus it can be reused -to_code = AsStringVisitor() - diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/bases.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/bases.py deleted file mode 100644 index 5ee11b3b..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/bases.py +++ /dev/null @@ -1,618 +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 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') - - def __init__(self, path=None): - if path is None: - self.path = set() - else: - self.path = path - self.lookupname = None - self.callcontext = None - self.boundnode = None - - 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) - clone.callcontext = self.callcontext - clone.boundnode = self.boundnode - return clone - - @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""" - try: - # avoid recursively inferring the same attr on the same class - if context: - 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): - return ((x is YES and x or Instance(x)) - for x in caller.args[0].infer()) - return self._proxied.infer_call_result(caller, context) - - -class BoundMethod(UnboundMethod): - """a special node representing a method bound to an instance""" - 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 - return 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 Ox%x>' % (self.__class__.__name__, - self._repr_name(), - self.fromlineno, - self.root().name, - id(self)) - - - def accept(self, visitor): - klass = self.__class__.__name__ - func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) - return func(self) - - def get_children(self): - for field in self._astroid_fields: - attr = getattr(self, field) - if attr is None: - continue - if isinstance(attr, (list, tuple)): - for elt in attr: - yield elt - else: - yield attr - - def last_child(self): - """an optimized version of list(get_children())[-1]""" - for field in self._astroid_fields[::-1]: - attr = getattr(self, field) - if not attr: # None or empty listy / tuple - continue - if isinstance(attr, (list, tuple)): - return attr[-1] - 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] - - def set_line_info(self, lastchild): - if self.lineno is None: - self.fromlineno = self._fixed_source_line() - else: - self.fromlineno = self.lineno - if lastchild is None: - self.tolineno = self.fromlineno - else: - self.tolineno = lastchild.tolineno - return - # 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 = _node.get_children().next() - 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/pylama/lint/pylama_pylint/astroid/brain/py2gi.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/brain/py2gi.py deleted file mode 100644 index dd9868db..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/brain/py2gi.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Astroid hooks for the Python 2 GObject introspection bindings. - -Helps with understanding everything imported from 'gi.repository' -""" - -import inspect -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) - - -# module specific transformation functions ##################################### - -def transform(module): - try: - tr = MODULE_TRANSFORMS[module.name] - except KeyError: - pass - else: - tr(module) -MANAGER.register_transform(nodes.Module, transform) - -# module specific transformation functions ##################################### - -def hashlib_transform(module): - template = ''' - -class %s(object): - def __init__(self, value=''): pass - def digest(self): - return u'' - def update(self, value): pass - def hexdigest(self): - return u'' -''' - - algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') - classes = "".join(template % hashfunc for hashfunc in algorithms) - - fake = AstroidBuilder(MANAGER).string_build(classes) - - for hashfunc in algorithms: - module.locals[hashfunc] = fake.locals[hashfunc] - -def collections_transform(module): - fake = AstroidBuilder(MANAGER).string_build(''' - -class defaultdict(dict): - default_factory = None - def __missing__(self, key): pass - -class deque(object): - maxlen = 0 - def __init__(iterable=None, maxlen=None): pass - def append(self, x): pass - def appendleft(self, x): pass - def clear(self): pass - def count(self, x): return 0 - def extend(self, iterable): pass - def extendleft(self, iterable): pass - def pop(self): pass - def popleft(self): pass - def remove(self, value): pass - def reverse(self): pass - def rotate(self, n): pass - def __iter__(self): return self - -''') - - for klass in ('deque', 'defaultdict'): - module.locals[klass] = fake.locals[klass] - -def pkg_resources_transform(module): - fake = AstroidBuilder(MANAGER).string_build(''' - -def resource_exists(package_or_requirement, resource_name): - pass - -def resource_isdir(package_or_requirement, resource_name): - pass - -def resource_filename(package_or_requirement, resource_name): - pass - -def resource_stream(package_or_requirement, resource_name): - pass - -def resource_string(package_or_requirement, resource_name): - pass - -def resource_listdir(package_or_requirement, resource_name): - pass - -def extraction_error(): - pass - -def get_cache_path(archive_name, names=()): - pass - -def postprocess(tempname, filename): - pass - -def set_extraction_path(path): - pass - -def cleanup_resources(force=False): - pass - -''') - - for func_name, func in fake.locals.items(): - module.locals[func_name] = func - - -def urlparse_transform(module): - fake = AstroidBuilder(MANAGER).string_build(''' - -def urlparse(url, scheme='', allow_fragments=True): - return ParseResult() - -class ParseResult(object): - def __init__(self): - self.scheme = '' - self.netloc = '' - self.path = '' - self.params = '' - self.query = '' - self.fragment = '' - self.username = None - self.password = None - self.hostname = None - self.port = None - - def geturl(self): - return '' -''') - - for func_name, func in fake.locals.items(): - module.locals[func_name] = func - -def subprocess_transform(module): - if PY3K: - communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) - else: - communicate = ('string', 'string') - fake = AstroidBuilder(MANAGER).string_build(''' - -class Popen(object): - returncode = pid = 0 - stdin = stdout = stderr = file() - - 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 - - def communicate(self, input=None): - return %r - def wait(self): - return self.returncode - def poll(self): - return self.returncode - def send_signal(self, signal): - pass - def terminate(self): - pass - def kill(self): - pass - ''' % (communicate, )) - - for func_name, func in fake.locals.items(): - module.locals[func_name] = func - - - -MODULE_TRANSFORMS['hashlib'] = hashlib_transform -MODULE_TRANSFORMS['collections'] = collections_transform -MODULE_TRANSFORMS['pkg_resources'] = pkg_resources_transform -MODULE_TRANSFORMS['urlparse'] = urlparse_transform -MODULE_TRANSFORMS['subprocess'] = subprocess_transform - -# namedtuple support ########################################################### - -def infer_named_tuple(node, context=None): - """Specific inference function for namedtuple CallFunc node""" - def infer_first(node): - try: - value = node.infer().next() - 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 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.split() - except AttributeError: - attributes = [infer_first(const).value for const in names.elts] - except (AttributeError, exceptions.InferenceError): - 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(nodes.Tuple._proxied) - # 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] - - fake = AstroidBuilder(MANAGER).string_build(''' -class %(name)s(tuple): - 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'] - # we use UseInferenceDefault, we can't be a generator so return an iterator - return iter([class_node]) - -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_named_tuple), - AsStringRegexpPredicate('namedtuple', 'func')) diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/builder.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/builder.py deleted file mode 100644 index b6ceff82..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/builder.py +++ /dev/null @@ -1,238 +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 . -"""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. -""" - -__docformat__ = "restructuredtext en" - -import sys -from os.path import splitext, basename, exists, abspath - -from logilab.common.modutils import modpath_from_file - -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 _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): - byte_stream = open(filename, 'bU') - encoding = detect_encoding(byte_stream.readline)[0] - byte_stream.close() - stream = open(filename, 'U', encoding=encoding) - try: - data = stream.read() - except UnicodeError, uex: # wrong encodingg - # detect_encoding returns utf-8 if no encoding specified - msg = 'Wrong (%s) or no encoding specified' % encoding - raise AstroidBuildingException(msg) - return stream, encoding, data - -else: - import re - - _ENCODING_RGX = re.compile("\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, exc: - msg = 'Unable to load file %r (%s)' % (path, exc) - raise AstroidBuildingException(msg) - except SyntaxError, exc: # py3k encoding specification error - raise AstroidBuildingException(exc) - except LookupError, exc: # unknown encoding - raise AstroidBuildingException(exc) - # get module name if necessary - if modname is None: - try: - modname = '.'.join(modpath_from_file(path)) - except ImportError: - modname = splitext(basename(path))[0] - # build astroid representation - module = self._data_build(data, modname, path) - return self._post_build(module, encoding) - - def string_build(self, data, modname='', path=None): - """build astroid from source code string and return rebuilded astroid""" - module = self._data_build(data, modname, path) - module.file_bytes = data.encode('utf-8') - return self._post_build(module, 'utf-8') - - def _post_build(self, module, encoding): - """handles encoding and delayed nodes - after a module has been built - """ - module.file_encoding = encoding - self._manager.cache_module(module) - # post tree building steps after we stored the module in the cache: - for from_node in module._from_nodes: - if from_node.modname == '__future__': - for symbol, _ in from_node.names: - module.future_imports.add(symbol) - self.add_from_names_to_locals(from_node) - # handle delayed assattr nodes - for delayed in module._delayed_assattr: - self.delayed_assattr(delayed) - return module - - def _data_build(self, data, modname, path): - """build tree node from data and add some informations""" - # this method could be wrapped with a pickle/cache function - node = parse(data + '\n') - if path is not None: - node_file = abspath(path) - else: - node_file = '' - if modname.endswith('.__init__'): - modname = modname[:-9] - package = True - else: - package = path and path.find('__init__.py') > -1 or False - rebuilder = TreeRebuilder(self._manager) - module = rebuilder.visit_module(node, modname, package) - module.file = module.path = node_file - module._from_nodes = rebuilder._from_nodes - module._delayed_assattr = rebuilder._delayed_assattr - return module - - def add_from_names_to_locals(self, node): - """store imported names to the locals; - resort the locals if coming from a delayed node - """ - - _key_func = lambda node: node.fromlineno - def sort_locals(my_list): - my_list.sort(key=_key_func) - for (name, asname) in node.names: - if name == '*': - try: - imported = node.root().import_module(node.modname) - except AstroidBuildingException: - 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/pylama/lint/pylama_pylint/astroid/exceptions.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/exceptions.py deleted file mode 100644 index 3889e2e7..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/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/pylama/lint/pylama_pylint/astroid/inference.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/inference.py deleted file mode 100644 index 35cce332..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/inference.py +++ /dev/null @@ -1,393 +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 - -MANAGER = AstroidManager() - - -class CallContext: - """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 infer_name(self, context=None): - """infer a Name: use name lookup rules""" - frame, stmts = self.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(self.modname) - 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""" - #context = context.clone() - 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 = self.value.infer(context).next() - if value is YES: - yield YES - return - - index = self.slice.infer(context).next() - if index is YES: - yield YES - return - - if isinstance(index, nodes.Const): - try: - assigned = value.getitem(index.value, context) - except AttributeError: - raise InferenceError() - except (IndexError, TypeError): - yield YES - return - for infered in assigned.infer(context): - yield infered - else: - raise InferenceError() -nodes.Subscript._infer = path_wrapper(infer_subscript) -nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript) - - -UNARY_OP_METHOD = {'+': '__pos__', - '-': '__neg__', - '~': '__invert__', - 'not': None, # XXX not '__nonzero__' - } - -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) - - -BIN_OP_METHOD = {'+': '__add__', - '-': '__sub__', - '/': '__div__', - '//': '__floordiv__', - '*': '__mul__', - '**': '__power__', - '%': '__mod__', - '&': '__and__', - '|': '__or__', - '^': '__xor__', - '<<': '__lshift__', - '>>': '__rshift__', - } - -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/pylama/lint/pylama_pylint/astroid/manager.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/manager.py deleted file mode 100644 index 058e845e..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/manager.py +++ /dev/null @@ -1,336 +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) -""" - -__docformat__ = "restructuredtext en" - -import os -from os.path import dirname, join, isdir, exists - -from logilab.common.modutils import NoSourceFile, is_python_source, \ - file_from_modpath, load_module_from_name, modpath_from_file, \ - get_module_files, get_source_file, zipimport -from logilab.common.configuration import OptionsProviderMixIn - -from astroid.exceptions import AstroidBuildingException - -def astroid_wrapper(func, modname): - """wrapper to give to AstroidManager.project_from_files""" - print 'parsing %s...' % modname - try: - return func(modname) - except AstroidBuildingException, exc: - print exc - except Exception, exc: - import traceback - traceback.print_exc() - -def _silent_no_wrap(func, modname): - """silent wrapper that doesn't do anything; can be used for tests""" - return func(modname) - -def safe_repr(obj): - try: - return repr(obj) - except: - return '???' - - - -class AstroidManager(OptionsProviderMixIn): - """the astroid manager, responsible to build astroid from files - or modules. - - Use the Borg pattern. - """ - - name = 'astroid loader' - options = (("ignore", - {'type' : "csv", 'metavar' : "", - 'dest' : "black_list", "default" : ('CVS',), - 'help' : "add (may be a directory) to the black list\ -. It should be a base name, not a path. You may set this option multiple times\ -."}), - ("project", - {'default': "No Name", 'type' : 'string', 'short': 'p', - 'metavar' : '', - 'help' : 'set the project name.'}), - ) - brain = {} - def __init__(self): - self.__dict__ = AstroidManager.brain - if not self.__dict__: - OptionsProviderMixIn.__init__(self) - self.load_defaults() - # NOTE: cache entries are added by the [re]builder - self.astroid_cache = {} - self._mod_file_cache = {} - self.transforms = {} - - def ast_from_file(self, filepath, modname=None, fallback=True, source=False): - """given a module name, return the astroid object""" - try: - filepath = get_source_file(filepath, include_no_ext=True) - source = True - except NoSourceFile: - pass - if modname is None: - try: - modname = '.'.join(modpath_from_file(filepath)) - except ImportError: - modname = filepath - if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: - return self.astroid_cache[modname] - if source: - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).file_build(filepath, modname) - elif fallback and modname: - return self.ast_from_module_name(modname) - raise AstroidBuildingException('unable to get astroid for file %s' % - filepath) - - def ast_from_module_name(self, modname, context_file=None): - """given a module name, return the astroid object""" - if modname in self.astroid_cache: - return self.astroid_cache[modname] - if modname == '__main__': - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).string_build('', modname) - old_cwd = os.getcwd() - if context_file: - os.chdir(dirname(context_file)) - try: - filepath = self.file_from_module_name(modname, context_file) - if filepath is not None and not is_python_source(filepath): - module = self.zip_import_data(filepath) - if module is not None: - return module - if filepath is None or not is_python_source(filepath): - try: - module = load_module_from_name(modname) - except Exception, ex: - msg = 'Unable to load module %s (%s)' % (modname, ex) - raise AstroidBuildingException(msg) - return self.ast_from_module(module, modname) - return self.ast_from_file(filepath, modname, fallback=False) - finally: - os.chdir(old_cwd) - - def zip_import_data(self, filepath): - if zipimport is None: - return None - from astroid.builder import AstroidBuilder - builder = AstroidBuilder(self) - for ext in ('.zip', '.egg'): - try: - eggpath, resource = filepath.rsplit(ext + '/', 1) - except ValueError: - continue - try: - importer = zipimport.zipimporter(eggpath + ext) - zmodname = resource.replace('/', '.') - if importer.is_package(resource): - zmodname = zmodname + '.__init__' - module = builder.string_build(importer.get_source(resource), - zmodname, filepath) - return module - except: - continue - return None - - def file_from_module_name(self, modname, contextfile): - try: - value = self._mod_file_cache[(modname, contextfile)] - except KeyError: - try: - value = file_from_modpath(modname.split('.'), - context_file=contextfile) - except ImportError, ex: - msg = 'Unable to load module %s (%s)' % (modname, ex) - value = AstroidBuildingException(msg) - self._mod_file_cache[(modname, contextfile)] = value - if isinstance(value, AstroidBuildingException): - raise value - return value - - def ast_from_module(self, module, modname=None): - """given an imported module, return the astroid object""" - modname = modname or module.__name__ - if modname in self.astroid_cache: - return self.astroid_cache[modname] - try: - # some builtin modules don't have __file__ attribute - filepath = module.__file__ - if is_python_source(filepath): - return self.ast_from_file(filepath, modname) - except AttributeError: - pass - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).module_build(module, modname) - - def ast_from_class(self, klass, modname=None): - """get astroid for the given class""" - if modname is None: - try: - modname = klass.__module__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for class %s' % safe_repr(klass)) - modastroid = self.ast_from_module_name(modname) - return modastroid.getattr(klass.__name__)[0] # XXX - - - def infer_ast_from_something(self, obj, context=None): - """infer astroid for the given class""" - if hasattr(obj, '__class__') and not isinstance(obj, type): - klass = obj.__class__ - else: - klass = obj - try: - modname = klass.__module__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for %s' % safe_repr(klass)) - except Exception, 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, ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving name for %s: %s' - % (safe_repr(klass), ex)) - # take care, on living object __module__ is regularly wrong :( - modastroid = self.ast_from_module_name(modname) - if klass is obj: - for infered in modastroid.igetattr(name, context): - yield infered - else: - for infered in modastroid.igetattr(name, context): - yield infered.instanciate_class() - - def project_from_files(self, files, func_wrapper=astroid_wrapper, - project_name=None, black_list=None): - """return a Project from a list of files or modules""" - # build the project representation - project_name = project_name or self.config.project - black_list = black_list or self.config.black_list - project = Project(project_name) - for something in files: - if not exists(something): - fpath = file_from_modpath(something.split('.')) - elif isdir(something): - fpath = join(something, '__init__.py') - else: - fpath = something - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None: - continue - # XXX why is first file defining the project.path ? - project.path = project.path or astroid.file - project.add_module(astroid) - base_name = astroid.name - # recurse in package except if __init__ was explicitly given - if astroid.package and something.find('__init__') == -1: - # recurse on others packages / modules if this is a package - for fpath in get_module_files(dirname(astroid.file), - black_list): - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None or astroid.name == base_name: - continue - project.add_module(astroid) - return project - - def register_transform(self, node_class, transform, predicate=None): - """Register `transform(node)` function to be applied on the given - Astroid's `node_class` if `predicate` is None or return a true value - when called with the node as argument. - - The transform function may return a value which is then used to - substitute the original node in the tree. - """ - self.transforms.setdefault(node_class, []).append( (transform, predicate) ) - - def unregister_transform(self, node_class, transform, predicate=None): - """Unregister the given transform.""" - self.transforms[node_class].remove( (transform, predicate) ) - - def transform(self, node): - """Call matching transforms for the given node if any and return the - transformed node. - """ - cls = node.__class__ - if cls not in self.transforms: - # no transform registered for this class of node - return node - - transforms = self.transforms[cls] - orig_node = node # copy the reference - for transform_func, predicate in transforms: - if predicate is None or predicate(node): - ret = transform_func(node) - # if the transformation function returns something, it's - # expected to be a replacement for the node - if ret is not None: - if node is not orig_node: - # node has already be modified by some previous - # transformation, warn about it - warn('node %s substitued 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) - - -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/pylama/lint/pylama_pylint/astroid/mixins.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/mixins.py deleted file mode 100644 index 5e7b7878..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/mixins.py +++ /dev/null @@ -1,122 +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 astroid.exceptions import (AstroidBuildingException, InferenceError, - NotFoundError) - - -class BlockRangeMixIn(object): - """override block range """ - def set_line_info(self, lastchild): - self.fromlineno = self.lineno - self.tolineno = lastchild.tolineno - self.blockstart_tolineno = self._blockstart_toline() - - 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): - """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 - # 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, 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/pylama/lint/pylama_pylint/astroid/node_classes.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/node_classes.py deleted file mode 100644 index 01dc8d92..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/node_classes.py +++ /dev/null @@ -1,928 +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 - -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 - - -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 = stmt.infer(context).next() - 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 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""" - _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 - - 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, basestring): - 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, basestring): - 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.iteritems()] - - 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 - - def _blockstart_toline(self): - if self.name: - return self.name.tolineno - elif self.type: - return self.type.tolineno - else: - return self.lineno - - def set_line_info(self, lastchild): - self.fromlineno = self.lineno - self.tolineno = lastchild.tolineno - self.blockstart_tolineno = self._blockstart_toline() - - 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 - def _blockstart_toline(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 - - def _blockstart_toline(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 _blockstart_toline(self): - return self.lineno - - 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 _blockstart_toline(self): - return self.lineno - - 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 - - def _blockstart_toline(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 - - def _blockstart_toline(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/pylama/lint/pylama_pylint/astroid/nodes.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/nodes.py deleted file mode 100644 index 263ab476..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/nodes.py +++ /dev/null @@ -1,73 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -""" -on all nodes : - .is_statement, returning true if the node should be considered as a - statement node - .root(), returning the root node of the tree (i.e. a Module) - .previous_sibling(), returning previous sibling statement node - .next_sibling(), returning next sibling statement node - .statement(), returning the first parent node marked as statement node - .frame(), returning the first node defining a new local scope (i.e. - Module, Function or Class) - .set_local(name, node), define an identifier on the first parent frame, - with the node defining it. This is used by the astroid builder and should not - be used from out there. - -on From and Import : - .real_name(name), - - -""" - -__docformat__ = "restructuredtext en" - -from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \ - AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \ - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \ - Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \ - From, Getattr, Global, If, IfExp, Import, Index, Keyword, \ - List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \ - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \ - const_factory -from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \ - ListComp, SetComp, Function, Class - -ALL_NODE_CLASSES = ( - Arguments, AssAttr, Assert, Assign, AssName, AugAssign, - Backquote, BinOp, BoolOp, Break, - CallFunc, Class, Compare, Comprehension, Const, Continue, - Decorators, DelAttr, DelName, Delete, - Dict, DictComp, Discard, - Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, - For, From, Function, - Getattr, GenExpr, Global, - If, IfExp, Import, Index, - Keyword, - Lambda, List, ListComp, - Name, Nonlocal, - Module, - Pass, Print, - Raise, Return, - Set, SetComp, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, - UnaryOp, - While, With, - Yield, YieldFrom - ) - diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/protocols.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/protocols.py deleted file mode 100644 index e66b802c..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/protocols.py +++ /dev/null @@ -1,322 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to handle python protocols for nodes -where it makes sense. -""" - -__doctype__ = "restructuredtext en" - -from astroid.exceptions import InferenceError, NoDefault -from astroid.node_classes import unpack_infer -from astroid.bases import copy_context, \ - raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES -from astroid.nodes import const_factory -from astroid import nodes - -# 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 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) - - -# 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, exc: # 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: - yield const_factory(()) - return - if name == self.kwarg: - yield const_factory({}) - 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 - for infered in callcontext.infer_argument(self.parent, node.name, context): - yield infered - return - for infered in _arguments_infer_argname(self, node.name, context): - yield infered -nodes.Arguments.assigned_stmts = arguments_assigned_stmts - - -def assign_assigned_stmts(self, node, context=None, asspath=None): - if not asspath: - yield self.value - return - for infered in _resolve_asspart(self.value.infer(context), asspath, context): - yield infered -nodes.Assign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) -nodes.AugAssign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) - - -def _resolve_asspart(parts, asspath, context): - """recursive function to resolve multiple assignments""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if hasattr(part, 'getitem'): - try: - assigned = part.getitem(index, context) - # XXX raise a specific exception to avoid potential hiding of - # unexpected exception ? - except (TypeError, IndexError): - return - if not asspath: - # we achieved to resolved the assignment path, don't infer the - # last part - yield assigned - elif assigned is YES: - return - else: - # we are not yet on the last part of the path search on each - # possibly inferred value - try: - for infered in _resolve_asspart(assigned.infer(context), - asspath, context): - yield infered - except InferenceError: - return - - -def excepthandler_assigned_stmts(self, node, context=None, asspath=None): - for assigned in unpack_infer(self.type): - if isinstance(assigned, nodes.Class): - assigned = Instance(assigned) - yield assigned -nodes.ExceptHandler.assigned_stmts = raise_if_nothing_infered(excepthandler_assigned_stmts) - - -def with_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - for _, vars in self.items: - if vars is None: - continue - for lst in vars.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.nodes: - yield item -nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts) - - diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/raw_building.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/raw_building.py deleted file mode 100644 index bb685a9e..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/raw_building.py +++ /dev/null @@ -1,361 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to 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) - -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) - -EmptyNode.has_underlying_object = lambda self: self.object is not _marker - -def attach_const_node(node, name, value): - """create a Const node and register it in the locals of the given - node with the specified name - """ - if not name in node.special_attributes: - _attach_local_node(node, const_factory(value), name) - -def attach_import_node(node, modname, membername): - """create a From node and register it in the locals of the given - node with the specified name - """ - from_node = From(modname, [(membername, None)]) - _attach_local_node(node, from_node, membername) - - -def build_module(name, doc=None): - """create and initialize a astroid Module node""" - node = Module(name, doc, pure_python=False) - node.package = False - node.parent = None - return node - -def build_class(name, basenames=(), doc=None): - """create and initialize a astroid Class node""" - node = Class(name, doc) - for base in basenames: - basenode = Name() - basenode.name = base - node.bases.append(basenode) - basenode.parent = node - return node - -def build_function(name, args=None, defaults=None, flag=0, doc=None): - """create and initialize a astroid Function node""" - args, defaults = args or [], defaults or [] - # first argument is now a list of decorators - func = Function(name, doc) - func.args = argsnode = Arguments() - argsnode.args = [] - for arg in args: - argsnode.args.append(Name()) - argsnode.args[-1].name = arg - argsnode.args[-1].parent = argsnode - argsnode.defaults = [] - for default in defaults: - argsnode.defaults.append(const_factory(default)) - argsnode.defaults[-1].parent = argsnode - argsnode.kwarg = None - argsnode.vararg = None - argsnode.parent = func - if args: - register_arguments(func) - return func - - -def build_from_import(fromname, names): - """create and initialize an astroid From import statement""" - return From(fromname, [(name, None) for name in names]) - -def register_arguments(func, args=None): - """add given arguments to local - - args is a list that may contains nested lists - (i.e. def func(a, (b, c, d)): ...) - """ - if args is None: - args = func.args.args - if func.args.vararg: - func.set_local(func.args.vararg, func.args) - if func.args.kwarg: - func.set_local(func.args.kwarg, func.args) - for arg in args: - if isinstance(arg, Name): - func.set_local(arg.name, arg) - else: - register_arguments(func, arg.elts) - -def object_build_class(node, member, localname): - """create astroid for a living class object""" - basenames = [base.__name__ for base in member.__bases__] - return _base_class_object_build(node, member, basenames, - localname=localname) - -def object_build_function(node, member, localname): - """create astroid for a living function object""" - args, varargs, varkw, defaults = getargspec(member) - if varargs is not None: - args.append(varargs) - if varkw is not None: - args.append(varkw) - func = build_function(getattr(member, '__name__', None) or localname, args, - defaults, member.func_code.co_flags, member.__doc__) - node.add_local_node(func, localname) - -def object_build_datadescriptor(node, member, name): - """create astroid for a living data descriptor object""" - return _base_class_object_build(node, member, [], name) - -def object_build_methoddescriptor(node, member, localname): - """create astroid for a living method descriptor object""" - # FIXME get arguments ? - func = build_function(getattr(member, '__name__', None) or localname, - doc=member.__doc__) - # set node's arguments to None to notice that we have no information, not - # and empty argument list - func.args.args = None - node.add_local_node(func, localname) - -def _base_class_object_build(node, member, basenames, name=None, localname=None): - """create astroid for a living class object, with a given set of base names - (e.g. ancestors) - """ - klass = build_class(name or getattr(member, '__name__', None) or localname, - basenames, member.__doc__) - klass._newstyle = isinstance(member, type) - node.add_local_node(klass, localname) - try: - # limit the instantiation trick since it's too dangerous - # (such as infinite test execution...) - # this at least resolves common case such as Exception.args, - # OSError.errno - if issubclass(member, Exception): - instdict = member().__dict__ - else: - raise TypeError - except: - pass - else: - for name, obj in instdict.items(): - valnode = EmptyNode() - valnode.object = obj - valnode.parent = klass - valnode.lineno = 1 - klass.instance_attrs[name] = [valnode] - return klass - - - - -class InspectBuilder(object): - """class for building nodes from living object - - this is actually a really minimal representation, including only Module, - Function and Class nodes and some others as guessed. - """ - - # astroid from living objects ############################################### - - def __init__(self): - self._done = {} - self._module = None - - def inspect_build(self, module, modname=None, path=None): - """build astroid from a living module (i.e. using inspect) - this is used when there is no python source code available (either - because it's a built-in module or because the .py is not available) - """ - self._module = module - if modname is None: - modname = module.__name__ - try: - node = build_module(modname, module.__doc__) - except AttributeError: - # in jython, java modules have no __doc__ (see #109562) - node = build_module(modname) - node.file = node.path = path and abspath(path) or path - node.name = modname - MANAGER.cache_module(node) - node.package = hasattr(module, '__path__') - self._done = {} - self.object_build(node, module) - return node - - def object_build(self, node, obj): - """recursive method which create a partial ast from real objects - (only function, class, and method are handled) - """ - if obj in self._done: - return self._done[obj] - self._done[obj] = node - for name in dir(obj): - try: - member = getattr(obj, name) - except AttributeError: - # damned ExtensionClass.Base, I know you're there ! - attach_dummy_node(node, name) - continue - if ismethod(member): - member = member.im_func - if isfunction(member): - # verify this is not an imported function - filename = getattr(member.func_code, '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)): - #if obj is object: - # print 'skippp', obj, name, member - 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 boot strapping ################################################### ### -Astroid_BUILDER = InspectBuilder() - -_CONST_PROXY = {} -def astroid_boot_strapping(): - """astroid boot strapping the builtins module""" - # this boot strapping is necessary since we need the Const nodes to - # inspect_build builtins, and then we can proxy Const - from logilab.common.compat import builtins - astroid_builtin = Astroid_BUILDER.inspect_build(builtins) - for cls, node_cls in CONST_CLS.items(): - if cls is type(None): - proxy = build_class('NoneType') - proxy.parent = astroid_builtin - else: - proxy = astroid_builtin.getattr(cls.__name__)[0] - if cls in (dict, list, set, tuple): - node_cls._proxied = proxy - else: - _CONST_PROXY[cls] = proxy - -astroid_boot_strapping() - -# TODO : find a nicer way to handle this situation; -# However __proxied introduced an -# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870) -def _set_proxied(const): - return _CONST_PROXY[const.value.__class__] -Const._proxied = property(_set_proxied) - -from types import GeneratorType -Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__) -Astroid_BUILDER.object_build(Generator._proxied, GeneratorType) - diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/rebuilder.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/rebuilder.py deleted file mode 100644 index 40a614f8..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/rebuilder.py +++ /dev/null @@ -1,954 +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 warnings import warn -from _ast import (Expr as Discard, Str, Name, Attribute, - # binary operators - Add, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor, - LShift, RShift, - # logical operators - And, Or, - # unary operators - UAdd, USub, Not, Invert, - # comparison operators - Eq, Gt, GtE, In, Is, IsNot, Lt, LtE, NotEq, NotIn, - ) - -from astroid import nodes as new - - -_BIN_OP_CLASSES = {Add: '+', - BitAnd: '&', - BitOr: '|', - BitXor: '^', - Div: '/', - FloorDiv: '//', - Mod: '%', - Mult: '*', - Pow: '**', - Sub: '-', - LShift: '<<', - RShift: '>>'} - -_BOOL_OP_CLASSES = {And: 'and', - Or: 'or'} - -_UNARY_OP_CLASSES = {UAdd: '+', - USub: '-', - Not: 'not', - Invert: '~'} - -_CMP_OP_CLASSES = {Eq: '==', - Gt: '>', - GtE: '>=', - In: 'in', - Is: 'is', - IsNot: 'is not', - Lt: '<', - LtE: '<=', - NotEq: '!=', - NotIn: 'not in'} - -CONST_NAME_TRANSFORMS = {'None': None, - 'True': True, - 'False': False} - -REDIRECT = {'arguments': 'Arguments', - 'Attribute': 'Getattr', - 'comprehension': 'Comprehension', - 'Call': 'CallFunc', - 'ClassDef': 'Class', - "ListCompFor": 'Comprehension', - "GenExprFor": 'Comprehension', - 'excepthandler': 'ExceptHandler', - 'Expr': 'Discard', - 'FunctionDef': 'Function', - 'GeneratorExp': 'GenExpr', - 'ImportFrom': 'From', - 'keyword': 'Keyword', - 'Repr': 'Backquote', - } -PY3K = sys.version_info >= (3, 0) -PY34 = sys.version_info >= (3, 4) - -def _init_set_doc(node, newnode): - newnode.doc = None - try: - if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str): - newnode.tolineno = node.body[0].lineno - 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 - if hasattr(oldnode, 'lineno'): - newnode.lineno = oldnode.lineno - if hasattr(oldnode, 'col_offset'): - 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 - newnode.set_line_info(newnode.last_child()) # set_line_info accepts None - -def _infer_metaclass(node): - if isinstance(node, Name): - return node.id - elif isinstance(node, Attribute): - return node.attr - -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) - newnode.set_line_info(newnode.last_child()) - 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._metaclass = [''] - self._global_names = [] - self._from_nodes = [] - self._delayed_assattr = [] - self._visit_meths = {} - self._transform = manager.transform - - def visit_module(self, node, modname, package): - """visit a Module node by returning a fresh instance of it""" - newnode = new.Module(modname, None) - newnode.package = package - _lineno_parent(node, newnode, parent=None) - _init_set_doc(node, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.set_line_info(newnode.last_child()) - return 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() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.args = [self.visit(child, newnode) for child in node.args] - self.asscontext = None - newnode.defaults = [self.visit(child, newnode) for child in node.defaults] - newnode.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 and PY34: - vararg = vararg.arg - if kwarg and PY34: - kwarg = kwarg.arg - 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) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_assattr(self, node, parent): - """visit a AssAttr node by returning a fresh instance of it""" - assc, self.asscontext = self.asscontext, None - newnode = new.AssAttr() - _lineno_parent(node, newnode, parent) - newnode.expr = self.visit(node.expr, newnode) - self.asscontext = assc - self._delayed_assattr.append(newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_assert(self, node, parent): - """visit a Assert node by returning a fresh instance of it""" - newnode = new.Assert() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - if node.msg is not None: - newnode.fail = self.visit(node.msg, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_assign(self, node, parent): - """visit a Assign node by returning a fresh instance of it""" - newnode = new.Assign() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None - newnode.value = self.visit(node.value, newnode) - # 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 - elif getattr(newnode.targets[0], 'name', None) == '__metaclass__': - # XXX check more... - self._metaclass[-1] = _infer_metaclass(node.value) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_assname(self, node, parent, node_name=None): - '''visit a node and return a AssName node''' - newnode = new.AssName() - _set_infos(node, newnode, parent) - newnode.name = node_name - self._save_assignment(newnode) - return newnode - - def visit_augassign(self, node, parent): - """visit a AugAssign node by returning a fresh instance of it""" - newnode = new.AugAssign() - _lineno_parent(node, newnode, parent) - newnode.op = _BIN_OP_CLASSES[node.op.__class__] + "=" - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_backquote(self, node, parent): - """visit a Backquote node by returning a fresh instance of it""" - newnode = new.Backquote() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_binop(self, node, parent): - """visit a BinOp node by returning a fresh instance of it""" - newnode = new.BinOp() - _lineno_parent(node, newnode, parent) - newnode.left = self.visit(node.left, newnode) - newnode.right = self.visit(node.right, newnode) - newnode.op = _BIN_OP_CLASSES[node.op.__class__] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_boolop(self, node, parent): - """visit a BoolOp node by returning a fresh instance of it""" - newnode = new.BoolOp() - _lineno_parent(node, newnode, parent) - newnode.values = [self.visit(child, newnode) for child in node.values] - newnode.op = _BOOL_OP_CLASSES[node.op.__class__] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_break(self, node, parent): - """visit a Break node by returning a fresh instance of it""" - newnode = new.Break() - _set_infos(node, newnode, parent) - return newnode - - def visit_callfunc(self, node, parent): - """visit a CallFunc node by returning a fresh instance of it""" - newnode = new.CallFunc() - _lineno_parent(node, newnode, parent) - newnode.func = self.visit(node.func, newnode) - newnode.args = [self.visit(child, newnode) for child in node.args] - if node.starargs is not None: - newnode.starargs = self.visit(node.starargs, newnode) - if node.kwargs is not None: - newnode.kwargs = self.visit(node.kwargs, newnode) - newnode.args.extend(self.visit(child, newnode) for child in node.keywords) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_class(self, node, parent): - """visit a Class node to become astroid""" - self._metaclass.append(self._metaclass[-1]) - newnode = new.Class(node.name, None) - _lineno_parent(node, newnode, parent) - _init_set_doc(node, newnode) - newnode.bases = [self.visit(child, newnode) for child in node.bases] - newnode.body = [self.visit(child, newnode) for child in node.body] - if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6 - newnode.decorators = self.visit_decorators(node, newnode) - newnode.set_line_info(newnode.last_child()) - metaclass = self._metaclass.pop() - if PY3K: - newnode._newstyle = True - else: - if not newnode.bases: - # no base classes, detect new / style old style according to - # current scope - newnode._newstyle = metaclass in ('type', 'ABCMeta') - 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)] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_comprehension(self, node, parent): - """visit a Comprehension node by returning a fresh instance of it""" - newnode = new.Comprehension() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.ifs = [self.visit(child, newnode) for child in node.ifs] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_decorators(self, node, parent): - """visit a Decorators node by returning a fresh instance of it""" - # /!\ node is actually a _ast.Function node while - # parent is a 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_delete(self, node, parent): - """visit a Delete node by returning a fresh instance of it""" - newnode = new.Delete() - _lineno_parent(node, newnode, parent) - self.asscontext = "Del" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_dict(self, node, parent): - """visit a Dict node by returning a fresh instance of it""" - newnode = new.Dict() - _lineno_parent(node, newnode, parent) - newnode.items = [(self.visit(key, newnode), self.visit(value, newnode)) - for key, value in zip(node.keys, node.values)] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_dictcomp(self, node, parent): - """visit a DictComp node by returning a fresh instance of it""" - newnode = new.DictComp() - _lineno_parent(node, newnode, parent) - newnode.key = self.visit(node.key, newnode) - newnode.value = self.visit(node.value, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_discard(self, node, parent): - """visit a Discard node by returning a fresh instance of it""" - newnode = new.Discard() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_ellipsis(self, node, parent): - """visit an Ellipsis node by returning a fresh instance of it""" - newnode = 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_exec(self, node, parent): - """visit an Exec node by returning a fresh instance of it""" - newnode = new.Exec() - _lineno_parent(node, newnode, parent) - newnode.expr = self.visit(node.body, newnode) - if node.globals is not None: - newnode.globals = self.visit(node.globals, newnode) - if node.locals is not None: - newnode.locals = self.visit(node.locals, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_extslice(self, node, parent): - """visit an ExtSlice node by returning a fresh instance of it""" - newnode = new.ExtSlice() - _lineno_parent(node, newnode, parent) - newnode.dims = [self.visit(dim, newnode) for dim in node.dims] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_for(self, node, parent): - """visit a For node by returning a fresh instance of it""" - newnode = new.For() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_from(self, node, parent): - """visit a From node by returning a fresh instance of it""" - names = [(alias.name, alias.asname) for alias in node.names] - newnode = new.From(node.module or '', names, node.level 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) - newnode.set_line_info(newnode.last_child()) - 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_getattr(self, node, parent): - """visit a Getattr node by returning a fresh instance of it""" - if self.asscontext == "Del": - # FIXME : maybe we should reintroduce and visit_delattr ? - # for instance, deactivating asscontext - newnode = new.DelAttr() - elif self.asscontext == "Ass": - # FIXME : maybe we should call visit_assattr ? - newnode = new.AssAttr() - self._delayed_assattr.append(newnode) - else: - newnode = new.Getattr() - _lineno_parent(node, newnode, parent) - asscontext, self.asscontext = self.asscontext, None - newnode.expr = self.visit(node.value, newnode) - self.asscontext = asscontext - newnode.attrname = node.attr - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_global(self, node, parent): - """visit an Global node to become 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_ifexp(self, node, parent): - """visit a IfExp node by returning a fresh instance of it""" - newnode = new.IfExp() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = self.visit(node.body, newnode) - newnode.orelse = self.visit(node.orelse, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_import(self, node, parent): - """visit a Import node by returning a fresh instance of it""" - newnode = new.Import() - _set_infos(node, newnode, parent) - newnode.names = [(alias.name, alias.asname) for alias in node.names] - # save import names in parent's locals: - for (name, asname) in newnode.names: - name = asname or name - newnode.parent.set_local(name.split('.')[0], newnode) - return newnode - - def visit_index(self, node, parent): - """visit a Index node by returning a fresh instance of it""" - newnode = new.Index() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_keyword(self, node, parent): - """visit a Keyword node by returning a fresh instance of it""" - newnode = new.Keyword() - _lineno_parent(node, newnode, parent) - newnode.arg = node.arg - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_lambda(self, node, parent): - """visit a Lambda node by returning a fresh instance of it""" - newnode = new.Lambda() - _lineno_parent(node, newnode, parent) - newnode.args = self.visit(node.args, newnode) - newnode.body = self.visit(node.body, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_list(self, node, parent): - """visit a List node by returning a fresh instance of it""" - newnode = new.List() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_listcomp(self, node, parent): - """visit a ListComp node by returning a fresh instance of it""" - newnode = new.ListComp() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_name(self, node, parent): - """visit a Name node by returning a fresh instance of it""" - # True and False can be assigned to something in py2x, so we have to - # check first the asscontext - if self.asscontext == "Del": - newnode = new.DelName() - elif self.asscontext is not None: # Ass - assert self.asscontext == "Ass" - newnode = new.AssName() - elif node.id in CONST_NAME_TRANSFORMS: - newnode = new.Const(CONST_NAME_TRANSFORMS[node.id]) - _set_infos(node, newnode, parent) - return newnode - else: - newnode = new.Name() - _lineno_parent(node, newnode, parent) - newnode.name = node.id - # XXX REMOVE me : - if self.asscontext in ('Del', 'Ass'): # 'Aug' ?? - self._save_assignment(newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_bytes(self, node, parent): - """visit a Bytes node by returning a fresh instance of Const""" - newnode = new.Const(node.s) - _set_infos(node, newnode, parent) - return newnode - - def visit_num(self, node, parent): - """visit a Num node by returning a fresh instance of Const""" - newnode = new.Const(node.n) - _set_infos(node, newnode, parent) - return newnode - - def visit_pass(self, node, parent): - """visit a Pass node by returning a fresh instance of it""" - newnode = 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = new.Raise() - _lineno_parent(node, newnode, parent) - if node.type is not None: - newnode.exc = self.visit(node.type, newnode) - if node.inst is not None: - newnode.inst = self.visit(node.inst, newnode) - if node.tback is not None: - newnode.tback = self.visit(node.tback, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_return(self, node, parent): - """visit a Return node by returning a fresh instance of it""" - newnode = new.Return() - _lineno_parent(node, newnode, parent) - if node.value is not None: - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_set(self, node, parent): - """visit a 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_setcomp(self, node, parent): - """visit a SetComp node by returning a fresh instance of it""" - newnode = new.SetComp() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_slice(self, node, parent): - """visit a Slice node by returning a fresh instance of it""" - newnode = new.Slice() - _lineno_parent(node, newnode, parent) - if node.lower is not None: - newnode.lower = self.visit(node.lower, newnode) - if node.upper is not None: - newnode.upper = self.visit(node.upper, newnode) - if node.step is not None: - newnode.step = self.visit(node.step, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_subscript(self, node, parent): - """visit a Subscript node by returning a fresh instance of it""" - newnode = new.Subscript() - _lineno_parent(node, newnode, parent) - subcontext, self.asscontext = self.asscontext, None - newnode.value = self.visit(node.value, newnode) - newnode.slice = self.visit(node.slice, newnode) - self.asscontext = subcontext - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_tryexcept(self, node, parent): - """visit a TryExcept node by returning a fresh instance of it""" - newnode = new.TryExcept() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.handlers = [self.visit(child, newnode) for child in node.handlers] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_tryfinally(self, node, parent): - """visit a TryFinally node by returning a fresh instance of it""" - newnode = new.TryFinally() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_tuple(self, node, parent): - """visit a Tuple node by returning a fresh instance of it""" - newnode = new.Tuple() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_unaryop(self, node, parent): - """visit a UnaryOp node by returning a fresh instance of it""" - newnode = new.UnaryOp() - _lineno_parent(node, newnode, parent) - newnode.operand = self.visit(node.operand, newnode) - newnode.op = _UNARY_OP_CLASSES[node.op.__class__] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_while(self, node, parent): - """visit a While node by returning a fresh instance of it""" - newnode = new.While() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_with(self, node, parent): - 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_yield(self, node, parent): - """visit a Yield node by returning a fresh instance of it""" - 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] - 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] - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_nonlocal(self, node, parent): - """visit a Nonlocal node and return a new instance of it""" - newnode = new.Nonlocal(node.names) - _set_infos(node, newnode, parent) - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = new.Raise() - _lineno_parent(node, newnode, parent) - # no traceback; anyway it is not used in Pylint - if node.exc is not None: - newnode.exc = self.visit(node.exc, newnode) - if node.cause is not None: - newnode.cause = self.visit(node.cause, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - def visit_starred(self, node, parent): - """visit a Starred node and return a new instance of it""" - newnode = new.Starred() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - - 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] - excnode.set_line_info(excnode.last_child()) - 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] - newnode.set_line_info(newnode.last_child()) - 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] - newnode.set_line_info(newnode.last_child()) - 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) - 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/pylama/lint/pylama_pylint/astroid/scoped_nodes.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/scoped_nodes.py deleted file mode 100644 index 889baa0e..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/scoped_nodes.py +++ /dev/null @@ -1,1118 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module contains the classes for "scoped" node, i.e. which are opening a -new local scope in the language definition : Module, Class, Function (and -Lambda, GenExpr, DictComp and SetComp to some extent). -""" -from __future__ import with_statement - -__doctype__ = "restructuredtext en" - -import sys -from itertools import chain -try: - from io import BytesIO -except ImportError: - from cStringIO import StringIO as BytesIO - -from logilab.common.compat import builtins -from logilab.common.decorators import cached, cachedproperty - -from astroid.exceptions import NotFoundError, \ - AstroidBuildingException, InferenceError -from astroid.node_classes import Const, DelName, DelAttr, \ - Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ - LookupMixIn, const_factory as cf, unpack_infer, Name -from astroid.bases import NodeNG, InferenceContext, Instance,\ - YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \ - BUILTINS -from astroid.mixins import FilterStmtsMixin -from astroid.bases import Statement -from astroid.manager import AstroidManager - - -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 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 zip(self.keys(), self.values()) - - - def __contains__(self, name): - return name in self.locals - has_key = __contains__ - -# Module ##################################################################### - -class Module(LocalsDictNodeNG): - _astroid_fields = ('body',) - - fromlineno = 0 - lineno = 0 - - # attributes below are set by the builder module or by raw factories - - # the file from which as been extracted the astroid representation. It may - # be None if the representation has been built from a built-in module - file = None - # Alternatively, if built from a string/bytes, this can be set - file_bytes = None - # encoding of python source file, so we can get unicode out of it (python2 - # only) - file_encoding = None - # the module name - name = None - # boolean for astroid built from source (i.e. ast) - pure_python = None - # boolean for package module - package = None - # dictionary of globals with name as key and node defining the global - # as value - globals = None - - # Future imports - future_imports = None - - # names of python special attributes (handled by getattr impl.) - special_attributes = set(('__name__', '__doc__', '__file__', '__path__', - '__dict__')) - # names of module attributes available through the global scope - scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) - - def __init__(self, name, doc, pure_python=True): - self.name = name - self.doc = doc - self.pure_python = pure_python - self.locals = self.globals = {} - self.body = [] - self.future_imports = set() - - @property - def file_stream(self): - if self.file_bytes is not None: - return BytesIO(self.file_bytes) - if self.file is not None: - return open(self.file, 'rb') - return None - - def block_range(self, lineno): - """return block line numbers. - - start from the beginning whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def scope_lookup(self, node, name, offset=0): - if name in self.scope_attrs and not name in self.locals: - try: - return self, self.getattr(name) - except NotFoundError: - return self, () - return self._scope_lookup(node, name, offset) - - def pytype(self): - return '%s.module' % BUILTINS - - def display_type(self): - return 'Module' - - def getattr(self, name, context=None, ignore_locals=False): - if name in self.special_attributes: - if name == '__file__': - return [cf(self.file)] + self.locals.get(name, []) - if name == '__path__' and self.package: - return [List()] + self.locals.get(name, []) - return std_special_attributes(self, name) - if not ignore_locals and name in self.locals: - return self.locals[name] - if self.package: - try: - return [self.import_module(name, relative_only=True)] - except AstroidBuildingException: - raise NotFoundError(name) - except SyntaxError: - raise NotFoundError(name) - except Exception:# XXX pylint tests never pass here; do we need it? - import traceback - traceback.print_exc() - raise NotFoundError(name) - getattr = remove_nodes(getattr, DelName) - - def igetattr(self, name, context=None): - """inferred getattr""" - # set lookup name since this is necessary to infer on import nodes for - # instance - 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): - def absolute_import_activated(self): - for stmt in self.locals.get('absolute_import', ()): - if isinstance(stmt, From) and stmt.modname == '__future__': - return True - return False - else: - absolute_import_activated = lambda self: True - - def import_module(self, modname, relative_only=False, level=None): - """import the given module considering self as context""" - 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 - # However, we can not analyse dynamically constructed __all__ - try: - all = self['__all__'] - except KeyError: - return [name for name in self.keys() if not name.startswith('_')] - try: - explicit = all.assigned_stmts().next() - except InferenceError: - return [name for name in self.keys() if not name.startswith('_')] - except AttributeError: - # not an assignment node - # XXX infer? - return [name for name in self.keys() if not name.startswith('_')] - try: - # should be a Tuple/List of constant string / 1 string not allowed - return [const.value for const in explicit.elts] - except AttributeError: - return [name for name in self.keys() if not name.startswith('_')] - - -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 _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 not isinstance(node, Name): - continue - try: - for infered in node.infer(): - if not isinstance(infered, Class): - continue - for ancestor in infered.ancestors(): - if isinstance(ancestor, Class): - if (ancestor.name == 'classmethod' and - ancestor.root().name == BUILTINS): - return 'classmethod' - elif (ancestor.name == 'staticmethod' and - ancestor.root().name == BUILTINS): - return 'staticmethod' - except InferenceError: - pass - return self._type - - -class Lambda(LocalsDictNodeNG, FilterStmtsMixin): - _astroid_fields = ('args', 'body',) - name = '' - - # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' - type = 'function' - - def __init__(self): - self.locals = {} - self.args = [] - self.body = [] - - def pytype(self): - if 'method' in self.type: - return '%s.instancemethod' % BUILTINS - return '%s.function' % BUILTINS - - def display_type(self): - if 'method' in self.type: - return 'Method' - return 'Function' - - def callable(self): - return True - - def argnames(self): - """return a list of argument names""" - if self.args.args: # maybe None with builtin functions - names = _rec_get_names(self.args.args) - else: - names = [] - if self.args.vararg: - names.append(self.args.vararg) - if self.args.kwarg: - names.append(self.args.kwarg) - return names - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - return self.body.infer(context) - - def scope_lookup(self, node, name, offset=0): - if node in self.args.defaults or node in self.args.kw_defaults: - frame = self.parent.frame() - # line offset to avoid that def func(f=func) resolve the default - # value to the defined function - offset = -1 - else: - # check this is not used in function decorators - frame = self - return frame._scope_lookup(node, name, offset) - - -class Function(Statement, Lambda): - _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.decorators = None - self.name = name - self.doc = doc - self.extra_decorators = [] - self.instance_attrs = {} - - def set_line_info(self, lastchild): - self.fromlineno = self.lineno - # lineno is the line number of the first decorator, we want the def statement lineno - if self.decorators is not None: - self.fromlineno += sum(node.tolineno - node.lineno + 1 - for node in self.decorators.nodes) - if self.args.fromlineno < self.fromlineno: - self.args.fromlineno = self.fromlineno - self.tolineno = lastchild.tolineno - self.blockstart_tolineno = 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 = node.infer().next() - 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 - try: - return self.nodes_of_class((Yield, YieldFrom), - skip_klass=(Function, Lambda)).next() - except StopIteration: - return False - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - if self.is_generator(): - yield Generator() - return - returns = self.nodes_of_class(Return, skip_klass=Function) - for returnnode in returns: - if returnnode.value is None: - yield Const(None) - else: - try: - for infered in returnnode.value.infer(context): - yield infered - except InferenceError: - yield YES - - -def _rec_get_names(args, names=None): - """return a list of all argument names""" - if names is None: - names = [] - for arg in args: - if isinstance(arg, Tuple): - _rec_get_names(arg.elts, names) - else: - names.append(arg.name) - return names - - -# Class ###################################################################### - - -def _is_metaclass(klass): - """ Return if the given class can be - used as a metaclass. - """ - if klass.name == 'type': - return True - for base in klass.bases: - try: - for baseobj in base.infer(): - 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): - 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) - # print >> sys.stderr, '_class_type', repr(klass) - for base in klass.ancestors(recurs=False): - name = _class_type(base, ancestors) - if name != 'class': - if name == 'metaclass' and not _is_metaclass(klass): - # don't propagate it if the current class - # can't be a metaclass - continue - klass._type = base.type - break - if klass._type is None: - klass._type = 'class' - return klass._type - -def _iface_hdlr(iface_node): - """a handler function used by interfaces to handle suspicious - interface nodes - """ - return True - - -class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): - - # some of the attributes below are set by the builder module or - # by a raw factories - - # a dictionary of class instances attributes - _astroid_fields = ('decorators', 'bases', 'body') # name - - decorators = None - special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', - '__bases__', '__mro__', '__subclasses__')) - blockstart_tolineno = None - - _type = None - type = property(_class_type, - doc="class'type, possible values are 'class' | " - "'metaclass' | 'interface' | 'exception'") - - def __init__(self, name, doc): - self.instance_attrs = {} - self.locals = {} - self.bases = [] - self.body = [] - self.name = name - self.doc = doc - - def _newstyle_impl(self, context=None): - if context is None: - context = InferenceContext() - if self._newstyle is not None: - return self._newstyle - for base in self.ancestors(recurs=False, context=context): - if base._newstyle_impl(context): - self._newstyle = True - break - 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") - - def set_line_info(self, lastchild): - self.fromlineno = self.lineno - self.blockstart_tolineno = self.bases and self.bases[-1].tolineno or self.fromlineno - if lastchild is not None: - self.tolineno = lastchild.tolineno - # else this is a class with only a docstring, then tolineno is (should be) already ok - - 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 infer_call_result(self, caller, context=None): - """infer what a class is returning when called""" - yield Instance(self) - - def scope_lookup(self, node, name, offset=0): - if node in self.bases: - frame = self.parent.frame() - # line offset to avoid that class A(A) resolve the ancestor to - # the defined class - offset = -1 - else: - frame = self - return frame._scope_lookup(node, name, offset) - - # list of parent class as a list of string (i.e. names as they appear - # in the class definition) XXX bw compat - def basenames(self): - return [bnode.as_string() for bnode in self.bases] - basenames = property(basenames) - - def ancestors(self, recurs=True, context=None): - """return an iterator on the node base classes in a prefixed - depth first order - - :param recurs: - boolean indicating if it should recurse or return direct - ancestors only - """ - # FIXME: should be possible to choose the resolution order - # XXX inference make infinite loops possible here (see BaseTransformer - # manipulation in the builder module for instance) - yielded = set([self]) - if context is None: - context = InferenceContext() - 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 baseobj in yielded: - continue # cf xxx above - yielded.add(baseobj) - yield baseobj - if recurs: - for grandpa in baseobj.ancestors(True, context): - if grandpa in yielded: - continue # cf xxx above - yielded.add(grandpa) - yield grandpa - except InferenceError: - # XXX log error ? - continue - - def local_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their locals - """ - for astroid in self.ancestors(context=context): - if name in astroid: - yield astroid - - def instance_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their instance attribute dictionary - """ - for astroid in self.ancestors(context=context): - if name in astroid.instance_attrs: - yield astroid - - def has_base(self, node): - return node in self.bases - - def local_attr(self, name, context=None): - """return the list of assign node associated to name in this class - locals or in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - try: - return self.locals[name] - except KeyError: - # get if from the first parent implementing it if any - for class_node in self.local_attr_ancestors(name, context): - return class_node.locals[name] - raise NotFoundError(name) - local_attr = remove_nodes(local_attr, DelAttr) - - def instance_attr(self, name, context=None): - """return the astroid nodes associated to name in this class instance - attributes dictionary and in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - values = 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 by - having a ``__metaclass__`` class attribute. - """ - 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 - - try: - meta = self.getattr('__metaclass__')[0] - except NotFoundError: - return - try: - infered = meta.infer().next() - 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 diff --git a/pymode/libs/pylama/lint/pylama_pylint/astroid/utils.py b/pymode/libs/pylama/lint/pylama_pylint/astroid/utils.py deleted file mode 100644 index 1cd0e778..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/astroid/utils.py +++ /dev/null @@ -1,236 +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 -""" - -__docformat__ = "restructuredtext en" - -from astroid.exceptions import AstroidBuildingException -from astroid.builder import parse - - -class ASTWalker: - """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 name, local_node in node.items(): - 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/pylama/lint/pylama_pylint/logilab/common/__init__.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/__init__.py deleted file mode 100644 index 8d063e2c..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/__init__.py +++ /dev/null @@ -1,171 +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" -from logilab.common.__pkginfo__ import version as __version__ - -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 __nonzero__(self): - return False - -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']] - """ - 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/pylama/lint/pylama_pylint/logilab/common/__pkginfo__.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/__pkginfo__.py deleted file mode 100644 index d3be5552..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/__pkginfo__.py +++ /dev/null @@ -1,53 +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 . -"""logilab.common packaging information""" -__docformat__ = "restructuredtext en" -import sys -import os - -distname = 'logilab-common' -modname = 'common' -subpackage_of = 'logilab' -subpackage_master = True - -numversion = (0, 61, 0) -version = '.'.join([str(num) for num in numversion]) - -license = 'LGPL' # 2.1 or later -description = "collection of low-level Python packages and modules used by Logilab projects" -web = "http://www.logilab.org/project/%s" % distname -mailinglist = "mailto://python-projects@lists.logilab.org" -author = "Logilab" -author_email = "contact@logilab.fr" - - -from os.path import join -scripts = [join('bin', 'pytest')] -include_dirs = [join('test', 'data')] - -install_requires = [] -if sys.version_info < (2, 7): - install_requires.append('unittest2 >= 0.5.1') -if os.name == 'nt': - install_requires.append('colorama') - -classifiers = ["Topic :: Utilities", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 3", - ] diff --git a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/changelog.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/changelog.py deleted file mode 100644 index 74f51241..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/changelog.py +++ /dev/null @@ -1,236 +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 - -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, basestring): - 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, 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/pylama/lint/pylama_pylint/logilab/common/compat.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/compat.py deleted file mode 100644 index 8983ece9..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/compat.py +++ /dev/null @@ -1,243 +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` -""" - -from __future__ import generators - -__docformat__ = "restructuredtext en" - -import os -import sys -import types -from warnings import warn - -import __builtin__ as builtins # 2to3 will tranform '__builtin__' to '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) - -# XXX callable built-in seems back in all python versions -try: - callable = builtins.callable -except AttributeError: - from collections import Callable - def callable(something): - return isinstance(something, Callable) - del Callable - -# 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 - -if sys.version_info < (3, 0): - raw_input = raw_input -else: - raw_input = input - -# 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 - -# Where do pickles come from? -try: - import cPickle as pickle -except ImportError: - import pickle - -from logilab.common.deprecation import deprecated - -from itertools import izip, chain, imap -if sys.version_info < (3, 0):# 2to3 will remove the imports - izip = deprecated('izip exists in itertools since py2.3')(izip) - imap = deprecated('imap exists in itertools since py2.3')(imap) -chain = deprecated('chain exists in itertools since py2.3')(chain) - -sum = deprecated('sum exists in builtins since py2.3')(sum) -enumerate = deprecated('enumerate exists in builtins since py2.3')(enumerate) -frozenset = deprecated('frozenset exists in builtins since py2.4')(frozenset) -reversed = deprecated('reversed exists in builtins since py2.4')(reversed) -sorted = deprecated('sorted exists in builtins since py2.4')(sorted) -max = deprecated('max exists in builtins since py2.4')(max) - - -# Python2.5 builtins -try: - any = any - all = all -except NameError: - def any(iterable): - """any(iterable) -> bool - - Return True if bool(x) is True for any x in the iterable. - """ - for elt in iterable: - if elt: - return True - return False - - def all(iterable): - """all(iterable) -> bool - - Return True if bool(x) is True for all values x in the iterable. - """ - for elt in iterable: - if not elt: - return False - return True - - -# Python2.5 subprocess added functions and exceptions -try: - from subprocess import Popen -except ImportError: - # gae or python < 2.3 - - class CalledProcessError(Exception): - """This exception is raised when a process run by check_call() returns - a non-zero exit status. The exit status will be stored in the - returncode attribute.""" - def __init__(self, returncode, cmd): - self.returncode = returncode - self.cmd = cmd - def __str__(self): - return "Command '%s' returned non-zero exit status %d" % (self.cmd, - self.returncode) - - def call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - """ - # workaround: subprocess.Popen(cmd, stdout=sys.stdout) fails - # see http://bugs.python.org/issue1531862 - if "stdout" in kwargs: - fileno = kwargs.get("stdout").fileno() - del kwargs['stdout'] - return Popen(stdout=os.dup(fileno), *popenargs, **kwargs).wait() - return Popen(*popenargs, **kwargs).wait() - - def check_call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete. If - the exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - """ - retcode = call(*popenargs, **kwargs) - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - if retcode: - raise CalledProcessError(retcode, cmd) - return retcode - -try: - from os.path import relpath -except ImportError: # python < 2.6 - from os.path import curdir, abspath, sep, commonprefix, pardir, join - def relpath(path, start=curdir): - """Return a relative version of a path""" - - if not path: - raise ValueError("no path specified") - - start_list = abspath(start).split(sep) - path_list = abspath(path).split(sep) - - # Work out how much of the filepath is shared by start and path. - i = len(commonprefix([start_list, path_list])) - - rel_list = [pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return curdir - return join(*rel_list) - - -# XXX don't know why tests don't pass if I don't do that : -_real_set, set = set, deprecated('set exists in builtins since py2.4')(set) -if (2, 5) <= sys.version_info[:2]: - InheritableSet = _real_set -else: - class InheritableSet(_real_set): - """hacked resolving inheritancy issue from old style class in 2.4""" - def __new__(cls, *args, **kwargs): - if args: - new_args = (args[0], ) - else: - new_args = () - obj = _real_set.__new__(cls, *new_args) - obj.__init__(*args, **kwargs) - return obj - -# XXX shouldn't we remove this and just let 2to3 do his job ? -# range or xrange? -try: - range = xrange -except NameError: - range = range - -# ConfigParser was renamed to the more-standard configparser -try: - import configparser -except ImportError: - import ConfigParser as configparser - -try: - import json -except ImportError: - try: - import simplejson as json - except ImportError: - json = None diff --git a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/configuration.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/configuration.py deleted file mode 100644 index fa93a056..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/configuration.py +++ /dev/null @@ -1,1094 +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 - >>> -""" -__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 ConfigParser import ConfigParser, NoOptionError, NoSectionError, \ - DuplicateSectionError -from warnings import warn - -from logilab.common.compat import callable, raw_input, 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 = raw_input(question).strip() - return value or None - -def _make_input_function(opttype): - def input_validator(optdict, question): - while True: - value = raw_input(question) - if not value.strip(): - return None - try: - return _call_validator(opttype, optdict, None, value) - except optik_ext.OptionValueError, 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, (str, unicode)) 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 >> stream, _encode(comment(doc), encoding) - print >> stream, '[%s]' % section - 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 >> stream - print >> stream, _encode(help, encoding) - else: - print >> stream - if value is None: - print >> stream, '#%s=' % optname - else: - value = _encode(value, encoding).strip() - print >> stream, '%s=%s' % (optname, value) - -format_section = ini_format_section - -def rest_format_section(stream, section, options, encoding=None, doc=None): - """format an options section using the INI format""" - encoding = _get_encoding(encoding, stream) - if section: - print >> stream, '%s\n%s' % (section, "'"*len(section)) - if doc: - print >> stream, _encode(normalize_text(doc, line_len=79, indent=''), - encoding) - print >> stream - for optname, optdict, value in options: - help = optdict.get('help') - print >> stream, ':%s:' % optname - if help: - help = normalize_text(help, line_len=79, indent=' ') - print >> stream, _encode(help, encoding) - if value: - value = _encode(format_option_value(optdict, value), encoding) - print >> stream, '' - print >> stream, ' Default: ``%s``' % value.replace("`` ", "```` ``") - -# 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 = 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 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 >> stream, '\n' - 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 >> sys.stderr, msg - 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 provider in self.options_providers: - for section, option, optdict in provider.all_options(): - try: - value = parser.get(section, option) - provider.set_option(option, value, optdict=optdict) - except (NoSectionError, NoOptionError), ex: - 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/pylama/lint/pylama_pylint/logilab/common/decorators.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/decorators.py deleted file mode 100644 index 34bbd3a9..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/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. """ -__docformat__ = "restructuredtext en" - -import sys -import types -from time import clock, time - -from logilab.common.compat import callable, method_type - -# XXX rewrite so we can use the decorator syntax when keyarg has to be specified - -def _is_generator_function(callableobj): - return callableobj.func_code.co_flags & 0x20 - -class cached_decorator(object): - def __init__(self, cacheattr=None, keyarg=None): - self.cacheattr = cacheattr - self.keyarg = keyarg - def __call__(self, callableobj=None): - assert not _is_generator_function(callableobj), \ - 'cannot cache generator function: %s' % callableobj - if callableobj.func_code.co_argcount == 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__ - wrapped.func_name = self.callable.func_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/pylama/lint/pylama_pylint/logilab/common/deprecation.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/deprecation.py deleted file mode 100644 index 02e4edbb..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/deprecation.py +++ /dev/null @@ -1,188 +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.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 - class DeprecatedClass(new_class): - """FIXME: There might be a better way to handle old/new-style class - """ - def __init__(self, *args, **kwargs): - self.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/pylama/lint/pylama_pylint/logilab/common/graph.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/graph.py deleted file mode 100644 index d62e8c09..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/graph.py +++ /dev/null @@ -1,276 +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 - -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 additionnal_param.iteritems(): - 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 - if mapfile: - subprocess.call([self.renderer, '-Tcmapx', '-o', mapfile, '-T', target, dot_sourcepath, '-o', outputfile], - shell=use_shell) - else: - subprocess.call([self.renderer, '-T', target, - dot_sourcepath, '-o', outputfile], - shell=use_shell) - os.unlink(dot_sourcepath) - return outputfile - - def emit(self, line): - """Adds to final output.""" - self.lines.append(line) - - def emit_edge(self, name1, name2, **props): - """emit an edge from to . - edge properties: see http://www.graphviz.org/doc/info/attrs.html - """ - attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] - n_from, n_to = normalize_node_id(name1), normalize_node_id(name2) - self.emit('%s -> %s [%s];' % (n_from, n_to, ', '.join(sorted(attrs))) ) - - def emit_node(self, name, **props): - """emit a node with given properties. - node properties: see http://www.graphviz.org/doc/info/attrs.html - """ - attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] - self.emit('%s [%s];' % (normalize_node_id(name), ', '.join(sorted(attrs)))) - -def normalize_node_id(nid): - """Returns a suitable DOT node id for `nid`.""" - return '"%s"' % nid - -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/pylama/lint/pylama_pylint/logilab/common/interface.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/interface.py deleted file mode 100644 index 3ea4ab7e..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/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/pylama/lint/pylama_pylint/logilab/common/modutils.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/modutils.py deleted file mode 100644 index 27568412..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/modutils.py +++ /dev/null @@ -1,695 +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 has key -""" -from __future__ import with_statement - -__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 - -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=1) -# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to -# non-valid path, see https://bugs.pypy.org/issue1164 -except DistutilsPlatformError: - STD_LIB_DIR = '//' - -EXT_LIB_DIR = get_python_lib() - -BUILTIN_MODULES = dict(zip(sys.builtin_module_names, - [1]*len(sys.builtin_module_names))) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - -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, 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=1): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=1): - """Load a python module from its splitted name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package splitted on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = find_module(part, path) - module = load_module(curname, mp_file, mp_filename, mp_desc) - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, '__file__', '') - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):]) ) - path = [dirname( _file )] - prevmodule = module - return module - - -def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def _check_init(path, mod_path): - """check there are some __init__.py all along the way""" - for part in mod_path: - path = join(path, part) - if not _has_init(path): - return False - return True - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding splitted module's name - (i.e name of a module or package splitted on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package splitted in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding splitted module's name - """ - base = splitext(abspath(filename))[0] - if extrapath is not None: - for path_ in extrapath: - path = abspath(path_) - if path and base[:len(path)] == path: - submodpath = [pkg for pkg in base[len(path):].split(os.sep) - if pkg] - if _check_init(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - for path in sys.path: - path = abspath(path) - if path and base.startswith(path): - modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] - if _check_init(path, modpath[:-1]): - return modpath - raise ImportError('Unable to find module for %s in %s' % ( - filename, ', \n'.join(sys.path))) - - - -def file_from_modpath(modpath, path=None, context_file=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file, giving priority to source file over precompiled - file if it exists - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.') - (this means explicit relative imports that start with dots have - empty strings in this list!) - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the path to the module's file or None if it's an integrated - builtin module such as 'sys' - """ - if context_file is not None: - context = dirname(context_file) - else: - context = context_file - if modpath[0] == 'xml': - # handle _xmlplus - try: - return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context) - except ImportError: - return _file_from_modpath(modpath, path, context) - elif modpath == ['os', 'path']: - # FIXME: currently ignoring search_path... - return os.path.__file__ - return _file_from_modpath(modpath, path, context) - - - -def get_module_part(dotted_name, context_file=None): - """given a dotted name return the module part of the name : - - >>> get_module_part('logilab.common.modutils.get_module_part') - 'logilab.common.modutils' - - :type dotted_name: str - :param dotted_name: full name of the identifier we are interested in - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the module part of the name or None if we have not been able at - all to import the given name - - XXX: deprecated, since it doesn't handle package precedence over module - (see #10066) - """ - # os.path trick - if dotted_name.startswith('os.path'): - return 'os.path' - parts = dotted_name.split('.') - if context_file is not None: - # first check for builtin module which won't be considered latter - # in that case (path != None) - if parts[0] in BUILTIN_MODULES: - if len(parts) > 2: - raise ImportError(dotted_name) - return parts[0] - # don't use += or insert, we want a new list to be created ! - path = None - starti = 0 - if parts[0] == '': - assert context_file is not None, \ - 'explicit relative import, but no context_file?' - path = [] # prevent resolving the import non-relatively - starti = 1 - while parts[starti] == '': # for all further dots: change context - starti += 1 - context_file = dirname(context_file) - for i in range(starti, len(parts)): - try: - file_from_modpath(parts[starti:i+1], - path=path, context_file=context_file) - except ImportError: - if not i >= max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_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`""" - for modname, module in sys.modules.items(): - modfile = getattr(module, '__file__', None) - if modfile: - for directory in directories: - if modfile.startswith(directory): - del sys.modules[modname] - break - - -def is_python_source(filename): - """ - rtype: bool - return: True if the filename is a python source file - """ - return splitext(filename)[1][1:] in PY_SOURCE_EXTS - - - -def is_standard_module(modname, std_path=(STD_LIB_DIR,)): - """try to guess if a module is a standard python module (by default, - see `std_path` parameter's description) - - :type modname: str - :param modname: name of the module we are interested in - - :type std_path: list(str) or tuple(str) - :param std_path: list of path considered has standard - - - :rtype: bool - :return: - true if the module: - - is located on the path listed in one of the directory in `std_path` - - is a built-in module - """ - modname = modname.split('.')[0] - try: - filename = file_from_modpath([modname]) - except ImportError, ex: - # import failed, i'm probably not so wrong by supposing it's - # not standard... - return 0 - # modules which are not living in a file are considered standard - # (sys and __builtin__ for instance) - if filename is None: - return 1 - filename = abspath(filename) - if filename.startswith(EXT_LIB_DIR): - return 0 - for path in std_path: - if filename.startswith(abspath(path)): - return 1 - 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 _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 len(modpath) > 1: - # setuptools has added into sys.modules a module object with proper - # __path__, get back information from there - module = sys.modules[modpath.pop(0)] - path = module.__path__ - imported = [] - while modpath: - modname = modpath[0] - # take care to changes in find_module implementation wrt builtin modules - # - # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) - # >>> imp.find_module('posix') - # (None, 'posix', ('', '', 6)) - # - # Python 3.3.1 (default, Apr 26 2013, 12:08:46) - # >>> imp.find_module('posix') - # (None, None, ('', '', 6)) - try: - _, mp_filename, mp_desc = find_module(modname, path) - except ImportError: - if checkeggs: - return _search_zip(modpath, pic)[:2] - raise - else: - if checkeggs and mp_filename: - fullabspath = [abspath(x) for x in _path] - try: - pathindex = fullabspath.index(dirname(abspath(mp_filename))) - emtype, emp_filename, zippath = _search_zip(modpath, pic) - if pathindex > _path.index(zippath): - # an egg takes priority - return emtype, emp_filename - except ValueError: - # XXX not in _path - pass - except ImportError: - pass - checkeggs = False - imported.append(modpath.pop(0)) - mtype = mp_desc[2] - if modpath: - if mtype != PKG_DIRECTORY: - raise ImportError('No module %s in %s' % ('.'.join(modpath), - '.'.join(imported))) - # XXX guess if package is using pkgutil.extend_path by looking for - # those keywords in the first four Kbytes - try: - with open(join(mp_filename, '__init__.py')) 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/pylama/lint/pylama_pylint/logilab/common/optik_ext.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/optik_ext.py deleted file mode 100644 index 49d685b1..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/optik_ext.py +++ /dev/null @@ -1,391 +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) -""" -__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 - -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): - from logilab.common.textutils import TIME_UNITS, apply_units - if isinstance(value, (int, long, float)): - return value - return apply_units(value, TIME_UNITS) - -def check_bytes(option, opt, value): - from logilab.common.textutils import BYTE_UNITS, apply_units - if hasattr(value, '__int__'): - return value - return apply_units(value, BYTE_UNITS) - -import types - -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 type(self.choices) not in (types.TupleType, types.ListType): - 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 >> stream, formatter.format_head(optparser, pkginfo, section) - print >> stream, optparser.format_option_help(formatter) - print >> stream, formatter.format_tail(pkginfo) - - -__all__ = ('OptionParser', 'Option', 'OptionGroup', 'OptionValueError', - 'Values') diff --git a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/textutils.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/textutils.py deleted file mode 100644 index f55c0040..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/textutils.py +++ /dev/null @@ -1,534 +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. - - >>> text_to_dict('''multiple=1 - ... multiple= 2 - ... single =3 - ... ''') - {'single': '3', '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/pylama/lint/pylama_pylint/logilab/common/tree.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/tree.py deleted file mode 100644 index 885eb0fa..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/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/pylama/lint/pylama_pylint/logilab/common/ureports/__init__.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/__init__.py deleted file mode 100644 index dcffcfa3..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/__init__.py +++ /dev/null @@ -1,174 +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. -""" -from __future__ import generators -__docformat__ = "restructuredtext en" - -import sys -from cStringIO import StringIO -from StringIO import StringIO as UStringIO - -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 ' '.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='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('#'+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=''): - """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('') - 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=''): - 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 = UStringIO() - 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/pylama/lint/pylama_pylint/logilab/common/ureports/docbook_writer.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/docbook_writer.py deleted file mode 100644 index e75cbe09..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/docbook_writer.py +++ /dev/null @@ -1,139 +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""" -from __future__ import generators -__docformat__ = "restructuredtext en" - -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/pylama/lint/pylama_pylint/logilab/common/ureports/html_writer.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/html_writer.py deleted file mode 100644 index 1d095034..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/html_writer.py +++ /dev/null @@ -1,131 +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 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 = '' - klass = getattr(layout, 'klass', None) - if klass: - attrs += ' class="%s"' % klass - nid = getattr(layout, 'id', None) - if nid: - attrs += ' 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('') - self.writeln('') - - def end_format(self, layout): - """finished to format a layout""" - if self.snippet is None: - self.writeln('') - self.writeln('') - - - def visit_section(self, layout): - """display a section as html, using div + h[section level]""" - self.section += 1 - self.writeln('' % self.handle_attrs(layout)) - self.format_children(layout) - self.writeln('') - self.section -= 1 - - def visit_title(self, layout): - """display a title using """ - self.write('' % (self.section, self.handle_attrs(layout))) - self.format_children(layout) - self.writeln('' % self.section) - - def visit_table(self, layout): - """display a table as html""" - self.writeln('' % 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('') - elif i+1 == len(table_content) and layout.rrheaders: - self.writeln('') - else: - self.writeln('' % (i%2 and 'even' or 'odd')) - for j in range(len(row)): - cell = row[j] or ' ' - 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('%s' % cell) - else: - self.writeln('%s' % cell) - self.writeln('') - self.writeln('') - - def visit_list(self, layout): - """display a list as html""" - self.writeln('' % self.handle_attrs(layout)) - for row in list(self.compute_content(layout)): - self.writeln('

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

    )""" - self.write('

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

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

    )""" - 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.write('
    ')
    -        self.write(layout.data.replace('&', '&').replace('<', '<'))
    -        self.write('
    ') - - def visit_text(self, layout): - """add some text""" - data = layout.data - if layout.escaped: - data = data.replace('&', '&').replace('<', '<') - self.write(data) diff --git a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/nodes.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/nodes.py deleted file mode 100644 index d63b5828..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/nodes.py +++ /dev/null @@ -1,201 +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 - -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, (str, unicode)), 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/pylama/lint/pylama_pylint/logilab/common/ureports/text_writer.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/text_writer.py deleted file mode 100644 index 04c8f263..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/ureports/text_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 . -"""Text formatting drivers for ureports""" -__docformat__ = "restructuredtext en" - -from logilab.common.textutils import linesep -from logilab.common.ureports import BaseWriter - - -TITLE_UNDERLINES = ['', '=', '-', '`', '.', '~', '^'] -BULLETS = ['*', '-'] - -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('.. _`%s`: %s' % (label, url)) - self.pending_urls = [] - self.section -= 1 - self.writeln() - - def visit_title(self, layout): - title = ''.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 = ' '.join(['%%-%ss'] * len(cols_width)) - format_strings = format_strings % tuple(cols_width) - format_strings = format_strings.split(' ') - table_linesep = '\n+' + '+'.join(['-'*w for w in cols_width]) + '+\n' - headsep = '\n+' + '+'.join(['='*w for w in cols_width]) + '+\n' - # FIXME: layout.cheaders - self.write(table_linesep) - for i in range(len(table_content)): - self.write('|') - line = table_content[i] - for j in range(len(line)): - self.write(format_strings[j] % line[j]) - self.write('|') - 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 = '%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('%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('`%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('::\n') - for line in layout.data.splitlines(): - self.writeln(' ' + line) - self.writeln() - - def visit_text(self, layout): - """add some text""" - self.write(layout.data) diff --git a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/visitor.py b/pymode/libs/pylama/lint/pylama_pylint/logilab/common/visitor.py deleted file mode 100644 index 802d2bef..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/logilab/common/visitor.py +++ /dev/null @@ -1,107 +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 - -# 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 = iterator.next() - while n: - result = n.accept(self) - n = iterator.next() - 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/pylama/lint/pylama_pylint/main.py b/pymode/libs/pylama/lint/pylama_pylint/main.py deleted file mode 100644 index 411ba31d..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/main.py +++ /dev/null @@ -1,113 +0,0 @@ -""" Pylint support. """ -from os import path as op, environ -import sys -import logging - -from pylama.lint import Linter as BaseLinter - -CURDIR = op.abspath(op.dirname(__file__)) -sys.path.insert(0, CURDIR) - -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/lint/pylama_pylint/pylint/__init__.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/__init__.py deleted file mode 100644 index eed1b62f..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/__init__.py +++ /dev/null @@ -1,44 +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 - -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/pylama/lint/pylama_pylint/pylint/__pkginfo__.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/__pkginfo__.py deleted file mode 100644 index 86488fa5..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/__pkginfo__.py +++ /dev/null @@ -1,74 +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""" -import sys - -modname = distname = 'pylint' - -numversion = (1, 2, 1) -version = '.'.join([str(num) for num in numversion]) - -if sys.version_info < (2, 6): - install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.1', - 'StringFormat'] -else: - install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.1'] - -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 = ['test'] diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/__init__.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/__init__.py deleted file mode 100644 index af7965be..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/__init__.py +++ /dev/null @@ -1,145 +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-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 astroid.utils import ASTWalker -from logilab.common.configuration import OptionsProviderMixIn - -from pylint.reporters import diff_string -from pylint.utils import register_plugins - -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 - 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, ASTWalker): - """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 = () - - def __init__(self, linter=None): - """checker instances should have the linter as argument - - linter is an object implementing ILinter - """ - ASTWalker.__init__(self, self) - self.name = self.name.lower() - OptionsProviderMixIn.__init__(self) - self.linter = linter - - def add_message(self, msg_id, line=None, node=None, args=None): - """add a message of a given type""" - self.linter.add_message(msg_id, line, node, args) - - # 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 BaseRawChecker(BaseChecker): - """base class for raw checkers""" - - def process_module(self, node): - """process a module - - the module's content is accessible via the stream object - - stream must implement the readline method - """ - warnings.warn("Modules that need access to the tokens should " - "use the ITokenChecker interface.", - DeprecationWarning) - stream = node.file_stream - stream.seek(0) # XXX may be removed with astroid > 0.23 - if sys.version_info <= (3, 0): - self.process_tokens(tokenize.generate_tokens(stream.readline)) - else: - self.process_tokens(tokenize.tokenize(stream.readline)) - - def process_tokens(self, tokens): - """should be overridden by subclasses""" - raise NotImplementedError() - - -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/pylama/lint/pylama_pylint/pylint/checkers/base.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/base.py deleted file mode 100644 index 8136d0f3..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/base.py +++ /dev/null @@ -1,1141 +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 sys -import astroid -from logilab.common.ureports import Table -from astroid import are_exclusive, InferenceError -import astroid.bases - -from pylint.interfaces import IAstroidChecker -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, - NoSuchArgumentError, - ) - - -import re - -# 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) -BAD_FUNCTIONS = ['map', 'filter', 'apply'] -if sys.version_info < (3, 0): - BAD_FUNCTIONS.append('input') - BAD_FUNCTIONS.append('file') - -# Name categories that are always consistent with all naming conventions. -EXEMPT_NAME_CATEGORIES = set(('exempt', 'ignore')) - -del re - -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. - 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 - -if sys.version_info < (3, 0): - PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty')) -else: - PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty')) -ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod', - 'abc.abstractclassmethod', 'abc.abstractstaticmethod')) - -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 decorated_with_abc(func): - """ Determine if the `func` node is decorated - with `abc` decorators (abstractmethod et co.) - """ - if func.decorators: - for node in func.decorators.nodes: - try: - infered = node.infer().next() - except InferenceError: - continue - if infered and infered.qname() in ABC_METHODS: - return True - -def has_abstract_methods(node): - """ Determine if the given `node` has - abstract methods, defined with `abc` module. - """ - return any(decorated_with_abc(meth) - for meth in node.mymethods()) - -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 with abstract methods instantiated', - 'abstract-class-instantiated', - 'Used when an abstract class with `abc.ABCMeta` as metaclass ' - 'has abstract methods and is instantiated.', - {'minversion': (3, 0)}), - '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.'), - } - - def __init__(self, linter): - _BasicChecker.__init__(self, linter) - - @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 = node.func.infer().next() - except astroid.InferenceError: - return - if not isinstance(infered, astroid.Class): - return - # __init__ was called - metaclass = infered.metaclass() - 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 - has_abstract_methods(infered)): - - self.add_message('abstract-class-instantiated', node=node) - break - return - if (metaclass.qname() == 'abc.ABCMeta' and - has_abstract_methods(infered)): - - self.add_message('abstract-class-instantiated', 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.'), - 'W0142': ('Used * or ** magic', - 'star-args', - 'Used when a function or method is called using `*args` or ' - '`**kwargs` to dispatch arguments. This doesn\'t improve ' - 'readability and should be used with care.'), - '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.'), - 'W0121': ('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)}), - - '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): - """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, - basestring): - # treat string statement in a separated message - 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 xrange(len(ordinary_args)): - if not isinstance(call.args[i], astroid.Name): - return - if node.args.args[i].name != call.args[i].name: - 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 - # check for dangerous default values as arguments - for default in node.args.defaults: - try: - value = default.infer().next() - except astroid.InferenceError: - continue - builtins = astroid.bases.BUILTINS - if (isinstance(value, astroid.Instance) and - value.qname() in ['.'.join([builtins, x]) for x in ('set', 'dict', 'list')]): - if value is default: - msg = default.as_string() - elif type(value) is astroid.Instance: - msg = '%s (%s)' % (default.as_string(), value.qname()) - else: - msg = '%s (%s)' % (default.as_string(), value.as_string()) - 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', 'old-raise-syntax') - def visit_raise(self, node): - """check if the node has a right sibling (if so, that's some unreachable - code) - """ - self._check_unreachable(node) - if sys.version_info >= (3, 0): - return - 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) - - @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', 'star-args', '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) - if node.starargs or node.kwargs: - scope = node.scope() - if isinstance(scope, astroid.Function): - toprocess = [(n, vn) for (n, vn) in ((node.starargs, scope.args.vararg), - (node.kwargs, scope.args.kwarg)) if n] - if toprocess: - for cfnode, fargname in toprocess[:]: - if getattr(cfnode, 'name', None) == fargname: - toprocess.remove((cfnode, fargname)) - if not toprocess: - return # star-args can be skipped - self.add_message('star-args', node=node.func) - - @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): - """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 = node.args[0].func.infer().next() - 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 _NAME_TYPES.iteritems(): - 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 = (# XXX use set - ('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 = {} - - 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) - - @check_messages('blacklisted-name', 'invalid-name') - def visit_class(self, node): - self._check_name('class', node.name, node) - for attr, anodes in node.instance_attrs.iteritems(): - 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. - if node.is_method() and overrides_a_method(node.parent.frame(), node.name): - return - self._check_name(_determine_function_name_type(node), - node.name, node) - # 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: - 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(): - 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 _is_multi_naming_match(self, match): - return (match is not None and - match.lastgroup is not None and - match.lastgroup not in EXEMPT_NAME_CATEGORIES) - - def _check_name(self, node_type, name, node): - """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 self._is_multi_naming_match(match): - name_group = self._find_name_group(node_type) - if name_group not in self._name_category: - self._name_category[name_group] = match.lastgroup - elif self._name_category[name_group] != match.lastgroup: - match = None - - if match is None: - 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)) - self.stats['badname_' + node_type] += 1 - - -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 - # 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) - else: - self._check_docstring(ftype, node) - - def _check_docstring(self, node_type, node, report_missing=True): - """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 - 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 - self.add_message('missing-docstring', node=node, args=(node_type,)) - elif not docstring.strip(): - self.stats['undocumented_'+node_type] += 1 - self.add_message('empty-docstring', node=node, args=(node_type,)) - - -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/pylama/lint/pylama_pylint/pylint/checkers/classes.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/classes.py deleted file mode 100644 index f5e2783f..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/classes.py +++ /dev/null @@ -1,792 +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 - -import astroid -from astroid import YES, Instance, are_exclusive, AssAttr -from astroid.bases import Generator - -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) - -if sys.version_info >= (3, 0): - NEXT_METHOD = '__next__' -else: - NEXT_METHOD = 'next' -ITER_METHODS = ('__iter__', '__getitem__') - -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 - - -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', # E0212 - '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', # E0214 - '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 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 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)', # W0224 - '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__.'), - '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.') - - - } - - -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.'} - ), - - ) - - 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({}) - 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) - - @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 - for attr, nodes in cnode.instance_attrs.iteritems(): - nodes = [n for n in nodes if not - isinstance(n.statement(), (astroid.Delete, astroid.AugAssign))] - if not nodes: - continue # error detected by typechecking - attr_defined = False - # check if any method attr is defined in is a defining method - for node in nodes: - if node.frame().name in defining_methods: - attr_defined = True - if not attr_defined: - # 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: - 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 - 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, str)): - 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].setdefault(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].setdefault(node.attrname, []).append(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): - - 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): - 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 accessed.iteritems(): - # 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: - node.instance_attr_ancestors(attr).next() - # 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 - """ - # check if this class abstract - if class_is_abstract(node): - return - for method in node.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 method.name in node.locals: - # it is redefined as an attribute or with a descriptor - continue - if method.is_abstract(pass_is_abstract=False): - self.add_message('abstract-method', node=node, - args=(method.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: - klass = expr.expr.infer().next() - if klass is YES: - continue - 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 not_called_yet.iteritems(): - 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, node=method1) - elif len(method1.args.defaults) < len(refmethod.args.defaults): - self.add_message('signature-differs', args=class_type, 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] = base_node.igetattr(method).next() - 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/pylama/lint/pylama_pylint/pylint/checkers/design_analysis.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/design_analysis.py deleted file mode 100644 index c9ef4dfa..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/design_analysis.py +++ /dev/null @@ -1,367 +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""" - -from astroid import Function, If, InferenceError - -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages - -import re - -# regexp for ignored argument name -IGNORED_ARGUMENT_NAMES = re.compile('_.*') - - -def class_is_abstract(klass): - """return true if the given class node should be considered as an abstract - class - """ - for attr in klass.values(): - if isinstance(attr, Function): - if attr.is_abstract(pass_is_abstract=False): - return True - return False - - -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.'), - - 'R0921': ('Abstract class not referenced', - 'abstract-class-not-used', - 'Used when an abstract class is not used as ancestor anywhere.'), - 'R0922': ('Abstract class is only referenced %s times', - 'abstract-class-little-used', - 'Used when an abstract class is used less than X times as \ - ancestor.'), - '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_abstracts = None - self._used_ifaces = None - self._abstracts = None - self._ifaces = None - self._stmts = 0 - - def open(self): - """initialize visit variables""" - self.stats = self.linter.add_stats() - self._returns = [] - self._branches = [] - self._used_abstracts = {} - self._used_ifaces = {} - self._abstracts = [] - self._ifaces = [] - - # Check 'R0921', 'R0922', 'R0923' - def close(self): - """check that abstract/interface classes are used""" - for abstract in self._abstracts: - if not abstract in self._used_abstracts: - self.add_message('abstract-class-not-used', node=abstract) - elif self._used_abstracts[abstract] < 2: - self.add_message('abstract-class-little-used', node=abstract, - args=self._used_abstracts[abstract]) - 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', - 'abstract-class-not-used', 'abstract-class-little-used', - 'interface-not-implemented') - def visit_class(self, node): - """check size of inheritance hierarchy and number of instance attributes - """ - self._inc_branch() - # 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 abstract / interface classes structures - if class_is_abstract(node): - self._abstracts.append(node) - elif 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 - for parent in node.ancestors(): - try: - self._used_abstracts[parent] += 1 - except KeyError: - self._used_abstracts[parent] = 1 - - @check_messages('too-many-ancestors', 'too-many-instance-attributes', - 'too-few-public-methods', 'too-many-public-methods', - 'abstract-class-not-used', 'abstract-class-little-used', - 'interface-not-implemented') - def leave_class(self, node): - """check number of public methods""" - nb_public_methods = 0 - special_methods = set() - for method in node.methods(): - if not method.name.startswith('_'): - nb_public_methods += 1 - if method.name.startswith("__"): - special_methods.add(method.name) - # Does the class contain less than 20 public methods ? - if nb_public_methods > self.config.max_public_methods: - self.add_message('too-many-public-methods', node=node, - args=(nb_public_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 5 public methods ? - if nb_public_methods < self.config.min_public_methods: - self.add_message('R0903', node=node, - args=(nb_public_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 - """ - self._inc_branch() - # init branch and returns counters - self._returns.append(0) - self._branches.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.pop() - 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(branches) - self._stmts += branches - - def visit_tryfinally(self, _): - """increments the branches counter""" - self._inc_branch(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(branches) - self._stmts += branches - - def visit_while(self, node): - """increments the branches counter""" - branches = 1 - if node.orelse: - branches += 1 - self._inc_branch(branches) - - visit_for = visit_while - - def _inc_branch(self, branchesnum=1): - """increments the branches counter""" - branches = self._branches - for i in xrange(len(branches)): - branches[i] += branchesnum - - # FIXME: make a nice report... - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(MisdesignChecker(linter)) diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/exceptions.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/exceptions.py deleted file mode 100644 index 7e0f3fca..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/exceptions.py +++ /dev/null @@ -1,306 +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 - -from logilab.common.compat import builtins -BUILTINS_NAME = builtins.__name__ -import astroid -from astroid import YES, Instance, unpack_infer - -from pylint.checkers import BaseChecker -from pylint.checkers.utils import is_empty, is_raising, check_messages -from pylint.interfaces import IAstroidChecker - -def infer_bases(klass): - """ Fully infer the bases of the klass node. - - This doesn't use .ancestors(), because we need - the non-inferable nodes (YES nodes), - which can't be retrieved from .ancestors() - """ - for base in klass.bases: - try: - inferit = base.infer().next() - except astroid.InferenceError: - continue - if inferit is YES: - yield inferit - else: - for base in infer_bases(inferit): - yield base - -PY3K = sys.version_info >= (3, 0) -OVERGENERAL_EXCEPTIONS = ('Exception',) - -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, instances or string 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.'), - - 'W0701': ('Raising a string exception', - 'raising-string', - 'Used when a string exception is raised.'), - '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):"'), - 'W0712': ('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)}), - 'W0713': ('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)}), - } - - -if sys.version_info < (3, 0): - EXCEPTIONS_MODULE = "exceptions" -else: - EXCEPTIONS_MODULE = "builtins" - -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('raising-string', '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: - try: - cause = node.cause.infer().next() - except astroid.InferenceError: - pass - else: - if cause is YES: - 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) - expr = node.exc - if self._check_raise_value(node, expr): - return - else: - try: - value = unpack_infer(expr).next() - except astroid.InferenceError: - return - self._check_raise_value(node, value) - - 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 isinstance(value, str): - self.add_message('raising-string', node=node) - else: - 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)): - 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, astroid.BinOp) and expr.op == '%': - self.add_message('raising-string', node=node) - elif isinstance(expr, (Instance, astroid.Class)): - if isinstance(expr, Instance): - expr = expr._proxied - if (isinstance(expr, astroid.Class) and - not inherit_from_std_ex(expr) and - expr.root().name != BUILTINS_NAME): - if expr.newstyle: - self.add_message('raising-non-exception', node=node) - else: - self.add_message('nonstandard-exception', node=node) - else: - value_found = False - else: - value_found = False - return value_found - - @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) - - @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 inherit_from_std_ex(infered): - self.add_message('indexing-exception', node=node) - except astroid.InferenceError: - return - - @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 nb_handlers == 1 and 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 nb_handlers == 1 and not is_raising(handler.body): - self.add_message('bare-except', node=handler) - # check if a "except:" is followed by some other - # except - elif 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(unpack_infer(handler.type)) - except astroid.InferenceError: - continue - for exc in excs: - # XXX skip other non class nodes - if exc is YES or 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 nb_handlers == 1 and not is_raising(handler.body)): - self.add_message('broad-except', args=exc.name, node=handler.type) - - if (not inherit_from_std_ex(exc) and - exc.root().name != BUILTINS_NAME): - # try to see if the exception is based on a C based - # exception, by infering all the base classes and - # looking for inference errors - bases = infer_bases(exc) - fully_infered = all(inferit is not YES - for inferit in bases) - if fully_infered: - self.add_message('catching-non-exception', - node=handler.type, - args=(exc.name, )) - - exceptions_classes += excs - - -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 - for parent in node.ancestors(recurs=False): - if inherit_from_std_ex(parent): - return True - return False - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(ExceptionsChecker(linter)) diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/format.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/format.py deleted file mode 100644 index 8b73049c..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/format.py +++ /dev/null @@ -1,943 +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 - -if not hasattr(tokenize, 'NL'): - raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") - -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)', # 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')]}) - } - - -if sys.version_info < (3, 0): - - MSGS.update({ - 'W0331': ('Use of the <> operator', - 'old-ne-operator', - 'Used when the deprecated "<>" operator is used instead \ - of "!=".'), - '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"'), - 'W0333': ('Use of the `` operator', - 'backtick', - 'Used when the deprecated "``" (backtick) operator is used ' - 'instead of the str() function.', - {'scope': WarningScope.NODE}), - }) - - -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 self._cont_stack[-1].context_type == CONTINUED: - # 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[next_align.keys()[0] + self._continuation_size] = True - else: - next_align = _Offsets(indentation + self._continuation_size, indentation) - paren_align = _Offsets(indentation + self._continuation_size, indentation) - 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)) - if self._is_block_opener and self._tokens.start_col(pos+1) - indentation == self._block_indent_size: - return _ContinuedIndent( - CONTINUED_BLOCK, - bracket, - pos, - _Offsets(self._tokens.start_col(pos)), - _BeforeBlockOffsets(self._tokens.start_col(pos+1), - self._tokens.start_col(pos+1) + self._continuation_size)) - else: - return _ContinuedIndent( - CONTINUED, - bracket, - pos, - _Offsets(self._tokens.start_col(pos)), - _Offsets(self._tokens.start_col(pos+1))) - - 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 - * use of <> instead of != - """ - - __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' : 80, '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.'}), - ) - - 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 xrange(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 tokens[i][1] == ',': - return 'comma' - elif tokens[i][1] == ':': - return ':' - elif tokens[i][1] in '()[]{}': - return 'bracket' - elif tokens[i][1] in ('<', '>', '<=', '>=', '!=', '=='): - return 'comparison' - else: - if self._inside_brackets('('): - return 'keyword argument assignment' - else: - return 'assignment' - - good_space = [True, True] - pairs = [(tokens[i-1], tokens[i]), (tokens[i], 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(tokens[i]) - count, state = _policy_string(policy) - self.add_message('bad-whitespace', line=tokens[i][2][0], - args=(count, state, position, construct, - _underline_token(tokens[i]))) - - def _inside_brackets(self, left): - return self._bracket_stack[-1] == left - - def _handle_old_ne_operator(self, tokens, i): - if tokens[i][1] == '<>': - self.add_message('old-ne-operator', line=tokens[i][2][0]) - - 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), - - (['<>'], self._handle_old_ne_operator), - ] - - 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._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() - 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: - self.add_message('too-many-lines', args=line_num, line=1) - - 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 offsets.iteritems() - 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): - # 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. If the last line ended with a - # comment and the new line contains only a comment, the line may also be - # indented to the start of the previous comment. - if (tokens.type(next_idx) == tokenize.COMMENT and - tokens.type(next_idx-2) == tokenize.COMMENT): - 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 xrange(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 - - @check_messages('backtick') - def visit_backquote(self, node): - self.add_message('backtick', node=node) - - 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/pylama/lint/pylama_pylint/pylint/checkers/imports.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/imports.py deleted file mode 100644 index 8b73c6f6..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/imports.py +++ /dev/null @@ -1,394 +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 logilab.common.graph import get_cycles, DotBackend -from logilab.common.modutils import get_module_part, is_standard_module -from logilab.common.ureports import VerbatimText, Paragraph - -import astroid -from astroid import are_exclusive - -from pylint.interfaces import IAstroidChecker -from pylint.utils import EmptyReport -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages - - -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(dep_info.iteritems()): - 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(dep_info.iteritems()): - 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.'), - '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 = {} - - 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'): - for cycle in get_cycles(self.import_graph): - 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(modnode, 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(modnode, 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, modnode, importnode, modname): - try: - return importnode.do_import_module(modname) - except astroid.InferenceError, ex: - if str(ex) != modname: - args = '%r (%s)' % (modname, ex) - else: - args = repr(modname) - 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""" - importedmodname = get_module_part(importedmodname) - 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.setdefault(context_name, set()) - if not importedmodname 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(self._external_dependencies_info().iteritems()) - 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.base_name - self.__ext_dep_info = result = {} - for importee, importers in self.stats['dependencies'].iteritems(): - 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.base_name - self.__int_dep_info = result = {} - for importee, importers in self.stats['dependencies'].iteritems(): - 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/pylama/lint/pylama_pylint/pylint/checkers/logging.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/logging.py deleted file mode 100644 index cbdf0f2a..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/logging.py +++ /dev/null @@ -1,213 +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 - -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/.'), - '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']) - - -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, unused_node): - """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.Const): - self._check_format_string(node, format_pos) - - 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, basestring): - # 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, 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/pylama/lint/pylama_pylint/pylint/checkers/misc.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/misc.py deleted file mode 100644 index d1b7c216..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/misc.py +++ /dev/null @@ -1,90 +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 - - -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): - match = notes.search(line) - if match: - self.add_message('fixme', args=line[match.start():-1], line=lineno) - - def _check_encoding(self, lineno, line, file_encoding): - try: - return unicode(line, file_encoding) - except UnicodeDecodeError, 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 - """ - stream = module.file_stream - stream.seek(0) # XXX may be removed with astroid > 0.23 - if self.config.notes: - notes = re.compile('|'.join(self.config.notes)) - else: - notes = None - if module.file_encoding: - encoding = module.file_encoding - else: - encoding = 'ascii' - 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/pylama/lint/pylama_pylint/pylint/checkers/newstyle.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/newstyle.py deleted file mode 100644 index f801c443..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/newstyle.py +++ /dev/null @@ -1,151 +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 -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages - -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__ usage - """ - if '__slots__' in node and not node.newstyle: - self.add_message('slots-on-old-class', node=node) - # 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': - self.add_message('old-style-class', node=node) - - @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)): - name = node.func.name - if name == 'property': - self.add_message('property-on-old-class', node=node) - - @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): - 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': - if not klass.newstyle: - # super should not be used on an old style class - self.add_message('super-on-old-class', node=node) - else: - # super first arg should be the class - if not call.args and sys.version_info[0] == 3: - # unless Python 3 - continue - - try: - supcls = (call.args and call.args[0].infer().next() - or None) - except astroid.InferenceError: - continue - - if supcls is None: - self.add_message('missing-super-argument', node=call) - 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, )) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(NewStyleConflictChecker(linter)) diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/raw_metrics.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/raw_metrics.py deleted file mode 100644 index 71fecf68..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/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/pylama/lint/pylama_pylint/pylint/checkers/similar.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/similar.py deleted file mode 100644 index cf671bf6..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/similar.py +++ /dev/null @@ -1,365 +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 -""" -import sys -from itertools import izip - -from logilab.common.ureports import Table - -from pylint.interfaces import IRawChecker -from pylint.checkers import BaseChecker, table_lines_from_stats - - -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""" - stream.seek(0) # XXX may be removed with astroid > 0.23 - 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 = {} - for num, lineset1, idx1, lineset2, idx2 in self._iter_sims(): - duplicate = no_duplicates.setdefault(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 no_duplicates.iteritems(): - 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( - izip(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 = {} - for line_no, line in enumerate(self._stripped_lines): - if line: - index.setdefault(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 - """ - self.append_stream(self.linter.current_name, node.file_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: - sim.append_stream(filename, open(filename)) - sim.run() - sys.exit(0) - -if __name__ == '__main__': - Run() diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/stdlib.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/stdlib.py deleted file mode 100644 index a1c31337..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/stdlib.py +++ /dev/null @@ -1,69 +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 re -import sys - -import astroid - -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers import utils - -_VALID_OPEN_MODE_REGEX = r'^(r?U|[rwa]\+?b?)$' - -if sys.version_info >= (3, 0): - OPEN_MODULE = '_io' -else: - OPEN_MODULE = '__builtin__' - -class OpenModeChecker(BaseChecker): - __implements__ = (IAstroidChecker,) - name = 'open_mode' - - msgs = { - 'W1501': ('"%s" is not a valid mode for open.', - 'bad-open-mode', - 'Python supports: r, w, a modes with b, +, and U options. ' - 'See http://docs.python.org/2/library/functions.html#open'), - } - - @utils.check_messages('bad-open-mode') - def visit_callfunc(self, node): - """Visit a CallFunc node.""" - if hasattr(node, 'func'): - infer = utils.safe_infer(node.func) - if infer and infer.root().name == OPEN_MODULE: - if getattr(node.func, 'name', None) in ('open', 'file'): - self._check_open_mode(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') - if mode_arg: - mode_arg = utils.safe_infer(mode_arg) - if (isinstance(mode_arg, astroid.Const) - and not re.match(_VALID_OPEN_MODE_REGEX, mode_arg.value)): - self.add_message('bad-open-mode', node=node, args=(mode_arg.value)) - except (utils.NoSuchArgumentError, TypeError): - pass - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(OpenModeChecker(linter)) - diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/strings.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/strings.py deleted file mode 100644 index 04cf1bc7..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/strings.py +++ /dev/null @@ -1,304 +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 astroid - -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker -from pylint.checkers import BaseChecker, BaseTokenChecker -from pylint.checkers import utils -from pylint.checkers.utils import check_messages - -_PY3K = sys.version_info >= (3, 0) - -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"), - } - -OTHER_NODES = (astroid.Const, astroid.List, astroid.Backquote, - astroid.Lambda, astroid.Function, - astroid.ListComp, astroid.SetComp, astroid.GenExpr) - -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, basestring)): - return - format_string = left.value - try: - required_keys, required_num_args = \ - utils.parse_format_string(format_string) - except utils.UnsupportedFormatCharacter, 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, basestring): - 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') - and 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)) - - -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, start_col), _, _) 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, start_col) - - def process_string_token(self, token, start_row, start_col): - for i, c in enumerate(token): - if c in '\'\"': - quote_char = c - break - 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, start_col) - - def process_non_raw_string_token(self, prefix, string_body, start_row, - start_col): - """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. - start_col: integer column 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/pylama/lint/pylama_pylint/pylint/checkers/typecheck.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/typecheck.py deleted file mode 100644 index 25f7612e..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/typecheck.py +++ /dev/null @@ -1,451 +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 pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers.utils import safe_infer, is_super, check_messages - -MSGS = { - 'E1101': ('%s %r has no %r member', - 'no-member', - 'Used when a variable is accessed for an unexistent member.'), - 'E1102': ('%s is not callable', - 'not-callable', - 'Used when an object being called has been inferred to a non \ - callable object'), - 'E1103': ('%s %r has no %r member (but some types could not be inferred)', - 'maybe-no-member', - 'Used when a variable is accessed for an unexistent member, but \ - astroid was not able to interpret all possible types of this \ - variable.'), - '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.'), - 'E1122': ('Duplicate keyword argument %r in %s call', - 'duplicate-keyword-arg', - 'Used when a function call passes the same keyword argument \ - multiple times.', - {'maxversion': (2, 6)}), - '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)}), - } - -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', 'maybe-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 - 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) - if inference_failure: - msgid = 'maybe-no-member' - else: - msgid = 'no-member' - self.add_message(msgid, node=node, - args=(owner.display_type(), name, - node.attrname)) - - @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) - - @check_messages(*(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 = arg.arg - if keyword in keyword_args: - self.add_message('duplicate-keyword-arg', node=node, args=keyword) - keyword_args.add(keyword) - 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()) - - 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)) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(TypeChecker(linter)) diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/utils.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/utils.py deleted file mode 100644 index e7d85d41..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/utils.py +++ /dev/null @@ -1,416 +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 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 - - -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: - scope, stmts = node.lookup(name) - 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 = inferit.next() - except astroid.InferenceError: - return - try: - inferit.next() - 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, vars in _node.items: - if expr.parent_of(var_node): - break - if (vars and - isinstance(vars, astroid.AssName) and - vars.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 char not in 'diouxXeEfFgGcrs%': - 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, 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 diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/variables.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/variables.py deleted file mode 100644 index dc8d1115..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/checkers/variables.py +++ /dev/null @@ -1,741 +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 -from copy import copy - -import astroid -from astroid import are_exclusive, builtin_lookup, AstroidBuildingException - -from logilab.common.modutils import file_from_modpath - -from pylint.interfaces import IAstroidChecker -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) - - -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 = klass.local_attr_ancestors(name).next() - 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 - -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 import %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.' - }), - ) - 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 node.locals.iteritems(): - 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 = node.igetattr('__all__').next() - if assigned is not astroid.YES: - for elt in getattr(assigned, 'elts', ()): - try: - elt_name = elt.infer().next() - except astroid.InferenceError: - continue - - if not isinstance(elt_name, astroid.Const) \ - or not isinstance(elt_name.value, basestring): - 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: - file_from_modpath(name.split(".")) - except ImportError: - self.add_message('undefined-all-variable', - args=elt_name, - node=elt) - except SyntaxError, exc: - # 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 - for name, stmts in not_consumed.iteritems(): - if any(isinstance(stmt, astroid.AssName) - and isinstance(stmt.ass_type(), astroid.AugAssign) - for stmt in stmts): - continue - stmt = stmts[0] - if isinstance(stmt, astroid.Import): - self.add_message('unused-import', args=name, node=stmt) - elif isinstance(stmt, astroid.From) and stmt.modname != '__future__': - if stmt.names[0][0] == '*': - self.add_message('unused-wildcard-import', args=name, node=stmt) - else: - self.add_message('unused-import', args=name, 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 - authorized_rgx = self.config.dummy_variables_rgx - called_overridden = False - argnames = node.argnames() - for name, stmts in not_consumed.iteritems(): - # 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 - # 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 XXX should be configurable - if node.name.startswith('cb_') or node.name.endswith('_cb'): - continue - self.add_message('unused-argument', args=name, node=stmt) - else: - 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: - # global but no assignment - 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, scope_type): - node_scope = node.scope() - if not isinstance(node_scope, (astroid.Lambda, astroid.Function)): - 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 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): - # XXX find a way to handle class scope in a smoother way - 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, scope_type) - 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, scope_type) - defstmt = defnode.statement() - defframe = defstmt.frame() - maybee0601 = True - if not frame is defframe: - maybee0601 = False - 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()) - if (maybee0601 - and stmt.fromlineno <= defstmt.fromlineno - and not is_defined_before(node) - and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))): - if defstmt is stmt and isinstance(node, (astroid.DelName, - astroid.AssName)): - 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) - 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 = node.infer_name_module(parts[0]).next() - 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 AstroidBuildingException: - return - except Exception, exc: - print 'Unhandled exception in VariablesChecker:', exc - 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, (astroid.Tuple, astroid.List)): - # attempt to check unpacking is properly balanced - values = infered.itered() - if len(targets) != len(values): - 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 - while module_names: - name = module_names.pop(0) - if name == '__dict__': - module = None - break - try: - module = module.getattr(name)[0].infer().next() - if module is astroid.YES: - return None - except astroid.NotFoundError: - 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. - """ - for klass in node.nodes_of_class(astroid.Class): - if klass._metaclass: - metaclass = klass.metaclass() - module_locals = self._to_consume[0][0] - - if isinstance(klass._metaclass, astroid.Name): - module_locals.pop(klass._metaclass.name, None) - if metaclass: - # if it uses a `metaclass=module.Class` - module_locals.pop(metaclass.root().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/pylama/lint/pylama_pylint/pylint/config.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/config.py deleted file mode 100644 index 992c2934..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/config.py +++ /dev/null @@ -1,156 +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 - -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) as stream: - return pickle.load(stream) - except: - return {} - -if sys.version_info < (3, 0): - _PICK_MOD = 'w' -else: - _PICK_MOD = 'wb' - -def save_results(results, base): - """pickle results""" - if not exists(PYLINT_HOME): - try: - os.mkdir(PYLINT_HOME) - except OSError: - print >> sys.stderr, 'Unable to create directory %s' % PYLINT_HOME - data_file = get_pdata_path(base, 1) - try: - with open(data_file, _PICK_MOD) as stream: - pickle.dump(results, stream) - except (IOError, OSError), ex: - print >> sys.stderr, 'Unable to create file %s: %s' % (data_file, ex) - -# 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/pylama/lint/pylama_pylint/pylint/interfaces.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/interfaces.py deleted file mode 100644 index 50f2c839..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/interfaces.py +++ /dev/null @@ -1,72 +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 logilab.common.interface import Interface - - -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.file_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/pylama/lint/pylama_pylint/pylint/lint.py b/pymode/libs/pylama/lint/pylama_pylint/pylint/lint.py deleted file mode 100644 index 529fbd44..00000000 --- a/pymode/libs/pylama/lint/pylama_pylint/pylint/lint.py +++ /dev/null @@ -1,1106 +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. -""" - -# import this first to avoid builtin namespace pollution -from pylint.checkers import utils - -import functools -import sys -import os -import tokenize -from warnings import warn - -from logilab.common.configuration import UnsupportedAction, OptionsManagerMixIn -from logilab.common.optik_ext import check_csv -from logilab.common.modutils import load_module_from_name, get_module_part -from logilab.common.interface import implements -from logilab.common.textutils import splitstrip, unquote -from logilab.common.ureports import Table, Text, Section -from logilab.common.__pkginfo__ import version as common_version - -from astroid import MANAGER, nodes, AstroidBuildingException -from astroid.__pkginfo__ import version as astroid_version - -from pylint.utils import ( - MSG_TYPES, OPTION_RGX, - PyLintASTWalker, UnknownMessage, MessagesHandlerMixIn, ReportsHandlerMixIn, - EmptyReport, WarningScope, - expand_modules, tokenize_module) -from pylint.interfaces import IRawChecker, ITokenChecker, IAstroidChecker -from pylint.checkers import (BaseTokenChecker, - table_lines_from_stats, - initialize as checkers_initialize) -from pylint.reporters import initialize as reporters_initialize -from pylint import config - -from pylint.__pkginfo__ import version - - - -def _get_python_path(filepath): - dirname = os.path.dirname(os.path.realpath( - os.path.expanduser(filepath))) - 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() - - -# 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): - def _warn_deprecated(option, optname, *args): - sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,)) - return {'short': shortname, 'help': 'DEPRECATED', 'hide': True, - 'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated} - - -class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, - 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__ = (ITokenChecker,) - - name = 'master' - priority = 0 - level = 0 - msgs = MSGS - may_be_disabled = False - - @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).'}), - - ('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': '