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 5fdf2fd2..40ca63ba 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,12 @@ .vimrc Gemfile.lock VimFlavor.lock +_ +build tags 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 b377db57..00000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: ruby -python: "2.7" -rvm: - - 1.9.3 -script: rake ci diff --git a/AUTHORS b/AUTHORS index 85247511..a4bcbf28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,34 +1,78 @@ -Maintainer: +Author: * Kirill Klenov +Maintainers: + +* Diego Rabatone Oliveira (https://github.com/diraol); Contributors: -* Alvin Francis (alvinfrancis); -* Anler Hp (ikame); -* Benjamin Ruston (bruston); -* Boris Filippov (frenzykryger); -* David Vogt (winged); -* Denis Kasak (dkasak); -* Dirk Wallenstein (dirkwallenstein); -* Florent Xicluna (florentx); -* Fredrik Henrysson (fhenrysson); -* Igor Guerrero (igorgue); -* Jonathan McCall (Jonnymcc); -* Kevin Deldycke (kdeldycke); -* Lowe Thiderman (thiderman); -* Martin Brochhaus (mbrochh); -* Matthew Moses (mlmoses) -* Mohammed (mbadran); -* Naoya Inada (naoina); -* Pedro Algarvio (s0undt3ch); -* Piet Delport (pjdelport); -* Robert David Grant (bgrant); -* Ronald Andreu Kaiser (cathoderay); +* 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 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); +* Jonathan McCall (http://github.com/Jonnymcc); +* Kevin Deldycke (http://github.com/kdeldycke); +* Kurtis Rader (https://github.com/krader1961); +* Lawrence Akka (https://github.com/lawrenceakka); +* lee (https://github.com/loyalpartner); +* Lie Ryan (https://github.com/lieryan/); +* Lowe Thiderman (http://github.com/thiderman); +* Martin Brochhaus (http://github.com/mbrochh); +* Matt Dodge (https://github.com/mattdodge); +* Matthew Moses (http://github.com/mlmoses); +* Maxim (https://github.com/mpyatishev); +* Mel Boyce (http://github.com/syngin); +* Mohammed (http://github.com/mbadran); +* Naoya Inada (http://github.com/naoina); +* Nate Zhang (https://github.com/natezhang93); +* nixon (https://github.com/nixon); +* Paweł Korzeniewski (https://github.com/korzeniewskipl); +* Pedro Algarvio (http://github.com/s0undt3ch); +* Phillip Cloud (http://github.com/cpcloud); +* Pi Delport (http://github.com/pjdelport); +* Robert David Grant (http://github.com/bgrant); +* Robin Schneider (https://github.com/ypid); +* Ronald Andreu Kaiser (http://github.com/cathoderay);; +* Samir Benmendil (https://github.com/Ram-Z); * Sorin Ionescu (sorin-ionescu); -* Steve Losh (sjl); -* lawrenceakka; -* lee (loyalpartner); -* nixon; -* tramchamploo; +* sphaugh (https://github.com/sphaugh); +* Steve Losh (http://github.com/sjl); +* Tommy Allen (https://github.com/tweekmonster); +* Tony Narlock (https://github.com/tony); +* tramchamploo (https://github.com/tramchamploo); +* Tyler Fenby (https://github.com/TFenby); +* Vincent Driessen (https://github.com/nvie); +* Wang Feng (https://github.com/mapler); +* Wayne Ye (https://github.com/WayneYe); +* Wes Turner (https://github.com/westurner); +* 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 083406fb..00000000 --- a/Changelog.rst +++ /dev/null @@ -1,266 +0,0 @@ -Changelog -========= - -## 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 82b34ab5..00000000 --- a/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -.PHONY: clean -clean: - find . -name "*.pyc" -delete - -.PHONY: test -test: - rake test - -.PHONY: pylama -pylama: - rm -rf pylibs/pylama - cp -r ~/Dropbox/projects/pylama/pylama pylibs/pylama - cp -r ~/Dropbox/projects/pylama/plugins/pylama_pylint/pylama_pylint/ pylibs/pylama/lint/pylama_pylint diff --git a/README.rst b/README.rst deleted file mode 100644 index 64afbac6..00000000 --- a/README.rst +++ /dev/null @@ -1,627 +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 - -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. - -There is no need to install pylint_, rope_ -or any other Python libraries on your system. - -- `Python motions and operators`_ (``]]``, ``3[[``, ``]]M``, ``vaC``, ``viM``, ``daC``, ``ciM``, ...) -- `Python code folding`_ -- `Virtualenv support`_ -- `Syntax highlighting`_ -- Highlight and auto fix unused imports -- Many static analysis linters (pylint_, pyflakes_, pylama_, ...) that can be run simultaneously -- `Code refactoring `_ (rope_) -- Strong code completion (rope_) -- Go to definition (``g`` for `:RopeGotoDefinition`) -- `Show documentation`_ (``K``) -- Run python code (``r``) -- Powerful customization settings_ -- 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.0 with python support - (also ``--with-features=big`` if you want ``g:pymode_lint_signs``) - - - -How to install -============== - - -Using pathogen (recomended) ----------------------------- -:: - - % 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. - - -Troubleshooting -=============== - -If your python-mode doesn't work: open any python file and type: :: - - :call pymode#troubleshooting#Test() - -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). - - -Settings -======== - -.. note:: See also ``:help PythonModeOptions`` - -To change these settings, edit your ``~/.vimrc``: :: - - " Disable pylint checking every save - let g:pymode_lint_write = 0 - - " Set key 'R' for run python code - let g:pymode_run_key = 'R' - - -Loading the Plugin ------------------- - -Default values: :: - - " Load the whole plugin - let g:pymode = 1 - - -Show documentation ------------------- - -Default values: :: - - " Load show documentation plugin - let g:pymode_doc = 1 - - " Show python documentation - let g:pymode_doc_key = 'K' - - -Run python code ---------------- - -Default values: :: - - " Load run code plugin - let g:pymode_run = 1 - - " Run python code - let g:pymode_run_key = 'r' - - -Code checking -------------- - -Default values: :: - - " Load pylint code plugin - let g:pymode_lint = 1 - - " Switch pylint, pyflakes, pep8, mccabe code-checkers - " Can have multiple values "pep8,pyflakes,mcccabe" - " Choices are: pyflakes, pep8, mccabe, pylint, pep257 - let g:pymode_lint_checker = "pyflakes,pep8,mccabe" - - " Skip errors and warnings - " E.g. "E501,W002", "E2,W" (Skip all Warnings and Errors startswith E2) and etc - let g:pymode_lint_ignore = "E501" - - " Select errors and warnings - " E.g. "E4,W" - let g:pymode_lint_select = "" - - " Run linter on the fly - let g:pymode_lint_onfly = 0 - - " Pylint configuration file - " If file not found use 'pylintrc' from python-mode plugin directory - let g:pymode_lint_config = "$HOME/.pylintrc" - - " Check code every save - let g:pymode_lint_write = 1 - - " Auto open cwindow if errors were found - let g:pymode_lint_cwindow = 1 - - " Show error message if cursor placed at the error line - let g:pymode_lint_message = 1 - - " Auto jump on first error - let g:pymode_lint_jump = 0 - - " Hold cursor in current window - " when quickfix is open - let g:pymode_lint_hold = 0 - - " Place error signs - let g:pymode_lint_signs = 1 - - " Maximum allowed mccabe complexity - let g:pymode_lint_mccabe_complexity = 8 - - " Minimal height of pylint error window - let g:pymode_lint_minheight = 3 - - " Maximal height of pylint error window - let g:pymode_lint_maxheight = 6 - - " Symbol definition - let g:pymode_lint_todo_symbol = 'WW' - let g:pymode_lint_comment_symbol = 'CC' - let g:pymode_lint_visual_symbol = 'RR' - let g:pymode_lint_error_symbol = 'EE' - let g:pymode_lint_info_symbol = 'II' - let g:pymode_lint_pyflakes_symbol = 'FF' - -.. note:: - Pylint options (ex. disable messages) may be defined in ``$HOME/pylint.rc`` - See the pylint documentation: http://pylint-messages.wikidot.com/all-codes - - -Rope refactoring library ------------------------- - -Default values: :: - - " Load rope plugin - let g:pymode_rope = 1 - - " Map keys for autocompletion - let g:pymode_rope_autocomplete_map = '' - - " Auto create and open ropeproject - let g:pymode_rope_auto_project = 1 - - " Enable autoimport - let g:pymode_rope_enable_autoimport = 1 - - " Auto generate global cache - let g:pymode_rope_autoimport_generate = 1 - - let g:pymode_rope_autoimport_underlineds = 0 - - let g:pymode_rope_codeassist_maxfixes = 10 - - let g:pymode_rope_sorted_completions = 1 - - let g:pymode_rope_extended_complete = 1 - - let g:pymode_rope_autoimport_modules = ["os","shutil","datetime"] - - let g:pymode_rope_confirm_saving = 1 - - let g:pymode_rope_global_prefix = "p" - - let g:pymode_rope_local_prefix = "r" - - let g:pymode_rope_vim_completion = 1 - - let g:pymode_rope_guess_project = 1 - - let g:pymode_rope_goto_def_newwin = "" - - let g:pymode_rope_always_show_complete_menu = 0 - - -Python code folding -------------------- - -Default values: :: - - " Enable python code folding - let g:pymode_folding = 1 - - -Python motions and operators --------------------------------- - -Default values: :: - - " Enable python objects and motion - let g:pymode_motion = 1 - - -Virtualenv support ------------------- - -Default values: :: - - " Auto fix vim python paths if virtualenv enabled - let g:pymode_virtualenv = 1 - - -Other stuff ------------ - -Default values: :: - - " Additional python paths - let g:pymode_paths = [] - - " Load breakpoints plugin - let g:pymode_breakpoint = 1 - - " Key for set/unset breakpoint - let g:pymode_breakpoint_key = 'b' - - " Autoremove unused whitespaces - let g:pymode_utils_whitespaces = 1 - - " Enable pymode indentation - let g:pymode_indent = 1 - - " Set default pymode python options - let g:pymode_options = 1 - - -Syntax highlighting -------------------- - -Default values: :: - - " Enable pymode's custom syntax highlighting - let g:pymode_syntax = 1 - - " Enable all python highlightings - let g:pymode_syntax_all = 1 - - " Highlight "print" as a function - let g:pymode_syntax_print_as_function = 0 - - " Highlight indentation errors - let g:pymode_syntax_indent_errors = g:pymode_syntax_all - - " Highlight trailing spaces - let g:pymode_syntax_space_errors = g:pymode_syntax_all - - " Highlight string formatting - let g:pymode_syntax_string_formatting = g:pymode_syntax_all - - " Highlight str.format syntax - let g:pymode_syntax_string_format = g:pymode_syntax_all - - " Highlight string.Template syntax - let g:pymode_syntax_string_templates = g:pymode_syntax_all - - " Highlight doc-tests - let g:pymode_syntax_doctests = g:pymode_syntax_all - - " Highlight builtin objects (__doc__, self, etc) - let g:pymode_syntax_builtin_objs = g:pymode_syntax_all - - " Highlight builtin functions - let g:pymode_syntax_builtin_funcs = g:pymode_syntax_all - - " Highlight exceptions - let g:pymode_syntax_highlight_exceptions = g:pymode_syntax_all - - " Highlight equal operator - let g:pymode_syntax_highlight_equal_operator = g:pymode_syntax_all - - " Highlight stars operator - let g:pymode_syntax_highlight_stars_operator = g:pymode_syntax_all - - " Highlight `self` - let g:pymode_syntax_highlight_self = g:pymode_syntax_all - - " For fast machines - let g:pymode_syntax_slow_sync = 0 - - -Default keys -============ - -.. note:: See also ``:help PythonModeKeys`` - -============== ============= -Keys Command -============== ============= -**K** Show python docs (``g:pymode_doc enabled``) --------------- ------------- -**** Rope autocomplete (``g:pymode_rope enabled``) --------------- ------------- -**g** Rope goto definition (``g:pymode_rope enabled``) --------------- ------------- -**d** Rope show documentation (``g:pymode_rope enabled``) --------------- ------------- -**f** Rope find occurrences (``g:pymode_rope enabled``) --------------- ------------- -**r** Run python (``g:pymode_run enabled``) --------------- ------------- -**b** Set, unset breakpoint (``g:pymode_breakpoint enabled``) --------------- ------------- -``[[`` 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``, ``C`` Select a class. Ex: ``vaC``, ``daC``, ``dC``, ``yaC``, ``yC``, ``caC``, ``cC`` (normal, operator modes) --------------- ------------- -``iC`` Select inner class. Ex: ``viC``, ``diC``, ``yiC``, ``ciC`` (normal, operator modes) --------------- ------------- -``aM``, ``M`` Select a function or method. Ex: ``vaM``, ``daM``, ``dM``, ``yaM``, ``yM``, ``caM``, ``cM`` (normal, operator modes) --------------- ------------- -``iM`` Select inner function or method. Ex: ``viM``, ``diM``, ``yiM``, ``ciM`` (normal, operator modes) -============== ============= - -.. note:: See also ``:help ropevim.txt`` - - -Commands -======== - -.. note:: See also ``:help PythonModeCommands`` - -==================== ============= -Command Description -==================== ============= -:Pydoc Show python documentation --------------------- ------------- -:PyLintToggle Enable/disable pylint --------------------- ------------- -:PyLintCheckerToggle Toggle code checker (pylint, pyflakes) --------------------- ------------- -:PyLint Check current buffer --------------------- ------------- -:PyLintAuto Automatically fix PEP8 errors --------------------- ------------- -:Pyrun Run current buffer in python -==================== ============= - -.. note:: See also ``:help ropevim.txt`` - - -F.A.Q. -====== - -Rope completion is very slow ----------------------------- - -rope_ creates a project-level service directory in ``.ropeproject``. - -If ``g:pymode_rope_guess_project`` is set (as it is by default), -and ``.ropeproject`` is not found in the current directory, -rope will walk upwards looking for a ``.ropeproject`` in every dir of the parent path. - -If rope finds ``.ropeproject`` in a parent dir, -it sets the project for all child dirs -and the scan may be slow for so many dirs and files. - -Solutions: - -- Disable ``g:pymode_rope_guess_project`` to make rope always create ``.ropeproject`` in the current dir. -- Delete ``.ropeproject`` from the parent dir to make rope create ``.ropeproject`` in the current dir. -- Press ``po`` or ``:RopeOpenProject`` to make rope create ``.ropeproject`` in the current dir. - - - -Pylint check is very slow -------------------------- - -In some projects, pylint_ may check slowly because it also scans imported modules if posible. -Alternately, use pyflakes_. - -.. note:: See also ``:help 'pymode_lint_checker'``. - -.. note:: You may ``set exrc`` and ``set secure`` in your ``vimrc`` to auto set custom settings from a ``.vimrc`` in your projects' directories. - Example: On Flask projects I automatically set ``g:pymode_lint_checker = "pyflakes"``, on django ``g:pymode_lint_cheker = "pylint"`` - - -OSX cannot import urandom -------------------------- - -See: https://groups.google.com/forum/?fromgroups=#!topic/vim_dev/2NXKF6kDONo - -The sequence of commands that fixed this: :: - - brew unlink python - brew unlink macvim - brew remove macvim - brew install -v --force macvim - brew link macvim - brew link python - - -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 - - -Copyright -========= - -Copyright © 2013 Kirill Klenov (klen_) - -Rope ------ -Copyright (C) 2006-2010 Ali Gholami Rudi - -Copyright (C) 2009-2010 Anton Gritsay - -https://pypi.python.org/pypi/rope - -https://pypi.python.org/pypi/ropemode - -http://rope.sourceforge.net/ropevim.html - - -Pylama ------- -Copyright (C) 2012-2013 Kirill Klenov - -https://pypi.python.org/pypi/pylama - -https://github.com/klen/pylama - - -Pylint ------- -Copyright (C) 2003-2011 LOGILAB S.A. (Paris, FRANCE). - -https://pypi.python.org/pypi/pylint - -https://bitbucket.org/logilab/pylint - -http://www.pylint.org/ - -http://www.logilab.fr/ - - -Pyflakes --------- - -Copyright (c) 2005 Divmod, Inc. - -https://pypi.python.org/pypi/pyflakes - -https://launchpad.net/pyflakes - -http://www.divmod.com/ - - -pep8 ----- -Copyright (C) 2006 Johann C. Rocholl - -https://pypi.python.org/pypi/pep8 - -http://github.com/jcrocholl/pep8 - -http://www.python.org/dev/peps/pep-0008/ - - -autopep8 --------- -Copyright (C) 2010-2011 Hideo Hattori - -http://github.com/hynek/vim-python-pep8-indent - - - -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 -.. _mccabe: http://en.wikipedia.org/wiki/Cyclomatic_complexity -.. |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 ccadf116..6b5a8839 100644 --- a/after/ftplugin/python.vim +++ b/after/ftplugin/python.vim @@ -1,43 +1,60 @@ -" Fix omnifunc -if g:pymode && g:pymode_rope && g:pymode_rope_vim_completion - setlocal omnifunc=RopeOmni +if !g:pymode + finish endif -" Motion {{{ - - if !pymode#Default('g:pymode_motion', 1) || g:pymode_motion - - nnoremap ]] :call pymode#motion#move('^\(class\\|def\)\s', '') - nnoremap [[ :call pymode#motion#move('^\(class\\|def\)\s', 'b') - nnoremap ]C :call pymode#motion#move('^\(class\\|def\)\s', '') - nnoremap [C :call pymode#motion#move('^\(class\\|def\)\s', 'b') - nnoremap ]M :call pymode#motion#move('^\s*def\s', '') - nnoremap [M :call pymode#motion#move('^\s*def\s', 'b') - - onoremap ]] :call pymode#motion#move('^\(class\\|def\)\s', '') - onoremap [[ :call pymode#motion#move('^\(class\\|def\)\s', 'b') - onoremap ]C :call pymode#motion#move('^\(class\\|def\)\s', '') - onoremap [C :call pymode#motion#move('^\(class\\|def\)\s', 'b') - onoremap ]M :call pymode#motion#move('^\s*def\s', '') - onoremap [M :call pymode#motion#move('^\s*def\s', 'b') - - vnoremap ]] :call pymode#motion#vmove('^\(class\\|def\)\s', '') - vnoremap [[ :call pymode#motion#vmove('^\(class\\|def\)\s', 'b') - vnoremap ]M :call pymode#motion#vmove('^\s*def\s', '') - vnoremap [M :call pymode#motion#vmove('^\s*def\s', 'b') - - onoremap C :call pymode#motion#select('^\s*class\s', 0) - onoremap aC :call pymode#motion#select('^\s*class\s', 0) - onoremap iC :call pymode#motion#select('^\s*class\s', 1) - vnoremap aC :call pymode#motion#select('^\s*class\s', 0) - vnoremap iC :call pymode#motion#select('^\s*class\s', 1) - - onoremap M :call pymode#motion#select('^\s*def\s', 0) - onoremap aM :call pymode#motion#select('^\s*def\s', 0) - onoremap iM :call pymode#motion#select('^\s*def\s', 1) - vnoremap aM :call pymode#motion#select('^\s*def\s', 0) - vnoremap iM :call pymode#motion#select('^\s*def\s', 1) +if g:pymode_motion + if !&magic + if g:pymode_warning + call pymode#error("Pymode motion requires `&magic` option. Enable them or disable g:pymode_motion") + endif + finish endif -" }}} + 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 + +if g:pymode_rope && g:pymode_rope_completion + + setlocal omnifunc=pymode#rope#completions + + if g:pymode_rope_completion_bind != "" + exe "inoremap " . g:pymode_rope_completion_bind . " =pymode#rope#complete(0)" + if tolower(g:pymode_rope_completion_bind) == '' + exe "inoremap =pymode#rope#complete(0)" + endif + endif + +endif diff --git a/after/indent/python.vim b/after/indent/python.vim index 8f4dc4b2..98399b40 100644 --- a/after/indent/python.vim +++ b/after/indent/python.vim @@ -2,7 +2,6 @@ if !g:pymode || !g:pymode_indent finish endif - setlocal nolisp setlocal tabstop=4 setlocal softtabstop=4 @@ -10,5 +9,5 @@ setlocal shiftwidth=4 setlocal shiftround setlocal expandtab setlocal autoindent -setlocal indentexpr=pymode#indent#Indent(v:lnum) +setlocal indentexpr=pymode#indent#get_indent(v:lnum) setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except diff --git a/autoload/pymode.vim b/autoload/pymode.vim index f67a0a76..b637289a 100644 --- a/autoload/pymode.vim +++ b/autoload/pymode.vim @@ -1,9 +1,7 @@ -" Python-mode base functions +" Pymode core functions - -fun! pymode#Default(name, default) "{{{ - " DESC: Set default value if it not exists - " +" DESC: Check variable and set default value if it not exists +fun! pymode#default(name, default) "{{{ if !exists(a:name) let {a:name} = a:default return 0 @@ -11,31 +9,43 @@ fun! pymode#Default(name, default) "{{{ return 1 endfunction "}}} +" DESC: Import python libs +fun! pymode#init(plugin_root, paths) "{{{ -fun! pymode#Option(name) "{{{ - - let name = 'b:pymode_' . a:name - if exists(name) - return eval(name) - endif + PymodePython import sys, vim + PymodePython sys.path.insert(0, vim.eval('a:plugin_root')) + PymodePython sys.path = vim.eval('a:paths') + sys.path - let name = 'g:pymode_' . a:name - return eval(name) +endfunction "}}} +" DESC: Show wide message +fun! pymode#wide_message(msg) "{{{ + let x=&ruler | let y=&showcmd + set noruler noshowcmd + redraw + echohl Debug | echo strpart("[Pymode] " . a:msg, 0, &columns-1) | echohl none + let &ruler=x | let &showcmd=y endfunction "}}} +" DESC: Show error +fun! pymode#error(msg) "{{{ + execute "normal \" + echohl ErrorMsg + echomsg "[Pymode]: error: " . a:msg + echohl None +endfunction "}}} -fun! pymode#QuickfixOpen(onlyRecognized, holdCursor, maxHeight, minHeight, jumpError) "{{{ - " DESC: Open quickfix window - " +" DESC: Open quickfix window +fun! pymode#quickfix_open(onlyRecognized, maxHeight, minHeight, jumpError) "{{{ let numErrors = len(filter(getqflist(), 'v:val.valid')) let numOthers = len(getqflist()) - numErrors if numErrors > 0 || (!a:onlyRecognized && numOthers > 0) + let num = winnr() botright copen exe max([min([line("$"), a:maxHeight]), a:minHeight]) . "wincmd _" if a:jumpError cc - elseif !a:holdCursor + elseif num != winnr() wincmd p endif else @@ -43,153 +53,96 @@ fun! pymode#QuickfixOpen(onlyRecognized, holdCursor, maxHeight, minHeight, jumpE endif redraw if numOthers > 0 - echo printf('Quickfix: %d(+%d)', numErrors, numOthers) - else - echo printf('Quickfix: %d', numErrors) - endif -endfunction "}}} - - -fun! pymode#PlaceSigns(bnum) "{{{ - " DESC: Place error signs - " - if has('signs') - call pymode#Default('b:pymode_signs', []) - - for item in b:pymode_signs - execute printf('sign unplace %d buffer=%d', item.lnum, item.bufnr) - endfor - let b:pymode_signs = [] - - if !pymode#Default("g:pymode_lint_signs_always_visible", 0) || g:pymode_lint_signs_always_visible - call RopeShowSignsRulerIfNeeded() - endif - - for item in filter(getqflist(), 'v:val.bufnr != ""') - call add(b:pymode_signs, item) - execute printf('sign place %d line=%d name=%s buffer=%d', item.lnum, item.lnum, "Pymode".item.type, item.bufnr) - endfor - + call pymode#wide_message(printf('Quickfix: %d(+%d)', numErrors, numOthers)) + elseif numErrors > 0 + call pymode#wide_message(printf('Quickfix: %d', numErrors)) endif endfunction "}}} - -fun! pymode#CheckProgram(name, append) "{{{ - " DESC: Check program is executable or redifined by user. - " - let name = 'g:' . a:name - if pymode#Default(name, a:name) - return 1 - endif - if !executable(eval(l:name)) - echoerr "Can't find '".eval(name)."'. Please set ".name .", or extend $PATH, ".a:append - return 0 - endif - return 1 -endfunction "}}} - - -fun! pymode#TempBuffer() "{{{ - " DESC: Open temp buffer. - " - pclose | botright 8new +" DESC: Open temp buffer. +fun! pymode#tempbuffer_open(name) "{{{ + pclose + exe g:pymode_preview_position . " " . g:pymode_preview_height . "new " . a:name setlocal buftype=nofile bufhidden=delete noswapfile nowrap previewwindow redraw endfunction "}}} - -fun! pymode#ShowStr(str) "{{{ - " DESC: Open temp buffer with `str`. - " - let g:pymode_curbuf = bufnr("%") - call pymode#TempBuffer() - put! =a:str - wincmd p - redraw +" DESC: Remove unused whitespaces +fun! pymode#trim_whitespaces() "{{{ + 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 + noautocmd write + catch /E212/ + call pymode#error("File modified and I can't save it. Please save it manually.") + return 0 + endtry + endif + return expand('%') != '' +endfunction "}}} -fun! pymode#ShowCommand(cmd) "{{{ - " DESC: Run command and open temp buffer with result - " - call pymode#TempBuffer() +fun! pymode#reload_buf_by_nr(nr) "{{{ + let cur = bufnr("") try - silent exec 'r!' . a:cmd - catch /.*/ - close - echoerr 'Command fail: '.a:cmd + exe "buffer " . a:nr + catch /E86/ + return endtry - redraw - normal gg - wincmd p + exe "e!" + exe "buffer " . cur endfunction "}}} - -fun! pymode#WideMessage(msg) "{{{ - " DESC: Show wide message - - let x=&ruler | let y=&showcmd - set noruler noshowcmd - redraw - echohl Debug | echo strpart(a:msg, 0, &columns-1) | echohl none - let &ruler=x | let &showcmd=y +fun! pymode#buffer_pre_write() "{{{ + let b:pymode_modified = &modified endfunction "}}} - -fun! pymode#BlockStart(lnum, ...) "{{{ - let pattern = a:0 ? a:1 : '^\s*\(@\|class\s.*:\|def\s\)' - let lnum = a:lnum + 1 - let indent = 100 - while lnum - let lnum = prevnonblank(lnum - 1) - let test = indent(lnum) - let line = getline(lnum) - if line =~ '^\s*#' " Skip comments - continue - elseif !test " Zero-level regular line - return lnum - elseif test >= indent " Skip deeper or equal lines - continue - " Indent is strictly less at this point: check for def/class - elseif line =~ pattern && line !~ '^\s*@' - return lnum +fun! pymode#buffer_post_write() "{{{ + if g:pymode_rope + if g:pymode_rope_regenerate_on_write && b:pymode_modified + call pymode#debug('regenerate') + call pymode#rope#regenerate() endif - let indent = indent(lnum) - endwhile - return 0 -endfunction "}}} - - -fun! pymode#BlockEnd(lnum, ...) "{{{ - let indent = a:0 ? a:1 : indent(a:lnum) - let lnum = a:lnum - while lnum - let lnum = nextnonblank(lnum + 1) - if getline(lnum) =~ '^\s*#' | continue - elseif lnum && indent(lnum) <= indent - return lnum - 1 + endif + if g:pymode_lint + if g:pymode_lint_unmodified || (g:pymode_lint_on_write && b:pymode_modified) + call pymode#debug('check code') + call pymode#lint#check() endif - endwhile - return line('$') + endif 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 + '|||||||||||' -fun! pymode#Modeline() "{{{ - let modeline = getline(prevnonblank('$')) - if modeline =~ '^#\s\+pymode:' - for ex in split(modeline, ':')[1:] - let [name, value] = split(ex, '=') - let {'b:pymode_'.name} = value - endfor + let l:info_separator = repeat('-', 79) + + if g:pymode_debug + 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#TrimWhiteSpace() "{{{ - let cursor_pos = getpos('.') - silent! %s/\s\+$// - call setpos('.', cursor_pos) +fun! pymode#quit() "{{{ + augroup pymode + au! * + augroup END endfunction "}}} - - -" vim: fdm=marker:fdl=0 diff --git a/autoload/pymode/breakpoint.vim b/autoload/pymode/breakpoint.vim index d8126202..98639b57 100644 --- a/autoload/pymode/breakpoint.vim +++ b/autoload/pymode/breakpoint.vim @@ -1,31 +1,50 @@ -" Quick set or delete a breakpoints -fun! pymode#breakpoint#Set(lnum) "{{{ +fun! pymode#breakpoint#init() "{{{ + + " If breakpoints are either disabled or already defined do nothing. + if ! g:pymode_breakpoint || g:pymode_breakpoint_cmd != '' + return + + " Else go for a 'smart scan' of the defaults. + else + + PymodePython << EOF + +from importlib.util import find_spec + +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 " Save file without any events - if &modifiable && &modified | noautocmd write | endif - -endfunction "}}} - -fun! pymode#breakpoint#SearchDebuger() "{{{ - -Python << EOF -from imp import find_module - -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 -EOF + call pymode#save() endfunction "}}} 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 9b076fa0..a7a753c5 100644 --- a/autoload/pymode/doc.vim +++ b/autoload/pymode/doc.vim @@ -1,21 +1,40 @@ " Python-mode search by documentation +" +PymodePython import pymode +fun! pymode#doc#find() "{{{ + " Extract the 'word' at the cursor, expanding leftwards across identifiers + " and the . operator, and rightwards across the identifier only. + " + " For example: + " import xml.dom.minidom + " ^ ! + " + " With the cursor at ^ this returns 'xml'; at ! it returns 'xml.dom'. + let l:line = getline(".") + let l:pre = l:line[:col(".") - 1] + let l:suf = l:line[col("."):] + let word = matchstr(pre, "[A-Za-z0-9_.]*$") . matchstr(suf, "^[A-Za-z0-9_]*") + call pymode#doc#show(word) +endfunction "}}} -fun! pymode#doc#Show(word) "{{{ +fun! pymode#doc#show(word) "{{{ if a:word == '' - echoerr "No name/symbol under cursor!" - else - Python import StringIO - Python sys.stdout, _ = StringIO.StringIO(), sys.stdout - Python help(vim.eval('a:word')) - Python sys.stdout, out = _, sys.stdout.getvalue() - if !g:pymode_test - call pymode#TempBuffer() - endif - Python vim.current.buffer.append(str(out).splitlines(), 0) - wincmd p + call pymode#error("No name/symbol under cursor!") + return 0 endif -endfunction "}}} + call pymode#tempbuffer_open('__doc__') + PymodePython pymode.get_documentation() + setlocal nomodifiable + setlocal nomodified + setlocal filetype=rst + if g:pymode_doc_vertical + wincmd L + endif + + normal gg + + wincmd p -" vim: fdm=marker:fdl=0 +endfunction "}}} diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim index 7d23d852..9cbb64d3 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -1,23 +1,37 @@ -" 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:def_regex = '^\s*\%(class\|def\) \w\+' -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*@\)\|\%(^\s*\%("""\|''''''\)\s*$\)' + while getline(fs) !~ s:def_regex && getline(fs) !~ s:docstring_begin_regex let fs = nextnonblank(fs + 1) endwhile + 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 windowwidth = winwidth(0) - nucolwidth - 3 + let has_numbers = &number || &relativenumber + let nucolwidth = &fdc + has_numbers * &numberwidth + let windowwidth = winwidth(0) - nucolwidth - 6 let foldedlinecount = v:foldend - v:foldstart " expand tabs into spaces @@ -25,52 +39,290 @@ 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 fillcharcount = windowwidth - len(line) - len(foldedlinecount) - return line . '…' . repeat(" ",fillcharcount) . foldedlinecount . '…' . ' ' + 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) - - if line =~ s:def_regex || line =~ s:decorator_regex - if prev_line =~ s:decorator_regex - return '=' - else - return ">".(indent / &shiftwidth + 1) - endif - endif + let prev_line = getline(a:lnum - 1) + let next_line = getline(a:lnum + 1) + + " Decorators {{{ + if line =~ s:decorator_regex + 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 - if line =~ s:doc_begin_regex - \ && line !~ s:doc_line_regex - \ && prev_line =~ s:def_regex - return ">".(indent / &shiftwidth + 1) + " 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 + while lnum > 0 + if getline(lnum) =~ s:def_regex + break + elseif getline(lnum) =~ s:decorator_regex + let decorated = 1 + break + endif + let lnum -= 1 + endwhile + if decorated + let l:foldcase = 'decorated function declaration' + let l:foldlevel = '=' + else + let l:foldcase = 'function declaration' + let l:foldlevel = '>'.(indent / &shiftwidth + 1) + endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + endif "}}} + + " Docstrings {{{ + + " TODO: A while loop now counts the number of open and closed folding in + " order to determine if it is a closing or opening folding. + " It is working but looks like it is an overkill. + + " Notice that an effect of this is that other docstring matches will not + " be one liners. + if line =~ s:docstring_line_regex + let l:foldcase = 'one-liner docstring' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif + if line =~ s:docstring_begin_regex + if s:Is_opening_folding(a:lnum) + let l:foldcase = 'open multiline docstring' + let l:foldlevel = 'a1' + endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + endif + if line =~ s:docstring_end_regex + if !s:Is_opening_folding(a:lnum) + let l:foldcase = 'close multiline docstring' + let l:foldlevel = 's1' + endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + endif "}}} - if line =~ s:doc_end_regex - \ && line !~ s:doc_line_regex - return "<".(indent / &shiftwidth + 1) + " 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 + endif + call setpos('.', s:save_cursor) + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + else + call setpos('.', s:save_cursor) endif + " endif " }}} + " Blank Line {{{ + " Comments: cases of blank lines: + " 1. After non blank line: gets folded with previous line. + " 1. Just after a block; in this case it gets folded with the block. + " 1. Between docstrings and imports. + " 1. Inside docstrings. + " 2. Inside functions/methods. + " 3. Between functions/methods. if line =~ s:blank_regex - if prev_line =~ s:blank_regex - return -1 - else - return '=' + if prev_line !~ s:blank_regex + let l:foldcase = 'blank line after non blank line' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + elseif a:lnum > line_block_start && a:lnum < line_block_end + let l:foldcase = 'blank line inside block' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif + " 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 {'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 - if indent == 0 - return 0 + " 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. + " 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] + else + let next_stmt_at_def_indent = -1 endif - return '=' + " 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) + let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0]) + else + let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0]) + endif + + 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 fb4d085d..e964f378 100644 --- a/autoload/pymode/indent.vim +++ b/autoload/pymode/indent.vim @@ -4,10 +4,10 @@ " Prev Maintainer: Eric Mc Sween (address invalid) " Original Author: David Bustos (address invalid) " Last Change: 2012-06-21 -" License: Public Domainlet +" License: Public Domain -function! pymode#indent#Indent(lnum) +function! pymode#indent#get_indent(lnum) " First line has indent 0 if a:lnum == 1 @@ -24,7 +24,9 @@ function! pymode#indent#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 @@ -66,21 +68,23 @@ function! pymode#indent#Indent(lnum) return -1 endif - " If this line is explicitly joined, try to find an indentation that looks - " good. + " If this line is explicitly joined, find the first indentation that is a + " multiple of four and will distinguish itself from next logical line. if pline =~ '\\$' - let compound_statement = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*' - let maybe_indent = matchend(getline(sslnum), compound_statement) - if maybe_indent != -1 - return maybe_indent + let maybe_indent = indent(sslnum) + &sw + let control_structure = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*' + if match(getline(sslnum), control_structure) != -1 + " add extra indent to avoid E125 + return maybe_indent + &sw else - return indent(sslnum) + &sw * 2 + " control structure not found + return maybe_indent endif endif " If the previous line ended with a colon and is not a comment, indent " relative to statement start. - if pline =~ ':\s*$' && pline !~ '^\s*#' + if pline =~ '^[^#]*:\s*\(#.*\)\?$' return indent(sslnum) + &sw endif @@ -101,14 +105,14 @@ endfunction " Find backwards the closest open parenthesis/bracket/brace. -function! s:SearchParensPair() +function! s:SearchParensPair() " {{{ let line = line('.') let col = col('.') " 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) @@ -140,11 +144,11 @@ function! s:SearchParensPair() call cursor(parlnum, parcol) endif return parlnum -endfunction +endfunction " }}} " Find the start of a multi-line statement -function! s:StatementStart(lnum) +function! s:StatementStart(lnum) " {{{ let lnum = a:lnum while 1 if getline(lnum - 1) =~ '\\$' @@ -159,11 +163,11 @@ function! s:StatementStart(lnum) endif endif endwhile -endfunction +endfunction " }}} " Find the block starter that matches the current line -function! s:BlockStarter(lnum, block_start_re) +function! s:BlockStarter(lnum, block_start_re) " {{{ let lnum = a:lnum let maxindent = 10000 " whatever while lnum > 1 @@ -181,4 +185,4 @@ function! s:BlockStarter(lnum, block_start_re) endif endwhile return -1 -endfunction +endfunction " }}} diff --git a/autoload/pymode/lint.vim b/autoload/pymode/lint.vim index 7e6bc9b2..29dd6168 100644 --- a/autoload/pymode/lint.vim +++ b/autoload/pymode/lint.vim @@ -1,108 +1,104 @@ -fun! pymode#lint#Check() "{{{ - " DESC: Run checkers on current file. - " - if !g:pymode_lint | return | endif - - if &modifiable && &modified - try - noautocmd write - catch /E212/ - echohl Error | echo "File modified and I can't save it. Cancel code checking." | echohl None - return 0 - endtry - endif +PymodePython from pymode.lint import code_check - let g:pymode_lint_buffer = bufnr('%') +call pymode#tools#signs#init() +call pymode#tools#loclist#init() - Python from pymode import lint - Python lint.check_file() - -endfunction " }}} +fun! pymode#lint#auto() "{{{ + if ! pymode#save() + return 0 + endif + PymodePython from pymode import auto + PymodePython auto() + cclose + call g:PymodeSigns.clear() + edit + call pymode#wide_message("AutoPep8 done.") +endfunction "}}} -fun! pymode#lint#Parse(bnum) - " DESC: Parse result of code checking. - " - call setqflist(g:qf_list, 'r') - if g:pymode_lint_signs - call pymode#PlaceSigns(a:bnum) +fun! pymode#lint#show_errormessage() "{{{ + let loclist = g:PymodeLocList.current() + if loclist.is_empty() + return endif - if g:pymode_lint_cwindow - call pymode#QuickfixOpen(0, g:pymode_lint_hold, g:pymode_lint_maxheight, g:pymode_lint_minheight, g:pymode_lint_jump) + let l = line('.') + if l == b:pymode_error_line + return endif - - if !len(g:qf_list) - call pymode#WideMessage('Code checking is completed. No errors found.') + let b:pymode_error_line = l + if has_key(loclist._messages, l) + call pymode#wide_message(loclist._messages[l]) + else + echo endif - -endfunction +endfunction "}}} -fun! pymode#lint#Toggle() "{{{ +fun! pymode#lint#toggle() "{{{ let g:pymode_lint = g:pymode_lint ? 0 : 1 - call pymode#lint#toggle_win(g:pymode_lint, "Pymode lint") + if g:pymode_lint + call pymode#wide_message("Code checking is enabled.") + else + call pymode#wide_message("Code checking is disabled.") + endif endfunction "}}} -fun! pymode#lint#ToggleWindow() "{{{ - let g:pymode_lint_cwindow = g:pymode_lint_cwindow ? 0 : 1 - call pymode#lint#toggle_win(g:pymode_lint_cwindow, "Pymode lint cwindow") -endfunction "}}} +fun! pymode#lint#check() "{{{ + " DESC: Run checkers on current file. + " + let loclist = g:PymodeLocList.current() + let b:pymode_error_line = -1 -fun! pymode#lint#ToggleChecker() "{{{ - let g:pymode_lint_checker = g:pymode_lint_checker == "pylint" ? "pyflakes" : "pylint" - echomsg "Pymode lint checker: " . g:pymode_lint_checker -endfunction "}}} + call loclist.clear() + + call pymode#wide_message('Code checking is running ...') + PymodePython code_check() -fun! pymode#lint#toggle_win(toggle, msg) "{{{ - if a:toggle - echomsg a:msg." enabled" - botright cwindow - if &buftype == "quickfix" - wincmd p + 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) + + call loclist.show() + + call pymode#lint#show_errormessage() + call pymode#wide_message('Found ' . loclist.num_errors() . ' error(s) and ' . loclist.num_warnings() . ' warning(s)') + +endfunction " }}} + + +fun! pymode#lint#tick_queue() "{{{ + + python import time + python print time.time() + + if mode() == 'i' + if col('.') == 1 + call feedkeys("\\", "n") + else + call feedkeys("\\", "n") endif else - echomsg a:msg." disabled" - cclose + call feedkeys("f\e", "n") endif endfunction "}}} -fun! pymode#lint#show_errormessage() "{{{ - if g:pymode_lint_buffer != bufnr('%') - return 0 - endif - let errors = getqflist() - if !len(errors) - return - endif - let [_, line, _, _] = getpos(".") - for e in errors - if e['lnum'] == line - call pymode#WideMessage(e['text']) - else - echo - endif - endfor -endfunction " }}} +fun! pymode#lint#stop() "{{{ + au! pymode CursorHold +endfunction "}}} -fun! pymode#lint#Auto() "{{{ - if &modifiable && &modified - try - noautocmd write - catch /E212/ - echohl Error | echo "File modified and I can't save it. Cancel operation." | echohl None - return 0 - endtry - endif - Python from pymode import auto - Python auto.fix_current_file() - cclose - edit - call pymode#WideMessage("AutoPep8 done.") +fun! pymode#lint#start() "{{{ + au! pymode CursorHold call pymode#lint#tick_queue() + call pymode#lint#tick_queue() endfunction "}}} diff --git a/autoload/pymode/motion.vim b/autoload/pymode/motion.vim index c8c58ca9..c88fb913 100644 --- a/autoload/pymode/motion.vim +++ b/autoload/pymode/motion.vim @@ -21,41 +21,85 @@ 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 = pymode#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 = pymode#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 = pymode#BlockEnd(lnum, indent(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, 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) + " Skip comments, deeper or equal lines + if line =~ '^\s*#' || test >= indent + continue + 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, 0] +endfunction "}}} + + +fun! s:BlockEnd(lnum, ...) "{{{ + let indent = a:0 ? a:1 : indent(a:lnum) + let lnum = a:lnum + while lnum + let lnum = nextnonblank(lnum + 1) + if getline(lnum) =~ '^\s*#' | continue + elseif lnum && indent(lnum) <= indent + return prevnonblank(lnum - 1) + endif + endwhile + return line('$') +endfunction "}}} " vim: fdm=marker:fdl=0 diff --git a/autoload/pymode/path.vim b/autoload/pymode/path.vim deleted file mode 100644 index a2793e4e..00000000 --- a/autoload/pymode/path.vim +++ /dev/null @@ -1,17 +0,0 @@ -fun! pymode#path#Activate(plugin_root) "{{{ - -Python << EOF -import sys, vim, os - -pymode_lib = 'pylibs' - -# if sys.version >= (3, 0, 0): -# pymode_lib = 'pylibs3' - -curpath = vim.eval("getcwd()") -libpath = os.path.join(vim.eval("a:plugin_root"), pymode_lib) - -sys.path = [libpath, curpath] + vim.eval("g:pymode_paths") + sys.path -EOF - -endfunction "}}} diff --git a/autoload/pymode/queue.vim b/autoload/pymode/queue.vim deleted file mode 100644 index d38208b7..00000000 --- a/autoload/pymode/queue.vim +++ /dev/null @@ -1,18 +0,0 @@ -fun! pymode#queue#Poll() "{{{ - - " Check current tasks - Python from pymode import queue - Python queue.check_task() - - " Update interval - if mode() == 'i' - if col('.') == 1 - call feedkeys("\\", "n") - else - call feedkeys("\\", "n") - endif - else - call feedkeys("f\e", "n") - endif - -endfunction "}}} diff --git a/autoload/pymode/rope.vim b/autoload/pymode/rope.vim new file mode 100644 index 00000000..36344d0a --- /dev/null +++ b/autoload/pymode/rope.vim @@ -0,0 +1,200 @@ +" Python-mode Rope support + +if ! g:pymode_rope + finish +endif + +PymodePython from pymode import rope + +call pymode#tools#loclist#init() + + +fun! pymode#rope#completions(findstart, base) + PymodePython rope.completions() +endfunction + +fun! pymode#rope#complete(dot) + if pumvisible() + if stridx('noselect', &completeopt) != -1 + return "\" + else + return "" + endif + endif + if a:dot + PymodePython rope.complete(True) + else + PymodePython rope.complete() + endif + return pumvisible() && stridx('noselect', &completeopt) != -1 ? "\\" : "" +endfunction + +fun! pymode#rope#complete_on_dot() "{{{ + if !exists("*synstack") + return "" + endif + for group in map(synstack(line('.'), col('.') - 1), 'synIDattr(v:val, "name")') + for name in ['pythonString', 'pythonComment', 'pythonNumber', 'pythonDocstring'] + if group == name + return "" + endif + endfor + endfor + if g:pymode_rope_autoimport_import_after_complete + PymodePython rope.complete_check() + endif + return pymode#rope#complete(1) +endfunction "}}} + +fun! pymode#rope#goto_definition() + PymodePython rope.goto() +endfunction + + +fun! pymode#rope#organize_imports() + if !pymode#save() + return 0 + endif + call pymode#wide_message('Organize imports ... ') + PymodePython rope.organize_imports() +endfunction + + +fun! pymode#rope#find_it() + let loclist = g:PymodeLocList.current() + let loclist._title = "Occurrences" + call pymode#wide_message('Finding Occurrences ...') + PymodePython rope.find_it() + call loclist.show() +endfunction + + +fun! pymode#rope#show_doc() + let l:output = [] + + PymodePython rope.show_doc() + + if !empty(l:output) + call pymode#tempbuffer_open('__doc____rope__') + call append(0, l:output) + setlocal nomodifiable + setlocal nomodified + setlocal filetype=rst + + normal gg + + wincmd p + endif +endfunction + + +fun! pymode#rope#regenerate() "{{{ + call pymode#wide_message('Regenerate Rope cache ... ') + PymodePython rope.regenerate() +endfunction "}}} + + +fun! pymode#rope#new(...) "{{{ + PymodePython rope.new() +endfunction "}}} + + +fun! pymode#rope#rename() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.RenameRefactoring().run() +endfunction "}}} + +fun! pymode#rope#rename_module() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.RenameRefactoring(True).run() +endfunction "}}} + +fun! pymode#rope#extract_method() range "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.ExtractMethodRefactoring().run() +endfunction "}}} + +fun! pymode#rope#extract_variable() range "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.ExtractVariableRefactoring().run() +endfunction "}}} + +fun! pymode#rope#undo() "{{{ + PymodePython rope.undo() +endfunction "}}} + +fun! pymode#rope#redo() "{{{ + PymodePython rope.redo() +endfunction "}}} + +fun! pymode#rope#inline() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.InlineRefactoring().run() +endfunction "}}} + +fun! pymode#rope#move() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.MoveRefactoring().run() +endfunction "}}} + +fun! pymode#rope#signature() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.ChangeSignatureRefactoring().run() +endfunction "}}} + +fun! pymode#rope#use_function() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.UseFunctionRefactoring().run() +endfunction "}}} + +fun! pymode#rope#module_to_package() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.ModuleToPackageRefactoring().run() +endfunction "}}} + +fun! pymode#rope#autoimport(word) "{{{ + PymodePython rope.autoimport() +endfunction "}}} + +fun! pymode#rope#generate_function() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.GenerateElementRefactoring('function').run() +endfunction "}}} + +fun! pymode#rope#generate_class() "{{{ + if !pymode#save() + return 0 + endif + PymodePython rope.GenerateElementRefactoring('class').run() +endfunction "}}} + +fun! pymode#rope#generate_package() "{{{ + if !pymode#save() + return 0 + 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 1ee90627..356409b6 100644 --- a/autoload/pymode/run.vim +++ b/autoload/pymode/run.vim @@ -27,6 +27,7 @@ let s:efm .= '%E File "%f"\, line %l%\C,' " match. A pointer (^) identifies the column in which the error occurs " (but will not be entirely accurate due to indention of Python code). let s:efm .= '%C%p^,' + " Any text, indented by more than two spaces contain useful information. " We want this to appear in the quickfix window, hence %+. let s:efm .= '%+C %.%#,' @@ -40,71 +41,30 @@ let s:efm .= '%Z%\S%\&%m,' " We can ignore any other lines (%-G) let s:efm .= '%-G%.%#' +PymodePython from pymode.run import run_code -" DESC: Save file if it modified and run python code -fun! pymode#run#Run(line1, line2) "{{{ - let l:code = getline(a:line1, a:line2) - let l:traceback = [] - - call setqflist([]) - call pymode#WideMessage("Code running.") - - try - - Python << EOF - -import StringIO, json - -_input = lambda s: vim.eval('input("%s")' % s) -context = dict(__name__='__main__', input=_input, raw_input=_input) -out, errors = "", [] -sys.stdout, stdout_ = StringIO.StringIO(), sys.stdout -sys.stderr, stderr_ = StringIO.StringIO(), sys.stderr - -lines = [l.rstrip() for l in vim.eval('l:code')] -indent = 0 -for line in lines: - if line: - indent = len(line) - len(line.lstrip()) - break -lines = [l[indent:] for l in lines] +" DESC: Run python code +fun! pymode#run#code_run(line1, line2) "{{{ -try: - code = compile('\n'.join(lines) + '\n', vim.current.buffer.name, 'exec') - exec(code, context) - -except SystemExit as e: - errors.append('test') - if e.code: - # A non-false code indicates abnormal termination. A false code will be treated as a - # successful run, and the error will be hidden from Vim - vim.command('echohl Error | echo "Script exited with code {0}" | echohl none'.format(e.code)) - vim.command('return') - -except Exception as e: - import traceback - err = traceback.format_exc() - -else: - err = sys.stderr.getvalue() - -out = sys.stdout.getvalue().strip() -errors += [e for e in err.splitlines() if e and "" not in e] + let l:output = [] + let l:traceback = [] + call setqflist([]) -sys.stdout, sys.stderr = stdout_, stderr_ + call pymode#wide_message("Code running ...") -for e in errors: - vim.command("call add(l:traceback, %s)" % json.dumps(e)) + try -if out: - vim.command("call pymode#TempBuffer()") - vim.current.buffer.append([x.decode("utf-8").encode(vim.eval('&enc')) for x in out.split('\n')], 0) - vim.command("wincmd p") -else: - vim.command('call pymode#WideMessage("No output.")') + PymodePython run_code() -EOF + if len(l:output) + call pymode#tempbuffer_open('__run__') + call append(line('$'), l:output) + normal dd + wincmd p + else + call pymode#wide_message("No output.") + endif cexpr "" @@ -113,8 +73,9 @@ EOF let &efm = s:efm 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 @@ -125,7 +86,8 @@ EOF call setqflist(qflist) endif - call pymode#QuickfixOpen(0, g:pymode_lint_hold, g:pymode_lint_maxheight, g:pymode_lint_minheight, 0) + call pymode#quickfix_open(0, g:pymode_quickfix_maxheight, g:pymode_quickfix_maxheight, 0) + let &efm = l:_efm catch /E234/ diff --git a/autoload/pymode/tools/loclist.vim b/autoload/pymode/tools/loclist.vim new file mode 100644 index 00000000..b9121bdf --- /dev/null +++ b/autoload/pymode/tools/loclist.vim @@ -0,0 +1,99 @@ +let g:PymodeLocList= {} + + +fun! pymode#tools#loclist#init() "{{{ + return +endfunction "}}} + + +fun! g:PymodeLocList.init(raw_list) "{{{ + let obj = copy(self) + let loc_list = filter(copy(a:raw_list), 'v:val["valid"] == 1') + call obj.clear() + let obj._title = 'CodeCheck' + return obj +endfunction "}}} + + +fun! g:PymodeLocList.current() "{{{ + if !exists("b:pymode_loclist") + let b:pymode_loclist = g:PymodeLocList.init([]) + endif + return b:pymode_loclist +endfunction "}}} + + +fun! g:PymodeLocList.is_empty() "{{{ + 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._errlist = [] + let self._warnlist = [] + let self._messages = {} + let self._name = expand('%:t') +endfunction "}}} + + +fun! g:PymodeLocList.extend(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 + return self +endfunction "}}} + + +fun! g:PymodeLocList.filter(filters) "{{{ + let loclist = [] + for error in self.loclist() + let passes_filters = 1 + for key in keys(a:filters) + if get(error, key, '') !=? a:filters[key] + let passes_filters = 0 + break + endif + endfor + + if passes_filters + call add(loclist, error) + endif + + endfor + return loclist +endfunction "}}} + + +fun! g:PymodeLocList.show() "{{{ + call setloclist(0, self.loclist()) + if self.is_empty() + lclose + elseif g:pymode_lint_cwindow + let num = winnr() + lopen + setl nowrap + execute max([min([line("$"), g:pymode_quickfix_maxheight]), g:pymode_quickfix_minheight]) . "wincmd _" + if num != winnr() + call setwinvar(winnr(), 'quickfix_title', self._title . ' <' . self._name . '>') + exe num . "wincmd w" + endif + end +endfunction "}}} diff --git a/autoload/pymode/tools/signs.vim b/autoload/pymode/tools/signs.vim new file mode 100644 index 00000000..579573ed --- /dev/null +++ b/autoload/pymode/tools/signs.vim @@ -0,0 +1,57 @@ +let g:PymodeSigns = {} + + +fun! pymode#tools#signs#init() "{{{ + call g:PymodeSigns.setup() +endfunction "}}} + + +fun! g:PymodeSigns.enabled() "{{{ + return (g:pymode_lint_signs && has('signs')) +endfunction "}}} + + +fun! g:PymodeSigns.setup() "{{{ + if self.enabled() + execute 'sign define PymodeW text=' . g:pymode_lint_todo_symbol . " texthl=Todo" + execute 'sign define PymodeD text=' . g:pymode_lint_docs_symbol . " texthl=String" + execute 'sign define PymodeC text=' . g:pymode_lint_comment_symbol . " texthl=Comment" + execute 'sign define PymodeR text=' . g:pymode_lint_visual_symbol . " texthl=Visual" + execute 'sign define PymodeE text=' . g:pymode_lint_error_symbol . " texthl=Error" + execute 'sign define PymodeI text=' . g:pymode_lint_info_symbol . " texthl=Info" + execute 'sign define PymodeF text=' . g:pymode_lint_pyflakes_symbol . " texthl=Info" + endif + let self._sign_ids = [] + let self._next_id = 10000 + let self._messages = {} +endfunction "}}} + + +fun! g:PymodeSigns.refresh(loclist) "{{{ + if self.enabled() + call self.clear() + call self.place(a:loclist) + endif +endfunction "}}} + + +fun! g:PymodeSigns.clear() "{{{ + let ids = copy(self._sign_ids) + for i in ids + execute "sign unplace " . i + call remove(self._sign_ids, index(self._sign_ids, i)) + endfor +endfunction "}}} + + +fun! g:PymodeSigns.place(loclist) "{{{ + let seen = {} + 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) + execute printf('sign place %d line=%d name=%s buffer=%d', self._next_id, issue.lnum, "Pymode".issue.type[0], issue.bufnr) + let self._next_id += 1 + endif + endfor +endfunction "}}} diff --git a/autoload/pymode/troubleshooting.vim b/autoload/pymode/troubleshooting.vim deleted file mode 100644 index 8d15f73d..00000000 --- a/autoload/pymode/troubleshooting.vim +++ /dev/null @@ -1,103 +0,0 @@ -" DESC: Get debug information about pymode problem -fun! pymode#troubleshooting#Test() "{{{ - runtime ftplugin/python/init-pymode.vim - - 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 - - call append('0', ['Pymode diagnostic', - \ '===================', - \ 'VIM:' . v:version . ', OS: ' . os .', multi_byte:' . has('multi_byte') . ', pymode: ' . g:pymode_version, - \ '']) - - let python = 1 - let output = [] - - if !exists('#filetypeplugin') - call append('$', ['WARNING: ', 'Python-mode required :filetype plugin indent on', '']) - endif - - if !has('python') - call append('$', ['WARNING: ', 'Python-mode required vim compiled with +python.', - \ '"lint, rope, run, doc, virtualenv" features disabled.', '']) - let python = 0 - endif - - call append('$', 'Pymode variables:') - call append('$', '-------------------') - call append('$', 'let pymode = ' . string(g:pymode)) - if g:pymode - call append('$', 'let pymode_path = ' . string(g:pymode_path)) - if g:pymode_path - call append('$', 'let pymode_paths = ' . string(g:pymode_paths)) - end - - call append('$', 'let pymode_doc = ' . string(g:pymode_doc)) - if g:pymode_doc - call append('$', 'let pymode_doc_key = ' . string(g:pymode_doc_key)) - endif - - call append('$', 'let pymode_run = ' . string(g:pymode_run)) - if g:pymode_run - call append('$', 'let pymode_run_key = ' . string(g:pymode_run_key)) - endif - - call append('$', 'let pymode_lint = ' . string(g:pymode_lint)) - if g:pymode_lint - call append('$', 'let pymode_lint_checker = ' . string(g:pymode_lint_checker)) - call append('$', 'let pymode_lint_ignore = ' . string(g:pymode_lint_ignore)) - call append('$', 'let pymode_lint_select = ' . string(g:pymode_lint_select)) - call append('$', 'let pymode_lint_onfly = ' . string(g:pymode_lint_onfly)) - call append('$', 'let pymode_lint_config = ' . string(g:pymode_lint_config)) - call append('$', 'let pymode_lint_write = ' . string(g:pymode_lint_write)) - call append('$', 'let pymode_lint_cwindow = ' . string(g:pymode_lint_cwindow)) - call append('$', 'let pymode_lint_message = ' . string(g:pymode_lint_message)) - call append('$', 'let pymode_lint_signs = ' . string(g:pymode_lint_signs)) - call append('$', 'let pymode_lint_todo_symbol = ' . string(g:pymode_lint_todo_symbol)) - call append('$', 'let pymode_lint_comment_symbol = ' . string(g:pymode_lint_comment_symbol)) - call append('$', 'let pymode_lint_visual_symbol = ' . string(g:pymode_lint_visual_symbol)) - call append('$', 'let pymode_lint_error_symbol = ' . string(g:pymode_lint_error_symbol)) - call append('$', 'let pymode_lint_info_symbol = ' . string(g:pymode_lint_info_symbol)) - call append('$', 'let pymode_lint_pyflakes_symbol = ' . string(g:pymode_lint_pyflakes_symbol)) - call append('$', 'let pymode_lint_jump = ' . string(g:pymode_lint_jump)) - call append('$', 'let pymode_lint_hold = ' . string(g:pymode_lint_hold)) - call append('$', 'let pymode_lint_minheight = ' . string(g:pymode_lint_minheight)) - call append('$', 'let pymode_lint_maxheight = ' . string(g:pymode_lint_maxheight)) - endif - - call append('$', 'let pymode_rope = ' . string(g:pymode_rope)) - if g:pymode_rope - call append('$', 'let pymode_rope_autocomplete_map = ' . string(g:pymode_rope_autocomplete_map)) - call append('$', 'let pymode_rope_auto_project = ' . string(g:pymode_rope_auto_project)) - call append('$', 'let pymode_rope_auto_project_open = ' . string(g:pymode_rope_auto_project_open)) - end - call append('$', 'let pymode_folding = ' . string(g:pymode_folding)) - call append('$', 'let pymode_breakpoint = ' . string(g:pymode_breakpoint)) - call append('$', 'let pymode_syntax = ' . string(g:pymode_syntax)) - call append('$', 'let pymode_virtualenv = ' . string(g:pymode_virtualenv)) - if g:pymode_virtualenv - call append('$', 'let pymode_virtualenv_enabled = ' . string(g:pymode_virtualenv_enabled)) - endif - call append('$', 'pymode_utils_whitespaces:' . string(g:pymode_utils_whitespaces)) - call append('$', 'pymode_options:' . string(g:pymode_options)) - endif - - if python - call append('$', '') - call append('$', 'VIM python paths:') - call append('$', '-----------------') -python << EOF -vim.command('let l:output = %s' % repr(sys.path)) -EOF - call append('$', output) - call append('$', '') - endif - -endfunction "}}} diff --git a/autoload/pymode/virtualenv.vim b/autoload/pymode/virtualenv.vim index f407adb2..7401e94b 100644 --- a/autoload/pymode/virtualenv.vim +++ b/autoload/pymode/virtualenv.vim @@ -1,37 +1,17 @@ -fun! pymode#virtualenv#Activate() "{{{ +" Support virtualenv +" +PymodePython from pymode.virtualenv import enable_virtualenv - if !exists("$VIRTUAL_ENV") +fun! pymode#virtualenv#init() "{{{ + if !g:pymode_virtualenv || g:pymode_virtualenv_path == "" return endif - for env in g:pymode_virtualenv_enabled - if env == $VIRTUAL_ENV - return 0 - endif - endfor + PymodePython enable_virtualenv() - call add(g:pymode_virtualenv_enabled, $VIRTUAL_ENV) - -Python << EOF -import sys, vim, os - -ve_dir = vim.eval('$VIRTUAL_ENV') -ve_dir in sys.path or sys.path.insert(0, ve_dir) -activate_this = os.path.join(os.path.join(ve_dir, 'bin'), 'activate_this.py') - -# Fix for windows -if not os.path.exists(activate_this): - activate_this = os.path.join(os.path.join(ve_dir, 'Scripts'), 'activate_this.py') - -f = open(activate_this) -try: - source = f.read() -finally: - f.close() - -exec(compile(source, activate_this, 'exec'), dict(__file__=activate_this)) -EOF - - call pymode#WideMessage("Activate virtualenv: ".$VIRTUAL_ENV) +endfunction "}}} +fun! pymode#virtualenv#activate(path) "{{{ + let g:pymode_virtualenv_path = a:path + call pymode#virtualenv#init() endfunction "}}} 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 13767c98..7235b5d5 100644 --- a/doc/pymode.txt +++ b/doc/pymode.txt @@ -1,688 +1,812 @@ -*pymode.txt* *python-mode.txt* Python-mode for vim! +*pymode.txt* For Vim Version 8.0 Last change: 2019 March 08 + + ____ _ _ ____ _ _ _____ _ _ __ __ _____ ____ ____ ~ + ( _ \( \/ )(_ _)( )_( )( _ )( \( )___( \/ )( _ )( _ \( ___) ~ + )___/ \ / )( ) _ ( )(_)( ) ((___)) ( )(_)( )(_) ))__) ~ + (__) (__) (__) (_) (_)(_____)(_)\_) (_/\/\_)(_____)(____/(____) ~ + + + 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* - ____ _ _ ____ _ _ _____ _ _ __ __ _____ ____ ____ ~ - ( _ \( \/ )(_ _)( )_( )( _ )( \( )___( \/ )( _ )( _ \( ___) ~ - )___/ \ / )( ) _ ( )(_)( ) ((___)) ( )(_)( )(_) ))__) ~ - (__) (__) (__) (_) (_)(_____)(_)\_) (_/\/\_)(_____)(____/(____) ~ +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, +refactoring, and some other useful things. - Version: 0.6.19 +This plugin allows you to create python code in vim very easily. There is no +need to install the pylint or rope libraries on your system. -============================================================================== -CONTENTS *Python-mode-contents* +Python-mode contains all you need to develop python applications in Vim. - 1.Intro...................................|PythonMode| - 2.Options.................................|PythonModeOptions| - 2.1.Customisation details.............|PythonModeOptionsDetails| - 2.2.Modeline..........................|PythonModeModeline| - 3.Default Keys............................|PythonModeKeys| - 4.Commands................................|PythonModeCommands| - 5.FAQ.....................................|PythonModeFAQ| - 6.Credits.................................|PythonModeCredits| - 7.License.................................|PythonModeLicense| +Features: *pymode-features* -============================================================================== -1. Intro ~ - *PythonMode* +- 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 ... -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, -refactoring, and some other useful things. -This plugin allows you to create python code in vim very easily. There is no need -to install the pylint or rope libraries on your system. +=============================================================================== +2. Common functionality ~ + *pymode-common* +This script provides the following options that can customizes the behavior of +python-mode. These options should be set in your |vimrc|. -============================================================================== -2. Options ~ - *PythonModeOptions* +Find below the default values: - Note: - Pylint options (ex. disable messages) may be defined in '$HOME/pylint.rc' - See pylint documentation. -This script provides the following options that can customise the behaviour of -PythonMode. These options should be set in your vimrc. +Turn on the whole plugin. *'g:pymode'* +> + let g:pymode = 1 + +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. +> + let g:pymode_paths = [] + +Trim unused white spaces on save. *'g:pymode_trim_whitespaces'* +> + let g:pymode_trim_whitespaces = 1 + +Setup default python options. *'g:pymode_options'* +> + let g:pymode_options = 1 + +If this option is set to 1, pymode will enable the following options for +python buffers: > + + setlocal complete+=t + setlocal formatoptions-=t + if v:version > 702 && !&relativenumber + setlocal number + endif + setlocal nowrap + setlocal textwidth=79 + setlocal commentstring=#%s + setlocal define=^\s*\\(def\\\\|class\\) + +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 -|'pymode'| Turn off the whole plugin +Setup pymode |quickfix| window. -|'pymode_paths'| Additional python paths for pymode + *'g:pymode_quickfix_maxheight'* *'g:pymode_quickfix_minheight'* +> + let g:pymode_quickfix_minheight = 3 + let g:pymode_quickfix_maxheight = 6 -|'pymode_doc'| Turns off the documentation script +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 -|'pymode_doc_key'| Key for show documentation +Set where pymode |preview| window will appear. *'g:pymode_preview_position'* +> + let g:pymode_preview_position = 'botright' -|'pymode_run'| Turns off the run code script +Value is command which can influcece where new window created by `:new` command +will appear, eg. `:botright`. -|'pymode_run_key'| Key for run python code +------------------------------------------------------------------------------- +2.1. Python version ~ + *pymode-python-version* -|'pymode_lint'| Turns off pylint script +By default pymode will attempt to use Python 3, if available. However, you can +also disable all Python features of pymode. -|'pymode_lint_checker'| Switch code checkers (pylint, pyflakes, pep8, mccabe) + *'g:pymode_python'* +> + let g:pymode_python = 'python3' -|'pymode_lint_ignore'| Skip errors and warnings +Values are `python3`, `disable`. If value set to `disable` most +python-features of **pymode** will be disabled. -|'pymode_lint_select'| Select errors and warnings +Set value to `python3` if you are working with python3 projects. You could use +|exrc| -|'pymode_lint_onfly'| Run linter on the fly +------------------------------------------------------------------------------- +2.2 Python indentation ~ + *pymode-indent* -|'pymode_lint_config'| Filepath to pylint configuration +Pymode supports PEP8-compatible python indent. +Enable pymode indentation *'g:pymode_indent'* +> + let g:pymode_indent = 1 -|'pymode_lint_write'| Check code every save -|'pymode_lint_cwindow'| Show cwindow +Customization: -|'pymode_lint_message'| Show current line errors in bottom +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 -|'pymode_lint_signs'| Place signs +------------------------------------------------------------------------------- +2.3 Python folding ~ + *pymode-folding* -|'pymode_lint_todo_symbol'| Todo symbol +Enable pymode folding *'g:pymode_folding'* +> + let g:pymode_folding = 0 + +Currently folding is considered experimental. There are several issues with +its implementation. + +------------------------------------------------------------------------------- +2.4 Vim 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 (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 -|'pymode_lint_comment_symbol'| Comment symbol +------------------------------------------------------------------------------- +2.5 Show documentation ~ + *pymode-documentation* -|'pymode_lint_visual_symbol'| Visual symbol +Pymode could show documentation for current word by `pydoc`. -|'pymode_lint_error_symbol'| Error symbol +Commands: +*:PymodeDoc* — show documentation -|'pymode_lint_info_symbol'| Info symbol +Turns on the documentation script *'g:pymode_doc'* +> + let g:pymode_doc = 1 -|'pymode_lint_pyflakes_symbol'| PyFlakes' info symbol +Bind keys to show documentation for current word (selection) + *'g:pymode_doc_bind'* +> + let g:pymode_doc_bind = 'K' -|'pymode_lint_jump'| Auto jump to first error +------------------------------------------------------------------------------- +2.6 Support virtualenv ~ + *pymode-virtualenv* -|'pymode_lint_hold'| Hold cursor in current window +Commands: +*:PymodeVirtualenv* -- Activate virtualenv (path can be absolute or +relative to current working directory) -|'pymode_lint_minheight'| Minimal height of pylint error window +Enable automatic virtualenv detection *'g:pymode_virtualenv'* +> + let g:pymode_virtualenv = 1 -|'pymode_lint_mccabe_complexity'| Maximum allowed mccabe complexity +Set path to virtualenv manually *'g:pymode_virtualenv_path'* +> + let g:pymode_virtualenv_path = $VIRTUAL_ENV -|'pymode_lint_maxheight'| Maximal height of pylint error window +------------------------------------------------------------------------------- +2.7 Run code ~ + *pymode-run* -|'pymode_rope'| Turns off rope script +Commands: +*:PymodeRun* -- Run current buffer or selection -|'pymode_folding'| Turns on/off python folding +Turn on the run code script *'g:pymode_run'* +> + let g:pymode_run = 1 -|'pymode_breakpoint'| Turns off breakpoint script +Binds keys to run python code *'g:pymode_run_bind'* +> + let g:pymode_run_bind = 'r' -|'pymode_breakpoint_key'| Key for breakpoint +------------------------------------------------------------------------------- +2.8 Breakpoints ~ + *pymode-breakpoints* -|'pymode_virtualenv'| Turns off virtualenv +Pymode automatically detects available debugger (like pdb, ipdb, pudb) and user +can set/unset breakpoint with one key and without code checking and etc. -|'pymode_utils_whitespaces'| Remove unused whitespaces +Enable functionality *'g:pymode_breakpoint'* +> + let g:pymode_breakpoint = 1 -|'pymode_syntax'| Turns off the custom syntax highlighting +Bind keys +> + let g:pymode_breakpoint_bind = 'b' -|'pymode_syntax_all'| Enable all hightlight groups +Manually set breakpoint command (leave empty for automatic detection) +> + let g:pymode_breakpoint_cmd = '' -|'pymode_syntax_print_as_function'| Hightlight `print` as function -|'pymode_syntax_highlight_equal_operator'| Hightlight `=` +=============================================================================== +3. Code checking ~ + *pymode-lint* -|'pymode_syntax_highlight_stars_operator'| Hightlight `*` +Pymode supports `pylint`, `pep257`, `pycodestyle`, `pyflakes`, `mccabe` code +checkers. You could run several similar checkers. -|'pymode_syntax_highlight_self'| Hightlight `self` + Pymode uses Pylama library for code checking. Many options like skip + files, errors and etc could be defined in `pylama.ini` file or modelines. + Check Pylama documentation for details. -|'pymode_syntax_indent_errors'| Hightlight indentation errors + Pylint options (ex. disable messages) may be defined in `$HOME/pylint.rc` + See pylint documentation. -|'pymode_syntax_space_errors'| Hightlight trailing spaces as errors +Commands: +*:PymodeLint* -- Check code in current buffer +*:PymodeLintToggle* -- Toggle code checking +*:PymodeLintAuto* -- Fix PEP8 errors in current buffer automatically -|'pymode_syntax_string_formating'| Hightlight string formating +Turn on code checking *'g:pymode_lint'* +> + let g:pymode_lint = 1 -|'pymode_syntax_string_format'| Hightlight Str.format syntax +Check code on every save (if file has been modified) *'g:pymode_lint_on_write'* +> + let g:pymode_lint_on_write = 1 -|'pymode_syntax_string_templates'| Hightlight string templates +Check code on every save (every) *'g:pymode_lint_unmodified'* +> + let g:pymode_lint_unmodified = 0 -|'pymode_syntax_doc_tests'| Hightlight doctests +Check code when editing (on the fly) *'g:pymode_lint_on_fly'* +> + let g:pymode_lint_on_fly = 0 -|'pymode_syntax_builtin_objs'| Hightlight builtin objects +Show error message if cursor placed at the error line *'g:pymode_lint_message'* +> + let g:pymode_lint_message = 1 -|'pymode_syntax_builtin_types'| Hightlight builtin types +Default code checkers (you could set several) *'g:pymode_lint_checkers'* +> + let g:pymode_lint_checkers = ['pyflakes', 'pycodestyle', 'mccabe'] -|'pymode_syntax_builtin_functions'| Hightlight builtin functions +Values may be chosen from: `pylint`, `pycodestyle`, `mccabe`, `pep257`, `pyflakes`. -|'pymode_syntax_highlight_exceptions'| Hightlight builtin exceptions +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",] -|'pymode_indent'| Enable/Disable pymode PEP8 indentation +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"] -|'pymode_options'| Set default pymode options for - python codding +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 ... +> + let g:pymode_lint_sort = [] -|'pymode_motion'| Enable pymode motion stuff +Auto open cwindow (quickfix) if any errors have been found + *'g:pymode_lint_cwindow'* +> + let g:pymode_lint_cwindow = 1 - Note: - Also see |ropevim.txt| +Place error |signs| *'g:pymode_signs'* +> + let g:pymode_lint_signs = 1 +Definitions for |signs| +> + let g:pymode_lint_todo_symbol = 'WW' + let g:pymode_lint_comment_symbol = 'CC' + let g:pymode_lint_visual_symbol = 'RR' + let g:pymode_lint_error_symbol = 'EE' + let g:pymode_lint_info_symbol = 'II' + let g:pymode_lint_pyflakes_symbol = 'FF' ------------------------------------------------------------------------------- -2.1. Customisation details ~ - *PythonModeOptionsDetails* +------------------------------------------------------------------------------- +3.1 Set code checkers options ~ + *pymode-lint-options* -To enable any of the options below you should put the given line in your -'$HOME/.vimrc'. See |vimrc-intro|. +Pymode has the ability to set code checkers options from pymode variables: ------------------------------------------------------------------------------- -2.2. Modeline ~ - *PythonModeModeline* +Set PEP8 options *'g:pymode_lint_options_pycodestyle'* +> + let g:pymode_lint_options_pycodestyle = + \ {'max_line_length': g:pymode_options_max_line_length} -The VIM modeline `:help modeline` feature allows you to change pymode -options for the current file. Pymode modeline should always be the -last line in the vimrc file and look like: +See https://pep8.readthedocs.org/en/1.4.6/intro.html#configuration for more +info. +Set Pyflakes options *'g:pymode_lint_options_pyflakes'* > - # pymode:lint_ignore=E0202:doc=0:lint_write=0 -< + let g:pymode_lint_options_pyflakes = { 'builtins': '_' } -Examples: +Set mccabe options *'g:pymode_lint_options_mccabe'* +> + let g:pymode_lint_options_mccabe = { 'complexity': 12 } -Disable folding on current file: +Set pep257 options *'g:pymode_lint_options_pep257'* > - # pymode:folding=0 -< + let g:pymode_lint_options_pep257 = {} -Set linters and mccabe complexity. +Set pylint options *'g:pymode_lint_options_pylint'* > - # pymode:lint_checker=pip,mccabe:lint_mccabe_complexity=10 -< + let g:pymode_lint_options_pylint = + \ {'max-line-length': g:pymode_options_max_line_length} -These changes will work only in the current buffer. +See http://docs.pylint.org/features.html#options for more info. ------------------------------------------------------------------------------- - *'pymode'* -Values: 0 or 1. -Default: 1. -If this option is set to 0 then the whole plugin is disabled +=============================================================================== +4. Rope support ~ + *pymode-rope* ------------------------------------------------------------------------------- - *'pymode_paths'* -Values: List of strings -Default: []. +Pymode supports Rope refactoring operations, code completion and code assists. -This option sets additional python import paths +Commands: +|:PymodeRopeAutoImport| -- Resolve import for element under cursor +|:PymodeRopeModuleToPackage| -- Convert current module to package +|:PymodeRopeNewProject| -- Open new Rope project in current working directory +|:PymodeRopeRedo| -- Redo changes from last refactoring +|:PymodeRopeRegenerate| -- Regenerate the project cache +|:PymodeRopeRenameModule| -- Rename current module +|:PymodeRopeUndo| -- Undo changes from last refactoring ------------------------------------------------------------------------------- - *'pymode_doc'* -Values: 0 or 1. -Default: 1. -If this option is set to 0 then the doc script is disabled. +Turn on the rope script *'g:pymode_rope'* +> + let g:pymode_rope = 1 ------------------------------------------------------------------------------- - *'pymode_doc_key'* -Default: 'K'. +Set the prefix for rope commands *'g:pymode_rope_prefix'* +> + let g:pymode_rope_refix = '' -Set the key to show the show python documentation. +.ropeproject Folder ~ + *.ropeproject* ------------------------------------------------------------------------------- - *'pymode_run'* -Values: 0 or 1. -Default: 1. +*:PymodeRopeNewProject* [] -- Open new Rope project in the given path +*:PymodeRopeRegenerate* -- Regenerate the project cache -If this option is set to 0 then the run script is disabled. +Rope uses a folder inside projects for holding project configuration and data. +Its default name is `.ropeproject`. It is recommended that you do not add the +.ropeproject folder to version control system. ------------------------------------------------------------------------------- - *'pymode_run_key'* -Default: 'r'. +Currently it is used for things such as: -Set the key for running python code. +* The config.py file in this folder contains project configuration. Have + a look at the default config.py file (which is created when it + does not exist) for more information. +* It can be used for saving project history, so that the next time you open the + project you can undo past changes. +* It can be used to save information about object inferences. +* It can be used to save a global name cache, which is used for auto-import. ------------------------------------------------------------------------------- - *'pymode_lint'* -Values: 0 or 1. -Default: 1. +By default, if `.ropeproject` is not found in the current directory, rope will +look recursively for it in parent folders. -If this option is set to 0 then the pylint script is disabled. +Warning: If rope finds `.ropeproject` in a parent dir, it will use it with +all its child directories, which may slow scanning down (because of many, +possibly unrelated, files) ------------------------------------------------------------------------------- - *'pymode_lint_checker'* -Values: "pylint", "pyflakes", "pep8", "mccabe", "pep257" - You can set many checkers. E.g. "pyflakes,pep8,mccabe" ~ +Enable searching for |.ropeproject| in parent directories + *'g:pymode_rope_lookup_project'* +> + let g:pymode_rope_lookup_project = 0 -Default: "pyflakes,pep8,mccabe". +You can also manually set the rope project directory. If not specified rope will +use the current directory. + *'g:pymode_rope_project_root'* +> + let g:pymode_rope_project_root = "" -This option sets code checkers. ------------------------------------------------------------------------------- - *'pymode_lint_ignore'* -Values: IDs of errors, separated by commas or empty strings - E.g. "E501,W002", "E2,W" (Skip all Warnings and Errors startswith E2) and etc ~ +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 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'* +> + let g:pymode_rope_ropefolder='.ropeproject' -Default: "". -Skip errors and warnings. -See also: |'pymode_lint_select'|, |'pymode_lint_config'| +Show documentation for element under cursor ~ ------------------------------------------------------------------------------- - *'pymode_lint_select'* -Values: IDs of errors, separated by commas or empty strings - E.g. "W002,C" Force W002 and all C-ids ~ +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' -Default: "". +Regenerate project cache on every save (if file has been modified) +> + let g:pymode_rope_regenerate_on_write = 1 -Select errors and warnings. -See also: |'pymode_lint_ignore'|, |'pymode_lint_config'| +------------------------------------------------------------------------------- +4.1 Completion ~ + *pymode-completion* ------------------------------------------------------------------------------- - *'pymode_lint_onfly'* -Values: 0 or 1 -Default: 0 +By default you can use for autocompletion. The first entry will +be automatically selected and you can press to insert the entry in +your code. and / works too. -This option enables "on the fly" code checking +Autocompletion is also called by typing a period in |Insert| mode by default. ------------------------------------------------------------------------------- - *'pymode_lint_config'* -Values: 'Path to pylint configuration file' -Default: "$HOME/.pylintrc" +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 -This option sets the path to the pylint configuration file. If the -file is not found, use the 'pylintrc' file from python-mode sources. +Turn on code completion support in the plugin *'g:pymode_rope_completion'* +> + let g:pymode_rope_completion = 1 -See also: |'pymode_lint_ignore'|, |'pymode_lint_select'| +Turn on autocompletion when typing a period + *'g:pymode_rope_complete_on_dot'* +> + let g:pymode_rope_complete_on_dot = 1 ------------------------------------------------------------------------------- - *'pymode_lint_write'* -Values: 0 or 1. -Default: 1. +Keymap for autocomplete *'g:pymode_rope_completion_bind'* +> + let g:pymode_rope_completion_bind = '' -If this option is set to 0, then pylint auto-checking on every save is -disabled. +Extended autocompletion (rope could complete objects which have not been +imported) from project *'g:pymode_rope_autoimport'* +> + let g:pymode_rope_autoimport = 0 ------------------------------------------------------------------------------- - *'pymode_lint_cwindow'* -Values: 0 or 1. -Default: 1. +Load modules to autoimport by default *'g:pymode_rope_autoimport_modules'* +> + let g:pymode_rope_autoimport_modules = ['os', 'shutil', 'datetime'] -If this option is set to 0 then pylint will not show cwindow. +Offer to unresolved import object after completion. +> + let g:pymode_rope_autoimport_import_after_complete = 0 ------------------------------------------------------------------------------- - *'pymode_lint_message'* -Values: 0 or 1. -Default: 1. -If this option is set to 0 then pylint will not show errors at bottom. +------------------------------------------------------------------------------- +4.2 Find definition ~ + *pymode-rope-findit* ------------------------------------------------------------------------------- - *'pymode_lint_signs'* -Values: 0 or 1. -Default: 1. +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'* +> + let g:pymode_rope_goto_definition_bind = 'g' -If this option is set to 0 then pylint will not place error signs. +Command for open window when definition has been found +Values are (`e`, `new`, `vnew`) *'g:pymode_rope_goto_definition_cmd'* +> + let g:pymode_rope_goto_definition_cmd = 'new' ------------------------------------------------------------------------------- - *'pymode_lint_todo_symbol'* -Values: Symbol for todo signs. -Default: "WW". +------------------------------------------------------------------------------- +4.3 Refactoring ~ + *pymode-rope-refactoring* -This option sets the symbol to use in the gutter to display todo signs. +Rename method/function/class/variable in the project ~ ------------------------------------------------------------------------------- - *'pymode_lint_comment_symbol'* -Values: Symbol for comment signs. -Default: "CC". +Pymode can rename everything: classes, functions, modules, packages, methods, +variables and keyword arguments. -This option sets the symbol to use in the gutter to display comment signs. +Keymap for rename method/function/class/variables under cursor + *'g:pymode_rope_rename_bind'* +> + let g:pymode_rope_rename_bind = 'rr' ------------------------------------------------------------------------------- - *'pymode_lint_visual_symbol'* -Values: Symbol for visual signs. -Default: "RR". -This option sets the symbol to use in the gutter to display visual signs. +Rename a current module/package ~ ------------------------------------------------------------------------------- - *'pymode_lint_error_symbol'* -Values: Symbol for error signs. -Default: "EE". +*:PymodeRopeRenameModule* -- Rename current module -This option sets the symbol to use in the gutter to display error signs. +Keymap for rename current module *'g:pymode_rope_rename_module_bind'* +> + let g:pymode_rope_rename_module_bind = 'r1r' ------------------------------------------------------------------------------- - *'pymode_lint_info_symbol'* -Values: Symbol for info signs. -Default: "II". -This option sets the symbol to use in the gutter to display info signs. +Imports ~ ------------------------------------------------------------------------------- - *'pymode_lint_pyflakes_symbol'* -Values: Symbol for PyFlakes' info signs. -Default: "FF". +*:PymodeRopeAutoImport* -- Resolve import for element under cursor -This option sets the symbol to use in the gutter to display PyFlakes' info -signs. +Organize imports sorts imports, too. It does that according to PEP8. Unused +imports will be dropped. +Keymap *'g:pymode_rope_organize_imports_bind'* +> + let g:pymode_rope_organize_imports_bind = 'ro' ------------------------------------------------------------------------------- - *'pymode_lint_jump'* -Values: 0 or 1. -Default: 0. +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' -If this option is set to 0 then pylint will not jump to the first error. ------------------------------------------------------------------------------- - *'pymode_lint_hold'* -Values: 0 or 1. -Default: 0. +Convert module to package ~ + *'g:pymode_rope_module_to_package_bind'* -If this option is set to 0 then pylint will switch on the quickfix window when -it opens. Doesn't work when |'pymode_lint_jump'| is enabled. +*:PymodeRopeModuleToPackage* -- convert current module to package ------------------------------------------------------------------------------- - *'pymode_lint_minheight'* -Values: int -Default: 3. +Keybinding: +> + let g:pymode_rope_module_to_package_bind = 'r1p' -Set minimal height for the pylint cwindow. ------------------------------------------------------------------------------- - *'pymode_lint_mccabe_complexity'* -Values: int -Default: 8. +Extract method/variable ~ + *pymode-rope-extract* -Set minimal complexity for the mccabe linter. +Extract method/variable from selected lines. ------------------------------------------------------------------------------- - *'pymode_lint_maxheight'* -Values: int -Default: 6. + *'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' -Set maximal height for the pylint cwindow. ------------------------------------------------------------------------------- - *'pymode_rope'* -Values: 0 or 1. -Default: 1. +Use function ~ + *pymode-rope-use* -If this option is set to 0 then the rope script is disabled. +It tries to find the places in which a function can be used and changes the +code to call it instead. +> + let g:pymode_rope_use_function_bind = 'ru' ------------------------------------------------------------------------------- - *'pymode_breakpoint'* -Values: 0 or 1. -Default: 1. -If this option is set to 0 then the breakpoint script is disabled. +Move refactoring ~ + *pymode-rope-move* ------------------------------------------------------------------------------- - *'pymode_breakpoint_key'* -Default: 'b'. +Moving method/fields -Key for setting/unsetting breakpoints. +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. ------------------------------------------------------------------------------- - *'pymode_virtualenv'* -Values: 0 or 1. -Default: 1. +Moving global variable/class/function into another module -If this option is set to 0 then virtualenv support is disabled. +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. ------------------------------------------------------------------------------- - *'pymode_utils_whitespaces'* -Values: 0 or 1. -Default: 1. +Moving module variable/class/function into a package -Auto-remove unused whitespaces. +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. ------------------------------------------------------------------------------- - *'pymode_syntax'* -Values: 0 or 1. -Default: 1. +> + let g:pymode_rope_move_bind = 'rv' -If this option is set to 0 then the custom syntax highlighting will -not be used. +Change function signature ~ +> + let g:pymode_rope_change_signature_bind = 'rs' ------------------------------------------------------------------------------- - *'pymode_syntax_all'* -Values: 0 or 1. -Default: 1. -Enabling all hightlight groups. +------------------------------------------------------------------------------- +4.4 Undo/Redo changes ~ + *pymode-rope-undo* + *pymode-rope-redo* ------------------------------------------------------------------------------- - *'pymode_syntax_print_as_function'* -Values: 0 or 1. -Default: 0. +Commands: -Hightlight `print` as function +*:PymodeRopeUndo* -- Undo last changes in the project +*:PymodeRopeRedo* -- Redo last changes in the project ------------------------------------------------------------------------------- - *'pymode_syntax_highlight_equal_operator'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. -Hightlight `=` +=============================================================================== +5. Syntax ~ + *pymode-syntax* ------------------------------------------------------------------------------- - *'pymode_syntax_highlight_stars_operator'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Turn on pymode syntax *'g:pymode_syntax'* +> + let g:pymode_syntax = 1 -Hightlight `*` +Slower syntax synchronization that is better at handling code blocks in +docstrings. Consider disabling this on slower hardware. + *'g:pymode_syntax_slow_sync'* +> + let g:pymode_syntax_slow_sync = 1 ------------------------------------------------------------------------------- - *'pymode_syntax_highlight_self'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Enable all python highlights *'g:pymode_syntax_all'* +> + let g:pymode_syntax_all = 1 -Hightlight `self` +Highlight "print" as a function *'g:pymode_syntax_print_as_function'* +> + let g:pymode_syntax_print_as_function = 0 ------------------------------------------------------------------------------- - *'pymode_syntax_indent_errors'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Highlight "async/await" keywords *'g:pymode_syntax_highlight_async_await'* +> + let g:pymode_syntax_highlight_async_await = g:pymode_syntax_all -Hightlight indentation errors +Highlight '=' operator *'g:pymode_syntax_highlight_equal_operator'* +> + let g:pymode_syntax_highlight_equal_operator = g:pymode_syntax_all ------------------------------------------------------------------------------- - *'pymode_syntax_space_errors'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Highlight ':=' operator *'g:pymode_syntax_highlight_walrus_operator'* +> + let g:pymode_syntax_highlight_walrus_operator = g:pymode_syntax_all -Hightlight trailing spaces as errors +Highlight '*' operator *'g:pymode_syntax_highlight_stars_operator'* +> + let g:pymode_syntax_highlight_stars_operator = g:pymode_syntax_all ------------------------------------------------------------------------------- - *'pymode_syntax_string_formating'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Highlight 'self' keyword *'g:pymode_syntax_highlight_self'* +> + let g:pymode_syntax_highlight_self = g:pymode_syntax_all -Hightlight string formating +Highlight indent's errors *'g:pymode_syntax_indent_errors'* +> + let g:pymode_syntax_indent_errors = g:pymode_syntax_all ------------------------------------------------------------------------------- - *'pymode_syntax_string_format'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Highlight space's errors *'g:pymode_syntax_space_errors'* +> + let g:pymode_syntax_space_errors = g:pymode_syntax_all -Hightlight Str.format syntax +Highlight string formatting *'g:pymode_syntax_string_formatting'* + *'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 ------------------------------------------------------------------------------- - *'pymode_syntax_string_templates'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Highlight builtin objects (True, False, ...) *'g:pymode_syntax_builtin_objs'* +> + let g:pymode_syntax_builtin_objs = g:pymode_syntax_all -Hightlight string templates +Highlight builtin types (str, list, ...) *'g:pymode_syntax_builtin_types'* +> + let g:pymode_syntax_builtin_types = g:pymode_syntax_all ------------------------------------------------------------------------------- - *'pymode_syntax_string_doctests'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Highlight exceptions (TypeError, ValueError, ...) + *'g:pymode_syntax_highlight_exceptions'* +> + let g:pymode_syntax_highlight_exceptions = g:pymode_syntax_all -Hightlight doctests +Highlight docstrings as pythonDocstring (otherwise as pythonString) + *'g:pymode_syntax_docstrings'* +> + let g:pymode_syntax_docstrings = g:pymode_syntax_all ------------------------------------------------------------------------------- - *'pymode_syntax_builtin_objs'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. -Hightlight builtin objects +=============================================================================== +6. FAQ ~ + *pymode-faq* ------------------------------------------------------------------------------- - *'pymode_syntax_builtin_types'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +1. Python-mode doesn't work +--------------------------- -Hightlight builtin types +First remember to get the latest and updated version of the project source +code and also update the project submodules. ------------------------------------------------------------------------------- - *'pymode_syntax_builtin_functions'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Clear all python cache/compiled files (`*.pyc` files and `__pycache__` +directory and everything under it). In Linux/Unix/MacOS you can run: -Hightlight builtin functions +`find . -type f -name '*.pyc' -delete && find . -type d -name '__pycache__' -delete` ------------------------------------------------------------------------------- - *'pymode_syntax_highlight_exceptions'* -Values: 0 or 1. -Default: |'pymode_syntax_all'|. +Then start python mode with: +`vim -i NONE -u /debugvimrc.vim` -Hightlight builtin exceptions +Reproduce the error and submit your python mode debug file. You can check its +location with `:messages` for something like: ------------------------------------------------------------------------------- - *'pymode_indent'* -Values: 0 or 1. -Default: 1. +`pymode debug msg 1: Starting debug on: 2017-11-18 16:44:13 with file /tmp/pymode_debug_file.txt` -If this option is set to 1, pymode will enable python indentation support. +Please submit the entire content of the file along with a reasoning of why the +plugin seems broken. ------------------------------------------------------------------------------- - *'pymode_folding'* -Values: 0 or 1. -Default: 1. + *Underlined do check for sensitive information in the file before + *Underlined submitting! -If this option is set to 1, pymode will enable python-folding. ------------------------------------------------------------------------------- - *'pymode_options'* -Values: 0 or 1. -Default: 1. -If this option is set to 1, pymode will enable the following options for python -buffers: > +2. Rope completion is very slow *pymode-rope-slow* +------------------------------- - setlocal complete+=t - setlocal formatoptions-=t - setlocal number - setlocal nowrap - setlocal textwidth=80 - setlocal commentstring=#%s -< ------------------------------------------------------------------------------- - *'pymode_motion'* -Values: 0 or 1. -Default: 1. - -If this option is set to 1, pymode will enable some python motions. -Pymode-motion is beta. - -================ ============================ -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) -================ ============================ - - -============================================================================== -3. Default Keys ~ - *PythonModeKeys* - -To redefine keys, see: |PythonModeOptions| - -================ ============================ -Key Command -================ ============================ -K Show python docs for current word under cursor (`g:pymode_doc_key`) -C-Space Rope code assist (`g:pymode_rope_autocomplete_map`) -r Run current buffer (`g:pymode_run_key`) -b Set breakpoints (`g:pymode_breakpoint_key`) -[[ 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 C Operation with a class. - Ex: vaC, daC, dC, yaC, yC, caC, cC (normal, operator modes) -iC Operation with inner class. - Ex: viC, diC, yiC, ciC (normal, operator modes) -aM M Operation with function or method. - Ex: vaM, daM, dM, yaM, yM, caM, cM (normal, operator modes) -iM Operation with inner function or method. - Ex: viM, diM, yiM, ciM (normal, operator modes) -================ ============================ - - Note: - Also see: |RopeShortcuts| - - -============================================================================== -4. Commands ~ - *PythonModeCommands* - -*:Pydoc* *Pydoc* - Show python documentation - -*:PyLintToggle* *PyLintToggle* - Enable, disable pylint - -*:PyLint* *PyLint* - Check current buffer - -*:PyLintAuto* *PyLintAuto* - Automatically fix PEP8 errors in the current buffer - -*:Pyrun* *Pyrun* - Run current buffer - - -============================================================================== -5. FAQ ~ - *PythonModeFAQ* - -Python-mode doesn't work ------------------------- - -Open any python file and run ":call pymode#troubleshooting#Test()", -fix the warning or send me the output. - - -Rope completion is very slow ----------------------------- +Rope creates a project-level service directory in |.ropeproject| -To work, rope_ creates a service directory: `.ropeproject`. If -|'pymode_rope_guess_project'| is set on (as it is by default) and -`.ropeproject` is not found in the current dir, rope will scan for -`.ropeproject` in every dir in the parent path. If rope finds `.ropeproject` -in parent dirs, rope sets projectis for all child dirs and the scan may be -slow for many dirs and files. +If ``.ropeproject`` is not found in the current directory, rope will walk +upwards looking for a ``.ropeproject`` in every dir of the parent path. If +rope finds ``.ropeproject`` in a parent dir, it sets the project for all child +dirs and the scan may be slow for so many dirs and files. Solutions: -- Disable |'pymode_rope_guess_project'| to make rope always create - `.ropeproject` in the current dir. - Delete `.ropeproject` from the parent dir to make rope create `.ropeproject` in the current dir. -- Press `po` or `:RopeOpenProject` to force rope to create `.ropeproject` - in the current dir. +- Run ``:PymodeRopeNewProject`` to make rope create ``.ropeproject`` in the + current dir. +- Set |'g:pymode_rope_lookup_project'| to 0 for prevent searching in parent + dirs. +You may also set |'g:pymode_rope_project_root'| to manually specify the project +root path. -Pylint check is very slow -------------------------- -In some projects pylint_ may check slowly, because it also scans imported -modules if possible. Try using pyflakes: see |'pymode_lint_checker'|. +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 +|'g:pymode_lint_checkers'|. You may set |exrc| and |secure| in your |vimrc| to auto-set custom settings from `.vimrc` from your projects directories. -> - Example: On Flask projects I automatically set - 'g:pymode_lint_checker = "pyflakes"'. - On Django 'g:pymode_lint_checker = "pylint"' -< -OSX cannot import urandom -------------------------- +4. OSX cannot import urandom +---------------------------- See: https://groups.google.com/forum/?fromgroups=#!topic/vim_dev/2NXKF6kDONo @@ -694,11 +818,53 @@ The sequence of commands that fixed this: brew install -v --force macvim brew link macvim brew link python -< -============================================================================== -6. Credits ~ - *PythonModeCredits* +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/ @@ -712,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 @@ -732,18 +899,20 @@ The sequence of commands that fixed this: http://github.com/hynek/vim-python-pep8-indent -============================================================================== -7. License ~ - *PythonModeLicense* +=============================================================================== +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 :) -My address is: "Russia, 143401, Krasnogorsk, Shkolnaya 1-19" to "Kirill Klenov". -Thanks for your support! +If you like this plugin, I would very appreciated if you kindly send me a +postcard :) + +My address is: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill +Klenov". Thanks for your support! ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- - vim:tw=78:ts=8:ft=help:norl: + vim:tw=79:ts=8:ft=help:norl: diff --git a/doc/ropevim.txt b/doc/ropevim.txt deleted file mode 100644 index 0bf13bb8..00000000 --- a/doc/ropevim.txt +++ /dev/null @@ -1,340 +0,0 @@ -*ropevim.txt* *Ropevim* Rope in VIM - -============================================================================== -CONTENTS *Rope contents* - - 1.Refactoring Dialog......................|RopeRefactoringDialog| - 2.Finding Files...........................|RopeFindingFiles| - 3.Code Assist.............................|RopeCodeAssist| - 4.Enabling Autoimport.....................|RopeEnablingAutoimport| - 5.Filtering Resources.....................|RopeFilteringResources| - 6.Finding Occurrences.....................|RopeFindOccurrences| - 7.Dialog Batchset Command.................|RopeDialogBatchsetCommand| - 8.Variables...............................|RopeVariables| - 9.Keybindings.............................|RopeKeys| - - -============================================================================== -1. Refactoring Dialog ~ - *RopeRefactoringDialog* - -Ropevim refactorings use a special kind of dialog. Depending on the -refactoring, you'll be asked about the essential information a -refactoring needs to know (like the new name in rename refactoring). - -Next you'll see the base prompt of a refactoring dialog that shows -something like "Choose what to do". By entering the name of a -refactoring option you can set its value. After setting each option -you'll be returned back to the base prompt. Finally, you can ask rope -to perform, preview or cancel the refactoring. - -See |RopeKeys| section and try the refactorings yourself. - - -============================================================================== -2. Finding Files ~ - *RopeFindingFiles* - *:RopeFindFile* - *:RopeFindFileOtherWindow* - -By using |:RopeFindFile| (" p f" by default), you can search for -files in your project. When you complete the minibuffer you'll see -all files in the project; files are shown as their reversed paths. -For instance ``projectroot/docs/todo.txt`` is shown like -``todo.txt p 4 f") opens the -file in the other window. - - -============================================================================== -3. Code Assist ~ - *RopeCodeAssist* - *:RopeCodeAssist* - *:RopeLuckyAssist* - *'pymode_rope_vim_completion'* - *'pymode_rope_extended_complete'* - -|:RopeCodeAssist| command () will let you select from a list -of completions. |:RopeLuckyAssist| command () does not ask -anything; instead, it inserts the first proposal. - -You can tell ropevim to use vim's complete function in insert mode; -Add: > - - let pymode_rope_vim_completion=1 -< -to your '~/.vimrc' file. - - Note: - That when this variable is set, autoimport completions no longer - work since they need to insert an import to the top of the module, - too. - -By default autocomplete feature will use plain list of proposed completion -items. You can enable showing extended information about completion -proposals by setting : > - - let pymode_rope_extended_complete=1 -< -Completion menu list will show the proposed name itself, one letter which -shows where this proposal came from (it can be "L" for locals, "G" for -globals, "B" for builtins, or empty string if such scope definition is not -applicable), a short object type description (such as "func", "param", -"meth" and so forth) and a first line of proposed object's docstring (if it -has one). For function's keyword parameters the last field shows "*" symbol -if this param is required or "= " if it is not. - - -============================================================================== -4. Enabling Autoimport ~ - *RopeEnablingAutoimport* - *:RopevimAutoImport* - *:RopeGenerateAutoimportCache* - -Rope can propose and automatically import global names in other -modules. Rope maintains a cache of global names for each project. It -updates the cache only when modules are changed; if you want to cache -all your modules at once, use |:RopeGenerateAutoimportCache|. It -will cache all of the modules inside the project plus those whose -names are listed in |'pymode_rope_autoimport_modules'| list: > - - " add the name of modules you want to autoimport - let g:pymode_rope_autoimport_modules = ["os", "shutil"] -< -Now if you are in a buffer that contains: > - - rmtree -< - -and you execute |:RopevimAutoImport| you'll end up with: > - - from shutil import rmtree - rmtree -< -Also |:RopeCodeAssist| and |:RopeLuckyAssist| propose auto-imported -names by using "name : module" style. Selecting them will import -the module automatically. - - -============================================================================== -5. Filtering Resources ~ - *RopeFilteringResources* - -Some refactorings, restructuring and find occurrences take an option -called resources. This option can be used to limit the resources on -which a refactoring should be applied. - -It uses a simple format: each line starts with either '+' or '-'. -Each '+' means include the file (or its children if it's a folder) -that comes after it. '-' has the same meaning for exclusion. So -using: > - - +rope - +ropetest - -rope/contrib -< -means include all python files inside ``rope`` and ``ropetest`` -folders and their subfolder, but those that are in ``rope/contrib``. -Or: > - - -ropetest - -setup.py -< -means include all python files inside the project but ``setup.py`` and -those under ``ropetest`` folder. - - -============================================================================== -6. Finding Occurrences ~ - *RopeFindOccurrences* - -The find occurrences command (" f" by default) can be used to -find the occurrences of a python name. If ``unsure`` option is -``yes``, it will also show unsure occurrences; unsure occurrences are -indicated with a ``?`` mark in the end. - - Note: - That ropevim uses the quickfix feature of vim for - marking occurrence locations. - - -============================================================================== -7. Dialog Batchset Command ~ - *RopeDialogBatchsetCommand* - -When you use ropevim dialogs there is a command called ``batchset``. -It can set many options at the same time. After selecting this -command from dialog base prompt, you are asked to enter a string. - -``batchset`` strings can set the value of configs in two ways. The -single line form is like this: > - - name1 value1 - name2 value2 -< - -That is the name of config is followed its value. For multi-line -values you can use: > - - name1 - line1 - line2 - - name2 - line3 -< -Each line of the definition should start with a space or a tab. - Note: - That blank lines before the name of config definitions are ignored. - -``batchset`` command is useful when performing refactorings with long -configs, like restructurings: > - - pattern ${pycore}.create_module(${project}.root, ${name}) - - goal generate.create_module(${project}, ${name}) - - imports - from rope.contrib import generate - - args - pycore: type=rope.base.pycore.PyCore - project: type=rope.base.project.Project -< -.. ignore the two-space indents - -This is a valid ``batchset`` string for restructurings. - -Just for the sake of completeness, the reverse of the above -restructuring can be: > - - pattern ${create_module}(${project}, ${name}) - - goal ${project}.pycore.create_module(${project}.root, ${name}) - - args - create_module: name=rope.contrib.generate.create_module - project: type=rope.base.project.Project -< - -============================================================================== -8. Variables ~ - *RopeVariables* - -*'pymode_rope_codeassist_maxfixes'* The maximum number of syntax errors - to fix for code assists. - The default value is `1`. - -*'pymode_rope_local_prefix'* The prefix for ropevim refactorings. - Defaults to ` r`. - -*'pymode_rope_global_prefix'* The prefix for ropevim project commands - Defaults to ` p`. - -*'pymode_rope_enable_shortcuts'* Shows whether to bind ropevim shortcuts keys. - Defaults to `1`. - -*'pymode_rope_guess_project'* If non-zero, ropevim tries to guess and - open the project that contains the file on which - a ropevim command is performed when no project - is already open. - -*'pymode_rope_enable_autoimport'* Shows whether to enable autoimport. - -*'pymode_rope_autoimport_modules'* The name of modules whose global names should - be cached. |:RopeGenerateAutoimportCache| reads - this list and fills its cache. - -*'pymode_rope_autoimport_underlineds'* If set, autoimport will cache names starting - with underlines, too. - -*'pymode_rope_goto_def_newwin'* If set, ropevim will open a new buffer - for "go to definition" result if the definition - found is located in another file. By default the - file is open in the same buffer. - Values: '' -- same buffer, 'new' -- - horizontally split, 'vnew' -- - vertically split - -*'pymode_rope_always_show_complete_menu'* If set, rope autocompletion menu -always show. - - -============================================================================== -9. Keybinding ~ - *RopeKeys* - -Uses almost the same keybinding as ropemacs. - Note: - That global commands have a ` p` prefix and local commands - have a `` r`` prefix. - You can change that (see |RopeVariables| section). - - -================ ============================ -Key Command -================ ============================ -C-x p o |:RopeOpenProject| -C-x p k |:RopeCloseProject| -C-x p f |:RopeFindFile| -C-x p 4 f |:RopeFindFileOtherWindow| -C-x p u |:RopeUndo| -C-x p r |:RopeRedo| -C-x p c |:RopeProjectConfig| -C-x p n [mpfd] |:RopeCreate|(Module|Package|File|Directory) - |:RopeWriteProject| - -C-c r r |:RopeRename| -C-c r l |:RopeExtractVariable| -C-c r m |:RopeExtractMethod| -C-c r i |:RopeInline| -C-c r v |:RopeMove| -C-c r x |:RopeRestructure| -C-c r u |:RopeUseFunction| -C-c r f |:RopeIntroduceFactory| -C-c r s |:RopeChangeSignature| -C-c r 1 r |:RopeRenameCurrentModule| -C-c r 1 v |:RopeMoveCurrentModule| -C-c r 1 p |:RopeModuleToPackage| - -C-c r o |:RopeOrganizeImports| -C-c r n [vfcmp] |:RopeGenerate|(Variable|Function|Class|Module|Package) - -C-c r a / |:RopeCodeAssist| -C-c r a g |:RopeGotoDefinition| -C-c r a d |:RopeShowDoc| -C-c r a f |:RopeFindOccurrences| -C-c r a ? |:RopeLuckyAssist| -C-c r a j |:RopeJumpToGlobal| -C-c r a c |:RopeShowCalltip| - |:RopeAnalyzeModule| - - |:RopeAutoImport| - |:RopeGenerateAutoimportCache| -=============== ============================ - - -============================================================================== -10. Shortcuts ~ - *RopeShortcuts* - -Some commands are used very frequently; specially the commands in -code-assist group. You can define your own shortcuts like this: > - - :map g :call RopeGotoDefinition() - -< - -================ ============================ -Key Command -================ ============================ - |:RopeCodeAssist| - |:RopeLuckyAssist| - g |:RopeGotoDefinition| - d |:RopeShowDoc| - f |:RopeFindOccurrences| -================ ============================ - ------------------------------------------------------------------------------- - - vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: diff --git a/ftplugin/python/init-pymode.vim b/ftplugin/python/init-pymode.vim deleted file mode 100644 index f96c434a..00000000 --- a/ftplugin/python/init-pymode.vim +++ /dev/null @@ -1,357 +0,0 @@ -if pymode#Default('g:pymode_init', 1) - finish -endif - -call pymode#Default('g:pymode_test', 0) - -let g:pymode_version = "0.6.19" - -com! PymodeVersion echomsg "Current python-mode version: " . g:pymode_version - -" OPTION: g:pymode -- bool. Run pymode. -if pymode#Default('g:pymode', 1) || !g:pymode - " DESC: Disable script loading - finish -endif - -" DESC: Check python support -if !has('python') && !has('python3') - let g:pymode_virtualenv = 0 - let g:pymode_path = 0 - let g:pymode_lint = 0 - let g:pymode_doc = 0 - let g:pymode_breakpoint = 0 - let g:pymode_rope = 0 - let g:pymode_run = 0 -endif - -if has('python') - command! -nargs=1 Python python -elseif has('python3') - command! -nargs=1 Python python3 -end - -" Virtualenv {{{ - -if !pymode#Default("g:pymode_virtualenv", 1) || g:pymode_virtualenv - - call pymode#Default("g:pymode_virtualenv_enabled", []) - - " Add virtualenv paths - call pymode#virtualenv#Activate() - -endif - -" }}} - - -" DESC: Add pymode's pylibs to sys.path {{{ -if !pymode#Default('g:pymode_path', 1) || g:pymode_path - - call pymode#Default('g:pymode_paths', []) - call pymode#path#Activate(expand(":p:h:h:h")) - -endif " }}} - - -" Lint {{{ - -if !pymode#Default("g:pymode_lint", 1) || g:pymode_lint - - let g:qf_list = [] - let g:pymode_lint_buffer = 0 - - " OPTION: g:pymode_lint_write -- bool. Check code every save. - call pymode#Default("g:pymode_lint_write", 1) - - " OPTION: g:pymode_lint_async -- bool. Run a checkers asynchronously - call pymode#Default("g:pymode_lint_async", 1) - - " OPTION: g:pymode_lint_onfly -- bool. Check code every save. - call pymode#Default("g:pymode_lint_onfly", 0) - - " OPTION: g:pymode_lint_message -- bool. Show current line error message - call pymode#Default("g:pymode_lint_message", 1) - - " OPTION: g:pymode_lint_checker -- str. Choices are: pylint, pyflakes, pep8, mccabe - call pymode#Default("g:pymode_lint_checker", "pyflakes,pep8,mccabe") - - " OPTION: g:pymode_lint_config -- str. Path to pylint config file - call pymode#Default("g:pymode_lint_config", $HOME . "/.pylintrc") - - " OPTION: g:pymode_lint_cwindow -- bool. Auto open cwindow if errors find - call pymode#Default("g:pymode_lint_cwindow", 1) - - " OPTION: g:pymode_lint_jump -- int. Jump on first error. - call pymode#Default("g:pymode_lint_jump", 0) - - " OPTION: g:pymode_lint_hold -- int. Hold cursor on current window when - " quickfix open - call pymode#Default("g:pymode_lint_hold", 0) - - " OPTION: g:pymode_lint_minheight -- int. Minimal height of pymode lint window - call pymode#Default("g:pymode_lint_minheight", 3) - - " OPTION: g:pymode_lint_maxheight -- int. Maximal height of pymode lint window - call pymode#Default("g:pymode_lint_maxheight", 6) - - " OPTION: g:pymode_lint_ignore -- string. Skip errors and warnings (e.g. E4,W) - call pymode#Default("g:pymode_lint_ignore", "") - - " OPTION: g:pymode_lint_select -- string. Select errors and warnings (e.g. E4,W) - call pymode#Default("g:pymode_lint_select", "") - - " OPTION: g:pymode_lint_mccabe_complexity -- int. Maximum allowed complexity - call pymode#Default("g:pymode_lint_mccabe_complexity", 8) - - " OPTION: g:pymode_lint_signs_always_visible -- bool. Always show the - " errors ruller, even if there's no errors. - call pymode#Default("g:pymode_lint_signs_always_visible", 0) - - " OPTION: g:pymode_lint_todo_symbol -- string. Todo symbol. - call pymode#Default("g:pymode_lint_todo_symbol", "WW") - - " OPTION: g:pymode_lint_comment_symbol -- string. Comment symbol. - call pymode#Default("g:pymode_lint_comment_symbol", "CC") - - " OPTION: g:pymode_lint_visual_symbol -- string. Visual symbol. - call pymode#Default("g:pymode_lint_visual_symbol", "RR") - - " OPTION: g:pymode_lint_error_symbol -- string. Error symbol. - call pymode#Default("g:pymode_lint_error_symbol", "EE") - - " OPTION: g:pymode_lint_info_symbol -- string. Info symbol. - call pymode#Default("g:pymode_lint_info_symbol", "II") - - " OPTION: g:pymode_lint_pyflakes_symbol -- string. PyFlakes' info symbol. - call pymode#Default("g:pymode_lint_pyflakes_symbol", "FF") - - " OPTION: g:pymode_lint_signs -- bool. Place error signs - if (!pymode#Default("g:pymode_lint_signs", 1) || g:pymode_lint_signs) && has('signs') - - " DESC: Signs definition - execute 'sign define PymodeW text=' . g:pymode_lint_todo_symbol . " texthl=Todo" - execute 'sign define PymodeC text=' . g:pymode_lint_comment_symbol . " texthl=Comment" - execute 'sign define PymodeR text=' . g:pymode_lint_visual_symbol . " texthl=Visual" - execute 'sign define PymodeE text=' . g:pymode_lint_error_symbol . " texthl=Error" - execute 'sign define PymodeI text=' . g:pymode_lint_info_symbol . " texthl=Info" - execute 'sign define PymodeF text=' . g:pymode_lint_pyflakes_symbol . " texthl=Info" - - if !pymode#Default("g:pymode_lint_signs_always_visible", 0) || g:pymode_lint_signs_always_visible - " Show the sign's ruller if asked for, even it there's no error to show - sign define __dummy__ - autocmd BufRead,BufNew * call RopeShowSignsRulerIfNeeded() - endif - - endif - - " DESC: Set default pylint configuration - if !filereadable(g:pymode_lint_config) - let g:pymode_lint_config = expand(":p:h:h:h") . "/pylint.ini" - endif - - Python from pymode import queue - - au VimLeavePre * Python queue.stop_queue() - -endif - -" }}} - - -" Documentation {{{ - -if !pymode#Default("g:pymode_doc", 1) || g:pymode_doc - - " OPTION: g:pymode_doc_key -- string. Key for show python documantation. - call pymode#Default("g:pymode_doc_key", "K") - -endif - -" }}} - - -" Breakpoints {{{ - -if !pymode#Default("g:pymode_breakpoint", 1) || g:pymode_breakpoint - - if !pymode#Default("g:pymode_breakpoint_cmd", "import pdb; pdb.set_trace() # XXX BREAKPOINT") && has("python") - - call pymode#breakpoint#SearchDebuger() - - endif - - " OPTION: g:pymode_breakpoint_key -- string. Key for set/unset breakpoint. - call pymode#Default("g:pymode_breakpoint_key", "b") - -endif - -" }}} - - -" Execution {{{ - -if !pymode#Default("g:pymode_run", 1) || g:pymode_run - - " OPTION: g:pymode_doc_key -- string. Key for show python documentation. - call pymode#Default("g:pymode_run_key", "r") - -endif - -" }}} - - -" Rope {{{ - -if !pymode#Default("g:pymode_rope", 1) || g:pymode_rope - - " OPTION: g:pymode_rope_autocomplete_key -- str. Key for the rope - " autocompletion. - call pymode#Default("g:pymode_rope_autocomplete_map", "") - - " OPTION: g:pymode_rope_auto_project -- bool. Auto create ropeproject - call pymode#Default("g:pymode_rope_auto_project", 1) - - " OPTION: g:pymode_rope_auto_project_open -- bool. - " Auto open existing projects, ie, if the current directory has a - " `.ropeproject` subdirectory. - call pymode#Default("g:pymode_rope_auto_project_open", 1) - - " OPTION: g:pymode_rope_enable_autoimport -- bool. Enable autoimport - call pymode#Default("g:pymode_rope_enable_autoimport", 1) - - " OPTION: g:pymode_rope_autoimport_generate -- bool. - call pymode#Default("g:pymode_rope_autoimport_generate", 1) - - " OPTION: g:pymode_rope_autoimport_underlines -- bool. - call pymode#Default("g:pymode_rope_autoimport_underlineds", 0) - - " OPTION: g:pymode_rope_codeassist_maxfiles -- bool. - call pymode#Default("g:pymode_rope_codeassist_maxfixes", 10) - - " OPTION: g:pymode_rope_sorted_completions -- bool. - call pymode#Default("g:pymode_rope_sorted_completions", 1) - - " OPTION: g:pymode_rope_extended_complete -- bool. - call pymode#Default("g:pymode_rope_extended_complete", 1) - - " OPTION: g:pymode_rope_autoimport_modules -- array. - call pymode#Default("g:pymode_rope_autoimport_modules", ["os","shutil","datetime"]) - - " OPTION: g:pymode_rope_confirm_saving -- bool. - call pymode#Default("g:pymode_rope_confirm_saving", 1) - - " OPTION: g:pymode_rope_global_prefix -- string. - call pymode#Default("g:pymode_rope_global_prefix", "p") - - " OPTION: g:pymode_rope_local_prefix -- string. - call pymode#Default("g:pymode_rope_local_prefix", "r") - - " OPTION: g:pymode_rope_short_prefix -- string. - call pymode#Default("g:pymode_rope_short_prefix", "") - - " OPTION: g:pymode_rope_vim_completion -- bool. - call pymode#Default("g:pymode_rope_vim_completion", 1) - - " OPTION: g:pymode_rope_guess_project -- bool. - call pymode#Default("g:pymode_rope_guess_project", 1) - - " OPTION: g:pymode_rope_goto_def_newwin -- str ('new', 'vnew', ''). - call pymode#Default("g:pymode_rope_goto_def_newwin", "") - - " OPTION: g:pymode_rope_always_show_complete_menu -- bool. - call pymode#Default("g:pymode_rope_always_show_complete_menu", 0) - - " DESC: Init Rope - Python import ropevim - - fun! RopeCodeAssistInsertMode() "{{{ - call RopeCodeAssist() - return "" - endfunction "}}} - - fun! RopeOpenExistingProject() "{{{ - if isdirectory(getcwd() . '/.ropeproject') - " In order to pass it the quiet kwarg I need to open the project - " using python and not vim, which should be no major issue - Python ropevim._interface.open_project(quiet=True) - return "" - endif - endfunction "}}} - - fun! RopeLuckyAssistInsertMode() "{{{ - call RopeLuckyAssist() - return "" - endfunction "}}} - - fun! RopeOmni(findstart, base) "{{{ - if a:findstart - Python ropevim._interface._find_start() - return g:pymode_offset - else - call RopeOmniComplete() - return g:pythoncomplete_completions - endif - endfunction "}}} - - fun! RopeShowSignsRulerIfNeeded() "{{{ - if &ft == 'python' - execute printf('silent! sign place 1 line=1 name=__dummy__ file=%s', expand("%:p")) - endif - endfunction "}}} - - - " Rope menu - menu Rope.Autoimport :RopeAutoImport - menu Rope.ChangeSignature :RopeChangeSignature - menu Rope.CloseProject :RopeCloseProject - menu Rope.GenerateAutoImportCache :RopeGenerateAutoimportCache - menu Rope.ExtractVariable :RopeExtractVariable - menu Rope.ExtractMethod :RopeExtractMethod - menu Rope.Inline :RopeInline - menu Rope.IntroduceFactory :RopeIntroduceFactory - menu Rope.FindFile :RopeFindFile - menu Rope.OpenProject :RopeOpenProject - menu Rope.Move :RopeMove - menu Rope.MoveCurrentModule :RopeMoveCurrentModule - menu Rope.ModuleToPackage :RopeModuleToPackage - menu Rope.Redo :RopeRedo - menu Rope.Rename :RopeRename - menu Rope.RenameCurrentModule :RopeRenameCurrentModule - menu Rope.Restructure :RopeRestructure - menu Rope.Undo :RopeUndo - menu Rope.UseFunction :RopeUseFunction - - if !pymode#Default("g:pymode_rope_auto_project_open", 1) || g:pymode_rope_auto_project_open - call RopeOpenExistingProject() - endif - -endif - -" }}} - - -" OPTION: g:pymode_folding -- bool. Enable python-mode folding for pyfiles. -call pymode#Default("g:pymode_folding", 1) - -" OPTION: g:pymode_syntax -- bool. Enable python-mode syntax for pyfiles. -call pymode#Default("g:pymode_syntax", 1) - -" OPTION: g:pymode_indent -- bool. Enable/Disable pymode PEP8 indentation -call pymode#Default("g:pymode_indent", 1) - -" OPTION: g:pymode_utils_whitespaces -- bool. Remove unused whitespaces on save -call pymode#Default("g:pymode_utils_whitespaces", 1) - -" OPTION: g:pymode_options -- bool. To set some python options. -call pymode#Default("g:pymode_options", 1) - -" OPTION: g:pymode_updatetime -- int. Set updatetime for async pymode's operation -call pymode#Default("g:pymode_updatetime", 1000) - -" OPTION: g:pymode_modeline -- int. Support pymode modeline. -if pymode#Default('g:pymode_modeline', 1) || !g:pymode_modeline - au BufRead *.py call pymode#Modeline() -endif - -" vim: fdm=marker:fdl=0 diff --git a/ftplugin/python/pymode.vim b/ftplugin/python/pymode.vim index c1111c4e..a1370669 100644 --- a/ftplugin/python/pymode.vim +++ b/ftplugin/python/pymode.vim @@ -1,160 +1,258 @@ -runtime ftplugin/python/init-pymode.vim - -if !g:pymode +if !g:pymode || pymode#default('b:pymode', 1) finish endif +if g:pymode_python == 'disable' -" Parse pymode modeline -call pymode#Modeline() + if g:pymode_warning + call pymode#error("Pymode requires vim compiled with +python3 (exclusively). Most of features will be disabled.") + endif + finish -" Syntax highlight -if pymode#Option('syntax') - let python_highlight_all=1 -endif +else -" Options {{{ +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() -" Python other options -if pymode#Option('options') - setlocal complete+=t - setlocal formatoptions-=t - if v:version > 702 && !&relativenumber - setlocal number endif - setlocal nowrap - setlocal textwidth=79 - setlocal commentstring=#%s -endif -" }}} +endif +command! -buffer -nargs=1 PymodeVirtualenv call pymode#virtualenv#activate() -" Documentation {{{ +" Setup events for pymode +au! pymode BufWritePre call pymode#buffer_pre_write() +au! pymode BufWritePost call pymode#buffer_post_write() -if pymode#Option('doc') +" Run python code +if g:pymode_run - " DESC: Set commands - command! -buffer -nargs=1 Pydoc call pymode#doc#Show("") + command! -buffer -nargs=0 -range=% PymodeRun call pymode#run#code_run(, ) - " DESC: Set keys - exe "nnoremap " g:pymode_doc_key ":call pymode#doc#Show(expand(''))" - exe "vnoremap " g:pymode_doc_key ":call pymode#doc#Show(@*)" + exe "nnoremap " g:pymode_run_bind ":PymodeRun" + exe "vnoremap " g:pymode_run_bind ":PymodeRun" endif -" }}} +" Add/remove breakpoints +if g:pymode_breakpoint + exe "nnoremap " g:pymode_breakpoint_bind ":call pymode#breakpoint#operate(line('.'))" -" Lint {{{ +endif -if pymode#Option('lint') +" Python folding +if g:pymode_folding - " DESC: Set commands - command! -buffer -nargs=0 PyLintToggle :call pymode#lint#Toggle() - command! -buffer -nargs=0 PyLintWindowToggle :call pymode#lint#ToggleWindow() - command! -buffer -nargs=0 PyLintCheckerToggle :call pymode#lint#ToggleChecker() - command! -buffer -nargs=0 PyLint :call pymode#lint#Check() - command! -buffer -nargs=0 PyLintAuto :call pymode#lint#Auto() + setlocal foldmethod=expr + setlocal foldexpr=pymode#folding#expr(v:lnum) + setlocal foldtext=pymode#folding#text() - " DESC: Set autocommands - if pymode#Option('lint_write') - au BufWritePost PyLint - endif +endif - if pymode#Option('lint_onfly') - au InsertLeave PyLint - endif +" Remove unused whitespaces +if g:pymode_trim_whitespaces + au BufWritePre call pymode#trim_whitespaces() +endif - if pymode#Option('lint_message') - au CursorHold call pymode#lint#show_errormessage() - au CursorMoved call pymode#lint#show_errormessage() +" Custom options +if g:pymode_options + setlocal complete+=t + setlocal formatoptions-=t + if v:version > 702 && !&relativenumber + setlocal number endif - - " DESC: Run queue - if pymode#Option('lint_async') - let &l:updatetime = g:pymode_updatetime - au CursorHold call pymode#queue#Poll() - au BufLeave Python queue.stop_queue() + setlocal nowrap + exe "setlocal textwidth=" . g:pymode_options_max_line_length + if g:pymode_options_colorcolumn && exists('+colorcolumn') + setlocal colorcolumn=+1 endif - + setlocal commentstring=#%s + setlocal define=^\s*\\(def\\\\|class\\) endif -" }}} +if g:pymode_lint + command! -buffer -nargs=0 PymodeLintAuto :call pymode#lint#auto() + command! -buffer -nargs=0 PymodeLintToggle :call pymode#lint#toggle() + command! -buffer -nargs=0 PymodeLint :call pymode#lint#check() -" Rope {{{ + if v:version > 703 || (v:version == 703 && has('patch544')) + au! QuitPre call pymode#quit() + else + au! pymode BufWinLeave * silent! lclose + endif -if pymode#Option('rope') + let b:pymode_error_line = -1 - " DESC: Set keys - exe "noremap " . g:pymode_rope_short_prefix . "g :RopeGotoDefinition" - exe "noremap " . g:pymode_rope_short_prefix . "d :RopeShowDoc" - exe "noremap " . g:pymode_rope_short_prefix . "f :RopeFindOccurrences" - exe "noremap " . g:pymode_rope_short_prefix . "m :emenu Rope . " - inoremap =RopeLuckyAssistInsertMode() + if g:pymode_lint_on_fly + au! pymode InsertLeave PymodeLint + endif - let s:prascm = g:pymode_rope_always_show_complete_menu ? "" : "" + if g:pymode_lint_message + au! pymode CursorMoved + au! pymode CursorMoved call pymode#lint#show_errormessage() + endif - exe "inoremap " . g:pymode_rope_autocomplete_map . " =RopeCodeAssistInsertMode()" . s:prascm - if tolower(g:pymode_rope_autocomplete_map) == '' - exe "inoremap =RopeCodeAssistInsertMode()" . s:prascm + " Disabled for current release + if g:pymode_lint_async + " let &l:updatetime = g:pymode_lint_async_updatetime + " au! BufEnter call pymode#lint#start() + " au! BufLeave call pymode#lint#stop() endif endif -" }}} +" Show python documentation +if g:pymode_doc + " Set commands + command! -buffer -nargs=1 PymodeDoc call pymode#doc#show("") -" Execution {{{ + " Set keys + exe "nnoremap " g:pymode_doc_bind ":call pymode#doc#find()" + exe "vnoremap " g:pymode_doc_bind ":call pymode#doc#show(@*)" -if pymode#Option('run') +endif - " DESC: Set commands - command! -buffer -nargs=0 -range=% Pyrun call pymode#run#Run(, ) +" Rope support +if g:pymode_rope - " DESC: Set keys - exe "nnoremap " g:pymode_run_key ":Pyrun" - exe "vnoremap " g:pymode_run_key ":Pyrun" + if g:pymode_rope_goto_definition_bind != "" + exe "noremap " . g:pymode_rope_goto_definition_bind . " :call pymode#rope#goto_definition()" + endif + if g:pymode_rope_show_doc_bind != "" + exe "noremap " . g:pymode_rope_show_doc_bind . " :call pymode#rope#show_doc()" + endif + if g:pymode_rope_find_it_bind != "" + exe "noremap " . g:pymode_rope_find_it_bind . " :call pymode#rope#find_it()" + endif + if g:pymode_rope_organize_imports_bind != "" + exe "noremap " . g:pymode_rope_organize_imports_bind . " :call pymode#rope#organize_imports()" + endif -endif + if g:pymode_rope_rename_bind != "" + exe "noremap " . g:pymode_rope_rename_bind . " :call pymode#rope#rename()" + endif -" }}} + if g:pymode_rope_rename_module_bind != "" + exe "noremap " . g:pymode_rope_rename_module_bind . " :call pymode#rope#rename_module()" + endif + if g:pymode_rope_extract_method_bind != "" + exe "vnoremap " . g:pymode_rope_extract_method_bind . " :call pymode#rope#extract_method()" + endif -" Breakpoints {{{ + if g:pymode_rope_extract_variable_bind != "" + exe "vnoremap " . g:pymode_rope_extract_variable_bind . " :call pymode#rope#extract_variable()" + endif -if pymode#Option('breakpoint') + if g:pymode_rope_inline_bind != "" + exe "noremap " . g:pymode_rope_inline_bind . " :call pymode#rope#inline()" + endif - " DESC: Set keys - exe "nnoremap " g:pymode_breakpoint_key ":call pymode#breakpoint#Set(line('.'))" + if g:pymode_rope_move_bind != "" + exe "noremap " . g:pymode_rope_move_bind . " :call pymode#rope#move()" + endif -endif + if g:pymode_rope_change_signature_bind != "" + exe "noremap " . g:pymode_rope_change_signature_bind . " :call pymode#rope#signature()" + endif -" }}} + if g:pymode_rope_use_function_bind != "" + exe "noremap " . g:pymode_rope_use_function_bind . " :call pymode#rope#use_function()" + endif + if g:pymode_rope_generate_function_bind != "" + exe "noremap " . g:pymode_rope_generate_function_bind . " :call pymode#rope#generate_function()" + endif -" Utils {{{ + if g:pymode_rope_generate_package_bind != "" + exe "noremap " . g:pymode_rope_generate_package_bind . " :call pymode#rope#generate_package()" + endif -if pymode#Option('utils_whitespaces') - au BufWritePre call pymode#TrimWhiteSpace() -endif + if g:pymode_rope_generate_class_bind != "" + exe "noremap " . g:pymode_rope_generate_class_bind . " :call pymode#rope#generate_class()" + endif -" }}} + if g:pymode_rope_module_to_package_bind != "" + exe "noremap " . g:pymode_rope_module_to_package_bind . " :call pymode#rope#module_to_package()" + endif + if g:pymode_rope_autoimport_bind != "" + exe "noremap " . g:pymode_rope_autoimport_bind . " :PymodeRopeAutoImport" + endif -" Folding {{{ + if g:pymode_rope_completion && g:pymode_rope_complete_on_dot + inoremap . .=pymode#rope#complete_on_dot() + endif -if pymode#Option('folding') + command! -buffer -nargs=? PymodeRopeNewProject call pymode#rope#new() + command! -buffer PymodeRopeUndo call pymode#rope#undo() + command! -buffer PymodeRopeRedo call pymode#rope#redo() + command! -buffer PymodeRopeRenameModule call pymode#rope#rename_module() + command! -buffer PymodeRopeModuleToPackage call pymode#rope#module_to_package() + command! -buffer PymodeRopeRegenerate call pymode#rope#regenerate() - setlocal foldmethod=expr - setlocal foldexpr=pymode#folding#expr(v:lnum) - setlocal foldtext=pymode#folding#text() + if g:pymode_rope_autoimport + command! -buffer PymodeRopeAutoImport call pymode#rope#autoimport(expand('')) + endif endif -" }}} -" vim: fdm=marker:fdl=0 +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 new file mode 100644 index 00000000..b0d99270 --- /dev/null +++ b/plugin/pymode.vim @@ -0,0 +1,321 @@ +" vi: fdl=1 +let g:pymode_version = "0.14.0" + + +" 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 || &diff + " Update pymode status to prevent loading in other files and adding this + " condition to all of them. + let g:pymode = 0 + finish +endif + +" Pymode needs +filetype plugin on + +" OPTIONS: {{{ + +" Vim Python interpreter. Set to 'disable' for remove python features. +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) + +" Additional python paths +call pymode#default('g:pymode_paths', []) + +" Python documentation support +call pymode#default('g:pymode_doc', 1) +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", 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\|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) + +" Auto remove unused whitespaces on save +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", 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 +call pymode#default('g:pymode_virtualenv', 1) + +" Get path to virtualenv (by default take from shell) +call pymode#default('g:pymode_virtualenv_path', $VIRTUAL_ENV) + +" Service variable (don't set it manually) +call pymode#default('g:pymode_virtualenv_enabled', '') + +" }}} + +" RUN PYTHON {{{ +" +" Enable code running support +call pymode#default('g:pymode_run', 1) + +" Key's map for run python code +call pymode#default('g:pymode_run_bind', 'r') + +" }}} + +" CHECK CODE {{{ +" +" Code checking +call pymode#default('g:pymode_lint', 1) + +" Check code asynchronously +call pymode#default('g:pymode_lint_async', 1) +call pymode#default('g:pymode_lint_async_updatetime', 1000) + +" Check code every save if file has been modified +call pymode#default("g:pymode_lint_on_write", 1) + +" Check code every save (every) +call pymode#default("g:pymode_lint_unmodified", 0) + +" Check code on fly +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, 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", []) + +" Select errors and warnings (e.g. E4,W) +call pymode#default("g:pymode_lint_select", []) + +" Auto open cwindow if any errors has been finded +call pymode#default("g:pymode_lint_cwindow", 1) + +" If not emply, errors will be sort by defined relevance +" E.g. let g:pymode_lint_sort = ['E', 'C', 'I'] " Errors first 'E', +" after them 'C' and ... +call pymode#default("g:pymode_lint_sort", []) + +" Place error signs +call pymode#default("g:pymode_lint_signs", 1) + +" Symbol's definitions +call pymode#default("g:pymode_lint_todo_symbol", "WW") +call pymode#default("g:pymode_lint_docs_symbol", "DD") +call pymode#default("g:pymode_lint_comment_symbol", "CC") +call pymode#default("g:pymode_lint_visual_symbol", "RR") +call pymode#default("g:pymode_lint_error_symbol", "EE") +call pymode#default("g:pymode_lint_info_symbol", "II") +call pymode#default("g:pymode_lint_pyflakes_symbol", "FF") + +" Code checkers options +" TODO: check if most adequate name name is 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", + \ {'max-line-length': g:pymode_options_max_line_length}) + +call pymode#default("g:pymode_lint_options_mccabe", + \ {'complexity': 12}) + +call pymode#default("g:pymode_lint_options_pep257", {}) +call pymode#default("g:pymode_lint_options_pyflakes", { 'builtins': '_' }) + + +" }}} + +" SET/UNSET BREAKPOINTS {{{ +" + +" Create/remove breakpoints +call pymode#default('g:pymode_breakpoint', 1) + +" Key's map for add/remove breakpoint +call pymode#default('g:pymode_breakpoint_bind', 'b') + +" Default pattern for making breakpoints. Leave this empty for auto search available debuggers (pdb, ipdb, ...) +call pymode#default('g:pymode_breakpoint_cmd', '') + +" }}} + +" ROPE (refactoring, codeassist) {{{ +" +" Rope support +call pymode#default('g:pymode_rope', 0) +call pymode#default('g:pymode_rope_prefix', '') + +" System plugin variable +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 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) + + " Enable Rope completion + call pymode#default('g:pymode_rope_completion', 1) + + " Complete keywords from not imported modules (could make completion slower) + " Enable autoimport used modules + call pymode#default('g:pymode_rope_autoimport', 0) + + " 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']) + + " 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) + + " 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:pymode_rope_prefix . 'g') + + " set command for open definition (e, new, vnew) + call pymode#default('g:pymode_rope_goto_definition_cmd', 'new') + + " Bind keys for show documentation (leave empty for disable) + call pymode#default('g:pymode_rope_show_doc_bind', g:pymode_rope_prefix . 'd') + + " 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', 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', g:pymode_rope_prefix . 'rr') + + " 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', 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', g:pymode_rope_prefix . 'rm') + + " 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', g:pymode_rope_prefix . 'ri') + + " 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', g:pymode_rope_prefix . 'rnf') + + " 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', g:pymode_rope_prefix . 'rnp') + + " 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', g:pymode_rope_prefix . 'ru') + + " Regenerate project cache on every save + call pymode#default('g:pymode_rope_regenerate_on_write', 1) +endif + +" }}} + +" }}} + +" Prepare to plugin loading +if &compatible + set nocompatible +endif +filetype plugin on + +" UltiSnips Fixes +if !len(g:pymode_python) + 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 == 'python3' + + command! -nargs=1 PymodePython python3 + let g:UltiSnipsUsePythonVersion = 3 + +else + + let g:pymode_doc = 0 + let g:pymode_lint = 0 + let g:pymode_path = 0 + let g:pymode_rope = 0 + let g:pymode_run = 0 + let g:pymode_virtualenv = 0 + + command! -nargs=1 PymodePython echo + +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 new file mode 100644 index 00000000..9579796e --- /dev/null +++ b/pylama.ini @@ -0,0 +1,9 @@ +[pylama] +ignore=D213 +linters=pep8,pyflakes,pylint + +[pylama:pymode/libs*] +skip=1 + +[pylama:pylint] +disable=E1120,E1130,E1103,W1401,F0001 diff --git a/pylibs/autopep8.py b/pylibs/autopep8.py deleted file mode 100644 index da19ceee..00000000 --- a/pylibs/autopep8.py +++ /dev/null @@ -1,2307 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2010-2011 Hideo Hattori -# Copyright (C) 2011-2013 Hideo Hattori, Steven Myint -# -# 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 print_function -from __future__ import division - -import codecs -import copy -import fnmatch -import inspect -import os -import re -import signal -import sys -try: - from StringIO import StringIO -except ImportError: - from io import StringIO -import token -import tokenize -from optparse import OptionParser -import difflib -import tempfile - -from pylama.lint.pylama_pep8 import pep8 - - -try: - unicode -except NameError: - unicode = str - - -__version__ = '0.9' - - -CR = '\r' -LF = '\n' -CRLF = '\r\n' - - -PYTHON_SHEBANG_REGEX = re.compile(r'^#!.*\bpython[23]?\b') - - -# For generating line shortening candidates. -SHORTEN_OPERATOR_GROUPS = frozenset([ - frozenset([',']), - frozenset(['%']), - frozenset([',', '(', '[', '{']), - frozenset([',', '(', '[', '{', '%', '+', '-', '*', '/', '//']), -]) - - -DEFAULT_IGNORE = 'E24,W6' - - -def open_with_encoding(filename, encoding=None, mode='r'): - """Return opened file with a specific encoding.""" - if not encoding: - encoding = detect_encoding(filename) - - import io - 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 (SyntaxError, LookupError, UnicodeDecodeError): - return 'latin-1' - - -def read_from_filename(filename, readlines=False): - """Return contents of file.""" - with open_with_encoding(filename) as input_file: - return input_file.readlines() if readlines else input_file.read() - - -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: - if not blank_lines: - yield (0, 'E301 expected 1 blank line, found 0') - 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)) -pep8.register_check(extended_blank_lines) - - -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] - - e111 - - 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,w293 - - w391 - - """ - - def __init__(self, filename, options, contents=None): - self.filename = filename - if contents is None: - self.source = read_from_filename(filename, readlines=True) - else: - sio = StringIO(contents) - self.source = sio.readlines() - self.newline = find_newline(self.source) - self.options = options - self.indent_word = _get_indentword(unicode().join(self.source)) - - # method definition - self.fix_e111 = self.fix_e101 - self.fix_e128 = self.fix_e127 - self.fix_e129 = self.fix_e125 - 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_e703 = self.fix_e702 - self.fix_w191 = self.fix_e101 - - def _fix_source(self, results): - completed_lines = set() - for result in sorted(results, key=_priority_key): - if result['line'] in completed_lines: - continue - - fixed_methodname = 'fix_%s' % result['id'].lower() - if hasattr(self, fixed_methodname): - fix = getattr(self, fixed_methodname) - - is_logical_fix = len(inspect.getargspec(fix).args) > 2 - if is_logical_fix: - # Do not run logical fix if any lines have been modified. - if completed_lines: - continue - - logical = self._get_logical(result) - if not logical: - continue - - modified_lines = fix(result, logical) - else: - modified_lines = fix(result) - - 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("---> '%s' is not defined." % fixed_methodname, - file=sys.stderr) - info = result['info'].strip() - print('---> %s:%s:%s:%s' % (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) - - self._fix_source(filter_results(source=unicode().join(self.source), - results=results, - aggressive=self.options.aggressive)) - return unicode().join(self.source) - - def fix_e101(self, _): - """Reindent all lines.""" - reindenter = Reindenter(self.source, self.newline) - modified_line_numbers = reindenter.run() - if modified_line_numbers: - self.source = reindenter.fixed_lines() - return modified_line_numbers - else: - return [] - - def _find_logical(self): - # make a variable which is the index of all the starts of lines - logical_start = [] - logical_end = [] - last_newline = True - sio = StringIO(''.join(self.source)) - parens = 0 - for t in tokenize.generate_tokens(sio.readline): - 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(self, result): - """Return the logical line corresponding to the result. - - Assumes input is already E702-clean. - - """ - try: - (logical_start, logical_end) = self._find_logical() - except (IndentationError, tokenize.TokenError): - return None - - row = result['line'] - 1 - col = result['column'] - 1 - ls = None - le = None - for i in range(0, len(logical_start), 1): - 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 = self.source[ls[0]:le[0] + 1] - return ls, le, original - - def _fix_reindent(self, result, logical): - """Fix a badly indented line. - - This is done by adding or removing from its initial indent only. - - """ - assert logical - ls, _, original = logical - - rewrapper = Wrapper(original) - valid_indents = rewrapper.pep8_expected() - if not rewrapper.rel_indent: - return [] # pragma: no cover - if result['line'] > ls[0]: - # got a valid continuation line number from pep8 - row = result['line'] - ls[0] - 1 - # always pick the first option for this - valid = valid_indents[row] - got = rewrapper.rel_indent[row] - else: - return [] # pragma: no cover - line = ls[0] + row - # always pick the expected indent, for now. - indent_to = valid[0] - - if got != indent_to: - orig_line = self.source[line] - new_line = ' ' * (indent_to) + orig_line.lstrip() - if new_line == orig_line: - return [] - else: - self.source[line] = new_line - return [line + 1] # Line indexed at 1 - else: - return [] # pragma: no cover - - def fix_e121(self, result, logical): - """Fix indentation to be a multiple of four.""" - # Fix by adjusting initial indent level. - return self._fix_reindent(result, logical) - - def fix_e122(self, result, logical): - """Add absent indentation for hanging indentation.""" - # Fix by adding an initial indent. - return self._fix_reindent(result, logical) - - def fix_e123(self, result, logical): - """Align closing bracket to match opening bracket.""" - # Fix by deleting whitespace to the correct level. - assert logical - logical_lines = logical[2] - line_index = result['line'] - 1 - original_line = self.source[line_index] - - fixed_line = (_get_indentation(logical_lines[0]) + - original_line.lstrip()) - if fixed_line == original_line: - # Fall back to slower method. - return self._fix_reindent(result, logical) - else: - self.source[line_index] = fixed_line - - def fix_e124(self, result, logical): - """Align closing bracket to match visual indentation.""" - # Fix by inserting whitespace before the closing bracket. - return self._fix_reindent(result, logical) - - def fix_e125(self, result, logical): - """Indent to distinguish line from next logical line.""" - # Fix by indenting the line in error to the next stop. - modified_lines = self._fix_reindent(result, logical) - if modified_lines: - return modified_lines - else: - # Fallback - line_index = result['line'] - 1 - original_line = self.source[line_index] - self.source[line_index] = self.indent_word + original_line - - def fix_e126(self, result, logical): - """Fix over-indented hanging indentation.""" - # fix by deleting whitespace to the left - assert logical - logical_lines = logical[2] - line_index = result['line'] - 1 - original = self.source[line_index] - - fixed = (_get_indentation(logical_lines[0]) + - self.indent_word + original.lstrip()) - if fixed == original: - # Fall back to slower method. - return self._fix_reindent(result, logical) # pragma: no cover - else: - self.source[line_index] = fixed - - def fix_e127(self, result, logical): - """Fix visual indentation.""" - # Fix by inserting/deleting whitespace to the correct level. - modified_lines = self._align_visual_indent(result, logical) - if modified_lines != []: - return modified_lines - else: - # Fall back to slower method. - return self._fix_reindent(result, logical) - - def _align_visual_indent(self, result, logical): - """Correct visual indent. - - This includes over (E127) and under (E128) indented lines. - - """ - assert logical - logical_lines = logical[2] - line_index = result['line'] - 1 - original = self.source[line_index] - fixed = original - - if logical_lines[0].rstrip().endswith('\\'): - fixed = (_get_indentation(logical_lines[0]) + - self.indent_word + original.lstrip()) - else: - start_index = None - for symbol in '([{': - if symbol in logical_lines[0]: - found_index = logical_lines[0].find(symbol) - if start_index is None: - start_index = found_index - else: - start_index = min(start_index, found_index) - - if start_index is not None: - fixed = start_index * ' ' + original.lstrip() - - if fixed == original: - return [] - else: - self.source[line_index] = fixed - - def fix_e201(self, result): - """Remove extraneous whitespace.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - # When multiline strings are involved, pep8 reports the error as - # being at the start of the multiline string, which doesn't work - # for us. - if ('"""' in target or - "'''" in target or - target.rstrip().endswith('\\')): - return [] - - fixed = fix_whitespace(target, - offset=offset, - replacement='') - - if fixed == target: - return [] - else: - 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. - if ',' in result['info']: - 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') or - fixed.endswith('=\\\r\n') or - fixed.endswith('=\\\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 self.newline) - - 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 - - # When multiline strings are involved, pep8 reports the error as - # being at the start of the multiline string, which doesn't work - # for us. - if ('"""' in target or - "'''" in target or - target.rstrip().endswith('\\')): - 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 = self.newline - 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 = self.newline * 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 [] - - # pep8 (1.3.1) reports false positive if there is an import statement - # followed by a semicolon and some unrelated statement with commas in - # it. - if ';' in target: - return [] - - indentation = re.split(pattern=r'\bimport\b', - string=target, maxsplit=1)[0] - fixed = (target[:offset].rstrip('\t ,') + self.newline + - indentation + 'import ' + target[offset:].lstrip('\t ,')) - self.source[line_index] = fixed - - def fix_e501(self, result): - """Try to make lines fit within --max-line-length characters.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - if target.lstrip().startswith('#'): - # Shorten comment if it is the last comment line. - try: - if self.source[line_index + 1].lstrip().startswith('#'): - return [] - except IndexError: - pass - - # Wrap commented lines. - fixed = shorten_comment( - line=target, - newline=self.newline, - max_line_length=self.options.max_line_length) - if fixed == self.source[line_index]: - return [] - else: - self.source[line_index] = fixed - return - - indent = _get_indentation(target) - source = target[len(indent):] - assert source.lstrip() == source - sio = StringIO(source) - - # Check for multiline string. - try: - tokens = list(tokenize.generate_tokens(sio.readline)) - except (tokenize.TokenError, IndentationError): - multiline_candidate = break_multiline( - target, newline=self.newline, - indent_word=self.indent_word) - - if multiline_candidate: - self.source[line_index] = multiline_candidate - return - else: - return [] - - candidates = shorten_line( - tokens, source, indent, - self.indent_word, newline=self.newline, - aggressive=self.options.aggressive) - - candidates = list(sorted( - set(candidates), - key=lambda x: line_shortening_rank(x, - self.newline, - self.indent_word))) - - if self.options.verbose >= 4: - print(('-' * 79 + '\n').join([''] + candidates + ['']), - file=codecs.getwriter('utf-8')(sys.stderr.buffer - if hasattr(sys.stderr, - 'buffer') - else sys.stderr)) - - for _candidate in candidates: - assert _candidate is not None - - if (get_longest_length(_candidate, self.newline) >= - get_longest_length(target, self.newline)): - continue - - self.source[line_index] = _candidate - return - - return [] - - 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\\') + self.newline - - 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] + self.newline + - _get_indentation(target) + self.indent_word + - target[c:].lstrip('\n\r \t\\')) - self.source[result['line'] - 1] = fixed_source - - def fix_e702(self, result, logical): - """Put semicolon-separated compound statement on separate lines.""" - 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;') + self.newline - return - - offset = result['column'] - 1 - first = target[:offset].rstrip(';').rstrip() - second = (_get_indentation(logical_lines[0]) + - target[offset:].lstrip(';').lstrip()) - - self.source[line_index] = first + self.newline + second - - 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 - - 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] = '%s%s' % (fixed_line, self.newline) - - def fix_w293(self, result): - """Remove trailing whitespace on blank line.""" - assert not self.source[result['line'] - 1].strip() - self.source[result['line'] - 1] = self.newline - - def fix_w391(self, _): - """Remove trailing blank lines.""" - blank_count = 0 - for line in reversed(self.source): - line = line.rstrip() - if line: - break - else: - blank_count += 1 - - original_length = len(self.source) - self.source = self.source[:original_length - blank_count] - return range(1, 1 + original_length) - - -def fix_e26(source): - """Format block comments.""" - if '#' not in source: - # Optimization. - return source - - string_line_numbers = multiline_string_lines(source, - include_docstrings=True) - fixed_lines = [] - sio = StringIO(source) - for (line_number, line) in enumerate(sio.readlines(), start=1): - if (line.lstrip().startswith('#') and - line_number not in string_line_numbers): - - indentation = _get_indentation(line) - line = line.lstrip() - - # Normalize beginning if not a shebang. - if len(line) > 1: - # Leave multiple spaces like '# ' alone. - if line.count('#') > 1 or line[1].isalnum(): - 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, - UnicodeDecodeError, - UnicodeEncodeError, - IndentationError): - return source - - if ignore: - if ignore in new_text and ignore not in source: - return source - - return new_text - - -def fix_w602(source): - """Fix deprecated form of raising exception.""" - return refactor(source, ['raise'], - ignore='with_traceback') - - -def fix_w6(source): - """Fix various deprecated code (via lib2to3).""" - return refactor(source, - ['apply', - 'except', - 'exec', - 'execfile', - 'exitfunc', - 'has_key', - 'idioms', - 'import', - 'methodattrs', # Python >= 2.6 - 'ne', - 'numliterals', - 'operator', - 'paren', - 'reduce', - 'renames', - 'repr', - 'standarderror', - 'sys_exc', - 'throw', - 'tuple_params', - 'types', - 'xreadlines']) - - -def find_newline(source): - """Return type of newline used in source.""" - cr, lf, crlf = 0, 0, 0 - for s in source: - if s.endswith(CRLF): - crlf += 1 - elif s.endswith(CR): - cr += 1 - elif s.endswith(LF): - lf += 1 - _max = max(lf, cr, crlf) - if _max == lf: - return LF - elif _max == crlf: - return CRLF - else: - return CR - - -def _get_indentword(source): - """Return indentation type.""" - sio = StringIO(source) - indent_word = ' ' # Default in case source has no indentation - try: - for t in tokenize.generate_tokens(sio.readline): - if t[0] == token.INDENT: - indent_word = t[1] - break - except (tokenize.TokenError, IndentationError): - 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 = [ - # Global fixes. - 'e101', 'e111', 'w191', - # 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', - # Before breaking lines. - 'e121', 'e122', 'e123', 'e124', 'e125', 'e126', 'e127', 'e128', 'e129', - ] - key = pep8_result['id'].lower() - if key in priority: - return priority.index(key) - else: - # Lowest priority - return len(priority) - - -def shorten_line(tokens, source, indentation, indent_word, newline, - aggressive=False): - """Separate line at OPERATOR. - - Multiple candidates will be yielded. - - """ - for candidate in _shorten_line(tokens=tokens, - source=source, - indentation=indentation, - indent_word=indent_word, - newline=newline, - aggressive=aggressive): - 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, - newline=newline, - key_token_strings=key_token_strings, - aggressive=aggressive) - - if shortened is not None and shortened != source: - yield shortened - - -def _shorten_line(tokens, source, indentation, indent_word, newline, - aggressive=False): - """Separate line at OPERATOR. - - Multiple candidates will be yielded. - - """ - for tkn in tokens: - # Don't break on '=' after keyword as this violates PEP 8. - if token.OP == tkn[0] and tkn[1] != '=': - assert tkn[0] != token.INDENT - - offset = tkn[2][1] + 1 - first = source[: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[offset:].lstrip()) - if not second.strip(): - 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 tkn[1] in '+-*/': - fixed = first + ' \\' + newline + second - else: - fixed = first + newline + second - - # Only fix if syntax is okay. - if check_syntax(normalize_multiline(fixed, newline=newline) - if aggressive else fixed): - yield indentation + fixed - - -def _shorten_line_at_tokens(tokens, source, indentation, indent_word, newline, - key_token_strings, aggressive): - """Separate line by breaking at tokens in key_token_strings. - - This will always break the line at the first parenthesis. - - """ - offsets = [] - first_paren = True - for tkn in tokens: - token_type = tkn[0] - token_string = tkn[1] - next_offset = tkn[2][1] + 1 - - assert token_type != token.INDENT - - if token_string in key_token_strings or (first_paren and - token_string == '('): - # Don't split right before newline. - if next_offset < len(source) - 1: - offsets.append(next_offset) - - if token_string == '(': - first_paren = False - - current_indent = None - fixed = None - for line in split_at_offsets(source, offsets): - if fixed: - fixed += newline + 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, newline=newline) - if aggressive > 1 else fixed): - return indentation + fixed - else: - return None - - -def normalize_multiline(line, newline): - """Remove multiline-related code that will cause syntax error. - - This is for purposes of checking syntax. - - """ - for quote in '\'"': - dict_pattern = r'^{q}[^{q}]*{q} *: *'.format(q=quote) - if re.match(dict_pattern, line): - if not line.strip().endswith('}'): - line += '}' - return '{' + line - - if line.startswith('def ') and line.rstrip().endswith(':'): - # Do not allow ':' to be alone. That is invalid. - split_line = [item.strip() for item in line.split(newline)] - if ':' not in split_line and 'def' not in split_line: - return line[len('def'):].strip().rstrip(':') - - 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() - - -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, newline): - self.newline = newline - - # Raw file lines. - self.raw = input_text - self.after = None - - self.string_content_line_numbers = multiline_string_lines( - ''.join(self.raw)) - - # 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(self.raw, 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() + - line.strip() + newline) - - self.lines.insert(0, None) - self.index = 1 # index into self.lines of next line - - def run(self): - """Fix indentation and return modified line numbers. - - Line numbers are indexed at 1. - - """ - try: - stats = reindent_stats(tokenize.generate_tokens(self.getline)) - except (tokenize.TokenError, IndentationError): - return set() - # Remove trailing empty lines. - lines = self.lines - while lines and lines[-1] == self.newline: - lines.pop() - # Sentinel. - stats.append((len(lines), 0)) - # Map count of leading spaces to # we want. - have2want = {} - # Program after transformation. - after = self.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 * 4 - 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 * 4 - 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 == self.newline: - after.append(line) - else: - after.append(' ' * diff + line) - else: - remove = min(_leading_space_count(line), -diff) - after.append(line[remove:]) - - if self.raw == self.after: - return set() - else: - return (set(range(1, 1 + len(self.raw))) - - self.string_content_line_numbers) - - def fixed_lines(self): - return self.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 - - -class Wrapper(object): - - """Class for functions relating to continuation lines and line folding. - - Each instance operates on a single logical line. - - """ - - SKIP_TOKENS = frozenset([ - tokenize.COMMENT, tokenize.NL, tokenize.INDENT, - tokenize.DEDENT, tokenize.NEWLINE, tokenize.ENDMARKER - ]) - - def __init__(self, physical_lines): - self.lines = physical_lines - self.tokens = [] - self.rel_indent = None - sio = StringIO(''.join(physical_lines)) - for t in tokenize.generate_tokens(sio.readline): - if not len(self.tokens) and t[0] in self.SKIP_TOKENS: - continue - if t[0] != tokenize.ENDMARKER: - self.tokens.append(t) - - self.logical_line = self.build_tokens_logical(self.tokens) - - def build_tokens_logical(self, tokens): - """Build a logical line from a list of tokens. - - Return the logical line and a list of (offset, token) tuples. Does - not mute strings like the version in pep8.py. - - """ - # from pep8.py with minor modifications - logical = [] - previous = None - for t in tokens: - token_type, text = t[0:2] - if token_type in self.SKIP_TOKENS: - continue - if previous: - end_line, end = previous[3] - start_line, start = t[2] - if end_line != start_line: # different row - prev_text = self.lines[end_line - 1][end - 1] - if prev_text == ',' or (prev_text not in '{[(' - and text not in '}])'): - logical.append(' ') - elif end != start: # different column - fill = self.lines[end_line - 1][end:start] - logical.append(fill) - logical.append(text) - previous = t - logical_line = ''.join(logical) - assert logical_line.lstrip() == logical_line - assert logical_line.rstrip() == logical_line - return logical_line - - def pep8_expected(self): - """Replicate logic in pep8.py, to know what level to indent things to. - - Return a list of lists; each list represents valid indent levels for - the line in question, relative from the initial indent. However, the - first entry is the indent level which was expected. - - """ - # What follows is an adjusted version of - # pep8.py:continuation_line_indentation. All of the comments have been - # stripped and the 'yield' statements replaced with 'pass'. - if not self.tokens: - return # pragma: no cover - - first_row = self.tokens[0][2][0] - nrows = 1 + self.tokens[-1][2][0] - first_row - - # here are the return values - valid_indents = [list()] * nrows - indent_level = self.tokens[0][2][1] - valid_indents[0].append(indent_level) - - if nrows == 1: - # bug, really. - return valid_indents # pragma: no cover - - indent_next = self.logical_line.endswith(':') - - row = depth = 0 - parens = [0] * nrows - self.rel_indent = rel_indent = [0] * nrows - indent = [indent_level] - indent_chances = {} - last_indent = (0, 0) - last_token_multiline = None - - for token_type, text, start, end, _ in self.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)) - - if newline: - # This is where the differences start. Instead of looking at - # the line and determining whether the observed indent matches - # our expectations, we decide which type of indentation is in - # use at the given indent level, and return the offset. This - # algorithm is susceptible to "carried errors", but should - # through repeated runs eventually solve indentation for - # multiline expressions less than PEP8_PASSES_MAX lines long. - - if depth: - for open_row in range(row - 1, -1, -1): - if parens[open_row]: - break - else: - open_row = 0 - - # That's all we get to work with. This code attempts to - # "reverse" the below logic, and place into the valid indents - # list - vi = [] - add_second_chances = False - if token_type == tokenize.OP and text in ']})': - # this line starts with a closing bracket, so it needs to - # be closed at the same indent as the opening one. - if indent[depth]: - # hanging indent - vi.append(indent[depth]) - else: - # visual indent - vi.append(indent_level + rel_indent[open_row]) - elif depth and indent[depth]: - # visual indent was previously confirmed. - vi.append(indent[depth]) - add_second_chances = True - elif depth and True in indent_chances.values(): - # visual indent happened before, so stick to - # visual indent this time. - if depth > 1 and indent[depth - 1]: - vi.append(indent[depth - 1]) - else: - # stupid fallback - vi.append(indent_level + 4) - add_second_chances = True - elif not depth: - vi.append(indent_level + 4) - else: - # must be in hanging indent - hang = rel_indent[open_row] + 4 - vi.append(indent_level + hang) - - # about the best we can do without look-ahead - if (indent_next and vi[0] == indent_level + 4 and - nrows == row + 1): - vi[0] += 4 - - if add_second_chances: - # visual indenters like to line things up. - min_indent = vi[0] - for col, what in indent_chances.items(): - if col > min_indent and ( - what is True or - (what == str and token_type == tokenize.STRING) or - (what == text and token_type == tokenize.OP) - ): - vi.append(col) - vi = sorted(vi) - - valid_indents[row] = vi - - # Returning to original continuation_line_indentation() from - # pep8. - visual_indent = indent_chances.get(start[1]) - last_indent = start - rel_indent[row] = start[1] - indent_level - hang = rel_indent[row] - rel_indent[open_row] - - if token_type == tokenize.OP and text in ']})': - pass - elif visual_indent is True: - if not indent[depth]: - indent[depth] = start[1] - - # line altered: comments shouldn't define a visual indent - if parens[row] and not indent[depth] and token_type not in ( - tokenize.NL, tokenize.COMMENT - ): - indent[depth] = start[1] - indent_chances[start[1]] = True - elif token_type == tokenize.STRING or text in ( - 'u', 'ur', 'b', 'br' - ): - indent_chances[start[1]] = str - - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - parens[row] += 1 - elif text in ')]}' and depth > 0: - 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 and indent[depth]: # modified - 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: - indent_chances[start[1]] = text - - last_token_multiline = (start[0] != end[0]) - - return valid_indents - - -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) - return unicode(tool.refactor_string(source_text, name='')) - - -def break_multiline(source_text, newline, indent_word): - """Break first line of multiline code. - - Return None if a break is not possible. - - """ - indentation = _get_indentation(source_text) - - # Handle special case only. - for symbol in '([{': - # Only valid if symbol is not on a line by itself. - if (symbol in source_text and not source_text.strip() == symbol): - - if not source_text.rstrip()[-1] == ',': - continue - - index = 1 + source_text.find(symbol) - - if index <= len(indent_word) + len(indentation): - continue - - if is_probably_inside_string_or_comment(source_text, index - 1): - continue - - return ( - source_text[:index].rstrip() + newline + - indentation + indent_word + - source_text[index:].lstrip()) - - return None - - -def is_probably_inside_string_or_comment(line, index): - """Return True if index may be inside a string or comment.""" - # Make sure we are not in a string. - for quote in ['"', "'"]: - if quote in line: - if line.find(quote) <= index: - return True - - # Make sure we are not in a comment. - if '#' in line: - if line.find('#') <= index: - return True - - return False - - -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=False): - """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) - - split_source = [None] + source.splitlines() - - for r in results: - issue_id = r['id'].lower() - - if r['line'] in non_docstring_string_line_numbers: - if issue_id.startswith('e1'): - continue - elif issue_id in ['e501', 'w191']: - continue - - if r['line'] in all_string_line_numbers: - if issue_id in ['e501']: - continue - - # Filter out incorrect E101 reports when there are no tabs. - # pep8 will complain about this even if the tab indentation found - # elsewhere is in a multiline string. - if issue_id == 'e101' and '\t' not in split_source[r['line']]: - continue - - if issue_id in ['e711', 'e712'] and not aggressive: - 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. - - """ - sio = StringIO(source) - line_numbers = set() - previous_token_type = '' - try: - for t in tokenize.generate_tokens(sio.readline): - token_type = t[0] - start_row = t[2][0] - end_row = t[3][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 (IndentationError, tokenize.TokenError): - pass - - return line_numbers - - -def shorten_comment(line, newline, max_line_length): - """Return trimmed or split long comment line.""" - 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] + newline - elif 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 newline.join(split_lines) + newline - else: - return line + newline - - -def normalize_line_endings(lines): - """Return fixed line endings. - - All lines will be modified to use the most common line ending. - - """ - newline = find_newline(lines) - 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: - for ignored_code in [c.strip() for c in ignore]: - if mutual_startswith(code.lower(), ignored_code.lower()): - return False - - if select: - 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_string(source, options=None): - """Return fixed source code.""" - if not options: - options = parse_args([''])[0] - - sio = StringIO(source) - return fix_lines(sio.readlines(), options=options) - - -def fix_lines(source_lines, options, filename=''): - """Return fixed source code.""" - tmp_source = unicode().join(normalize_line_endings(source_lines)) - - # Keep a history to break out of cycles. - previous_hashes = set([hash(tmp_source)]) - - # Apply global fixes only once (for efficiency). - fixed_source = apply_global_fixes(tmp_source, options) - - passes = 0 - while True: - if options.pep8_passes >= 0 and passes > options.pep8_passes: - break - passes += 1 - - tmp_source = copy.copy(fixed_source) - - fix = FixPEP8(filename, options, contents=tmp_source) - fixed_source = fix.fix() - - if hash(fixed_source) in previous_hashes: - break - else: - previous_hashes.add(hash(fixed_source)) - - return fixed_source - - -def fix_file(filename, options=None, output=None): - if not options: - options = parse_args([filename])[0] - - original_source = read_from_filename(filename, readlines=True) - - 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 = 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 != ['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. - - Thsese are fixes that only need be done once (unlike those in FixPEP8, - which are dependent on pep8). - - """ - 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) - - 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 parse_args(args): - """Parse command-line options.""" - parser = OptionParser(usage='Usage: autopep8 [options] ' - '[filename [filename ...]]' - '\nUse filename \'-\' for stdin.', - version='%prog {0}'.format(__version__), - description=__doc__.split('\n')[0], - prog='autopep8') - parser.add_option('-v', '--verbose', action='count', dest='verbose', - default=0, - help='print verbose messages; ' - 'multiple -v result in more verbose messages') - parser.add_option('-d', '--diff', action='store_true', dest='diff', - help='print the diff for the fixed source') - parser.add_option('-i', '--in-place', action='store_true', - help='make changes to files in place') - parser.add_option('-r', '--recursive', action='store_true', - help='run recursively; must be used with --in-place or ' - '--diff') - parser.add_option('-j', '--jobs', type=int, metavar='n', default=1, - help='number of parallel jobs; ' - 'match CPU count if value is less than 1') - parser.add_option('-p', '--pep8-passes', metavar='n', - default=-1, type=int, - help='maximum number of additional pep8 passes ' - '(default: infinite)') - parser.add_option('-a', '--aggressive', action='count', default=0, - help='enable non-whitespace changes; ' - 'multiple -a result in more aggressive changes') - parser.add_option('--exclude', metavar='globs', - help='exclude files/directories that match these ' - 'comma-separated globs') - parser.add_option('--list-fixes', action='store_true', - help='list codes for fixes; ' - 'used by --ignore and --select') - parser.add_option('--ignore', metavar='errors', default='', - help='do not fix these errors/warnings ' - '(default: {0})'.format(DEFAULT_IGNORE)) - parser.add_option('--select', metavar='errors', default='', - help='fix only these errors/warnings (e.g. E4,W)') - parser.add_option('--max-line-length', metavar='n', default=79, type=int, - help='set maximum allowed line length ' - '(default: %default)') - options, args = parser.parse_args(args) - - if not len(args) and not options.list_fixes: - parser.error('incorrect number of arguments') - - if '-' in args and len(args) > 1: - parser.error('cannot mix stdin and regular files') - - if len(args) > 1 and not (options.in_place or options.diff): - parser.error('autopep8 only takes one filename as argument ' - 'unless the "--in-place" or "--diff" options are ' - 'used') - - if options.recursive and not (options.in_place or options.diff): - parser.error('--recursive must be used with --in-place or --diff') - - if options.exclude and not options.recursive: - parser.error('--exclude is only relevant when used with --recursive') - - if options.in_place and options.diff: - parser.error('--in-place and --diff are mutually exclusive') - - if options.max_line_length <= 0: - parser.error('--max-line-length must be greater than 0') - - if args == ['-'] and (options.in_place or options.recursive): - parser.error('--in-place or --recursive cannot be used with ' - 'standard input') - - if options.select: - options.select = options.select.split(',') - - if options.ignore: - options.ignore = options.ignore.split(',') - elif not options.select: - if options.aggressive: - # Enable everything by default if aggressive. - options.select = ['E', 'W'] - else: - options.ignore = DEFAULT_IGNORE.split(',') - - if options.exclude: - options.exclude = options.exclude.split(',') - else: - options.exclude = [] - - if options.jobs < 1: - # Do not import multiprocessing globally in case it is not supported - # on the platform. - import multiprocessing - options.jobs = multiprocessing.cpu_count() - - if options.jobs > 1 and not options.in_place: - parser.error('parallel jobs requires --in-place') - - return options, args - - -def supported_fixes(): - """Yield pep8 error codes that autopep8 fixes. - - Each item we yield is a tuple of the code followed by its description. - - """ - 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+', ' ', - getattr(instance, attribute).__doc__)) - - for (code, function) in sorted(global_fixes()): - yield (code.upper() + (4 - len(code)) * ' ', - re.sub(r'\s+', ' ', function.__doc__)) - - -def line_shortening_rank(candidate, newline, indent_word): - """Return rank of candidate. - - This is for sorting candidates. - - """ - rank = 0 - if candidate.strip(): - lines = candidate.split(newline) - - offset = 0 - if lines[0].rstrip()[-1] not in '([{': - for symbol in '([{': - offset = max(offset, 1 + lines[0].find(symbol)) - - max_length = max([offset + len(x.strip()) for x in lines]) - rank += max_length - rank += len(lines) - - bad_staring_symbol = { - '(': ')', - '[': ']', - '{': '}'}.get(lines[0][-1], None) - - if len(lines) > 1: - if (bad_staring_symbol and - lines[1].lstrip().startswith(bad_staring_symbol)): - rank += 20 - else: - rank -= 10 - - if lines[0].endswith('(['): - rank += 10 - - for current_line in lines: - for bad_start in ['.', '%', '+', '-', '/']: - if current_line.startswith(bad_start): - rank += 100 - - for ending in '([{': - # Avoid lonely opening. They result in longer lines. - if (current_line.endswith(ending) and - len(current_line.strip()) <= len(indent_word)): - rank += 100 - - if current_line.endswith('%'): - rank -= 20 - - # Try to break list comprehensions at the "for". - if current_line.lstrip().startswith('for'): - rank -= 50 - else: - rank = 100000 - - return max(0, rank) - - -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]) - previous_offset = current_offset - - result.append(line[current_offset:]) - - return result - - -def get_longest_length(text, newline): - """Return length of longest line.""" - return max([len(line) for line in text.split(newline)]) - - -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 temporary_file(): - """Return temporary file.""" - try: - return tempfile.NamedTemporaryFile(mode='w', encoding='utf-8') - except TypeError: - return tempfile.NamedTemporaryFile(mode='w') - - -def match_file(filename, exclude): - """Return True if file is okay for modifying/recursing.""" - if os.path.basename(filename).startswith('.'): - return False - - for pattern in exclude: - if fnmatch.fnmatch(filename, pattern): - return False - - if 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(f, exclude)] - directories[:] = [d for d in directories - if not d.startswith('.')] - 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(str(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 len(first_line) > 200: - # This is probably not even a text file. - return False - - if not PYTHON_SHEBANG_REGEX.match(first_line): - return False - - return True - - -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: - options, args = parse_args(sys.argv[1:]) - - if options.list_fixes: - for code, description in supported_fixes(): - print('{code} - {description}'.format( - code=code, description=description)) - return 0 - - if options.in_place or options.diff: - filenames = list(set(args)) - else: - assert len(args) == 1 - assert not options.recursive - if args == ['-']: - assert not options.in_place - temp = temporary_file() - temp.write(sys.stdin.read()) - temp.flush() - filenames = [temp.name] - else: - filenames = args[:1] - - fix_multiple_files(filenames, options, sys.stdout) - except KeyboardInterrupt: - return 1 # pragma: no cover - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/pylibs/pylama/__init__.py b/pylibs/pylama/__init__.py deleted file mode 100644 index f73e6931..00000000 --- a/pylibs/pylama/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -""" Code audit tool for python. - -:copyright: 2013 by Kirill Klenov. -:license: BSD, see LICENSE for more details. - -""" - -version_info = 2, 0, 1 - -__version__ = version = '.'.join(map(str, version_info)) -__project__ = __name__ -__author__ = "Kirill Klenov " -__license__ = "GNU LGPL" diff --git a/pylibs/pylama/config.py b/pylibs/pylama/config.py deleted file mode 100644 index 5db73ee0..00000000 --- a/pylibs/pylama/config.py +++ /dev/null @@ -1,193 +0,0 @@ -""" Parse arguments from command line and configuration files. """ -import fnmatch -from os import getcwd, path -from re import compile as re - -import logging -from argparse import ArgumentParser, Namespace as Options - -from . import version -from .core import LOGGER, STREAM -from .inirama import Namespace -from .lint import LINTERS - - -#: A default checkers -DEFAULT_LINTERS = 'pep8', 'pyflakes', 'mccabe' - -#: A default complexity for mccabe checker -DEFAULT_COMPLEXITY = 10 - -CURDIR = getcwd() -DEFAULT_INI_PATH = path.join(CURDIR, 'pylama.ini') - - -def parse_options( - args=None, async=False, select='', ignore='', linters=DEFAULT_LINTERS, - complexity=DEFAULT_COMPLEXITY, options=DEFAULT_INI_PATH): - """ Parse options from command line and configuration files. - - :return argparse.Namespace: - - """ - # Parse args from command string - parser = get_parser() - actions = dict((a.dest, a) for a in parser._actions) - options = Options( - async=_Default(async), format=_Default('pep8'), - select=_Default(select), ignore=_Default(ignore), - report=_Default(None), verbose=_Default(False), - linters=_Default(','.join(linters)), complexity=_Default(complexity), - options=_Default(options)) - - if not (args is None): - options = parser.parse_args(args) - - # Parse options from ini file - config = get_config(str(options.options)) - - # Compile options from ini - for k, v in config.default.items(): - value = getattr(options, k, _Default(None)) - if not isinstance(value, _Default): - continue - - action = actions.get(k) - LOGGER.info('Find option %s (%s)', k, v) - name, value = action.dest, action.type(v)\ - if callable(action.type) else v - if action.const: - value = bool(int(value)) - setattr(options, name, value) - - # Postprocess options - opts = dict(options.__dict__.items()) - for name, value in opts.items(): - if isinstance(value, _Default): - action = actions.get(name) - if action and callable(action.type): - value.value = action.type(value.value) - - setattr(options, name, value.value) - - # Parse file related options - options.file_params = dict() - for k, s in config.sections.items(): - if k != config.default_section: - mask = re(fnmatch.translate(k)) - options.file_params[mask] = dict(s) - options.file_params[mask]['lint'] = int( - options.file_params[mask].get('lint', 1) - ) - - return options - - -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) - - -def get_parser(): - """ Make command parser for pylama. - - :return ArgumentParser: - - """ - split_csp_str = lambda s: list( - set(i for i in s.strip().split(',') if i)) - - parser = ArgumentParser(description="Code audit tool for python.") - parser.add_argument( - "path", nargs='?', default=_Default(CURDIR), - help="Path on file or directory.") - - 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="Error format.") - - parser.add_argument( - "--select", "-s", default=_Default(''), type=split_csp_str, - help="Select errors and warnings. (comma-separated)") - - def parse_linters(csp_str): - result = list() - for name in split_csp_str(csp_str): - linter = LINTERS.get(name) - if linter: - result.append((name, linter)) - return result - - 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( - "--complexity", "-c", default=_Default(DEFAULT_COMPLEXITY), type=int, - help="Set mccabe complexity.") - - parser.add_argument("--report", "-r", help="Filename for 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=_Default(DEFAULT_INI_PATH), - help="Select configuration file. By default is '/pylama.ini'") - - return parser - - -def get_config(ini_path=DEFAULT_INI_PATH): - """ Load configuration from INI. - - :return Namespace: - - """ - config = Namespace() - config.default_section = 'main' - config.read(ini_path) - - return config - - -class _Default(object): - - def __init__(self, value): - self.value = value - - def __getattr__(self, name): - return getattr(self.value, name) - - def __str__(self): - return str(self.value) - - -# lint_ignore=R0914,W0212,E1103,C901 diff --git a/pylibs/pylama/core.py b/pylibs/pylama/core.py deleted file mode 100644 index 5655557a..00000000 --- a/pylibs/pylama/core.py +++ /dev/null @@ -1,160 +0,0 @@ -""" Pylama's core functionality. - -Prepare params, check a modeline and run the checkers. - -""" -import logging -import re -from .lint import LINTERS - -#: The skip pattern -SKIP_PATTERN = re.compile(r'# *noqa\b', re.I).search - -# Parse a modelines -MODELINE_RE = re.compile( - r'^\s*#\s+(?:pymode\:)?((?:lint[\w_]*=[^:\n\s]+:?)+)', re.I | re.M) - -# Setup a logger -LOGGER = logging.getLogger('pylama') -STREAM = logging.StreamHandler() -LOGGER.addHandler(STREAM) - - -def run(path, ignore=None, select=None, linters=None, config=None, **meta): - """ Run a code checkers with given params. - - :return errors: list of dictionaries with error's information - - """ - errors = [] - linters = linters or LINTERS.items() - params = dict(ignore=ignore, select=select) - code = None - try: - with open(path, 'rU') as f: - code = f.read() + '\n\n' - - params = prepare_params( - parse_modeline(code), config, ignore=ignore, select=select - ) - - if not params['lint']: - return errors - - for item in linters: - - if not isinstance(item, tuple): - item = (item, LINTERS.get(item)) - - name, linter = item - - if not linter or not linter.allow(path): - continue - - result = linter.run(path, code=code, **meta) - for e in result: - e['col'] = e.get('col') or 0 - e['lnum'] = e.get('lnum') or 0 - e['type'] = e.get('type') or 'E' - e['text'] = "{0} [{1}]".format((e.get( - 'text') or '').strip() - .replace("'", "\"").split('\n')[0], name) - e['filename'] = path or '' - errors.append(e) - - except IOError as e: - errors.append(dict( - lnum=0, type='E', col=0, text=str(e), filename=path or '')) - - except SyntaxError as e: - errors.append(dict( - lnum=e.lineno or 0, type='E', col=e.offset or 0, - text=e.args[0] + ' [%s]' % name, filename=path or '' - )) - - except Exception: - import traceback - logging.debug(traceback.format_exc()) - - errors = [er for er in errors if filter_errors(er, **params)] - - if code: - errors = filter_skiplines(code, errors) - - return sorted(errors, key=lambda x: x['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(*configs, **params): - """ Prepare and merge a params from modelines and configs. - - :return dict: - - """ - params['ignore'] = list(params.get('ignore') or []) - params['select'] = list(params.get('select') or []) - - for config in filter(None, configs): - for key in ('ignore', 'select'): - config.setdefault(key, config.get('lint_' + key, [])) - if not isinstance(config[key], list): - config[key] = config[key].split(',') - params[key] += config[key] - params['lint'] = config.get('lint', 1) - - params['ignore'] = set(params['ignore']) - params['select'] = set(params['select']) - params.setdefault('lint', 1) - return params - - -def filter_errors(e, select=None, ignore=None, **params): - """ Filter a erros by select and ignore options. - - :return bool: - - """ - if select: - for s in select: - if e['text'].startswith(s): - return True - - if ignore: - for s in ignore: - if e['text'].startswith(s): - return False - - return True - - -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 not er['lnum'] in removed] - - return errors diff --git a/pylibs/pylama/hook.py b/pylibs/pylama/hook.py deleted file mode 100644 index e35d26fe..00000000 --- a/pylibs/pylama/hook.py +++ /dev/null @@ -1,116 +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) if f.endswith('.py')], 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_) - if file_.endswith('.py'): - 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/pylibs/pylama/inirama.py b/pylibs/pylama/inirama.py deleted file mode 100644 index 1ba1a1eb..00000000 --- a/pylibs/pylama/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 - -import io -import re -import logging -from collections import MutableMapping -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) - ix = self.__map.pop(key) - self.__order = self.null - - def __iter__(self): - for key in self.__order: - if not key is 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 - - -__version__ = '0.5.0' -__project__ = 'Inirama' -__author__ = "Kirill Klenov " -__license__ = "BSD" - - -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', re.compile(r'[\w_]+\s*[:=].*'))] - - 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(MutableMapping): - - """ Representation of INI section. """ - - def __init__(self, namespace, *args, **kwargs): - super(Section, self).__init__(*args, **kwargs) - self.namespace = namespace - self.__storage__ = dict() - - def __setitem__(self, name, value): - self.__storage__[name] = str(value) - - def __getitem__(self, name): - return self.__storage__[name] - - def __delitem__(self, name): - del self.__storage__[name] - - def __len__(self): - return len(self.__storage__) - - def __iter__(self): - return iter(self.__storage__) - - def __repr__(self): - return "<{0} {1}>".format(self.__class__.__name__, str(dict(self))) - - def iteritems(self): - """ Impletment iteritems. """ - for key in self.__storage__.keys(): - yield key, self[key] - - items = lambda s: list(s.iteritems()) - - -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): - value = super(InterpolationSection, self).__getitem__(name) - 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 - - -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 - - for token in scanner.tokens: - if token[0] == 'KEY': - 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('[]') - - def __getitem__(self, name): - """ Look name in self sections. - - :return :class:`inirama.Section`: section - - """ - if not name 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 - -# lint_ignore=W0201,R0924,F0401 diff --git a/pylibs/pylama/lint/__init__.py b/pylibs/pylama/lint/__init__.py deleted file mode 100644 index 22aac2c2..00000000 --- a/pylibs/pylama/lint/__init__.py +++ /dev/null @@ -1,44 +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__) - - -LINTERS = dict() - - -from os import listdir, path as op - -curdir = op.dirname(__file__) -for p in listdir(curdir): - if p.startswith('pylama') and op.isdir(op.join(curdir, p)): - name = p[len('pylama_'):] - module = __import__( - 'pylama.lint.pylama_%s' % name, globals(), locals(), ['Linter']) - LINTERS[name] = getattr(module, 'Linter')() - -# try: - # from pkg_resources import iter_entry_points - - # for entry in iter_entry_points('pylama.linter'): - # LINTERS[entry.name] = entry.load()() -# except ImportError: - # pass diff --git a/pylibs/pylama/lint/pylama_mccabe/__init__.py b/pylibs/pylama/lint/pylama_mccabe/__init__.py deleted file mode 100644 index 0b035c07..00000000 --- a/pylibs/pylama/lint/pylama_mccabe/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -""" Check complexity. """ - -from .. import Linter as BaseLinter - - -class Linter(BaseLinter): - - """ Mccabe code complexity. """ - - @staticmethod - def run(path, code=None, complexity=8, **meta): - """ MCCabe code checking. - - :return list: List of errors. - - """ - from .mccabe import get_code_complexity - - return get_code_complexity(code, complexity, filename=path) or [] diff --git a/pylibs/pylama/lint/pylama_mccabe/mccabe.py b/pylibs/pylama/lint/pylama_mccabe/mccabe.py deleted file mode 100644 index 82bdf5f0..00000000 --- a/pylibs/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/pylibs/pylama/lint/pylama_pep257/__init__.py b/pylibs/pylama/lint/pylama_pep257/__init__.py deleted file mode 100644 index 4a1e6b39..00000000 --- a/pylibs/pylama/lint/pylama_pep257/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -""" Check PEP257. """ - -from .. import Linter as BaseLinter - - -class Linter(BaseLinter): - - """ Mccabe code complexity. """ - - @staticmethod - def run(path, **meta): - """ PEP257 code checking. - - :return list: List of errors. - - """ - f = open(path) - from .pep257 import check_source - - errors = [] - for er in check_source(f.read(), path): - errors.append(dict( - lnum=er.line, - col=er.char, - text='C0110 %s' % er.explanation.split('\n')[0].strip(), - type='W', - )) - return errors diff --git a/pylibs/pylama/lint/pylama_pep257/pep257.py b/pylibs/pylama/lint/pylama_pep257/pep257.py deleted file mode 100644 index a43dbae7..00000000 --- a/pylibs/pylama/lint/pylama_pep257/pep257.py +++ /dev/null @@ -1,703 +0,0 @@ -#! /usr/bin/env python -"""Static analysis tool for checking docstring conventions and style. - -About ------ - -Currently implemented checks cover most of PEP257: -http://www.python.org/dev/peps/pep-0257/ - -After PEP257 is covered and tested, other checks might be added, -e.g. NumPy docstring conventions is the first candidate: -https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt - -The main repository of this program is located at: -http://github.com/GreenSteam/pep257 - -Creating own checks -------------------- - -In order to add your own check, create a function in "Checks functions" -section below. The function should take 3 parameters: - -docstring : str - Docstring to check, as it is in file (with quotes). -context : str - Docstring's context (e.g. function's source code). -is_script : bool - Whether the docstring is script with #! or not. - -Depending on 1st parameter name, the function will be called with -different type of docstring: - - * module_docstring - * function_docstring - * class_docstring - * method_docstring - * def_docstring (i.e. function-docstrings + method-docstrings) - * docstring (i.e. all above docstring types) - -E.g. the following function will be fed only class-docstrings: - - def your_check(class_docstring, context, is_script): - pass - -If for a certain function, class, etc. a docstring does not exist, -then `None` will be passed, which should be taken into account. - -To signify that a check passed successfully simply `return` from the -check function. If a check failed, return `True`. If a check failed -and you can provide the precise position where it failed, return a -tuple (start_position, end_position), where start and end positions -are integers specifying where in `context` the failure occured. - -Also, see examples in "Check functions" section. - -""" -__version__ = '0.2.4' - -from curses.ascii import isascii -import inspect -from optparse import OptionParser -from os import walk -from os.path import abspath, basename, expanduser, isdir, isfile -from os.path import join as path_join -import re -import sys -import tokenize as tk - - -try: - from StringIO import StringIO -except ImportError: - # Python 3.0 and later - from io import StringIO - - -try: - all - any -except NameError: - # Python 2.4 and earlier - def all(iterable): - for element in iterable: - if not element: - return False - return True - - def any(iterable): - for element in iterable: - if element: - return True - return False - - -try: - next -except NameError: - # Python 2.5 and earlier - def next(obj): - return obj.next() - - -# -# Helper functions -# - -def cached(f): - """A decorator that caches function results. - - No cache expiration is currently done. - - """ - cache = {} - - def cached_func(*args, **kwargs): - key = (args, tuple(kwargs.items())) - if key in cache: - return cache[key] - else: - res = f(*args, **kwargs) - cache[key] = res - return res - return cached_func - - -def yield_list(f): - """Convert generator into list-returning function (decorator).""" - return lambda *arg, **kw: list(f(*arg, **kw)) - - -def remove_comments(s): - return re.sub('#[^\n]', '', s) - - -def abs_pos(marker, source): - """Return absolute position in source given (line, character) marker.""" - line, char = marker - lines = StringIO(source).readlines() - return len(''.join(lines[:line - 1])) + char - - -def rel_pos(abs_pos, source): - """Given absolute position, return relative (line, character) in source.""" - lines = StringIO(source).readlines() - nchars = len(source) - assert nchars >= abs_pos - while nchars > abs_pos: - assert nchars >= abs_pos - nchars -= len(lines[-1]) - lines.pop() - return len(lines) + 1, abs_pos - len(''.join(lines)) - - -def get_summary_line_info(thedocstring): - """Get the (summary_line, line_number) tuple for the given docstring. - - The returned 'summary_line' is the pep257 summary line and 'line_number' is - the zero-based docstring line number containing the summary line, which - will be either 0 (zeroth line) or 1 (first line). Any docstring checks - relating to the summary line should use this method to ensure consistent - treatment of the summary line. - - """ - lines = eval(thedocstring).split('\n') - first_line = lines[0].strip() - if len(lines) == 1 or len(first_line) > 0: - return first_line, 0 - return lines[1].strip(), 1 - - -# -# Parsing -# - - -def parse_module_docstring(source): - for kind, value, _, _, _ in tk.generate_tokens(StringIO(source).readline): - if kind in [tk.COMMENT, tk.NEWLINE, tk.NL]: - continue - elif kind == tk.STRING: # first STRING should be docstring - return value - else: - return None - - -def parse_docstring(source, what=''): - """Parse docstring given `def` or `class` source.""" - module_docstring = parse_module_docstring(source) - if what.startswith('module'): - return module_docstring - if module_docstring: - return module_docstring - token_gen = tk.generate_tokens(StringIO(source).readline) - try: - kind = None - while kind != tk.INDENT: - kind, _, _, _, _ = next(token_gen) - kind, value, _, _, _ = next(token_gen) - if kind == tk.STRING: # STRING after INDENT is a docstring - return value - except StopIteration: - pass - - -@yield_list -def parse_top_level(source, keyword): - """Parse top-level functions or classes.""" - token_gen = tk.generate_tokens(StringIO(source).readline) - kind, value, char = None, None, None - while True: - start, end = None, None - while not (kind == tk.NAME and value == keyword and char == 0): - kind, value, (line, char), _, _ = next(token_gen) - start = line, char - while not (kind == tk.DEDENT and value == '' and char == 0): - kind, value, (line, char), _, _ = next(token_gen) - end = line, char - yield source[abs_pos(start, source): abs_pos(end, source)] - - -@cached -def parse_functions(source): - return parse_top_level(source, 'def') - - -@cached -def parse_classes(source): - return parse_top_level(source, 'class') - - -def skip_indented_block(token_gen): - kind, value, start, end, raw = next(token_gen) - while kind != tk.INDENT: - kind, value, start, end, raw = next(token_gen) - indent = 1 - for kind, value, start, end, raw in token_gen: - if kind == tk.INDENT: - indent += 1 - elif kind == tk.DEDENT: - indent -= 1 - if indent == 0: - return kind, value, start, end, raw - - -@cached -@yield_list -def parse_methods(source): - source = ''.join(parse_classes(source)) - token_gen = tk.generate_tokens(StringIO(source).readline) - kind, value, char = None, None, None - while True: - start, end = None, None - while not (kind == tk.NAME and value == 'def'): - kind, value, (line, char), _, _ = next(token_gen) - start = line, char - kind, value, (line, char), _, _ = skip_indented_block(token_gen) - end = line, char - yield source[abs_pos(start, source): abs_pos(end, source)] - - -def parse_contexts(source, kind): - if kind == 'module_docstring': - return [source] - if kind == 'function_docstring': - return parse_functions(source) - if kind == 'class_docstring': - return parse_classes(source) - if kind == 'method_docstring': - return parse_methods(source) - if kind == 'def_docstring': - return parse_functions(source) + parse_methods(source) - if kind == 'docstring': - return ([parse_module_docstring(source)] + parse_functions(source) + - parse_classes(source) + parse_methods(source)) - - -# -# Framework -# - - -class Error(object): - - """Error in docstring style. - - * Stores relevant data about the error, - * provides format for printing an error, - * provides __lt__ method to sort errors. - - """ - - # options that define how errors are printed - explain = False - range = False - quote = False - - def __init__(self, filename, source, docstring, context, - explanation, start=None, end=None): - self.filename = filename - self.source = source - self.docstring = docstring - self.context = context - self.explanation = explanation.strip() - - if start is None: - self.start = source.find(context) + context.find(docstring) - else: - self.start = source.find(context) + start - self.line, self.char = rel_pos(self.start, self.source) - - if end is None: - self.end = self.start + len(docstring) - else: - self.end = source.find(context) + end - self.end_line, self.end_char = rel_pos(self.end, self.source) - - def __str__(self): - s = self.filename + ':%d:%d' % (self.line, self.char) - if self.range: - s += '..%d:%d' % (self.end_line, self.end_char) - if self.explain: - s += ': ' + self.explanation + '\n' - else: - s += ': ' + self.explanation.split('\n')[0].strip() - if self.quote: - quote = self.source[self.start:self.end].strip() - s += '\n> ' + '\n> '.join(quote.split('\n')) + '\n' - return s - - def __lt__(self, other): - return (self.filename, self.start) < (other.filename, other.start) - - -@yield_list -def find_checks(keyword): - for function in globals().values(): - if inspect.isfunction(function): - args = inspect.getargspec(function)[0] - if args and args[0] == keyword: - yield function - - -@yield_list -def check_source(source, filename): - keywords = ['module_docstring', 'function_docstring', - 'class_docstring', 'method_docstring', - 'def_docstring', 'docstring'] # TODO? 'nested_docstring'] - is_script = source.startswith('#!') or \ - basename(filename).startswith('test_') - for keyword in keywords: - for check in find_checks(keyword): - for context in parse_contexts(source, keyword): - docstring = parse_docstring(context, keyword) - result = check(docstring, context, is_script) - if result: - positions = [] if result is True else result - yield Error(filename, source, docstring, context, - check.__doc__, *positions) - - -def find_input_files(filenames): - """ Return a list of input files. - - `filenames` is a list of filenames, which may be either files - or directories. Files within subdirectories are added - recursively. - - """ - input_files = [] - - filenames = [abspath(expanduser(f)) for f in filenames] - for filename in filenames: - if isdir(filename): - for root, _dirs, files in walk(filename): - input_files += [path_join(root, f) for f in sorted(files) - if f.endswith(".py")] - elif isfile(filename): - input_files += [filename] - else: - print_error("%s is not a file or directory" % filename) - - return input_files - - -def check_files(filenames): - r"""Return list of docstring style errors found in files. - - Example - ------- - >>> import pep257 - >>> pep257.check_files(['one.py', 'two.py']) - ['one.py:23:1 PEP257 Use u\"\"\" for Unicode docstrings.'] - - """ - errors = [] - for filename in find_input_files(filenames): - errors.extend(check_source(open(filename).read(), filename)) - return [str(e) for e in errors] - - -def parse_options(): - parser = OptionParser(version=__version__) - parser.add_option('-e', '--explain', action='store_true', - help='show explanation of each error') - parser.add_option('-r', '--range', action='store_true', - help='show error start..end positions') - parser.add_option('-q', '--quote', action='store_true', - help='quote erroneous lines') - return parser.parse_args() - - -def print_error(message): - sys.stderr.write(message) - sys.stderr.write('\n') - sys.stderr.flush() - - -def main(options, arguments): - print('=' * 80) - print('Note: checks are relaxed for scripts (with #!) compared to modules') - Error.explain = options.explain - Error.range = options.range - Error.quote = options.quote - errors = [] - - for filename in find_input_files(arguments): - try: - f = open(filename) - except IOError: - print_error("Error opening file %s" % filename) - else: - try: - errors.extend(check_source(f.read(), filename)) - except IOError: - print_error("Error reading file %s" % filename) - except tk.TokenError: - print_error("Error parsing file %s" % filename) - finally: - f.close() - for error in sorted(errors): - print_error(str(error)) - return 1 if errors else 0 - - -# -# Check functions -# - - -def check_modules_have_docstrings(module_docstring, context, is_script): - """All modules should have docstrings. - - All modules should normally have docstrings. - - """ - if not module_docstring: # or not eval(module_docstring).strip(): - return 0, min(79, len(context)) - if not eval(module_docstring).strip(): - return True - - -def check_def_has_docstring(def_docstring, context, is_script): - """Exported definitions should have docstrings. - - ...all functions and classes exported by a module should also have - docstrings. Public methods (including the __init__ constructor) - should also have docstrings. - - """ - if is_script: - return # assume nothing is exported - def_name = context.split()[1] - if def_name.startswith('_') and not def_name.endswith('__'): - return # private, not exported - if not def_docstring: - return 0, len(context.split('\n')[0]) - if not eval(def_docstring).strip(): - return True - - -def check_class_has_docstring(class_docstring, context, is_script): - """Exported classes should have docstrings. - - ...all functions and classes exported by a module should also have - docstrings. - - """ - if is_script: - return # assume nothing is exported - class_name = context.split()[1] - if class_name.startswith('_'): - return # not exported - if not class_docstring: - return 0, len(context.split('\n')[0]) - if not eval(class_docstring).strip(): - return True - - -def check_triple_double_quotes(docstring, context, is_script): - r"""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\"\"\". - - """ - if docstring and not (docstring.startswith('"""') or - docstring.startswith('r"""') or - docstring.startswith('u"""')): - return True - - -def check_backslashes(docstring, context, is_script): - r"""Use r\"\"\" if any backslashes in your docstrings. - - Use r\"\"\"raw triple double quotes\"\"\" if you use any backslashes - (\\) in your docstrings. - - """ - if docstring and "\\" in docstring and not docstring.startswith('r"""'): - return True - - -def check_unicode_docstring(docstring, context, is_script): - r"""Use u\"\"\" for Unicode docstrings. - - For Unicode docstrings, use u\"\"\"Unicode triple-quoted stringsr\"\"\". - - """ - if (docstring and not all(isascii(char) for char in docstring) and - not docstring.startswith('u"""')): - return True - - -def check_one_liners(docstring, context, is_script): - """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 not docstring: - return - lines = docstring.split('\n') - if len(lines) > 1: - non_empty = [l for l in lines if any([c.isalpha() for c in l])] - if len(non_empty) == 1: - return True - - -def check_no_blank_before(def_docstring, context, is_script): - """No blank line before docstring in definitions. - - There's no blank line either before or after the docstring. - - """ - if not def_docstring: - return - before = remove_comments(context.split(def_docstring)[0]) - if before.split(':')[-1].count('\n') > 1: - return True - - -def check_ends_with_period(docstring, context, is_script): - """First line should end with a period. - - The [first line of a] docstring is a phrase ending in a period. - - """ - if not docstring: - return - (summary_line, line_number) = get_summary_line_info(docstring) - if not summary_line.endswith('.'): - return True - - -def check_imperative_mood(def_docstring, context, is_script): - """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 def_docstring and eval(def_docstring).strip(): - first_word = eval(def_docstring).strip().split()[0] - if first_word.endswith('s') and not first_word.endswith('ss'): - return True - - -def check_no_signature(def_docstring, context, is_script): - """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 not def_docstring: - return - def_name = context.split(def_docstring)[0].split()[1].split('(')[0] - first_line = eval(def_docstring).split('\n')[0] - if def_name + '(' in first_line.replace(' ', ''): - return True - - -def check_return_type(def_docstring, context, is_script): - """Return value type should be mentioned. - - However, the nature of the return value cannot be determined by - introspection, so it should be mentioned. - - """ - if (not def_docstring) or is_script: - return - if 'return' not in def_docstring.lower(): - tokens = list(tk.generate_tokens(StringIO(context).readline)) - after_return = [tokens[i + 1][0] for i, token in enumerate(tokens) - if token[1] == 'return'] - # not very precise (tk.OP ';' is not taken into account) - if set(after_return) - set([tk.COMMENT, tk.NL, tk.NEWLINE]) != set([]): - return True - - -def check_blank_after_summary(docstring, context, is_script): - """Blank line missing after one-line summary. - - 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 not docstring: - return - lines = eval(docstring).split('\n') - if len(lines) > 1: - (summary_line, line_number) = get_summary_line_info(docstring) - if len(lines) <= (line_number+1) or lines[line_number+1].strip() != '': - return True - - -def check_indent(docstring, context, is_script): - """The entire docstring should be indented same as code. - - The entire docstring is indented the same as the quotes at its - first line. - - """ - if (not docstring) or len(eval(docstring).split('\n')) == 1: - return - non_empty_lines = [line for line in eval(docstring).split('\n')[1:] - if line.strip()] - if not non_empty_lines: - return - indent = min([len(l) - len(l.lstrip()) for l in non_empty_lines]) - if indent != len(context.split(docstring)[0].split('\n')[-1]): - return True - - -def check_blank_before_after_class(class_docstring, context, is_script): - """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. - - """ - if not class_docstring: - return - before, after = context.split(class_docstring)[:2] - before_blanks = [not line.strip() for line in before.split('\n')] - after_blanks = [not line.strip() for line in after.split('\n')] - if before_blanks[-3:] != [False, True, True]: - return True - if not all(after_blanks) and after_blanks[:3] != [True, True, False]: - return True - - -def check_blank_after_last_paragraph(docstring, context, is_script): - """Multiline docstring should end with 1 blank line. - - The BDFL recommends inserting a blank line between the last - paragraph in a multi-line docstring and its closing quotes, - placing the closing quotes on a line by themselves. - - """ - if (not docstring) or len(eval(docstring).split('\n')) == 1: - return - blanks = [not line.strip() for line in eval(docstring).split('\n')] - if blanks[-3:] != [False, True, True]: - return True - - -if __name__ == '__main__': - try: - sys.exit(main(*parse_options())) - except KeyboardInterrupt: - pass diff --git a/pylibs/pylama/lint/pylama_pep8/__init__.py b/pylibs/pylama/lint/pylama_pep8/__init__.py deleted file mode 100644 index a1dadc43..00000000 --- a/pylibs/pylama/lint/pylama_pep8/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -""" Check PEP8. """ -from .. import Linter as BaseLinter -from .pep8 import BaseReport, StyleGuide - - -class Linter(BaseLinter): - - """ PEP8 code check. """ - - @staticmethod - def run(path, **meta): - """ PEP8 code checking. - - :return list: List of errors. - - """ - P8Style = StyleGuide(reporter=_PEP8Report) - return P8Style.input_file(path) - - -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) - - self.errors.append(dict( - text=text, - type=code, - col=offset + 1, - lnum=line_number, - )) - - def get_file_results(self): - """ Get errors. - - :return list: List of errors. - - """ - return self.errors diff --git a/pylibs/pylama/lint/pylama_pep8/pep8.py b/pylibs/pylama/lint/pylama_pep8/pep8.py deleted file mode 100644 index e0035b3d..00000000 --- a/pylibs/pylama/lint/pylama_pep8/pep8.py +++ /dev/null @@ -1,1879 +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-2013 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: -http://www.python.org/dev/peps/pep-0008/ - -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 -""" -__version__ = '1.4.7a0' - -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' -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') -SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE, - tokenize.INDENT, tokenize.DEDENT]) -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*,\s*\w+') -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_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. Most - editors have features that make this easy to do. - - 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""" - JCR: Trailing whitespace is superfluous. - FBM: Except when it occurs as part of a blank line (i.e. the line is - nothing but whitespace). According to Python docs[1] a line with only - whitespace is considered a blank line, and is to be ignored. However, - matching a blank line to its indentation level avoids mistakenly - terminating a multi-line statement (e.g. class declaration) when - pasting code into the standard Python interpreter. - - [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines - - 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): - r""" - JCR: Trailing blank lines are superfluous. - - Okay: spam(1) - W391: spam(1)\n - """ - if not physical_line.rstrip() and line_number == len(lines): - return 0, "W391 blank line at end of file" - - -def missing_newline(physical_line): - """ - JCR: The last line should have a newline. - - Reports warning W292. - """ - if physical_line.rstrip() == physical_line: - return len(physical_line), "W292 no newline at end of file" - - -def maximum_line_length(physical_line, max_line_length): - """ - Limit all lines to a maximum of 79 characters. - - There are still many devices around that are limited to 80 character - lines; plus, limiting windows to 80 characters makes it possible to have - several windows side-by-side. The default wrapping on such devices looks - ugly. Therefore, please limit all lines to a maximum of 79 characters. - For flowing long blocks of text (docstrings or comments), limiting the - length to 72 characters is recommended. - - Reports error E501. - """ - line = physical_line.rstrip() - length = len(line) - if length > max_line_length and not noqa(line): - 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, - 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_lines or previous_indent_level < indent_level or - DOCSTRING_REGEX.match(previous_logical)): - yield 0, "E301 expected 1 blank line, found 0" - elif blank_lines != 2: - yield 0, "E302 expected 2 blank lines, found %d" % blank_lines - - -def extraneous_whitespace(logical_line): - """ - Avoid extraneous whitespace in the following 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): - """ - JCR: 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 - - Okay: for item in items:\n pass - E112: for item in items:\npass - - Okay: a = 1\nb = 2 - E113: a = 1\n b = 2 - """ - if indent_char == ' ' and indent_level % 4: - yield 0, "E111 indentation is not a multiple of four" - indent_expect = previous_logical.endswith(':') - if indent_expect and indent_level <= previous_indent_level: - yield 0, "E112 expected an indented block" - if indent_level > previous_indent_level and not indent_expect: - yield 0, "E113 unexpected indentation" - - -def continued_indentation(logical_line, tokens, indent_level, hang_closing, - noqa, verbose): - r""" - 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 the following 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 (a or\n b):\n pass - E126: a = (\n 42) - E127: a = (24,\n 42) - E128: a = (24,\n 42) - """ - 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]] - 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 (tokenize.NL, tokenize.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 - - 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 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 visual_indent is True: - # visual indent is verified - if not indent[depth]: - indent[depth] = start[1] - elif visual_indent in (text, str): - # 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 continuation line " - "under-indented for visual indent") - elif hang == 4 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") - else: - # indent is broken - if hang <= 0: - error = "E122", "missing indentation or outdented" - elif indent[depth]: - error = "E127", "over-indented for visual indent" - elif hang % 4: - error = "E121", "indentation is not a multiple of four" - else: - error = "E126", "over-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 - - # keep track of bracket depth - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - 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] - 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: - # allow to line up tokens - indent_chances[start[1]] = text - - last_token_multiline = (start[0] != end[0]) - - if indent_next and expand_indent(line) == indent_level + 4: - yield (last_indent, "E125 continuation line does not distinguish " - "itself from next logical line") - - -def whitespace_before_parameters(logical_line, tokens): - """ - Avoid extraneous whitespace in the following situations: - - - Immediately before the open parenthesis that starts the argument - list of a function call. - - - Immediately 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 in the following situations: - - - More than one space around an assignment (or other) operator to - align it with another. - - 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""" - - 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). - - - Use spaces around arithmetic operators. - - 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 (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN): - # ERRORTOKEN is triggered by backticks in Python 3 - 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_type == tokenize.OP: - binary_usage = (prev_text in '}])') - elif prev_type == tokenize.NAME: - binary_usage = (prev_text not in KEYWORDS) - else: - binary_usage = (prev_type not in SKIP_TOKENS) - - if binary_usage: - 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 in the following situations: - - - More than one space around an assignment (or other) operator to - align it with another. - - 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): - """ - 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 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_inline_comment(logical_line, tokens): - """ - 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. - - Okay: x = x + 1 # Increment x - Okay: x = x + 1 # Increment x - E261: x = x + 1 # Increment x - E262: x = x + 1 #Increment x - E262: x = x + 1 # Increment x - """ - prev_end = (0, 0) - for token_type, text, start, end, line in tokens: - if token_type == tokenize.COMMENT: - if not line[:start[1]].strip(): - continue - 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(' ') - if symbol not in ('#', '#:') or comment[:1].isspace(): - yield start, "E262 inline comment should start with '# '" - 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 (multiple 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! - - 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 - """ - 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(')') and # (Python 3 annotation) - not LAMBDA_REGEX.search(before)): # lambda x: x - 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): - """ - 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_type(logical_line): - """ - Object type comparisons should always use isinstance() instead of - comparing 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): - r""" - The {}.has_key() method is removed in the Python 3. - Use the 'in' operation instead. - - Okay: if "alph" in d:\n print d["alph"] - W601: assert d.has_key('alph') - """ - pos = logical_line.find('.has_key(') - if pos > -1: - yield pos, "W601 .has_key() is deprecated, use 'in'" - - -def python_3000_raise_comma(logical_line): - """ - When raising an exception, use "raise ValueError('message')" - instead of the older form "raise ValueError, 'message'". - - The paren-using form is preferred because when the exception arguments - are long or include string formatting, you don't need to use line - continuation characters thanks to the containing parentheses. 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): - """ - != can also be written <>, but this is an obsolete usage kept for - backwards compatibility only. New code should always use !=. - 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): - """ - 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): - f = open(filename) - try: - return f.readlines() - finally: - f.close() - isidentifier = re.compile(r'[a-zA-Z_]\w*').match - stdin_get_value = sys.stdin.read -else: - # Python 3 - def readlines(filename): - f = open(filename, 'rb') - try: - 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): - f.close() - # Fall back if files are improperly declared - f = open(filename, encoding='latin-1') - return f.readlines() - finally: - f.close() - isidentifier = str.isidentifier - - def stdin_get_value(): - return TextIOWrapper(sys.stdin.buffer, errors='ignore').read() -readlines.__doc__ = " Read the source code." -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') - 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 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) - - -############################################################################## -# 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 where the first argument name - is '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.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): - 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) - report_invalid_syntax.__doc__ = " Check if the syntax is valid." - - def readline(self): - """ - Get the next line from the input buffer. - """ - self.line_number += 1 - if self.line_number > len(self.lines): - return '' - return self.lines[self.line_number - 1] - - def readline_check_physical(self): - """ - Check and return the next physical line. This method can be - used to feed tokenize.generate_tokens. - """ - line = self.readline() - if line: - self.check_physical(line) - 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 - if self.indent_char is None and line[:1] in WHITESPACE: - self.indent_char = line[0] - 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) - - def build_tokens_line(self): - """ - Build a logical line from tokens. - """ - self.mapping = [] - logical = [] - comments = [] - length = 0 - previous = None - for token in self.tokens: - token_type, text = token[0:2] - if token_type == tokenize.COMMENT: - comments.append(text) - continue - if token_type in SKIP_TOKENS: - continue - if token_type == tokenize.STRING: - text = mute_string(text) - if previous: - end_row, end = previous[3] - start_row, start = token[2] - if end_row != start_row: # different row - prev_text = self.lines[end_row - 1][end - 1] - if prev_text == ',' or (prev_text not in '{[(' - and text not in '}])'): - logical.append(' ') - length += 1 - elif end != start: # different column - fill = self.lines[end_row - 1][end:start] - logical.append(fill) - length += len(fill) - self.mapping.append((length, token)) - logical.append(text) - length += len(text) - previous = token - self.logical_line = ''.join(logical) - self.noqa = comments and noqa(''.join(comments)) - # With Python 2, if the line ends with '\r\r\n' the assertion fails - # assert self.logical_line.strip() == self.logical_line - - def check_logical(self): - """ - Build a line from tokens and run all logical checks on it. - """ - self.build_tokens_line() - self.report.increment_logical_line() - first_line = self.lines[self.mapping[0][1][2][0] - 1] - indent = first_line[:self.mapping[0][1][2][1]] - self.previous_indent_level = self.indent_level - self.indent_level = expand_indent(indent) - 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 result in self.run_check(check, argument_names): - offset, text = result - if isinstance(offset, tuple): - orig_number, orig_offset = offset - else: - for token_offset, token in self.mapping: - if offset >= token_offset: - orig_number = token[2][0] - orig_offset = (token[2][1] + offset - token_offset) - self.report_error(orig_number, orig_offset, text, check) - self.previous_logical = self.logical_line - - def check_ast(self): - 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 noqa(self.lines[lineno - 1]): - self.report_error(lineno, offset, text, check) - - def generate_tokens(self): - if self._io_error: - self.report_error(1, 0, 'E902 %s' % self._io_error, readlines) - tokengen = tokenize.generate_tokens(self.readline_check_physical) - try: - for token in tokengen: - yield token - except (SyntaxError, tokenize.TokenError): - self.report_invalid_syntax() - - 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) - if self._ast_checks: - self.check_ast() - self.line_number = 0 - self.indent_char = None - self.indent_level = 0 - self.previous_logical = '' - self.tokens = [] - self.blank_lines = blank_lines_before_comment = 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 == tokenize.NEWLINE: - if self.blank_lines < blank_lines_before_comment: - self.blank_lines = blank_lines_before_comment - self.check_logical() - self.tokens = [] - self.blank_lines = blank_lines_before_comment = 0 - elif token_type == tokenize.NL: - if len(self.tokens) == 1: - # The physical line contains only this token. - self.blank_lines += 1 - self.tokens = [] - elif token_type == tokenize.COMMENT and len(self.tokens) == 1: - if blank_lines_before_comment < self.blank_lines: - blank_lines_before_comment = self.blank_lines - self.blank_lines = 0 - if COMMENT_WITH_NL: - # The comment also ends a physical line - self.tokens = [] - 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(' ' * offset + '^') - if self._show_pep8 and doc: - print(doc.lstrip('\n').rstrip()) - 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) - options, self.paths = process_options( - parse_argv=parse_argv, config_file=config_file, parser=parser) - if args or kwargs: - # build options from dict - options_dict = dict(*args, **kwargs) - 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 - - for index, value in enumerate(options.exclude): - options.exclude[index] = value.rstrip('/') - 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 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) - 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): - """ - 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) - - 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]): - 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 options.verbose > 1: - print(" %s = %s" % (opt, config.get(pep8_section, opt))) - if opt.replace('_', '-') not in parser.config_options: - print("Unknown option: '%s'\n not in [%s]" % - (opt, ' '.join(parser.config_options))) - sys.exit(1) - 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) - 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 arglist and not parse_argv: - # Don't read the command line if the module is used as a library. - arglist = [] - 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)") - 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 = options.exclude.split(',') - 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.""" - 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/pylibs/pylama/lint/pylama_pyflakes/__init__.py b/pylibs/pylama/lint/pylama_pyflakes/__init__.py deleted file mode 100644 index c136b553..00000000 --- a/pylibs/pylama/lint/pylama_pyflakes/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -""" Check Pyflakes. """ - -from .. import Linter as BaseLinter - - -class Linter(BaseLinter): - - """ Pyflakes code check. """ - - @staticmethod - def run(path, code=None, **meta): - """ Pyflake code checking. - - :return list: List of errors. - - """ - import _ast - from .pyflakes import checker - - errors = [] - tree = compile(code, path, "exec", _ast.PyCF_ONLY_AST) - w = checker.Checker(tree, path) - 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 diff --git a/pylibs/pylama/lint/pylama_pyflakes/pyflakes/__init__.py b/pylibs/pylama/lint/pylama_pyflakes/pyflakes/__init__.py deleted file mode 100644 index ca95838a..00000000 --- a/pylibs/pylama/lint/pylama_pyflakes/pyflakes/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - -__version__ = '0.7.3a0' diff --git a/pylibs/pylama/lint/pylama_pyflakes/pyflakes/checker.py b/pylibs/pylama/lint/pylama_pyflakes/pyflakes/checker.py deleted file mode 100644 index 272caa6d..00000000 --- a/pylibs/pylama/lint/pylama_pyflakes/pyflakes/checker.py +++ /dev/null @@ -1,850 +0,0 @@ -""" -Main module. - -Implement the central Checker class. -Also, it models the Bindings and Scopes. -""" -import doctest -import os -import sys -try: - builtin_vars = dir(__import__('builtins')) - PY2 = False -except ImportError: - builtin_vars = dir(__import__('__builtin__')) - PY2 = True - -try: - import ast - iter_child_nodes = ast.iter_child_nodes -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) - - def iter_child_nodes(node): - """ - 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 node._fields: - field = getattr(node, name, None) - if isinstance(field, ast.AST): - yield field - elif isinstance(field, list): - for item in field: - yield item -# Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally) -if hasattr(ast, 'Try'): - ast_TryExcept = ast.Try - ast_TryFinally = () -else: - ast_TryExcept = ast.TryExcept - ast_TryFinally = ast.TryFinally - -from . 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() - - -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)) - - -class Importation(Binding): - """ - 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 - name = name.split('.')[0] - super(Importation, self).__init__(name, source) - - -class Argument(Binding): - """ - Represents binding a name as an argument. - """ - - -class Definition(Binding): - """ - A binding that defines a function or a class. - """ - - -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 names(self): - """ - Return a list of the names referenced by this binding. - """ - names = [] - if isinstance(self.source, ast.List): - for node in self.source.elts: - if isinstance(node, ast.Str): - names.append(node.s) - return names - - -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() - - 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 - withDoctest = ('PYFLAKES_NODOCTEST' not in os.environ) - - 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): - self._nodeHandlers = {} - self._deferredFunctions = [] - self._deferredAssignments = [] - self.deadScopes = [] - self.messages = [] - self.filename = filename - if builtins: - self.builtIns = self.builtIns.union(builtins) - 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: - export = isinstance(scope.get('__all__'), ExportBinding) - if export: - all = scope['__all__'].names() - if not scope.importStarred and \ - os.path.basename(self.filename) != '__init__.py': - # Look for possible mistakes in the export list - undefined = set(all) - set(scope) - for name in undefined: - self.report(messages.UndefinedExport, - scope['__all__'].source, name) - else: - all = [] - - # Look for imported names that aren't used. - for importation in scope.values(): - if isinstance(importation, Importation): - if not importation.used and importation.name not in all: - self.report(messages.UnusedImport, - importation.source, importation.name) - - def pushScope(self, scopeClass=FunctionScope): - self.scopeStack.append(scopeClass()) - - def pushFunctionScope(self): # XXX Deprecated - self.pushScope(FunctionScope) - - def pushClassScope(self): # XXX Deprecated - self.pushScope(ClassScope) - - def report(self, messageClass, *args, **kwargs): - self.messages.append(messageClass(self.filename, *args, **kwargs)) - - def hasParent(self, node, kind): - while hasattr(node, 'parent'): - node = node.parent - if isinstance(node, kind): - return True - - def getCommonAncestor(self, lnode, rnode, stop=None): - if not stop: - stop = self.root - if lnode is rnode: - return lnode - if stop in (lnode, rnode): - return stop - - if not hasattr(lnode, 'parent') or not hasattr(rnode, 'parent'): - return - if (lnode.level > rnode.level): - return self.getCommonAncestor(lnode.parent, rnode, stop) - if (rnode.level > lnode.level): - return self.getCommonAncestor(lnode, rnode.parent, stop) - return self.getCommonAncestor(lnode.parent, rnode.parent, stop) - - def descendantOf(self, node, ancestors, stop=None): - for a in ancestors: - if self.getCommonAncestor(node, a, stop) not in (stop, None): - return True - return False - - def onFork(self, parent, lnode, rnode, items): - return (self.descendantOf(lnode, items, parent) ^ - self.descendantOf(rnode, items, parent)) - - def differentForks(self, lnode, rnode): - """True, if lnode and rnode are located on different forks of IF/TRY""" - ancestor = self.getCommonAncestor(lnode, rnode) - if isinstance(ancestor, ast.If): - for fork in (ancestor.body, ancestor.orelse): - if self.onFork(ancestor, lnode, rnode, fork): - return True - elif isinstance(ancestor, ast_TryExcept): - body = ancestor.body + ancestor.orelse - for fork in [body] + [[hdl] for hdl in ancestor.handlers]: - if self.onFork(ancestor, lnode, rnode, fork): - return True - elif isinstance(ancestor, ast_TryFinally): - if self.onFork(ancestor, lnode, rnode, ancestor.body): - return True - return False - - def addBinding(self, node, value, reportRedef=True): - """ - Called when a binding is altered. - - - `node` is the statement responsible for the change - - `value` is the optional new value, a Binding instance, associated - with the binding; if None, the binding is deleted if it exists. - - if `reportRedef` is True (default), rebinding while unused will be - reported. - """ - redefinedWhileUnused = False - if not isinstance(self.scope, ClassScope): - for scope in self.scopeStack[::-1]: - existing = scope.get(value.name) - if (isinstance(existing, Importation) - and not existing.used - and (not isinstance(value, Importation) or - value.fullName == existing.fullName) - and reportRedef - and not self.differentForks(node, existing.source)): - redefinedWhileUnused = True - self.report(messages.RedefinedWhileUnused, - node, value.name, existing.source) - - existing = self.scope.get(value.name) - if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp): - if (existing and reportRedef - and not self.hasParent(existing.source, (ast.For, ast.ListComp)) - and not self.differentForks(node, existing.source)): - self.report(messages.RedefinedInListComp, - node, value.name, existing.source) - - if (isinstance(existing, Definition) - and not existing.used - and not self.differentForks(node, existing.source)): - self.report(messages.RedefinedWhileUnused, - node, value.name, existing.source) - else: - 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 = getattr(node, 'parent', None) - if isinstance(parent, (ast.For, ast.comprehension, ast.Tuple, ast.List)): - binding = Binding(name, node) - elif (parent is not None and name == '__all__' and - isinstance(self.scope, ModuleScope)): - binding = ExportBinding(name, parent.value) - 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): - for node in iter_child_nodes(tree): - self.handleNode(node, tree) - - 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.level = 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]) - if not docstring: - return - examples = self._getDoctestExamples(docstring) - except (ValueError, IndexError): - # e.g. line 6 of the docstring for has inconsistent - # leading whitespace: ... - return - node_offset = self.offset or (0, 0) - self.pushScope() - 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) - 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 - self.popScope() - - def ignore(self, node): - pass - - # "stmt" type nodes - RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \ - TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren - - CONTINUE = BREAK = PASS = ignore - - # "expr" type nodes - BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = YIELDFROM = \ - COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \ - STARRED = handleChildren - - NUM = STR = BYTES = ELLIPSIS = ignore - - # "slice" type nodes - SLICE = EXTSLICE = INDEX = handleChildren - - # expression contexts are node instances too, though being constants - LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore - - # same for operators - AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \ - BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \ - EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore - - # additional node types - COMPREHENSION = KEYWORD = handleChildren - - def GLOBAL(self, node): - """ - Keep track of globals declarations. - """ - if isinstance(self.scope, FunctionScope): - self.scope.globals.update(node.names) - - NONLOCAL = GLOBAL - - def LISTCOMP(self, node): - # handle generators before element - for gen in node.generators: - self.handleNode(gen, node) - self.handleNode(node.elt, node) - - def GENERATOREXP(self, node): - self.pushScope(GeneratorScope) - # handle generators before element - for gen in node.generators: - self.handleNode(gen, node) - self.handleNode(node.elt, node) - self.popScope() - - SETCOMP = GENERATOREXP - - def DICTCOMP(self, node): - self.pushScope(GeneratorScope) - for gen in node.generators: - self.handleNode(gen, node) - self.handleNode(node.key, node) - self.handleNode(node.value, node) - self.popScope() - - def FOR(self, node): - """ - Process bindings for loop variables. - """ - vars = [] - - def collectLoopVars(n): - if isinstance(n, ast.Name): - vars.append(n.id) - elif isinstance(n, ast.expr_context): - return - else: - for c in iter_child_nodes(n): - collectLoopVars(c) - - collectLoopVars(node.target) - for varn in vars: - if (isinstance(self.scope.get(varn), Importation) - # unused ones will get an unused import warning - and self.scope[varn].used): - self.report(messages.ImportShadowedByLoopVar, - node, varn, self.scope[varn].source) - - self.handleChildren(node) - - 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 FUNCTIONDEF(self, node): - for deco in node.decorator_list: - self.handleNode(deco, node) - self.addBinding(node, FunctionDefinition(node.name, node)) - self.LAMBDA(node) - if self.withDoctest: - self.deferFunction(lambda: self.handleDoctests(node)) - - def LAMBDA(self, node): - args = [] - - if PY2: - def addArgs(arglist): - for arg in arglist: - if isinstance(arg, ast.Tuple): - addArgs(arg.elts) - else: - if arg.id in args: - self.report(messages.DuplicateArgument, - node, arg.id) - args.append(arg.id) - addArgs(node.args.args) - defaults = node.args.defaults - else: - for arg in node.args.args + node.args.kwonlyargs: - if arg.arg in args: - self.report(messages.DuplicateArgument, - node, arg.arg) - args.append(arg.arg) - self.handleNode(arg.annotation, node) - if hasattr(node, 'returns'): # Only for FunctionDefs - for annotation in (node.args.varargannotation, - node.args.kwargannotation, node.returns): - self.handleNode(annotation, node) - defaults = node.args.defaults + node.args.kw_defaults - - # vararg/kwarg identifiers are not Name nodes - for wildcard in (node.args.vararg, node.args.kwarg): - if not wildcard: - continue - if wildcard in args: - self.report(messages.DuplicateArgument, node, wildcard) - args.append(wildcard) - for default in defaults: - self.handleNode(default, node) - - def runFunction(): - - self.pushScope() - for name in args: - self.addBinding(node, Argument(name, node), reportRedef=False) - 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) - 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 ASSIGN(self, node): - self.handleNode(node.value, node) - for target in node.targets: - self.handleNode(target, 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:" - for child in iter_child_nodes(node): - if child not in node.body: - self.handleNode(child, node) - - 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/pylibs/pylama/lint/pylama_pyflakes/pyflakes/messages.py b/pylibs/pylama/lint/pylama_pyflakes/pyflakes/messages.py deleted file mode 100644 index a4c31985..00000000 --- a/pylibs/pylama/lint/pylama_pyflakes/pyflakes/messages.py +++ /dev/null @@ -1,128 +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 = 'W0611 %r imported but unused' - - def __init__(self, filename, loc, name): - Message.__init__(self, filename, loc) - self.message_args = (name,) - - -class RedefinedWhileUnused(Message): - message = 'W0404 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 = 'W0621 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 = 'W0621 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 = "W0401 '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 = 'E0602 undefined name %r' - - def __init__(self, filename, loc, name): - Message.__init__(self, filename, loc) - self.message_args = (name,) - - -class DoctestSyntaxError(Message): - message = 'W0511 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 = 'E0603 undefined name %r in __all__' - - def __init__(self, filename, loc, name): - Message.__init__(self, filename, loc) - self.message_args = (name,) - - -class UndefinedLocal(Message): - message = ('E0602 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 = 'E1122 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 = 'W0621 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 = 'W0410 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 = 'W0612 local variable %r is assigned to but never used' - - def __init__(self, filename, loc, names): - Message.__init__(self, filename, loc) - self.message_args = (names,) diff --git a/pylibs/pylama/lint/pylama_pylint/__init__.py b/pylibs/pylama/lint/pylama_pylint/__init__.py deleted file mode 100644 index 7bdcb211..00000000 --- a/pylibs/pylama/lint/pylama_pylint/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -""" Description. """ - -# Module information -# ================== - - -__version__ = '0.1.0' -__project__ = 'pylama_pylint' -__author__ = "horneds " -__license__ = "BSD" - -from .main import Linter diff --git a/pylibs/pylama/lint/pylama_pylint/main.py b/pylibs/pylama/lint/pylama_pylint/main.py deleted file mode 100644 index 6132c2e8..00000000 --- a/pylibs/pylama/lint/pylama_pylint/main.py +++ /dev/null @@ -1,55 +0,0 @@ -""" Pylint support. """ - -from os import path as op, environ - -from pylama.lint import Linter as BaseLinter # noqa - - -PYLINT_RC = op.abspath(op.join(op.dirname(__file__), 'pylint.rc')) - - -class Linter(BaseLinter): - - """ Check code with pylint. """ - - @staticmethod - def run(path, **meta): # noqa - """ Pylint code checking. - - :return list: List of errors. - - """ - - from .pylint.lint import Run - from .pylint.reporters import BaseReporter - from .pylint.astroid import MANAGER - - 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] - )) - - pylintrc = op.join(environ.get('HOME', ''), '.pylintrc') - defattrs = '-r n' - if not op.exists(pylintrc): - defattrs += ' --rcfile={0}'.format(PYLINT_RC) - attrs = meta.get('pylint', defattrs.split()) - - runner = Run( - [path] + attrs, reporter=Reporter(), exit=False) - return runner.linter.reporter.errors diff --git a/pylibs/pylama/lint/pylama_pylint/pylint.rc b/pylibs/pylama/lint/pylama_pylint/pylint.rc deleted file mode 100644 index c58c4d0e..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint.rc +++ /dev/null @@ -1,25 +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) -# C0111: Missing docstring -# 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 -# W0401: Wildcard import %s -# W0613: Unused argument %r -# W0631: Using possibly undefined loop variable %r -# -disable = C0103,C0111,E1101,R0901,R0902,R0903,R0904,R0913,R0915,W0141,W0142,W0221,W0232,W0401,W0613,W0631 - -[TYPECHECK] -generated-members = REQUEST,acl_users,aq_parent,objects,DoesNotExist,_meta,status_code,content,context diff --git a/pylibs/pylama/lint/pylama_pylint/pylint/__init__.py b/pylibs/pylama/lint/pylama_pylint/pylint/__init__.py deleted file mode 100644 index dfb4386b..00000000 --- a/pylibs/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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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/pylibs/pylama/lint/pylama_pylint/pylint/__pkginfo__.py b/pylibs/pylama/lint/pylama_pylint/pylint/__pkginfo__.py deleted file mode 100644 index 997b9a59..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/__pkginfo__.py +++ /dev/null @@ -1,69 +0,0 @@ -# pylint: disable=W0622,C0103 -# 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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""pylint packaging information""" - -modname = distname = 'pylint' - -numversion = (1, 0, 0) -version = '.'.join([str(num) for num in numversion]) - -install_requires = ['logilab-common >= 0.53.0', 'astroid >= 0.24.3'] - -license = 'GPL' -description = "python code static checker" -web = 'http://www.pylint.org' -mailinglist = "mailto://python-projects@lists.logilab.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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/__init__.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/__init__.py deleted file mode 100644 index af17875d..00000000 --- a/pylibs/pylama/lint/pylama_pylint/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 .exceptions import * - -# make all node classes accessible from astroid package -from .nodes import * - -# trigger extra monkey-patching -from . import inference - -# more stuff available -from . import raw_building -from .bases import YES, Instance, BoundMethod, UnboundMethod -from .node_classes import are_exclusive, unpack_infer -from .scoped_nodes import builtin_lookup - -# make a manager instance (borg) as well as Project and Package classes -# accessible from astroid package -from .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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/__pkginfo__.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/__pkginfo__.py deleted file mode 100644 index a74b6b69..00000000 --- a/pylibs/pylama/lint/pylama_pylint/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, 0, 0) -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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/as_string.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/as_string.py deleted file mode 100644 index fcff19eb..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/as_string.py +++ /dev/null @@ -1,475 +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]) - 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""" - 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 _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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/bases.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/bases.py deleted file mode 100644 index 641f88ae..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/bases.py +++ /dev/null @@ -1,612 +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 .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: - # 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): - 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 .as_string import to_code - return to_code(self) - - def repr_tree(self, ids=False): - from .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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/builder.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/builder.py deleted file mode 100644 index 6a4347af..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/builder.py +++ /dev/null @@ -1,221 +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 .exceptions import AstroidBuildingException, InferenceError -from .raw_building import InspectBuilder -from .rebuilder import TreeRebuilder -from .manager import AstroidManager -from .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] - 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) - 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 - node = self.string_build(data, modname, path) - node.file_encoding = encoding - return node - - 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) - self._manager.astroid_cache[module.name] = module - # post tree building steps after we stored the module in the cache: - for from_node in module._from_nodes: - 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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/exceptions.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/exceptions.py deleted file mode 100644 index 3889e2e7..00000000 --- a/pylibs/pylama/lint/pylama_pylint/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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/inference.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/inference.py deleted file mode 100644 index f600781c..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/inference.py +++ /dev/null @@ -1,388 +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 . import nodes - -from .manager import AstroidManager -from .exceptions import (AstroidError, - InferenceError, NoDefault, NotFoundError, UnresolvableName) -from .bases import YES, Instance, InferenceContext, \ - _infer_stmts, copy_context, path_wrapper, raise_if_nothing_infered -from .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,)) - # 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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/manager.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/manager.py deleted file mode 100644 index 3ff2e14c..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/manager.py +++ /dev/null @@ -1,303 +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 .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: - return self.astroid_cache[modname] - if source: - from .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 .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 .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 .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) ) - -class Project: - """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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/mixins.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/mixins.py deleted file mode 100644 index 42bbb404..00000000 --- a/pylibs/pylama/lint/pylama_pylint/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 .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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/node_classes.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/node_classes.py deleted file mode 100644 index 52cd0a27..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/node_classes.py +++ /dev/null @@ -1,925 +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 .exceptions import NoDefault -from .bases import (NodeNG, Statement, Instance, InferenceContext, - _infer_stmts, YES, BUILTINS) -from .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 - -# 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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/nodes.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/nodes.py deleted file mode 100644 index 4deddde3..00000000 --- a/pylibs/pylama/lint/pylama_pylint/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 .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, \ - const_factory -from .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, - ) - diff --git a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/protocols.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/protocols.py deleted file mode 100644 index 109d1555..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/protocols.py +++ /dev/null @@ -1,320 +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 .exceptions import InferenceError, NoDefault -from .node_classes import unpack_infer -from .bases import copy_context, \ - raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES -from .nodes import const_factory -from . 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: - 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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/raw_building.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/raw_building.py deleted file mode 100644 index b7144a86..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/raw_building.py +++ /dev/null @@ -1,351 +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) - -from .node_classes import CONST_CLS -from .nodes import (Module, Class, Const, const_factory, From, - Function, EmptyNode, Name, Arguments) -from .bases import BUILTINS, Generator -from .manager import AstroidManager -MANAGER = AstroidManager() - -_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types - -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 - MANAGER.astroid_cache[modname] = 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 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/pylibs/pylama/lint/pylama_pylint/pylint/astroid/rebuilder.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/rebuilder.py deleted file mode 100644 index d0f6bde2..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/rebuilder.py +++ /dev/null @@ -1,940 +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, - # 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 . 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', - } - -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 - - - - -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 = {} - - def _transform(self, node): - try: - transforms = self._manager.transforms[type(node)] - except KeyError: - return node # no transform registered for this class of node - 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 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 = [] - newnode.vararg = node.vararg - newnode.kwarg = node.kwarg - # save argument names in locals: - if node.vararg: - newnode.parent.set_local(newnode.vararg, newnode) - if node.kwarg: - newnode.parent.set_local(newnode.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] = 'type' # XXX get the actual metaclass - 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 not newnode.bases: - # no base classes, detect new / style old style according to - # current scope - newnode._newstyle = metaclass == 'type' - 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""" - newnode = new.Yield() - _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 - - -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_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 self.visit_yield(node, parent) - -if sys.version_info >= (3, 0): - TreeRebuilder = TreeRebuilder3k - - diff --git a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/scoped_nodes.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/scoped_nodes.py deleted file mode 100644 index e52c89f7..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/scoped_nodes.py +++ /dev/null @@ -1,992 +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 - -from ..logilab.common.compat import builtins -from ..logilab.common.decorators import cached - -from .exceptions import NotFoundError, \ - AstroidBuildingException, InferenceError -from .node_classes import Const, DelName, DelAttr, \ - Dict, From, List, Pass, Raise, Return, Tuple, Yield, \ - LookupMixIn, const_factory as cf, unpack_infer -from .bases import NodeNG, InferenceContext, Instance,\ - YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \ - BUILTINS -from .mixins import FilterStmtsMixin -from .bases import Statement -from .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 - # 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 - - # 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 = [] - - @property - def file_stream(self): - 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 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 ################################################################### - - -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 - - 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, 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 _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 klass.name == 'type': - 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): - if _class_type(base, ancestors) != 'class': - 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): - # 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() diff --git a/pylibs/pylama/lint/pylama_pylint/pylint/astroid/utils.py b/pylibs/pylama/lint/pylama_pylint/pylint/astroid/utils.py deleted file mode 100644 index 322e25b2..00000000 --- a/pylibs/pylama/lint/pylama_pylint/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 .exceptions import AstroidBuildingException -from .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 .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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/__init__.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/__init__.py deleted file mode 100644 index dddc1bab..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/__init__.py +++ /dev/null @@ -1,152 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 os.path import dirname - -from ..astroid.utils import ASTWalker -from ..logilab.common.configuration import OptionsProviderMixIn - -from ..reporters import diff_string -from ..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 - # messages that are active for the current check - self.active_msgs = set() - - 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) - - def package_dir(self): - """return the base directory for the analysed package""" - return dirname(self.linter.base_file) - - # 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/base.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/base.py deleted file mode 100644 index 5282898e..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/base.py +++ /dev/null @@ -1,987 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""basic checker for Python code""" - -import sys -from .. import astroid -from ..logilab.common.ureports import Table -from ..astroid import are_exclusive, bases - -from ..interfaces import IAstroidChecker -from ..utils import EmptyReport -from ..reporters import diff_string -from . import BaseChecker -from .utils import ( - check_messages, - clobber_in_except, - is_builtin_object, - is_inside_except, - overrides_a_method, - safe_infer, - ) - - -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('__.*__') - -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): - continue - for _ in child.nodes_of_class(astroid.Break, skip_klass=loop_nodes): - return True - return False - - - -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 ('__builtin__.property', 'abc.abstractproperty')): - 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 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).'), - '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.'), - '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 - 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) - - 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 !'), - '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.'), - } - - options = (('required-attributes', - {'default' : (), 'type' : 'csv', - 'metavar' : '', - 'help' : 'Required attributes for module, separated by a ' - 'comma'} - ), - ('bad-functions', - {'default' : ('map', 'filter', 'apply', 'input'), - '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 = 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', 'exec-used') - 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) - 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 - - -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"', - 'invalid-name', - 'Used when the name doesn\'t match the regular expression \ - associated to its type (constant, variable, class...).'), - } - - options = (('module-rgx', - {'default' : MOD_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'module names'} - ), - ('const-rgx', - {'default' : CONST_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'module level names'} - ), - ('class-rgx', - {'default' : CLASS_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'class names'} - ), - ('function-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'function names'} - ), - ('method-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'method names'} - ), - ('attr-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'instance attribute names'} - ), - ('argument-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'argument names'}), - ('variable-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'variable names'} - ), - ('class-attribute-rgx', - {'default' : CLASS_ATTRIBUTE_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'attribute names in class bodies'} - ), - ('inlinevar-rgx', - {'default' : COMP_VAR_RGX, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match correct ' - 'list comprehension / generator expression variable \ - names'} - ), - # 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'} - ), - ) - - 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) - - @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 _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') - if regexp.match(name) is None: - type_label = {'inlinedvar': 'inlined variable', - 'const': 'constant', - 'attr': 'attribute', - 'class_attribute': 'class attribute' - }.get(node_type, node_type) - self.add_message('invalid-name', node=node, args=(type_label, name)) - 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 - if not overridden: - self._check_docstring(ftype, node) - else: - self._check_docstring(ftype, node) - - def _check_docstring(self, node_type, node): - """check the node has a non empty docstring""" - docstring = node.doc - if docstring is None: - 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/classes.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/classes.py deleted file mode 100644 index ecacecea..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/classes.py +++ /dev/null @@ -1,662 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""classes checker for Python code -""" -from __future__ import generators - -from .. import astroid -from ..astroid import YES, Instance, are_exclusive, AssAttr - -from ..interfaces import IAstroidChecker -from . import BaseChecker -from .utils import (PYMETHODS, overrides_a_method, - check_messages, is_attr_private, is_attr_protected, node_frame_class) - -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 affected in %s line %s hide 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.'), - - } - - -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('W0232', args=node, node=node) - - @check_messages('E0203', 'W0201') - 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 'W0201' not in self.active_msgs: - 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('W0201', 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('E0202', args=args, node=node) - except astroid.NotFoundError: - pass - - 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 'R0201' not in self.active_msgs: - 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('R0201', 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 'W0212' not in self.active_msgs: - return - - self._check_protected_attribute_access(node) - - def visit_assign(self, assign_node): - if 'W0212' not in self.active_msgs: - return - - 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('W0212', 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('W0212', 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 - # is it a class attribute ? - try: - 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: - 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('E0203', 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('W0211', args=first, node=node) - return - self._first_attrs[-1] = None - # class / regular method with no args - elif not node.args.args: - self.add_message('E0211', 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, - 'C0204', node.name) - # metaclass regular method - else: - self._check_first_arg_config(first, - self.config.valid_classmethod_first_arg, node, 'C0203', - node.name) - # regular class - else: - # class method - if node.type == 'classmethod': - self._check_first_arg_config(first, - self.config.valid_classmethod_first_arg, node, 'C0202', - node.name) - # regular method without self as argument - elif first != 'self': - self.add_message('E0213', 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.is_abstract(pass_is_abstract=False): - self.add_message('W0223', 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('E0221', 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('E0222', 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('F0220', 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 set(('W0231', 'W0233')) & self.active_msgs: - 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('W0233', 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('W0231', 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('F0202', 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('W0221', args=class_type, node=method1) - elif len(method1.args.defaults) < len(refmethod.args.defaults): - self.add_message('W0222', 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/design_analysis.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/design_analysis.py deleted file mode 100644 index f81efc37..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/design_analysis.py +++ /dev/null @@ -1,407 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""check for signs of poor design""" - -from ..astroid import Function, If, InferenceError - -from ..interfaces import IAstroidChecker -from . import BaseChecker -from .utils import check_messages - -import re - -# regexp for ignored argument name -IGNORED_ARGUMENT_NAMES = re.compile('_.*') - -SPECIAL_METHODS = [('Context manager', set(('__enter__', - '__exit__',))), - ('Container', set(('__len__', - '__getitem__',))), - ('Mutable container', set(('__setitem__', - '__delitem__',))), - ] - -class SpecialMethodChecker(object): - """A functor that checks for consistency of a set of special methods""" - def __init__(self, methods_found, on_error): - """Stores the set of __x__ method names that were found in the - class and a callable that will be called with args to R0024 if - the check fails - """ - self.methods_found = methods_found - self.on_error = on_error - - def __call__(self, methods_required, protocol): - """Checks the set of method names given to __init__ against the set - required. - - If they are all present, returns true. - If they are all absent, returns false. - If some are present, reports the error and returns false. - """ - required_methods_found = methods_required & self.methods_found - if required_methods_found == methods_required: - return True - if required_methods_found: - required_methods_missing = methods_required - self.methods_found - self.on_error((protocol, - ', '.join(sorted(required_methods_found)), - ', '.join(sorted(required_methods_missing)))) - return False - - -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.'), - 'R0924': ('Badly implemented %s, implements %s but not %s', - 'incomplete-protocol', - 'A class implements some of the special methods for a particular \ - protocol, but not all of them') - } - - -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('R0921', node=abstract) - elif self._used_abstracts[abstract] < 2: - self.add_message('R0922', node=abstract, - args=self._used_abstracts[abstract]) - for iface in self._ifaces: - if not iface in self._used_ifaces: - self.add_message('R0923', node=iface) - - @check_messages('R0901', 'R0902', 'R0903', 'R0904', 'R0921', 'R0922', 'R0923') - 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('R0901', 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('R0902', 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('R0901', 'R0902', 'R0903', 'R0904', 'R0921', 'R0922', 'R0923') - 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('R0904', 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 implement special methods consitently? - # If so, don't enforce minimum public methods. - check_special = SpecialMethodChecker( - special_methods, lambda args: self.add_message('R0924', node=node, args=args)) - protocols = [check_special(pmethods, pname) for pname, pmethods in SPECIAL_METHODS] - if True in protocols: - 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('R0911', 'R0912', 'R0913', 'R0914', 'R0915') - 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('R0913', 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('R0914', node=node, - args=(locnum, self.config.max_locals)) - # init statements counter - self._stmts = 1 - - @check_messages('R0911', 'R0912', 'R0913', 'R0914', 'R0915') - 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('R0911', node=node, - args=(returns, self.config.max_returns)) - branches = self._branches.pop() - if branches > self.config.max_branches: - self.add_message('R0912', node=node, - args=(branches, self.config.max_branches)) - # check number of statements - if self._stmts > self.config.max_statements: - self.add_message('R0915', 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/exceptions.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/exceptions.py deleted file mode 100644 index 3031075f..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/exceptions.py +++ /dev/null @@ -1,226 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""exceptions handling (raising, catching, exceptions classes) checker -""" -import sys - -from ..logilab.common.compat import builtins -BUILTINS_NAME = builtins.__name__ -from .. import astroid -from ..astroid import YES, Instance, unpack_infer - -from . import BaseChecker -from .utils import is_empty, is_raising, check_messages -from ..interfaces import IAstroidChecker - - -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).'), - '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'), - - '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.'), - '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)}), - } - - -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('W0701', 'W0710', 'E0702', 'E0710', 'E0711') - def visit_raise(self, node): - """visit raise possibly inferring value""" - # ignore empty raise - if node.exc is None: - return - 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('W0701', node=node) - else: - self.add_message('E0702', 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('E0702', 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('E0711', node=node) - elif isinstance(expr, astroid.BinOp) and expr.op == '%': - self.add_message('W0701', 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('E0710', node=node) - else: - self.add_message('W0710', node=node) - else: - value_found = False - else: - value_found = False - return value_found - - - @check_messages('W0712') - 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('W0712', node=node) - @check_messages('W0702', 'W0703', 'W0704', 'W0711', 'E0701') - 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('W0704', 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('W0702', 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('E0701', node=node, args=msg) - - elif isinstance(handler.type, astroid.BoolOp): - self.add_message('W0711', 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('E0701', 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('W0703', args=exc.name, node=handler.type) - 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/format.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/format.py deleted file mode 100644 index bf53234a..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/format.py +++ /dev/null @@ -1,403 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 re, sys -import tokenize -if not hasattr(tokenize, 'NL'): - raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") - -from ..logilab.common.textutils import pretty_match -from ..astroid import nodes - -from ..interfaces import ITokenChecker, IAstroidChecker -from . import BaseTokenChecker -from .utils import check_messages -from ..utils import WarningScope, OPTION_RGX - -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.'), - '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}), - 'C0322': ('Operator not preceded by a space\n%s', - 'no-space-before-operator', - 'Used when one of the following operator (!= | <= | == | >= | < ' - '| > | = | \\+= | -= | \\*= | /= | %) is not preceded by a space.', - {'scope': WarningScope.NODE}), - 'C0323': ('Operator not followed by a space\n%s', - 'no-space-after-operator', - 'Used when one of the following operator (!= | <= | == | >= | < ' - '| > | = | \\+= | -= | \\*= | /= | %) is not followed by a space.', - {'scope': WarningScope.NODE}), - 'C0324': ('Comma not followed by a space\n%s', - 'no-space-after-comma', - 'Used when a comma (",") is not followed by a space.', - {'scope': WarningScope.NODE}), - } - -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}), - }) - -# simple quoted string rgx -SQSTRING_RGX = r'"([^"\\]|\\.)*?"' -# simple apostrophed rgx -SASTRING_RGX = r"'([^'\\]|\\.)*?'" -# triple quoted string rgx -TQSTRING_RGX = r'"""([^"]|("(?!"")))*?(""")' -# triple apostrophe'd string rgx -TASTRING_RGX = r"'''([^']|('(?!'')))*?(''')" - -# finally, the string regular expression -STRING_RGX = re.compile('(%s)|(%s)|(%s)|(%s)' % (TQSTRING_RGX, TASTRING_RGX, - SQSTRING_RGX, SASTRING_RGX), - re.MULTILINE|re.DOTALL) - -COMMENT_RGX = re.compile("#.*$", re.M) - -OPERATORS = r'!=|<=|==|>=|<|>|=|\+=|-=|\*=|/=|%' - -OP_RGX_MATCH_1 = r'[^(]*(?|=|\+|-|\*|/|!|%%|&|\|)(%s).*' % OPERATORS -OP_RGX_SEARCH_1 = r'(?|=|\+|-|\*|/|!|%%|&|\|)(%s)' % OPERATORS - -OP_RGX_MATCH_2 = r'[^(]*(%s)(?!\s|=|>|<).*' % OPERATORS -OP_RGX_SEARCH_2 = r'(%s)(?!\s|=|>)' % OPERATORS - -BAD_CONSTRUCT_RGXS = ( - - (re.compile(OP_RGX_MATCH_1, re.M), - re.compile(OP_RGX_SEARCH_1, re.M), - 'C0322'), - - (re.compile(OP_RGX_MATCH_2, re.M), - re.compile(OP_RGX_SEARCH_2, re.M), - 'C0323'), - - (re.compile(r'.*,[^(\s|\]|}|\))].*', re.M), - re.compile(r',[^\s)]', re.M), - 'C0324'), - ) - - -def get_string_coords(line): - """return a list of string positions (tuple (start, end)) in the line - """ - result = [] - for match in re.finditer(STRING_RGX, line): - result.append( (match.start(), match.end()) ) - return result - -def in_coords(match, string_coords): - """return true if the match is in the string coord""" - mstart = match.start() - for start, end in string_coords: - if mstart >= start and mstart < end: - return True - return False - -def check_line(line): - """check a line for a bad construction - if it founds one, return a message describing the problem - else return None - """ - cleanstr = COMMENT_RGX.sub('', STRING_RGX.sub('', line)) - for rgx_match, rgx_search, msg_id in BAD_CONSTRUCT_RGXS: - if rgx_match.match(cleanstr): - string_positions = get_string_coords(line) - for match in re.finditer(rgx_search, line): - if not in_coords(match, string_positions): - return msg_id, pretty_match(match, line.rstrip()) - - -class FormatChecker(BaseTokenChecker): - """checks for : - * unauthorized constructions - * strict indentation - * line length - * use of <> instead of != - """ - - __implements__ = (ITokenChecker, IAstroidChecker) - - # 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.')}), - ('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).'}), - ) - def __init__(self, linter=None): - BaseTokenChecker.__init__(self, linter) - self._lines = None - self._visited_lines = None - - def new_line(self, tok_type, line, line_num, junk): - """a new line has been encountered, process it if necessary""" - if not tok_type in junk: - self._lines[line_num] = line.split('\n')[0] - self.check_lines(line, line_num) - - 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). - """ - indent = tokenize.INDENT - dedent = tokenize.DEDENT - newline = tokenize.NEWLINE - junk = (tokenize.COMMENT, tokenize.NL) - indents = [0] - check_equal = 0 - line_num = 0 - previous = None - self._lines = {} - self._visited_lines = {} - new_line_delay = False - for (tok_type, token, start, _, line) in tokens: - if new_line_delay: - new_line_delay = False - self.new_line(tok_type, line, line_num, junk) - if start[0] != line_num: - if previous is not None and previous[0] == tokenize.OP and previous[1] == ';': - self.add_message('W0301', line=previous[2]) - previous = None - 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 delay checking the new line until - # the next token. - if tok_type == tokenize.INDENT: - new_line_delay = True - else: - self.new_line(tok_type, line, line_num, junk) - if tok_type not in (indent, dedent, newline) + junk: - previous = tok_type, token, start[0] - - if tok_type == tokenize.OP: - if token == '<>': - self.add_message('W0331', line=line_num) - elif tok_type == tokenize.NUMBER: - if token.endswith('l'): - self.add_message('W0332', line=line_num) - - elif tok_type == 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 = 1 - - elif tok_type == indent: - check_equal = 0 - self.check_indent_level(token, indents[-1]+1, line_num) - indents.append(indents[-1]+1) - - elif tok_type == 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 = 1 - if len(indents) > 1: - del indents[-1] - - elif check_equal and tok_type not in junk: - # this is the first "real 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 - check_equal = 0 - self.check_indent_level(line, indents[-1], line_num) - - line_num -= 1 # to be ok with "wc -l" - if line_num > self.config.max_module_lines: - self.add_message('C0302', args=line_num, line=1) - - @check_messages('C0321' ,'C03232', 'C0323', 'C0324') - 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: - prev_line = node.parent.statement().fromlineno - line = node.fromlineno - assert line, node - if prev_line == line and self._visited_lines.get(line) != 2: - # py2.5 try: except: finally: - if not (isinstance(node, nodes.TryExcept) - and isinstance(node.parent, nodes.TryFinally) - and node.fromlineno == node.parent.fromlineno): - self.add_message('C0321', node=node) - self._visited_lines[line] = 2 - 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('') - try: - msg_def = check_line('\n'.join(lines)) - if msg_def: - self.add_message(msg_def[0], node=node, args=msg_def[1]) - except KeyError: - # FIXME: internal error ! - pass - - @check_messages('W0333') - def visit_backquote(self, node): - self.add_message('W0333', 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('C0304', line=i) - else: - stripped_line = line.rstrip() - if line != stripped_line + '\n': - self.add_message('C0303', 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('C0301', 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('W0312', 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('W0311', 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/imports.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/imports.py deleted file mode 100644 index 3c321bbb..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/imports.py +++ /dev/null @@ -1,386 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""imports checkers for Python code""" - -from ..logilab.common.graph import get_cycles, DotBackend -from ..logilab.common.modutils import is_standard_module -from ..logilab.common.ureports import VerbatimText, Paragraph - -from .. import astroid -from ..astroid import are_exclusive - -from ..interfaces import IAstroidChecker -from ..utils import EmptyReport -from . import BaseChecker -from .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.'), - } - -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 - - options = (('deprecated-modules', - {'default' : ('regsub', 'TERMIOS', 'Bastion', 'rexec'), - '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('R0401'): - for cycle in get_cycles(self.import_graph): - self.add_message('R0401', 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('W0410', node=node) - return - 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_message('W0401', args=basename, node=node) - continue - 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("F0401", 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 'W0403' not in self.active_msgs: - 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('W0403', args=(importedasname, importedmodnode.name), - node=importnode) - - def _add_imported_module(self, node, importedmodname): - """notify an imported module, used to analyze dependencies""" - context_name = node.root().name - if context_name == importedmodname: - # module importing itself ! - self.add_message('W0406', 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) - if is_standard_module(importedmodname, (self.package_dir(),)): - # 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('W0402', 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 'W0404' not in self.active_msgs: - 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('W0404', 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/logging.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/logging.py deleted file mode 100644 index 5f2381ca..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/logging.py +++ /dev/null @@ -1,178 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""checker for use of Python logging -""" - -from .. import astroid -from . import BaseChecker, utils -from .. import interfaces - -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(BaseChecker): - """Checks use of the logging module.""" - - __implements__ = interfaces.IAstroidChecker - name = 'logging' - msgs = MSGS - - 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_name = None - - 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 == 'logging': - if as_name: - self._logging_name = as_name - else: - self._logging_name = 'logging' - - @utils.check_messages(*(MSGS.keys())) - def visit_callfunc(self, node): - """Checks calls to (simple forms of) logging methods.""" - if (not isinstance(node.func, astroid.Getattr) - or not isinstance(node.func.expr, astroid.Name)): - return - try: - logger_class = [inferred for inferred in node.func.expr.infer() if ( - isinstance(inferred, astroid.Instance) - and any(ancestor for ancestor in inferred._proxied.ancestors() if ( - ancestor.name == 'Logger' - and ancestor.parent.name == 'logging')))] - except astroid.exceptions.InferenceError: - return - if (node.func.expr.name != self._logging_name and not logger_class): - return - self._check_convenience_methods(node) - self._check_log_methods(node) - - def _check_convenience_methods(self, node): - """Checks calls to logging convenience methods (like logging.warn).""" - if node.func.attrname not in CHECKED_CONVENIENCE_FUNCTIONS: - return - 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 - if isinstance(node.args[0], astroid.BinOp) and node.args[0].op == '%': - self.add_message('W1201', node=node) - elif isinstance(node.args[0], astroid.Const): - self._check_format_string(node, 0) - - def _check_log_methods(self, node): - """Checks calls to logging.log(level, format, *format_args).""" - if node.func.attrname != 'log': - return - 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 - if isinstance(node.args[1], astroid.BinOp) and node.args[1].op == '%': - self.add_message('W1201', node=node) - elif isinstance(node.args[1], astroid.Const): - self._check_format_string(node, 1) - - 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 = self._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, e: - c = format_string[e.index] - self.add_message('E1200', node=node, args=(c, ord(c), e.index)) - return - except utils.IncompleteFormatString: - self.add_message('E1201', node=node) - return - if num_args > required_num_args: - self.add_message('E1205', node=node) - elif num_args < required_num_args: - self.add_message('E1206', node=node) - - def _count_supplied_tokens(self, 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/misc.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/misc.py deleted file mode 100644 index dee53e62..00000000 --- a/pylibs/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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ..interfaces import IRawChecker -from . 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('W0511', 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('W0512', 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/newstyle.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/newstyle.py deleted file mode 100644 index 715282a9..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/newstyle.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) 2005-2006 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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""check for new / old style related problems -""" -import sys - -from .. import astroid - -from ..interfaces import IAstroidChecker -from . import BaseChecker -from .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.'), - 'E1002': ('Use of super on an old style class', - 'super-on-old-class', - 'Used when an old style class uses the super builtin.'), - '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 on Python 2'), - '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'), - '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".') - } - - -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('E1001', 'C1001') - def visit_class(self, node): - """check __slots__ usage - """ - if '__slots__' in node and not node.newstyle: - self.add_message('E1001', 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('C1001', node=node) - - @check_messages('W1001') - 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('W1001', node=node) - - @check_messages('E1002', 'E1003', 'E1004') - 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('E1002', 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 and sys.version_info[0] == 2: - self.add_message('missing-super-argument', node=call) - continue - - if klass is not supcls: - supcls = getattr(supcls, 'name', supcls) - self.add_message('E1003', node=call, args=(supcls, )) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(NewStyleConflictChecker(linter)) diff --git a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/raw_metrics.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/raw_metrics.py deleted file mode 100644 index 4d450da1..00000000 --- a/pylibs/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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ..interfaces import ITokenChecker -from ..utils import EmptyReport -from ..checkers import BaseTokenChecker -from ..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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/similar.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/similar.py deleted file mode 100644 index f22116e9..00000000 --- a/pylibs/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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""a similarities / code duplication command line tool and pylint checker -""" -import sys -from itertools import izip - -from ..logilab.common.ureports import Table - -from ..interfaces import IRawChecker -from . 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/stdlib.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/stdlib.py deleted file mode 100644 index 51450983..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/stdlib.py +++ /dev/null @@ -1,68 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""Checkers for various standard library functions.""" - -import re -import sys - -from .. import astroid - -from ..interfaces import IAstroidChecker -from . import BaseChecker, BaseTokenChecker, 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('W1501') - 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('W1501', 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/strings.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/strings.py deleted file mode 100644 index 3d5e8051..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/strings.py +++ /dev/null @@ -1,297 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""Checker for string formatting operations. -""" - -import sys -import tokenize - -from .. import astroid - -from ..interfaces import ITokenChecker, IAstroidChecker -from . import BaseChecker, BaseTokenChecker, utils - -_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 few 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 many 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 - - @utils.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('E1300', node=node, args=(c, ord(c), e.index)) - return - except utils.IncompleteFormatString: - self.add_message('E1301', node=node) - return - if required_keys and required_num_args: - # The format string uses both named and unnamed format - # specifiers. - self.add_message('E1302', 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('W1300', 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('E1304', node=node, args=key) - for key in keys: - if key not in required_keys: - self.add_message('W1301', node=node, args=key) - elif isinstance(args, OTHER_NODES + (astroid.Tuple,)): - type_name = type(args).__name__ - self.add_message('E1303', 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('E1305', node=node) - elif num_args < required_num_args: - self.add_message('E1306', 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, "), - } - - @utils.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('E1310', node=node, - args=(func.bound.name, func.name)) - - -class StringConstantChecker(BaseTokenChecker): - """Check string literals""" - __implements__ = (ITokenChecker,) - 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_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 and 'b' not in prefix: - pass # unicode by default - else: - self.add_message('W1402', line=start_row, args=(match, )) - elif next_char not in self.ESCAPE_CHARACTERS: - self.add_message('W1401', 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/pylibs/pylama/lint/pylama_pylint/pylint/checkers/typecheck.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/typecheck.py deleted file mode 100644 index e8938827..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/typecheck.py +++ /dev/null @@ -1,415 +0,0 @@ -# Copyright (c) 2006-2010 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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""try to find more bugs in the code using astroid inference capabilities -""" - -import re -import shlex - -from .. import astroid -from ..astroid import InferenceError, NotFoundError, YES, Instance - -from ..interfaces import IAstroidChecker -from . import BaseChecker -from .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 passed for parameter %s in function call', - 'no-value-for-parameter', - 'Used when a function call passes too few arguments.'), - 'E1121': ('Too many positional arguments for function call', - 'too-many-function-args', - 'Used when a function call passes too many positional \ - arguments.'), - 'E1122': ('Duplicate keyword argument %r in function call', - 'duplicate-keyword-arg', - 'Used when a function call passes the same keyword argument \ - multiple times.', - {'maxversion': (2, 6)}), - 'E1123': ('Passing unexpected keyword argument %r in function 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': ('Parameter %r passed as both positional and keyword argument', - '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', - 'missing-kwoa', - 'Used when a function call doesn\'t pass a mandatory \ - keyword-only argument.', - {'minversion': (3, 0)}), - } - -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-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('E1101', 'E1103') - 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 optparse'Values class - if owner.name == 'Values' and owner.root().name == 'optparse': - 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 = 'E1103' - else: - msgid = 'E1101' - self.add_message(msgid, node=node, - args=(owner.display_type(), name, - node.attrname)) - - @check_messages('E1111', 'W1111') - 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('E1111', node=node) - else: - for rnode in returns: - if not (isinstance(rnode.value, astroid.Const) - and rnode.value.value is None): - break - else: - self.add_message('W1111', 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('E1122', 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('E1102', node=node, args=node.func.as_string()) - - # Note that BoundMethod is a subclass of UnboundMethod (huh?), so must - # come first in this 'if..else'. - if isinstance(called, astroid.BoundMethod): - # Bound methods have an extra implicit 'self' argument. - num_positional_args += 1 - elif isinstance(called, astroid.UnboundMethod): - if called.decorators is not None: - for d in called.decorators.nodes: - if isinstance(d, astroid.Name) and (d.name == 'classmethod'): - # Class methods have an extra implicit 'cls' argument. - num_positional_args += 1 - break - elif (isinstance(called, astroid.Function) or - isinstance(called, astroid.Lambda)): - pass - else: - return - - 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('E1121', node=node) - 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('E1124', node=node, args=keyword) - 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('E1124', node=node, args=keyword) - 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('E1123', node=node, args=keyword) - - # 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('E1120', node=node, args=display_name) - - for name in kwparams: - defval, assigned = kwparams[name] - if defval is None and not assigned: - self.add_message('E1125', node=node, args=name) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(TypeChecker(linter)) diff --git a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/utils.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/utils.py deleted file mode 100644 index 1a7dca9e..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/utils.py +++ /dev/null @@ -1,409 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""some functions that may be useful for various checkers -""" - -import re -import string - -from .. 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.name,))) - 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 == '__builtin__' - -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 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 - _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): - c = format_string[i] - if c == '%': - i, c = next_char(i) - # Parse the mapping key (optional). - key = None - if c == '(': - depth = 1 - i, c = next_char(i) - key_start = i - while depth != 0: - if c == '(': - depth += 1 - elif c == ')': - depth -= 1 - i, c = next_char(i) - key_end = i - 1 - key = format_string[key_start:key_end] - - # Parse the conversion flags (optional). - while c in '#0- +': - i, c = next_char(i) - # Parse the minimum field width (optional). - if c == '*': - num_args += 1 - i, c = next_char(i) - else: - while c in string.digits: - i, c = next_char(i) - # Parse the precision (optional). - if c == '.': - i, c = next_char(i) - if c == '*': - num_args += 1 - i, c = next_char(i) - else: - while c in string.digits: - i, c = next_char(i) - # Parse the length modifier (optional). - if c in 'hlL': - i, c = next_char(i) - # Parse the conversion type (mandatory). - if c not in 'diouxXeEfFgGcrs%': - raise UnsupportedFormatCharacter(i) - if key: - keys.add(key) - elif c != '%': - 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 not position and not keyword: - raise ValueError('Must specify at least one of: position or keyword.') - try: - if position and not isinstance(callfunc_node.args[position], astroid.Keyword): - return callfunc_node.args[position] - except IndexError as error: - raise NoSuchArgumentError(error) - if keyword: - for arg in callfunc_node.args: - if isinstance(arg, astroid.Keyword) and arg.arg == keyword: - return arg.value - raise NoSuchArgumentError diff --git a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/variables.py b/pylibs/pylama/lint/pylama_pylint/pylint/checkers/variables.py deleted file mode 100644 index afe0f945..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/checkers/variables.py +++ /dev/null @@ -1,619 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""variables checkers for Python code -""" - -import sys -from copy import copy - -from .. import astroid -from ..astroid import are_exclusive, builtin_lookup, AstroidBuildingException - -from ..interfaces import IAstroidChecker -from . import BaseChecker -from .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 - - -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: ' - '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'), - - } - -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 beginning of \ - the name of dummy variables (i.e. 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('W0622', args=name, node=stmts[0]) - - @check_messages('W0611', 'W0614', 'W0622', 'E0603', 'E0604') - 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('E0604', args=elt.as_string(), node=elt) - continue - elt_name = elt.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: - self.add_message('E0603', args=elt_name, node=elt) - # 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(): - stmt = stmts[0] - if isinstance(stmt, astroid.Import): - self.add_message('W0611', args=name, node=stmt) - elif isinstance(stmt, astroid.From) and stmt.modname != '__future__': - if stmt.names[0][0] == '*': - self.add_message('W0614', args=name, node=stmt) - else: - self.add_message('W0611', 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 set(('W0621', 'W0622')) & self.active_msgs: - 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('W0621', args=(name, line), node=stmt) - elif is_builtin(name): - # do not print Redefining builtin for additional builtins - self.add_message('W0622', 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 set(('W0612', 'W0613')) & self.active_msgs: - 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('W0613', args=name, node=stmt) - else: - self.add_message('W0612', args=name, node=stmt) - - @check_messages('W0601', 'W0602', 'W0603', 'W0604', 'W0622') - def visit_global(self, node): - """check names imported exists in the global scope""" - frame = node.frame() - if isinstance(frame, astroid.Module): - self.add_message('W0604', 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('W0602', 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('W0622', args=name, node=node) - break - if anode.frame() is module: - # module level assignment - break - else: - # global undefined at the module scope - self.add_message('W0601', args=name, node=node) - default_message = False - if default_message: - self.add_message('W0603', node=node) - - 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 'W0631' not in self.active_msgs: - 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('W0631', args=name, node=node) - - @check_messages('W0623') - def visit_excepthandler(self, node): - for name in get_all_elements(node.name): - clobbering, args = clobber_in_except(name) - if clobbering: - self.add_message('W0623', 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: - 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: - 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 - 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('E0602', args=name, node=node) - elif self._to_consume[-1][-1] != 'lambda': - # E0601 may *not* occurs in lambda scope - self.add_message('E0601', args=name, node=node) - if not isinstance(node, astroid.AssName): # Aug AssName - del to_consume[name] - else: - del consumed[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('E0602', args=name, node=node) - - @check_messages('E0611') - 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('E0611') - 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') - def visit_assign(self, node): - """Check unbalanced tuple unpacking for assignments""" - if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)): - return - try: - infered = node.value.infer().next() - except astroid.InferenceError: - return - if not isinstance(infered, (astroid.Tuple, astroid.List)): - return - targets = node.targets[0].itered() - values = infered.itered() - if any(not isinstance(target_node, astroid.AssName) - for target_node in targets): - return - if len(targets) != len(values): - self.add_message('unbalanced-tuple-unpacking', - node=node, - args=(len(targets), len(values))) - - 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('E0611', 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('E0611', 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() - -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/pylibs/pylama/lint/pylama_pylint/pylint/config.py b/pylibs/pylama/lint/pylama_pylint/pylint/config.py deleted file mode 100644 index 192f2548..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/config.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (c) 2003-2012 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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""utilities for Pylint configuration : - -* pylintrc -* pylint.d (PYLINTHOME) -""" - -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') - -if not exists(PYLINT_HOME): - try: - os.mkdir(PYLINT_HOME) - except OSError: - print >> sys.stderr, 'Unable to create directory %s' % PYLINT_HOME - -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: - return pickle.load(open(data_file)) - except: - return {} - -if sys.version_info < (3, 0): - _PICK_MOD = 'w' -else: - _PICK_MOD = 'wb' - -def save_results(results, base): - """pickle results""" - data_file = get_pdata_path(base, 1) - try: - pickle.dump(results, open(data_file, _PICK_MOD)) - 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): - 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 data of persistent 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. If not found, it will use the first -existing file among (~/.pylintrc, /etc/pylintrc). -''' % 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/pylibs/pylama/lint/pylama_pylint/pylint/interfaces.py b/pylibs/pylama/lint/pylama_pylint/pylint/interfaces.py deleted file mode 100644 index 7193c65a..00000000 --- a/pylibs/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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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/pylibs/pylama/lint/pylama_pylint/pylint/lint.py b/pylibs/pylama/lint/pylama_pylint/pylint/lint.py deleted file mode 100644 index 25c5377b..00000000 --- a/pylibs/pylama/lint/pylama_pylint/pylint/lint.py +++ /dev/null @@ -1,1065 +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., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 .checkers import utils - -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 -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 .utils import ( - MSG_TYPES, OPTION_RGX, - PyLintASTWalker, UnknownMessage, MessagesHandlerMixIn, ReportsHandlerMixIn, - EmptyReport, WarningScope, - expand_modules, tokenize_module) -from .interfaces import IRawChecker, ITokenChecker, IAstroidChecker -from .checkers import (BaseTokenChecker, - table_lines_from_stats, - initialize as checkers_initialize) -from .reporters import initialize as reporters_initialize -from . import config - -from .__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.'), - 'F0004': ('unexpected inferred value %s', - 'unexpected-inferred-value', - 'Used to indicate that some value of an unexpected type has been \ - inferred.'), - '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', - 'locally-disabled', - 'Used when an inline option disables a message or a messages \ - category.'), - 'I0012': ('Locally enabling %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'), - 'I0014': ('Used deprecated directive "pylint:disable-all" or "pylint:disable=all"', - 'deprecated-disable-all', - 'You should preferably use "pylint:skip-file" as this directive ' - 'has a less confusing name. Do this only if you are sure that all ' - 'people running Pylint on your code have version >= 0.26'), - '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.'), - - - '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.'), - } - - -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': '