diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3b087e6f..84607ec8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,6 +1,6 @@ [bumpversion] commit = True -current_version = 0.9.2 +current_version = 0.14.0 files = plugin/pymode.vim tag = True tag_name = {new_version} @@ -9,3 +9,6 @@ tag_name = {new_version} search = Version: {current_version} replace = Version: {new_version} +[bumpversion:file:CHANGELOG.md] +search = Version: {current_version} +replace = Version: {new_version} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..8847bdf2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +liberapay: diraol diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..be619481 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 150 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 21 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: inactive +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: > + Closed due to inactivity. diff --git a/.github/workflows/test_pymode.yml b/.github/workflows/test_pymode.yml new file mode 100644 index 00000000..332dcdad --- /dev/null +++ b/.github/workflows/test_pymode.yml @@ -0,0 +1,71 @@ +name: Testing python-mode + +on: [push] + +jobs: + test-python-3_8: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install dependencies + run: | + sudo apt update + export PYTHON_CONFIGURE_OPTS="--enable-shared" + sudo apt install -yqq libncurses5-dev libgtk2.0-dev libatk1.0-dev libcairo2-dev libx11-dev libxpm-dev libxt-dev python3-dev lua5.2 liblua5.2-dev libperl-dev git + sudo apt remove --purge -yqq vim vim-runtime gvim + - name: build and install vim from source + working-directory: /tmp + run: | + export PYTHON_CONFIGURE_OPTS="--enable-shared" + git clone https://github.com/vim/vim.git + cd vim + ./configure --with-features=huge --enable-multibyte --enable-python3interp=yes --with-python3-config-dir=/usr/lib/python3.8/config-3.8m-x86_64-linux-gnu --enable-perlinterp=yes --enable-luainterp=yes --enable-cscope --prefix=/usr/local + sudo make && sudo make install + - name: Install python-mode + run: | + export PYMODE_DIR="${HOME}/work/python-mode/python-mode" + mkdir -p ${HOME}/.vim/pack/foo/start/ + ln -s ${PYMODE_DIR} ${HOME}/.vim/pack/foo/start/python-mode + cp ${PYMODE_DIR}/tests/utils/pymoderc ${HOME}/.pymoderc + cp ${PYMODE_DIR}/tests/utils/vimrc ${HOME}/.vimrc + touch ${HOME}/.vimrc.before ${HOME}/.vimrc.after + - name: Run python-mode test script + run: | + alias python=python3 + cd ${HOME}/work/python-mode/python-mode + git submodule update --init --recursive + git submodule sync + bash tests/test.sh + test-python-3_9: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install dependencies + run: | + sudo apt update + export PYTHON_CONFIGURE_OPTS="--enable-shared" + sudo apt install -yqq libncurses5-dev libgtk2.0-dev libatk1.0-dev libcairo2-dev libx11-dev libxpm-dev libxt-dev python3-dev lua5.2 liblua5.2-dev libperl-dev git + sudo apt remove --purge -yqq vim vim-runtime gvim + - name: build and install vim from source + working-directory: /tmp + run: | + export PYTHON_CONFIGURE_OPTS="--enable-shared" + git clone https://github.com/vim/vim.git + cd vim + ./configure --with-features=huge --enable-multibyte --enable-python3interp=yes --with-python3-config-dir=/usr/lib/python3.9/config-3.9m-x86_64-linux-gnu --enable-perlinterp=yes --enable-luainterp=yes --enable-cscope --prefix=/usr/local + sudo make && sudo make install + - name: Install python-mode + run: | + export PYMODE_DIR="${HOME}/work/python-mode/python-mode" + mkdir -p ${HOME}/.vim/pack/foo/start/ + ln -s ${PYMODE_DIR} ${HOME}/.vim/pack/foo/start/python-mode + cp ${PYMODE_DIR}/tests/utils/pymoderc ${HOME}/.pymoderc + cp ${PYMODE_DIR}/tests/utils/vimrc ${HOME}/.vimrc + touch ${HOME}/.vimrc.before ${HOME}/.vimrc.after + - name: Run python-mode test script + run: | + alias python=python3 + cd ${HOME}/work/python-mode/python-mode + git submodule update --init --recursive + git submodule sync + bash tests/test.sh diff --git a/.gitignore b/.gitignore index f5674a78..40ca63ba 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ test.py todo.txt vendor vim.py +vim_session_*.vim +__*/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..59d00541 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,59 @@ +[submodule "submodules/autopep8"] + path = submodules/autopep8 + url = https://github.com/hhatto/autopep8 + ignore = dirty + shallow = true +[submodule "submodules/pycodestyle"] + path = submodules/pycodestyle + url = https://github.com/PyCQA/pycodestyle + ignore = dirty + shallow = true +[submodule "submodules/pydocstyle"] + path = submodules/pydocstyle + url = https://github.com/PyCQA/pydocstyle + ignore = dirty + shallow = true +[submodule "submodules/mccabe"] + path = submodules/mccabe + url = https://github.com/PyCQA/mccabe + ignore = dirty + shallow = true +[submodule "submodules/pyflakes"] + path = submodules/pyflakes + url = https://github.com/PyCQA/pyflakes + ignore = dirty + shallow = true +[submodule "submodules/snowball_py"] + path = submodules/snowball_py + url = https://github.com/diraol/snowball_py + ignore = dirty + branch = develop + shallow = true +[submodule "submodules/pylint"] + path = submodules/pylint + url = https://github.com/PyCQA/pylint + shallow = true +[submodule "submodules/rope"] + path = submodules/rope + url = https://github.com/python-rope/rope + shallow = true +[submodule "submodules/astroid"] + path = submodules/astroid + url = https://github.com/PyCQA/astroid + shallow = true +[submodule "submodules/pylama"] + path = submodules/pylama + url = https://github.com/klen/pylama + shallow = true +[submodule "submodules/toml"] + path = submodules/toml + url = https://github.com/uiri/toml.git +[submodule "submodules/pytoolconfig"] + path = submodules/pytoolconfig + url = https://github.com/bagel897/pytoolconfig.git +[submodule "submodules/tomli"] + path = submodules/tomli + url = https://github.com/hukkin/tomli.git +[submodule "submodules/appdirs"] + path = submodules/appdirs + url = https://github.com/ActiveState/appdirs.git diff --git a/.ruby-gemset b/.ruby-gemset deleted file mode 100644 index 5ded393e..00000000 --- a/.ruby-gemset +++ /dev/null @@ -1 +0,0 @@ -vim-flavor diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 67b8bc0d..00000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -ruby-1.9.3 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e10ed9f1..00000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: ruby -python: "2.7" -rvm: - - 1.9.3 -script: - - make travis diff --git a/AUTHORS b/AUTHORS index cc3de277..a4bcbf28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,35 +1,48 @@ -Maintainers: +Author: * Kirill Klenov -* Bryce Guinta (https://github.com/brycepg) +Maintainers: + +* Diego Rabatone Oliveira (https://github.com/diraol); Contributors: * Alvin Francis (http://github.com/alvinfrancis); -* Andriy Kohut (https://github.com/andriykohut) +* Amir Eldor (https://github.com/amireldor); +* Andriy Kohut (https://github.com/andriykohut); * Anler Hp (http://github.com/ikame); * Anton Parkhomenko (http://github.com/chuwy); * Ashley Hewson (http://github.com/ashleyh); +* Ben Davis (https://github.com/bendavis78); * Benjamin Ruston (http://github.com/bruston); +* Boatx (https://github.com/boatx); * Boris Filippov (http://github.com/frenzykryger); -* Brad Mease (http://github.com/bmease) -* Brendan Maguire (https://github.com/brendanmaguire) -* Daniel Hahler (http://github.com/blueyed) +* Brad Belyeu (https://github.com/bbelyeu); +* Brad Mease (http://github.com/bmease); +* Brendan Maguire (https://github.com/brendanmaguire); +* Bryce Guinta (https://github.com/brycepg); +* Daniel Hahler (http://github.com/blueyed); * David Vogt (http://github.com/winged); * Denis Kasak (http://github.com/dkasak); * Dimitrios Semitsoglou-Tsiapos (https://github.com/dset0x); * Dirk Wallenstein (http://github.com/dirkwallenstein); +* Felipe M. Vieira (https://github.com/fmv1992) +* Filip Poboril (https://github.com/fpob); * Florent Xicluna (http://github.com/florentx); * Fredrik Henrysson (http://github.com/fhenrysson); +* fwuzju (https://github.com/fwuzju); +* Grzegorz Janik (http://github.com/glujan); * Igor Guerrero (http://github.com/igorgue); -* Jacob Niehus (https://github.com/wilywampa) -* Jason Harvey (http://github.com/alienth) -* Jay Rainey (https://github.com/jawrainey) +* Jacob Niehus (https://github.com/wilywampa); +* Jason Harvey (http://github.com/alienth); +* Jay Rainey (https://github.com/jawrainey); * Jonathan McCall (http://github.com/Jonnymcc); * Kevin Deldycke (http://github.com/kdeldycke); * Kurtis Rader (https://github.com/krader1961); * Lawrence Akka (https://github.com/lawrenceakka); +* lee (https://github.com/loyalpartner); +* Lie Ryan (https://github.com/lieryan/); * Lowe Thiderman (http://github.com/thiderman); * Martin Brochhaus (http://github.com/mbrochh); * Matt Dodge (https://github.com/mattdodge); @@ -39,26 +52,27 @@ Contributors: * Mohammed (http://github.com/mbadran); * Naoya Inada (http://github.com/naoina); * Nate Zhang (https://github.com/natezhang93); +* nixon (https://github.com/nixon); * Paweł Korzeniewski (https://github.com/korzeniewskipl); * Pedro Algarvio (http://github.com/s0undt3ch); * Phillip Cloud (http://github.com/cpcloud); -* Piet Delport (http://github.com/pjdelport); +* Pi Delport (http://github.com/pjdelport); * Robert David Grant (http://github.com/bgrant); * Robin Schneider (https://github.com/ypid); * Ronald Andreu Kaiser (http://github.com/cathoderay);; * Samir Benmendil (https://github.com/Ram-Z); * Sorin Ionescu (sorin-ionescu); +* sphaugh (https://github.com/sphaugh); * Steve Losh (http://github.com/sjl); * Tommy Allen (https://github.com/tweekmonster); * Tony Narlock (https://github.com/tony); +* tramchamploo (https://github.com/tramchamploo); * Tyler Fenby (https://github.com/TFenby); * Vincent Driessen (https://github.com/nvie); * Wang Feng (https://github.com/mapler); * Wayne Ye (https://github.com/WayneYe); * Wes Turner (https://github.com/westurner); -* bendavis78 (https://github.com/bendavis78); -* fwuzju (https://github.com/fwuzju); -* lee (https://github.com/loyalpartner); -* nixon (https://github.com/nixon); -* sphaugh (https://github.com/sphaugh); -* tramchamploo (https://github.com/tramchamploo); +* Yury A. Kartynnik (https://github.com/kartynnik); +* Xiangyu Xu (https://github.com/bkbncn); +* Zach Himsel (https://github.com/zhimsel); +* Nathan Pemberton (https://github.com/NathanTP); diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..4e7668dd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,445 @@ +# Changelog + +## TODO + +## 2023-07-02 0.14.0 + +- Update submodules + - Fix Errors related to these updates +- Improve tests outputs +- Fix Global and Module MoveRefactoring (#1141) Thanks to @lieryan +- Text object/operator/motion mapping to select logical line (#1145). Thanks to + @lieryan +- Remove dead keywords and builtins; add match, case (#1149). Thanks to + @NeilGirdhar +- Add syntax highlight for walrus (#1147) Thanks to @fpob +- Add configurable prefix for rope commands (#1137) TThanks to @NathanTP +- Add option g:pymode_indent_hanging_width for different hanging indentation + width (#1138). Thanks to @wookayin + +## 2020-10-08 0.13.0 + +- Add toml submodule + +## 2020-10-08 0.12.0 + +- Improve breakpoint feature +- Improve debugging script +- Update submodules +- Improve tests + +## 2020-05-28 0.11.0 + +- Move changelog rst syntax to markdown +- `pymode_rope`: check disables +- BREAKING CHANGE: Remove supoort for python 2. From 0.11.0 on we will focus on + supporting python 3+ (probably 3.5+). +- Inspect why files starting with the following code do not get loaded: + + ```python + def main(): + pass + + if __name__ == '__main__': + main() + ``` + +- added github actions test suit and remove travis +- improved submodules cloning (shallow) +- Removes `six` submodule +- Fix motion mapping +- Fix breakpoint feature + +## 2019-05-11 0.10.0 + +After many changes, including moving most of our dependencies from copied +source code to submodules, and lot's of problems maintaining Python 2 and +Python 3 support, this (0.10.x) is the last version of python-mode that will +support Python 2. Some patches may be done in order to fix issues related to +Python 2 or some backward compatible changes that can be applied. + +## 2017-07-xxx 0.9.5 + +- pylama: migrated to submodule + +## 2017-07-11 0.9.4 + +- pylama: fixed erratic behavior of skip option causing unintended + skipping of lint checkers + +- PEP257 requires `snowbalstemmer`: added as submodule + +- Fixed handling of `g:pymode_lint_ignore` and `g:pymode_lint_select`: from + strings to list + +- Migrated modules from pymode/libs to + [submodules/](https://github.com/fmv1992/python-mode/tree/develop/submodules) + + - Rationale: no need to single handedly update each module; + removes burden from developers + +- Improved folding accuracy + + - Improved nested definitions folding + - Improved block delimiting + +## (changelog poorly maintained) 0.8.2 + +- Pylama updated to version 5.0.5 +- Rope libs updated +- Add wdb to debugger list in breakpoint cmd +- Add `pymode_options_max_line_length` option +- Add ability to set related checker options `:help pymode-lint-options` + Options added: `pymode_lint_options_pep8`, `pymode_lint_options_pep257`, + `pymode_lint_options_mccabe`, `pymode_lint_options_pyflakes`, + `pymode_lint_options_pylint` +- Highlight comments inside class/function arg lists +- Don't fold single line def +- Don't skip a line when the first docstring contains text +- Add Python documentation vertical display option +- Rope: correct refactoring function calls + +## 2014-06-11 0.8.1 + +- Pylama updated to version 3.3.2 + +- Get fold's expression symbol from &fillchars; + +- Fixed error when setting `g:pymode_breakpoint_cmd` (expobrain); + +- Fixed code running; + +- Ability to override rope project root and .ropeproject folder + +- Added path argument to PymodeRopeNewProject which skips prompt + +- Disable `pymode_rope_lookup_project` by default + +- Options added: + - `pymode_rope_project_root`, `pymode_rope_ropefolder` + +## 2013-12-04 0.7.8b + +- Update indentation support; + +- Python3 support; + +- Removed pymode modeline support; + +- Disabled async code checking support; + +- Options changes: + - `pymode_doc_key` -> `pymode_doc_bind` + - `pymode_run_key` -> `pymode_run_bind` + - `pymode_breakpoint_key` -> `pymode_breakpoint_bind` + - `pymode_breakpoint_template` -> `pymode_breakpoint_cmd` + - `pymode_lint_write` -> `pymode_lint_on_write` + - `pymode_lint_onfly` -> `pymode_lint_on_fly` + - `pymode_lint_checker` -> `pymode_lint_checkers` + - `pymode_lint_minheight` -> `pymode_quickfix_minheight` + - `pymode_lint_maxheight` -> `pymode_quickfix_maxheight` + - `pymode_rope_autocomplete_map` -> `pymode_rope_completion_bind` + - `pymode_rope_enable_autoimport` -> `pymode_rope_autoimport` + +- Options removed: + + - `pymode_lint_hold`, `pymode_lint_config`, `pymode_lint_jump`, + `pymode_lint_signs_always_visible`, `pymode_rope_extended_complete`, + `pymode_rope_auto_project`, `pymode_rope_autoimport_generate`, + `pymode_rope_autoimport_underlines`, `pymode_rope_codeassist_maxfixes`, + `pymode_rope_sorted_completions`, `pymode_rope_extended_complete`, + `pymode_rope_confirm_saving`, `pymode_rope_global_prefix`, + `pymode_rope_local_prefix`, `pymode_rope_vim_completion`, + `pymode_rope_guess_project`, `pymode_rope_goto_def_newwin`, + `pymode_rope_always_show_complete_menu` + +- Options added: + + - `pymode_rope_regenerate_on_write`, `pymode_rope_completion`, + `pymode_rope_complete_on_dot`, `pymode_lint_sort`, + `pymode_rope_lookup_project`, `pymode_lint_unmodified` + +- Commands added: + + - `PymodeVirtualenv` + +- Commands changed: + + - `PyDoc` -> `PymodeDoc` + - `Pyrun` -> `PymodeRun` + - `PyLintToggle` -> `PymodeLintToggle` + - `PyLint` -> `PymodeLint` + - `PyLintAuto` -> `PymodeLintAuto` + - `RopeOpenProject` -> `PymodeRopeNewProject` + - `RopeUndo` -> `PymodeRopeUndo` + - `RopeRedo` -> `PymodeRopeRedo` + - `RopeRenameCurrentModule` -> `PymodeRopeRenameModule` + - `RopeModuleToPackage` -> `PymodeRopeModuleToPackage` + - `RopeGenerateAutoimportCache` -> `PymodeRopeRegenerate` + - `RopeOrgamizeImports` -> `PymodeRopeAutoImport` + +- Commands removed: + - `PyLintCheckerToggle`, `RopeCloseProject`, `RopeProjectConfig`, + `RopeRename`, `RopeCreate<...>`, `RopeWriteProject`, `RopeRename`, + `RopeExtractVariable`, `RopeExtractMethod`, `RopeInline`, `RopeMove`, + `RopeRestructure`, `RopeUseFunction`, `RopeIntroduceFactory`, + `RopeChangeSignature`, `RopeMoveCurrentModule`, `RopeGenerate<...>`, + `RopeAnalizeModule`, `RopeAutoImport` + +## 2013-10-29 0.6.19 + +- Added `g:pymode_rope_autocomplete_map` option; +- Removed `g:pymode_rope_map_space` option; +- Added PEP257 checker; +- Support 'pudb' in breakpoints; +- Pyrun can now operate on a range of lines, and does not need to save + (c) lawrenceakka +- Update pylama to version 1.5.0 +- Add a set of `g:pymode_lint_*_symbol` options (c) kdeldycke; +- Support virtualenv for python3 (c) mlmoses; + +## 2013-05-15 0.6.18 + +- Fixed autopep8 (PyLintAuto) command; +- Fix error on non-ascii characters in docstrings; +- Update python syntax; + +## 2013-05-03 0.6.17 + +- Update Pylint to version 0.28.0; +- Update pyflakes to version 0.7.3; +- Fixed lint\_ignore options bug; +- Fixed encoding problems when code running; + +## 2013-04-26 0.6.16 + +- Improvement folding (thanks @alvinfrancis); + +## 2013-04-01 0.6.15 + +- Bugfix release + +## 2013-03-16 0.6.14 + +- Update PEP8 to version 1.4.5; +- Update Pylint to version 0.27.0; +- Update pyflakes to version 0.6.1; +- Update autopep8 to version 0.8.7; +- Fix breakpoint definition; +- Update python syntax; +- Fixed run-time error when output non-ascii in multibyte locale; +- Move initialization into ftplugin as it is python specific; +- Pyrex (Cython) files support; +- Support raw\_input in run python code; + +## 2012-09-07 0.6.10 + +- Dont raise an exception when Logger has no message handler (c) nixon +- Improve performance of white space removal (c) Dave Smith +- Improve ropemode support (c) s0undt3ch +- Add `g:pymode_updatetime` option +- Update autopep8 to version 0.8.1 + +## 2012-09-07 0.6.9 + +- Update autopep8 +- Improve `pymode#troubleshooting#Test()` + +## 2012-09-06 0.6.8 + +- Add PEP8 indentation `:help pymode_indent` + +## 2012-08-15 0.6.7 + +- Fix documentation. Thanks (c) bgrant; +- Fix pymode "async queue" support. + +## 2012-08-02 0.6.6 + +- Updated Pep8 to version 1.3.3 +- Updated Pylint to version 0.25.2 +- Fixed virtualenv support for windows users +- Added pymode modeline `:help PythonModeModeline` +- Added diagnostic tool `:call pymode#troubleshooting#Test()` +- Added PyLintAuto command `:help PyLintAuto` +- Code checking is async operation now +- More, more fast the pymode folding +- Repaired execution of python code + +## 2012-05-24 0.6.4 + +- Add `pymode_paths` option +- Rope updated to version 0.9.4 + +## 2012-04-18 0.6.3 + +- Fix pydocs integration + +## 2012-04-10 0.6.2 + +- Fix `pymode_run` for "unnamed" clipboard +- Add `pymode_lint_mccabe_complexity` option +- Update Pep8 to version 1.0.1 +- Warning! Change `pymode_rope_goto_def_newwin` option for open "goto + definition" in new window, set it to 'new' or 'vnew' for horizontally or + vertically split If you use default behaviour (in the same buffer), not + changes needed. + +## 2012-03-13 0.6.0 + +- Add `pymode_lint_hold` option +- Improve pymode loading speed +- Add pep8, mccabe lint checkers +- Now `g:pymode_lint_checker` can have many values Ex. "pep8,pyflakes,mccabe" +- Add `pymode_lint_ignore` and `pymode_lint_select` options +- Fix rope keys +- Fix python motion in visual mode +- Add folding `pymode_folding` +- Warning: `pymode_lint_checker` now set to 'pyflakes,pep8,mccabe' by default + +## 2012-02-12 0.5.8 + +- Fix pylint for Windows users +- Python documentation search running from Vim (delete `g:pydoc` option) +- Python code execution running from Vim (delete `g:python` option) + +## 2012-02-11 0.5.7 + +- Fix `g:pymode_lint_message` mode error +- Fix breakpoints +- Fix python paths and virtualenv detection + +## 2012-02-06 0.5.6 + +- Fix `g:pymode_syntax` option +- Show error message in bottom part of screen see `g:pymode_lint_message` +- Fix pylint for windows users +- Fix breakpoint command (Use pdb when idpb not installed) + +## 2012-01-17 0.5.5 + +- Add a sign for info messages from pylint. (c) Fredrik Henrysson +- Change motion keys: vic - viC, dam - daM and etc +- Add `g:pymode_lint_onfly` option + +## 2012-01-09 0.5.3 + +- Prevent the configuration from breaking python-mode (c) Dirk Wallenstein + +## 2012-01-08 0.5.2 + +- Fix ropeomnicompletion +- Add preview documentation + +## 2012-01-06 0.5.1 + +- Happy new year! +- Objects and motion fixes + +## 2011-11-30 0.5.0 + +- Add python objects and motions (beta) `:h pymode_motion` + +## 2011-11-27 0.4.8 + +- Add PyLintWindowToggle command +- Fix some bugs + +## 2011-11-23 0.4.6 + +- Enable all syntax highlighting For old settings set in your vimrc: + + ``` + let g:pymode_syntax_builtin_objs = 0 + let g:pymode_syntax_builtin_funcs = 0 + ``` + +- Change namespace of syntax variables See README + +## 2011-11-18 0.4.5 + +- Add `g:pymode_syntax` option +- Highlight 'self' keyword + +## 2011-11-16 0.4.4 + +- Minor fixes + +## 2011-11-11 0.4.3 + +- Fix pyflakes + +## 2011-11-09 0.4.2 + +- Add FAQ +- Some refactoring and fixes + +## 2011-11-08 0.4.0 + +- Add alternative code checker "pyflakes" See `:h pymode_lint_checker` +- Update install docs + +## 2011-10-30 0.3.3 + +- Fix RopeShowDoc + +## 2011-10-28 0.3.2 + +- Add `g:pymode_options_*` stuff, for ability to disable default pymode options + for python buffers + +## 2011-10-27 0.3.1 + +- Add `g:pymode_rope_always_show_complete_menu` option +- Some pylint fixes + +## 2011-10-25 0.3.0 + +- Add `g:pymode_lint_minheight` and `g:pymode_lint_maxheight` options +- Fix PyLintToggle +- Fix Rope and PyLint libs loading + +## 2011-10-21 0.2.12 + +- Auto open cwindow with results on rope find operations + +## 2011-10-20 0.2.11 + +- Add `pymode_lint_jump` option + +## 2011-10-19 0.2.10 + +- Minor fixes (virtualenv loading, buffer commands) + +## 2011-10-18 0.2.6 + +- Add `` shortcut for macvim users. +- Add VIRTUALENV support + +## 2011-10-17 0.2.4 + +- Add current work path to sys.path +- Add `g:pymode` option (disable/enable pylint and rope) +- Fix pylint copyright +- Hotfix rope autocomplete + +## 2011-10-15 0.2.1 + +- Change rope variables (`ropevim_` -> `pymode_rope_`) +- Add `pymode_rope_auto_project` option (default: 1) +- Update and fix docs +- `pymode_rope_extended_complete` set by default +- Auto generate rope project and cache +- `r a` for RopeAutoImport + +## 2011-10-12 0.1.4 + +- Add default pylint configuration + +## 2011-10-12 0.1.3 + +- Fix pylint and update docs + +## 2011-10-11 0.1.2 + +- First public release diff --git a/Changelog.rst b/Changelog.rst deleted file mode 100644 index e396eb69..00000000 --- a/Changelog.rst +++ /dev/null @@ -1,356 +0,0 @@ -Changelog -========= - -* Pylama updated to version 5.0.5 -* Rope libs updated -* Add wdb to debugger list in breakpoint cmd -* Add 'pymode_options_max_line_length' option -* Add ability to set related checker options `:help pymode-lint-options` - Options added: 'pymode_lint_options_pep8', 'pymode_lint_options_pep257', - 'pymode_lint_options_mccabe', 'pymode_lint_options_pyflakes', - 'pymode_lint_options_pylint' -* Highlight comments inside class/function arg lists -* Don't fold single line def -* Don't skip a line when the first docstring contains text -* Add Python documentation vertical display option -* Rope: correct refactoring function calls - - -## 2014-06-11 0.8.1 -------------------- -* Pylama updated to version 3.3.2 -* Get fold's expression symbol from &fillchars; -* Fixed error when setting g:pymode_breakpoint_cmd (expobrain); -* Fixed code running; -* Ability to override rope project root and .ropeproject folder -* Added path argument to `PymodeRopeNewProject` which skips prompt -* Disable `pymode_rope_lookup_project` by default -* Options added: - 'pymode_rope_project_root', 'pymode_rope_ropefolder' - - -## 2013-12-04 0.7.8b --------------------- - * Update indentation support; - * Python3 support; - * Removed pymode modeline support; - * Disabled async code checking support; - * Options changes: - 'pymode_doc_key' -> 'pymode_doc_bind' - 'pymode_run_key' -> 'pymode_run_bind' - 'pymode_breakpoint_key' -> 'pymode_breakpoint_bind' - 'pymode_breakpoint_template' -> 'pymode_breakpoint_cmd' - 'pymode_lint_write' -> 'pymode_lint_on_write' - 'pymode_lint_onfly' -> 'pymode_lint_on_fly' - 'pymode_lint_checker' -> 'pymode_lint_checkers' - 'pymode_lint_minheight' -> 'pymode_quickfix_minheight' - 'pymode_lint_maxheight' -> 'pymode_quickfix_maxheight' - 'pymode_rope_autocomplete_map' -> 'pymode_rope_completion_bind' - 'pymode_rope_enable_autoimport' -> 'pymode_rope_autoimport' - - * Options removed: - - 'pymode_lint_hold', 'pymode_lint_config', 'pymode_lint_jump', - 'pymode_lint_signs_always_visible', 'pymode_rope_extended_complete', - 'pymode_rope_auto_project', 'pymode_rope_autoimport_generate', - 'pymode_rope_autoimport_underlines', 'pymode_rope_codeassist_maxfixes', - 'pymode_rope_sorted_completions', 'pymode_rope_extended_complete', - 'pymode_rope_confirm_saving', 'pymode_rope_global_prefix', - 'pymode_rope_local_prefix', 'pymode_rope_vim_completion', - 'pymode_rope_guess_project', 'pymode_rope_goto_def_newwin', - 'pymode_rope_always_show_complete_menu' - - * Options added: - 'pymode_rope_regenerate_on_write', 'pymode_rope_completion', - 'pymode_rope_complete_on_dot', 'pymode_lint_sort', - 'pymode_rope_lookup_project', 'pymode_lint_unmodified' - - * Commands added: - 'PymodeVirtualenv' - - * Commands changed: - 'PyDoc' -> 'PymodeDoc' - 'Pyrun' -> 'PymodeRun' - 'PyLintToggle' -> 'PymodeLintToggle' - 'PyLint' -> 'PymodeLint' - 'PyLintAuto' -> 'PymodeLintAuto' - 'RopeOpenProject' -> 'PymodeRopeNewProject' - 'RopeUndo' -> 'PymodeRopeUndo' - 'RopeRedo' -> 'PymodeRopeRedo' - 'RopeRenameCurrentModule' -> 'PymodeRopeRenameModule' - 'RopeModuleToPackage' -> 'PymodeRopeModuleToPackage' - 'RopeGenerateAutoimportCache' -> 'PymodeRopeRegenerate' - 'RopeOrgamizeImports' -> 'PymodeRopeAutoImport' - - * Commands removed: - 'PyLintCheckerToggle', 'RopeCloseProject', 'RopeProjectConfig', - 'RopeRename', 'RopeCreate<...>', 'RopeWriteProject', 'RopeRename', - 'RopeExtractVariable', 'RopeExtractMethod', 'RopeInline', 'RopeMove', - 'RopeRestructure', 'RopeUseFunction', 'RopeIntroduceFactory', - 'RopeChangeSignature', 'RopeMoveCurrentModule', - 'RopeGenerate<...>', 'RopeAnalizeModule', 'RopeAutoImport', - - -## 2013-10-29 0.6.19 --------------------- -* Added `g:pymode_rope_autocomplete_map` option; -* Removed `g:pymode_rope_map_space` option; -* Added PEP257 checker; -* Support 'pudb' in breakpoints; -* Pyrun can now operate on a range of lines, and does not need to save (c) lawrenceakka -* Update pylama to version 1.5.0 -* Add a set of `g:pymode_lint_*_symbol` options (c) kdeldycke; -* Support virtualenv for python3 (c) mlmoses; - -## 2013-05-15 0.6.18 --------------------- -* Fixed autopep8 (`PyLintAuto`) command; -* Fix error on non-ascii characters in docstrings; -* Update python syntax; - -## 2013-05-03 0.6.17 --------------------- -* Update `Pylint` to version 0.28.0; -* Update `pyflakes` to version 0.7.3; -* Fixed `lint_ignore` options bug; -* Fixed encoding problems when code running; - -## 2013-04-26 0.6.16 --------------------- -* Improvement folding (thanks @alvinfrancis); - -## 2013-04-01 0.6.15 --------------------- -* Bugfix release - -## 2013-03-16 0.6.14 --------------------- -* Update `PEP8` to version 1.4.5; -* Update `Pylint` to version 0.27.0; -* Update `pyflakes` to version 0.6.1; -* Update `autopep8` to version 0.8.7; -* Fix breakpoint definition; -* Update python syntax; -* Fixed run-time error when output non-ascii in multibyte locale; -* Move initialization into ftplugin as it is python specific; -* Pyrex (Cython) files support; -* Support `raw_input` in run python code; - -## 2012-09-07 0.6.10 --------------------- -* Dont raise an exception when Logger has no message handler (c) nixon -* Improve performance of white space removal (c) Dave Smith -* Improve ropemode support (c) s0undt3ch -* Add `g:pymode_updatetime` option -* Update autopep8 to version 0.8.1 - -## 2012-09-07 0.6.9 -------------------- -* Update autopep8 -* Improve pymode#troubleshooting#Test() - -## 2012-09-06 0.6.8 -------------------- -* Add PEP8 indentation ":help 'pymode_indent'" - -## 2012-08-15 0.6.7 -------------------- -* Fix documentation. Thanks (c) bgrant; -* Fix pymode "async queue" support. - -## 2012-08-02 0.6.6 -------------------- -* Updated Pep8 to version 1.3.3 -* Updated Pylint to version 0.25.2 -* Fixed virtualenv support for windows users -* Added pymode modeline ':help PythonModeModeline' -* Added diagnostic tool ':call pymode#troubleshooting#Test()' -* Added `PyLintAuto` command ':help PyLintAuto' -* Code checking is async operation now -* More, more fast the pymode folding -* Repaired execution of python code - -## 2012-05-24 0.6.4 -------------------- -* Add 'pymode_paths' option -* Rope updated to version 0.9.4 - -## 2012-04-18 0.6.3 -------------------- -* Fix pydocs integration - -## 2012-04-10 0.6.2 -------------------- -* Fix pymode_run for "unnamed" clipboard -* Add 'pymode_lint_mccabe_complexity' option -* Update Pep8 to version 1.0.1 -* Warning! Change 'pymode_rope_goto_def_newwin' option - for open "goto definition" in new window, set it to 'new' or 'vnew' - for horizontally or vertically split - If you use default behaviour (in the same buffer), not changes needed. - -## 2012-03-13 0.6.0 -------------------- -* Add 'pymode_lint_hold' option -* Improve pymode loading speed -* Add pep8, mccabe lint checkers -* Now g:pymode_lint_checker can have many values - Ex. "pep8,pyflakes,mccabe" -* Add 'pymode_lint_ignore' and 'pymode_lint_select' options -* Fix rope keys -* Fix python motion in visual mode -* Add folding 'pymode_folding' -* Warning: 'pymode_lint_checker' now set to 'pyflakes,pep8,mccabe' by default - -## 2012-02-12 0.5.8 -------------------- -* Fix pylint for Windows users -* Python documentation search running from Vim (delete g:pydoc option) -* Python code execution running from Vim (delete g:python option) - -## 2012-02-11 0.5.7 -------------------- -* Fix 'g:pymode_lint_message' mode error -* Fix breakpoints -* Fix python paths and virtualenv detection - -## 2012-02-06 0.5.6 -------------------- -* Fix 'g:pymode_syntax' option -* Show error message in bottom part of screen - see 'g:pymode_lint_message' -* Fix pylint for windows users -* Fix breakpoint command (Use pdb when idpb not installed) - -## 2012-01-17 0.5.5 -------------------- -* Add a sign for info messages from pylint. - (c) Fredrik Henrysson -* Change motion keys: vic - viC, dam - daM and etc -* Add 'g:pymode_lint_onfly' option - -## 2012-01-09 0.5.3 -------------------- -* Prevent the configuration from breaking python-mode - (c) Dirk Wallenstein - -## 2012-01-08 0.5.2 -------------------- -* Fix ropeomnicompletion -* Add preview documentation - -## 2012-01-06 0.5.1 -------------------- -* Happy new year! -* Objects and motion fixes - -## 2011-11-30 0.5.0 -------------------- -* Add python objects and motions (beta) - :h pymode_motion - -## 2011-11-27 0.4.8 -------------------- -* Add `PyLintWindowToggle` command -* Fix some bugs - -## 2011-11-23 0.4.6 -------------------- -* Enable all syntax highlighting - For old settings set in your vimrc: - let g:pymode_syntax_builtin_objs = 0 - let g:pymode_syntax_builtin_funcs = 0 - -* Change namespace of syntax variables - See README - -## 2011-11-18 0.4.5 -------------------- -* Add 'g:pymode_syntax' option -* Highlight 'self' keyword - -## 2011-11-16 0.4.4 -------------------- -* Minor fixes - -## 2011-11-11 0.4.3 -------------------- -* Fix pyflakes - -## 2011-11-09 0.4.2 -------------------- -* Add FAQ -* Some refactoring and fixes - -## 2011-11-08 0.4.0 -------------------- -* Add alternative code checker "pyflakes" - See :h 'pymode_lint_checker' -* Update install docs - -## 2011-10-30 0.3.3 -------------------- -* Fix RopeShowDoc - -## 2011-10-28 0.3.2 -------------------- -* Add 'g:pymode_options_*' stuff, for ability - to disable default pymode options for python buffers - -## 2011-10-27 0.3.1 -------------------- -* Add 'g:pymode_rope_always_show_complete_menu' option -* Some pylint fixes - -## 2011-10-25 0.3.0 -------------------- -* Add g:pymode_lint_minheight and g:pymode_lint_maxheight - options -* Fix PyLintToggle -* Fix Rope and PyLint libs loading - -## 2011-10-21 0.2.12 --------------------- -* Auto open cwindow with results - on rope find operations - -## 2011-10-20 0.2.11 --------------------- -* Add 'pymode_lint_jump' option - -## 2011-10-19 0.2.10 --------------------- -* Minor fixes (virtualenv loading, buffer commands) - -## 2011-10-18 0.2.6 -------------------- -* Add shortcut for macvim users. -* Add VIRTUALENV support - -## 2011-10-17 0.2.4 -------------------- -* Add current work path to sys.path -* Add 'g:pymode' option (disable/enable pylint and rope) -* Fix pylint copyright -* Hotfix rope autocomplete - -## 2011-10-15 0.2.1 -------------------- -* Change rope variables (ropevim_ -> pymode_rope_) -* Add "pymode_rope_auto_project" option (default: 1) -* Update and fix docs -* 'pymode_rope_extended_complete' set by default -* Auto generate rope project and cache -* "r a" for RopeAutoImport - -## 2011-10-12 0.1.4 -------------------- -* Add default pylint configuration - -## 2011-10-12 0.1.3 -------------------- -* Fix pylint and update docs - -## 2011-10-11 0.1.2 -------------------- -* First public release diff --git a/Gemfile b/Gemfile deleted file mode 100644 index a87f4e1a..00000000 --- a/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://rubygems.org' - -gem 'vim-flavor', '~> 1.1' diff --git a/Makefile b/Makefile deleted file mode 100644 index 5e5a0990..00000000 --- a/Makefile +++ /dev/null @@ -1,98 +0,0 @@ -PYMODE = $(CURDIR)/pymode -LIBS = $(PYMODE)/libs -PYLAMA = $(LIBS)/pylama - -.PHONY: clean -clean: - find $(CURDIR) -name "*.pyc" -delete - rm -rf $(CURDIR)/build - rm -rf *.deb - -VERSION?=minor -# target: release - Bump version -release: - git fetch origin - git checkout master - git rebase - git merge develop - bumpversion $(VERSION) - git checkout develop - git rebase - git merge master - git push origin develop master - git push --tags - -.PHONY: minor -minor: release - -.PHONY: patch -patch: - make release VERSION=patch - -.PHONY: major -major: - make release VERSION=major - -# Temporary disable rope tests on Travis -.PHONY: travis -travis: - rake test - -.PHONY: test t -test: - bundle install - rm -rf $(CURDIR)/.ropeproject - rake test -t: test - -.PHONY: pylama -pylama: - rm -rf $(PYLAMA) - make $(PYLAMA) - make $(PYLAMA)/lint/pylama_pylint - @pip install --upgrade --force-reinstall --target=$(LIBS) pydocstyle - @pip install --upgrade --force-reinstall --target=$(LIBS) pycodestyle - @pip install --upgrade --force-reinstall --target=$(LIBS) pyflakes - @pip install --upgrade --force-reinstall --target=$(LIBS) mccabe - @pip install --upgrade --force-reinstall --target=$(LIBS) pylint - @find $(LIBS) -name *.dist-info -type d | xargs rm -rf - @find $(LIBS) -name *.egg-info -type d | xargs rm -rf - @find $(LIBS) -name test* -type d | xargs rm -rf - -.PHONY: rope -rope: - @git clone https://github.com/python-rope/rope.git $(CURDIR)/_/rope - @rm -rf $(CURDIR)/pymode/libs/rope - @cp -r $(CURDIR)/_/rope/rope $(CURDIR)/pymode/libs/. - -$(PYLAMA): - cp -r $$PRJDIR/pylama/pylama $(PYLAMA) - -$(PYLAMA)/lint/pylama_pylint: - cp -r $$PRJDIR/pylama/plugins/pylama_pylint/pylama_pylint/ $(PYLAMA)/lint/pylama_pylint - -$(CURDIR)/build: - mkdir -p $(CURDIR)/build/usr/share/vim/addons - mkdir -p $(CURDIR)/build/usr/share/vim/registry - cp -r after autoload doc ftplugin plugin pymode syntax $(CURDIR)/build/usr/share/vim/addons/. - cp -r python-mode.yaml $(CURDIR)/build/usr/share/vim/registry/. - -PACKAGE_VERSION?=$(shell git describe --tags `git rev-list master --tags --max-count=1`) -PACKAGE_NAME="vim-python-mode" -PACKAGE_MAINTAINER="Kirill Klenov " -PACKAGE_URL=http://github.com/klen/python-mode -deb: clean $(CURDIR)/build - @fpm -s dir -t deb -a all \ - -n $(PACKAGE_NAME) \ - -v $(PACKAGE_VERSION) \ - -m $(PACKAGE_MAINTAINER) \ - --url $(PACKAGE_URL) \ - --license "GNU lesser general public license" \ - --description "Vim-Swissknife for python" \ - --deb-user root \ - --deb-group root \ - -C $(CURDIR)/build \ - -d "python2.7" \ - -d "vim-addon-manager" \ - usr - @mv *.deb ~/Dropbox/projects/deb/load diff --git a/README.rst b/README.rst deleted file mode 100644 index 0706f0c6..00000000 --- a/README.rst +++ /dev/null @@ -1,367 +0,0 @@ -|logo| Python-mode, Python in VIM -################################# - -.. image:: https://travis-ci.org/python-mode/python-mode.png?branch=develop - :target: https://travis-ci.org/python-mode/python-mode - ------ - -*The project needs contributors* - -** Python-mode Slack Channel is here: https://python-mode.herokuapp.com/ ** - ------ - -| -| Src: https://github.com/python-mode/python-mode -| Homepage: https://klen.github.io/python-mode/ -| Docs: https://github.com/python-mode/python-mode/blob/develop/doc/pymode.txt -| - -Python-mode is a vim plugin that helps you to create python code very quickly -by utilizing libraries including -`pylint`_, `rope`_, pydoc_, `pyflakes`_, `pep8`_, `autopep8`_, -`pep257`_ and `mccabe`_ -for features like static analysis, refactoring, folding, completion, -documentation, and more. - -The plugin contains all you need to develop python applications in Vim. - -There is no need to install `pylint`_, `rope`_ -or any other `Python Libraries`_ on your system. - -- Support Python version 2.6+ and 3.2+ -- Syntax highlighting -- Virtualenv support -- Run python code (``r``) -- Add/remove breakpoints (``b``) -- Improved Python indentation -- Python folding -- Python motions and operators (``]]``, ``3[[``, ``]]M``, ``vaC``, ``viM``, - ``daC``, ``ciM``, ...) -- Code checking (pylint_, pyflakes_, pylama_, ...) that can be run - simultaneously (``:PymodeLint``) -- Autofix PEP8 errors (``:PymodeLintAuto``) -- Search in python documentation (``K``) -- Code refactoring (rope_) -- Strong code completion (rope_) -- Go to definition (``g`` for `:RopeGotoDefinition`) -- And more, more ... - -See (very old) screencast here: http://www.youtube.com/watch?v=67OZNp9Z0CQ -(sorry for quality, this is my first screencast) Another old presentation here: -http://www.youtube.com/watch?v=YhqsjUUHj6g - -**To read python-mode documentation in Vim, see** ``:help pymode`` - - -.. contents:: - - -Requirements -============ - -- VIM >= 7.3 (mostly features needed `+python` or `+python3` support) - (also ``--with-features=big`` if you want ``g:pymode_lint_signs``) - - -How to install -============== - -Using pathogen (recommended) ----------------------------- -:: - - % cd ~/.vim - % mkdir -p bundle && cd bundle - % git clone https://github.com/python-mode/python-mode.git - -- Enable `pathogen `_ - in your ``~/.vimrc``: :: - - " Pathogen load - filetype off - - call pathogen#infect() - call pathogen#helptags() - - filetype plugin indent on - syntax on - - -Manually --------- -:: - - % git clone https://github.com/python-mode/python-mode.git - % cd python-mode - % cp -R * ~/.vim - -Then rebuild **helptags** in vim:: - - :helptags ~/.vim/doc/ - - -.. note:: **filetype-plugin** (``:help filetype-plugin-on``) and - **filetype-indent** (``:help filetype-indent-on``) - must be enabled to use python-mode. - - -Debian packages ---------------- -|Repository URL: https://klen.github.io/python-mode/deb/ - -Install with commands: - -:: - - add-apt-repository https://klen.github.io/python-mode/deb main - apt-get update - apt-get install vim-python-mode - -If you are getting the message: "The following signatures couldn't be verified because the public key is not available": :: - - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B5DF65307000E266 - -`vim-python-mode` using `vim-addons`, so after installation just enable -`python-mode` with command: :: - - vim-addons install python-mode - - -Troubleshooting -=============== - -If your python-mode doesn't work: - -1. Load Vim with only python-mode enabled (use `debug.vim` from pymode): :: - - vim -u /debug.vim - -And try to repeat your case. If no error occurs, seems like problem isn't in the -plugin. - -2. Type `:PymodeTroubleshooting` - -And fix any warnings or copy the output and send it to me. (For example, by -creating a `new github issue `_ -if one does not already exist for the problem). - - -Customization -============= - -You can override the default key bindings by redefining them in your `.vimrc`, for example: :: - - " Override go-to.definition key shortcut to Ctrl-] - let g:pymode_rope_goto_definition_bind = "" - - " Override run current python file key shortcut to Ctrl-Shift-e - let g:pymode_run_bind = "" - - " Override view python doc key shortcut to Ctrl-Shift-d - let g:pymode_doc_bind = "" - - -Frequent Problems -================= - -Read this section before opening an issue on the tracker. - -Python 3 Syntax ---------------- - -By default python-mode uses python 2 syntax checking. To enable python 3 -syntax checking (e.g. for async) add:: - - let g:pymode_python = 'python3' - -To your vimrc or exrc file - - -Documentation -============= - -Documentation is available in your vim ``:help pymode`` - - -Bugtracker -=========== - -If you have any suggestions, bug reports or -annoyances please report them to the issue tracker -at https://github.com/python-mode/python-mode/issues - - -Contributing -============ - -* Kirill Klenov (horneds@gmail.com) -* Bryce Guinta (https://github.com/brycepg) - -Also see the `AUTHORS` file. - -Development of python-mode happens at github: -https://github.com/python-mode/python-mode - -Please make a pull request to `development` branch and add yourself to -`AUTHORS`. - -Source Links -=================== -- `doc/pymode.txt - `__ - -- ``:help pymode`` -- `plugin/pymode.vim - `__ - -- python-mode VIM plugin -- `syntax/python.vim - `__ - -- python-mode ``python.vim`` VIM syntax -- `syntax/pyrex.vim - `__ - -- ``pyrex.vim`` VIM syntax (pyrex, Cython) -- `t/ - `__ - -- ``*.vim`` more python-mode VIM configuration -- `pymode/ - `__ - -- ``*.py`` -- python-mode Python module -- `pymode/libs/ - `__ - -- ``*.py`` -- `Python Libraries <#python-libraries>`__ - - -Python Libraries ------------------- -Vendored Python modules are located -mostly in -`pymode/libs/ `__. - - -====== -rope -====== -| PyPI: https://pypi.python.org/pypi/rope -| Src: https://github.com/python-rope/rope -| Docs: https://github.com/python-rope/rope/blob/master/docs/overview.rst -| Docs: https://github.com/python-rope/rope/blob/master/docs/library.rst - -======================== -ropemode -======================== -| PyPI: https://pypi.python.org/pypi/ropemode -| Src: https://github.com/python-rope/ropemode - -========= -ropevim -========= -| PyPI: https://pypi.python.org/pypi/ropevim -| Src: https://github.com/python-rope/ropevim -| Docs: https://github.com/python-rope/ropevim/blob/master/doc/ropevim.txt - -======= -pylama -======= -| PyPI: https://pypi.python.org/pypi/pylama -| Src: https://github.com/klen/pylama - -======== -pylint -======== -| PyPI: https://pypi.python.org/pypi/pylint -| Src: https://bitbucket.org/logilab/pylint -| Homepage: http://www.pylint.org/ -| Docs: http://docs.pylint.org/ -| Docs: http://docs.pylint.org/message-control.html -| Docs: http://docs.pylint.org/faq.html#message-control -| ErrCodes: http://pylint-messages.wikidot.com/all-codes -| ErrCodes: http://pylint-messages.wikidot.com/all-messages - -========== -pyflakes -========== -| PyPI: https://pypi.python.org/pypi/pyflakes -| Src: https://github.com/pyflakes/pyflakes -| ErrCodes: https://flake8.readthedocs.org/en/latest/warnings.html - -====== -pep8 -====== -| PyPI: https://pypi.python.org/pypi/pep8 -| Src: http://github.com/jcrocholl/pep8 -| PEP 8: http://www.python.org/dev/peps/pep-0008/ -| PEP 8: http://legacy.python.org/dev/peps/pep-0008/ -| Docs: https://pep8.readthedocs.org/en/latest/ -| Docs: https://pep8.readthedocs.org/en/latest/intro.html#configuration -| ErrCodes: https://pep8.readthedocs.org/en/latest/intro.html#error-codes - -========= -autopep8 -========= -| PyPI: https://pypi.python.org/pypi/autopep8 -| Src: https://github.com/hhatto/autopep8 - -======= -pep257 -======= -| PyPI: https://pypi.python.org/pypi/pep257 -| Src: http://github.com/GreenSteam/pep257 -| Docs: https://pep257.readthedocs.org/en/latest/ -| PEP 257: http://www.python.org/dev/peps/pep-0257/ -| ErrCodes: https://pep257.readthedocs.org/en/latest/error_codes.html - -======= -mccabe -======= -| PyPI: https://pypi.python.org/pypi/mccabe -| Src: https://github.com/flintwork/mccabe -| Docs: https://en.wikipedia.org/wiki/Cyclomatic_complexity - - -Vim Libraries ---------------- -Vendored Vim modules are located mostly in ``t/``. - -====================== -Python syntax for vim -====================== -| Src: http://www.hlabs.spb.ru/vim/python.vim - - -===================== -PEP8 VIM indentation -===================== -| Src: http://github.com/hynek/vim-python-pep8-indent - - - -Copyright -========= - -Copyright © 2013-2015 Kirill Klenov (klen_) - -License -======= - -Licensed under a `GNU lesser general public license`_. - -If you like this plugin, I would very appreciated if you kindly send me a postcard :) -My address is here: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill Klenov". -**Thanks for support!** - -.. _GNU lesser general public license: http://www.gnu.org/copyleft/lesser.html -.. _klen: https://klen.github.com/ -.. _pydoc: http://docs.python.org/library/pydoc.html -.. _pathogen: https://github.com/tpope/vim-pathogen -.. _rope_: https://pypi.python.org/pypi/rope -.. _pylama_: https://github.com/klen/pylama -.. _pylint_: https://bitbucket.org/logilab/pylint -.. _pyflakes_: https://pypi.python.org/pypi/pyflakes -.. _autopep8_: https://github.com/hhatto/autopep8 -.. _pep257_: http://github.com/GreenSteam/pep257 -.. _mccabe_: https://github.com/flintwork/mccabe -.. _pythonvim: http://www.hlabs.spb.ru/vim/python.vim -.. _pep8_: http://github.com/jcrocholl/pep8 -.. _pep8indent: http://github.com/hynek/vim-python-pep8-indent -.. |logo| image:: https://raw.github.com/python-mode/python-mode/develop/logo.png diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 63a3a361..00000000 --- a/Rakefile +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env rake - -task :ci => [:dump, :test] - -task :dump do - sh 'vim --version' -end - -task :test do - sh 'bundle exec vim-flavor test' -end diff --git a/after/ftplugin/python.vim b/after/ftplugin/python.vim index 0dec7542..6b5a8839 100644 --- a/after/ftplugin/python.vim +++ b/after/ftplugin/python.vim @@ -11,36 +11,38 @@ if g:pymode_motion finish endif - nnoremap ]] :call pymode#motion#move('v^(classdef)s', '') - nnoremap [[ :call pymode#motion#move('v^(classdef)s', 'b') - nnoremap ]C :call pymode#motion#move('v^(classdef)s', '') - nnoremap [C :call pymode#motion#move('v^(classdef)s', 'b') - nnoremap ]M :call pymode#motion#move('^s*defs', '') - nnoremap [M :call pymode#motion#move('^s*defs', 'b') - - onoremap ]] :call pymode#motion#move('v^(classdef)s', '') - onoremap [[ :call pymode#motion#move('v^(classdef)s', 'b') - onoremap ]C :call pymode#motion#move('v^(classdef)s', '') - onoremap [C :call pymode#motion#move('v^(classdef)s', 'b') - onoremap ]M :call pymode#motion#move('^s*defs', '') - onoremap [M :call pymode#motion#move('^s*defs', 'b') - - vnoremap ]] :call pymode#motion#vmove('v^(classdef)s', '') - vnoremap [[ :call pymode#motion#vmove('v^(classdef)s', 'b') - vnoremap ]M :call pymode#motion#vmove('^s*defs', '') - vnoremap [M :call pymode#motion#vmove('^s*defs', 'b') - - onoremap C :call pymode#motion#select('^s*classs', 0) - onoremap aC :call pymode#motion#select('^s*classs', 0) - onoremap iC :call pymode#motion#select('^s*classs', 1) - vnoremap aC :call pymode#motion#select('^s*classs', 0) - vnoremap iC :call pymode#motion#select('^s*classs', 1) - - onoremap M :call pymode#motion#select('^s*defs', 0) - onoremap aM :call pymode#motion#select('^s*defs', 0) - onoremap iM :call pymode#motion#select('^s*defs', 1) - vnoremap aM :call pymode#motion#select('^s*defs', 0) - vnoremap iM :call pymode#motion#select('^s*defs', 1) + nnoremap ]] :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + nnoremap [[ :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + nnoremap ]C :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + nnoremap [C :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + nnoremap ]M :call pymode#motion#move('^s*(asyncs+)=defs', '') + nnoremap [M :call pymode#motion#move('^s*(asyncs+)=defs', 'b') + + onoremap ]] :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + onoremap [[ :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + onoremap ]C :call pymode#motion#move('^(class%(asyncs+)=def)s', '') + onoremap [C :call pymode#motion#move('^(class%(asyncs+)=def)s', 'b') + onoremap ]M :call pymode#motion#move('^s*(asyncs+)=defs', '') + onoremap [M :call pymode#motion#move('^s*(asyncs+)=defs', 'b') + + vnoremap ]] :call pymode#motion#vmove('^(class%(asyncs+)=def)s', '') + vnoremap [[ :call pymode#motion#vmove('^(class%(asyncs+)=def)s', 'b') + vnoremap ]M :call pymode#motion#vmove('^s*(asyncs+)=defs', '') + vnoremap [M :call pymode#motion#vmove('^s*(asyncs+)=defs', 'b') + + onoremap C :call pymode#motion#select_c('^s*classs', 0) + onoremap aC :call pymode#motion#select_c('^s*classs', 0) + onoremap iC :call pymode#motion#select_c('^s*classs', 1) + vnoremap aC :call pymode#motion#select_c('^s*classs', 0) + vnoremap iC :call pymode#motion#select_c('^s*classs', 1) + + onoremap M :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 0) + onoremap aM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 0) + onoremap iM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 1) + vnoremap aM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 0) + vnoremap iM :call pymode#motion#select('^s*(asyncs+)=@', '^s*(asyncs+)=defs', 1) + + onoremap V :call pymode#rope#select_logical_line() endif @@ -53,6 +55,6 @@ if g:pymode_rope && g:pymode_rope_completion if tolower(g:pymode_rope_completion_bind) == '' exe "inoremap =pymode#rope#complete(0)" endif - end + endif -end +endif diff --git a/autoload/pymode.vim b/autoload/pymode.vim index 723af9b5..b637289a 100644 --- a/autoload/pymode.vim +++ b/autoload/pymode.vim @@ -62,7 +62,7 @@ endfunction "}}} " DESC: Open temp buffer. fun! pymode#tempbuffer_open(name) "{{{ pclose - exe "botright 8new " . a:name + exe g:pymode_preview_position . " " . g:pymode_preview_height . "new " . a:name setlocal buftype=nofile bufhidden=delete noswapfile nowrap previewwindow redraw endfunction "}}} @@ -71,12 +71,11 @@ endfunction "}}} fun! pymode#trim_whitespaces() "{{{ if g:pymode_trim_whitespaces let cursor_pos = getpos('.') - silent! %s/\s\+$// + silent! %s/\s\+$//e call setpos('.', cursor_pos) endif endfunction "}}} - fun! pymode#save() "{{{ if &modifiable && &modified try @@ -120,9 +119,25 @@ fun! pymode#buffer_post_write() "{{{ endfunction "}}} fun! pymode#debug(msg) "{{{ + " Pymode's debug function. + " Should be called by other pymode's functions to report outputs. See + " the function PymodeDebugFolding for example. + " TODO: why echom here creates a problem? + " echom '' . a:msg + '|||||||||||' + + let l:info_separator = repeat('-', 79) + if g:pymode_debug - let g:pymode_debug += 1 - echom string(g:pymode_debug) . ': ' . string(a:msg) + if ! exists('g:pymode_debug_counter') + let g:pymode_debug_counter = 0 + endif + let g:pymode_debug_counter += 1 + " NOTE: Print a separator for every message except folding ones (since + " they could be many). + if a:msg !~ 'has folding:' + echom l:info_separator + endif + echom '' . 'pymode debug msg ' . g:pymode_debug_counter . ': ' . a:msg endif endfunction "}}} diff --git a/autoload/pymode/breakpoint.vim b/autoload/pymode/breakpoint.vim index c3189aad..98639b57 100644 --- a/autoload/pymode/breakpoint.vim +++ b/autoload/pymode/breakpoint.vim @@ -1,35 +1,34 @@ fun! pymode#breakpoint#init() "{{{ - if !g:pymode_breakpoint + " If breakpoints are either disabled or already defined do nothing. + if ! g:pymode_breakpoint || g:pymode_breakpoint_cmd != '' return - endif - - if g:pymode_breakpoint_cmd == '' - let g:pymode_breakpoint_cmd = 'import pdb; pdb.set_trace() # XXX BREAKPOINT' - - if g:pymode_python == 'disable' - return - endif - endif + " Else go for a 'smart scan' of the defaults. + else PymodePython << EOF -from imp import find_module +from importlib.util import find_spec -for module in ('wdb', 'pudb', 'ipdb'): - try: - find_module(module) - vim.command('let g:pymode_breakpoint_cmd = "import %s; %s.set_trace() # XXX BREAKPOINT"' % (module, module)) - break - except ImportError: - continue +if sys.version_info >= (3, 7): + vim.command('let g:pymode_breakpoint_cmd = "breakpoint()"') +else: + for module in ('wdb', 'pudb', 'ipdb', 'pdb'): + if find_spec(module): + vim.command('let g:pymode_breakpoint_cmd = "import %s; %s.set_trace() # XXX BREAKPOINT"' % (module, module)) + break EOF + endif endfunction "}}} fun! pymode#breakpoint#operate(lnum) "{{{ + if g:pymode_breakpoint_cmd == '' + echoerr("g:pymode_breakpoint_cmd is empty") + return -1 + endif let line = getline(a:lnum) if strridx(line, g:pymode_breakpoint_cmd) != -1 normal dd diff --git a/autoload/pymode/debug.vim b/autoload/pymode/debug.vim new file mode 100644 index 00000000..2be5149c --- /dev/null +++ b/autoload/pymode/debug.vim @@ -0,0 +1,67 @@ +" Set debugging functions. + +" DESC: Get debug information about pymode problem. +fun! pymode#debug#sysinfo() "{{{ + " OS info. {{{ + let l:os_name = "Unknown" + if has('win16') || has('win32') || has('win64') + let l:os_name = "Windows" + else + let l:os_name = substitute(system('uname'), "\n", "", "") + endif + call pymode#debug("Operating system: " . l:os_name) + " }}} + " Loaded scripts info. {{{ + call pymode#debug("Scriptnames:") + let l:scriptnames_var = execute('scriptnames') + " }}} + " Variables info. {{{ + " Drop verbose file temporarily to prevent the 'let' from showing up. + let l:tmp = &verbosefile + set verbosefile= + let l:all_variables = filter( + \ split(execute('let', 'silent!'), '\n'), + \ 'v:val =~ "^pymode"') + let &verbosefile = l:tmp + " NOTE: echom does not display multiline messages. Thus a for loop is + " needed. + call pymode#debug("Pymode variables:") + for pymodevar in sort(l:all_variables) + echom pymodevar + endfor + " }}} + " Git commit info. {{{ + " Find in the scriptnames the first occurence of 'python-mode'. Then parse + " the result outputting its path. This is in turn fed into the git command. + call pymode#debug("Git commit: ") + let l:pymode_folder = substitute( + \ filter( + \ split(l:scriptnames_var, '\n'), + \ 'v:val =~ "/python-mode/"')[0], + \ '\(^\s\+[0-9]\+:\s\+\)\([/~].*python-mode\/\)\(.*\)', + \ '\2', + \ '') + let l:git_head_sha1 = system('git -C ' . expand(l:pymode_folder). ' rev-parse HEAD ' ) + echom join(filter(split(l:git_head_sha1, '\zs'), 'v:val =~? "[0-9A-Fa-f]"'), '') + " }}} + " Git submodules status. {{{ + call pymode#debug("Git submodule status:") + let l:git_submodule_status = system('git -C ' . expand(l:pymode_folder). ' submodule status') + for submodule in split(l:git_submodule_status, '\n') + echom submodule + endfor + " }}} + call pymode#debug("End of pymode#debug#sysinfo") +endfunction "}}} + +" DESC: Define debug folding function. +function! pymode#debug#foldingexpr(lnum) "{{{ + let l:get_folding_result = pymode#folding#foldcase(a:lnum) + " NOTE: the 'has folding:' expression is special in the pymode#debug. + call pymode#debug( + \ 'line ' . a:lnum + \ . ' has folding: ' . l:get_folding_result['foldlevel'] + \ . ' with foldcase ' . l:get_folding_result['foldcase']) + return l:get_folding_result['foldlevel'] +endfunction +" }}} diff --git a/autoload/pymode/doc.vim b/autoload/pymode/doc.vim index b89eb0e7..a7a753c5 100644 --- a/autoload/pymode/doc.vim +++ b/autoload/pymode/doc.vim @@ -32,6 +32,9 @@ fun! pymode#doc#show(word) "{{{ if g:pymode_doc_vertical wincmd L endif + + normal gg + wincmd p endfunction "}}} diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim index 3b29aebb..9cbb64d3 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -1,5 +1,3 @@ -" 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. @@ -8,26 +6,25 @@ let s:def_regex = g:pymode_folding_regex let s:blank_regex = '^\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:doc_begin_regex = '^\s*[uUrR]\=\%("""\|''''''\)' -let s:doc_end_regex = '\%("""\|''''''\)\s*$' +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:doc_general_regex = '\%("""\|''''''\)' -let s:doc_line_regex = '^\s*[uUrR]\=\("""\|''''''\).\+\1\s*$' +let s:docstring_general_regex = '\%("""\|''''''\)' let s:symbol = matchstr(&fillchars, 'fold:\zs.') " handles multibyte characters if s:symbol == '' let s:symbol = ' ' endif " '''''''' - fun! pymode#folding#text() " {{{ let fs = v:foldstart - while getline(fs) !~ s:def_regex && getline(fs) !~ s:doc_begin_regex + while getline(fs) !~ s:def_regex && getline(fs) !~ s:docstring_begin_regex let fs = nextnonblank(fs + 1) endwhile - if getline(fs) =~ s:doc_end_regex && getline(fs) =~ s:doc_begin_regex + if getline(fs) =~ s:docstring_end_regex && getline(fs) =~ s:docstring_begin_regex let fs = nextnonblank(fs + 1) endif let line = getline(fs) @@ -49,6 +46,19 @@ 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) @@ -56,18 +66,26 @@ fun! pymode#folding#expr(lnum) "{{{ " Decorators {{{ if line =~ s:decorator_regex - return ">".(indent / &shiftwidth + 1) + let l:foldcase = 'decorator declaration' + let l:foldlevel = '>'.(indent / &shiftwidth + 1) + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif "}}} " Definition {{{ if line =~ s:def_regex + + " TODO: obscure case. " If indent of this line is greater or equal than line below " and previous non blank line does not end with : (that is, is not a " definition) " Keep the same indentation - if indent(a:lnum) >= indent(a:lnum+1) && getline(prevnonblank(a:lnum)) !~ ':\s*$' - return '=' - endif + " 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 @@ -81,10 +99,13 @@ fun! pymode#folding#expr(lnum) "{{{ let lnum -= 1 endwhile if decorated - return '=' + let l:foldcase = 'decorated function declaration' + let l:foldlevel = '=' else - return ">".(indent / &shiftwidth + 1) + let l:foldcase = 'function declaration' + let l:foldlevel = '>'.(indent / &shiftwidth + 1) endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif "}}} " Docstrings {{{ @@ -95,102 +116,126 @@ fun! pymode#folding#expr(lnum) "{{{ " Notice that an effect of this is that other docstring matches will not " be one liners. - if line =~ s:doc_line_regex - return "=" + 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:doc_begin_regex - " echom 'just entering' + if line =~ s:docstring_begin_regex if s:Is_opening_folding(a:lnum) - " echom 'entering at line ' . a:lnum - return ">".(indent / &shiftwidth + 1) + let l:foldcase = 'open multiline docstring' + let l:foldlevel = 'a1' endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif - if line =~ s:doc_end_regex + if line =~ s:docstring_end_regex if !s:Is_opening_folding(a:lnum) - " echom 'leaving at line ' . a:lnum - return "<".(indent / &shiftwidth + 1) + let l:foldcase = 'close multiline docstring' + let l:foldlevel = 's1' endif + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif "}}} - " Nested Definitions {{{ - " Handle nested defs but only for files shorter than - " g:pymode_folding_nest_limit lines due to performance concerns - if line('$') < g:pymode_folding_nest_limit && indent(prevnonblank(a:lnum)) - let curpos = getpos('.') - try - let last_block = s:BlockStart(a:lnum) - let last_block_indent = indent(last_block) - - " Check if last class/def is not indented and therefore can't be - " nested. - if last_block_indent - call cursor(a:lnum, 0) - let next_def = searchpos(s:def_regex, 'nW')[0] - let next_def_indent = next_def ? indent(next_def) : -1 - let last_block_end = s:BlockEnd(last_block) - - " If the next def has greater indent than the previous def, it - " is nested one level deeper and will have its own fold. If - " the class/def containing the current line is on the first - " line it can't be nested, and if this block ends on the last - " line, it contains no trailing code that should not be - " folded. Finally, if the next non-blank line after the end of - " the previous def is less indented than the previous def, it - " is not part of the same fold as that def. Otherwise, we know - " the current line is at the end of a nested def. - if next_def_indent <= last_block_indent && last_block > 1 && last_block_end < line('$') - \ && indent(nextnonblank(last_block_end)) >= last_block_indent - - " Include up to one blank line in the fold - if getline(last_block_end) =~ s:blank_regex - let fold_end = min([prevnonblank(last_block_end - 1), last_block_end]) + 1 - else - let fold_end = last_block_end - endif - if a:lnum == fold_end - return 's1' - else - return '=' - endif - endif + " Blocks. {{{ + let s:save_cursor = getcurpos() + let line_block_start = s:BlockStart(a:lnum) + let line_block_end = s:BlockEnd(a:lnum) + let prev_line_block_start = s:BlockStart(a:lnum - 1) + if line !~ s:blank_regex + if line_block_start == prev_line_block_start + \ || a:lnum - line_block_start == 1 + let l:foldcase = 'non blank line; first line of block or part of it' + let l:foldlevel = '=' + elseif indent < indent(prevnonblank(a:lnum - 1)) + if indent == 0 + let l:foldcase = 'non blank line; zero indent' + let l:foldlevel = 0 + else + let l:foldcase = 'non blank line; non zero indent' + let l:foldlevel = indent(line_block_start) / &shiftwidth + 1 endif - finally - call setpos('.', curpos) - endtry - endif " }}} + endif + call setpos('.', s:save_cursor) + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + else + call setpos('.', s:save_cursor) + endif + " endif " }}} " Blank Line {{{ + " Comments: cases of blank lines: + " 1. After non blank line: gets folded with previous line. + " 1. Just after a block; in this case it gets folded with the block. + " 1. Between docstrings and imports. + " 1. Inside docstrings. + " 2. Inside functions/methods. + " 3. Between functions/methods. if line =~ s:blank_regex - if prev_line =~ s:blank_regex - if indent(a:lnum + 1) == 0 && next_line !~ s:blank_regex && next_line !~ s:doc_general_regex - if s:Is_opening_folding(a:lnum) - " echom a:lnum - return "=" - else - " echom "not " . a:lnum - return 0 - endif - endif - return -1 - else - return '=' + if prev_line !~ s:blank_regex + let l:foldcase = 'blank line after non blank line' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + elseif a:lnum > line_block_start && a:lnum < line_block_end + let l:foldcase = 'blank line inside block' + let l:foldlevel = '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endif + " if prev_line =~ s:blank_regex + " if indent(a:lnum + 1) == 0 && next_line !~ s:blank_regex && next_line !~ s:docstring_general_regex + " if s:Is_opening_folding(a:lnum) + " let l:foldcase = 'case 1' + " let l:foldlevel = '=' + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " else + " let l:foldcase = 'case 2' + " let l:foldlevel = 0 + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " endif + " endif + " let l:foldcase = 'case 3' + " let l:foldlevel = -1 + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " else + " let l:foldcase = 'case 4' + " let l:foldlevel = '=' + " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} + " endif endif " }}} - return '=' + return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel} endfunction "}}} fun! s:BlockStart(lnum) "{{{ + " Returns the definition statement line which encloses the current line. + + let line = getline(a:lnum) + if line !~ s:blank_regex + let l:inferred_indent = indent(a:lnum) + else + let l:inferred_indent = prevnonblank(a:lnum) + endif + " Note: Make sure to reset cursor position after using this function. call cursor(a:lnum, 0) " In case the end of the block is indented to a higher level than the def " statement plus one shiftwidth, we need to find the indent level at the " bottom of that if/for/try/while/etc. block. - let last_def = searchpos(s:def_regex, 'bcnW')[0] + " Flags from searchpos() (same as search()): + " b: search Backward instead of forward + " n: do Not move the cursor + " W: don't Wrap around the end of the file + let previous_definition = searchpos(s:def_regex, 'bnW') + + while previous_definition[0] != 1 && previous_definition != [0, 0] + \ && indent(previous_definition[0]) >= l:inferred_indent + let previous_definition = searchpos(s:def_regex, 'bnW') + call cursor(previous_definition[0] - 1, 0) + endwhile + let last_def = previous_definition[0] if last_def + call cursor(last_def, 0) let last_def_indent = indent(last_def) call cursor(last_def, 0) let next_stmt_at_def_indent = searchpos('\v^\s{'.last_def_indent.'}[^[:space:]#]', 'nW')[0] @@ -200,75 +245,84 @@ fun! s:BlockStart(lnum) "{{{ " Now find the class/def one shiftwidth lower than the start of the " aforementioned indent block. - if next_stmt_at_def_indent && next_stmt_at_def_indent < a:lnum + if next_stmt_at_def_indent && (next_stmt_at_def_indent < a:lnum) let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0]) else let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0]) endif - return searchpos('\v^\s{,'.max_indent.'}(def |class )\w', 'bcnW')[0] + + let result = searchpos('\v^\s{,'.max_indent.'}(def |class )\w', 'bcnW')[0] + + return result + endfunction "}}} +function! Blockstart(x) + let save_cursor = getcurpos() + return s:BlockStart(a:x) + call setpos('.', save_cursor) +endfunction fun! s:BlockEnd(lnum) "{{{ " Note: Make sure to reset cursor position after using this function. call cursor(a:lnum, 0) return searchpos('\v^\s{,'.indent('.').'}\S', 'nW')[0] - 1 endfunction "}}} +function! Blockend(lnum) + let save_cursor = getcurpos() + return s:BlockEnd(a:lnum) + call setpos('.', save_cursor) +endfunction function! s:Is_opening_folding(lnum) "{{{ - " Helper function to see if docstring is opening or closing + " Helper function to see if multi line docstring is opening or closing. - " Cache the result so the loop runs only once per change + " Cache the result so the loop runs only once per change. if get(b:, 'fold_changenr', -1) == changenr() - return b:fold_cache[a:lnum] "If odd then it is an opening + 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 - let number_of_folding = 0 " To be analized if odd/even to inform if it is opening or closing. - let has_open_docstring = 0 " To inform is already has an open docstring. - let extra_docstrings = 0 " To help skipping ''' and """ which are not docstrings + " 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('$')) - call add(b:fold_cache, number_of_folding % 2) let i_line = getline(i) - if i_line =~ s:doc_line_regex - " echom "case 00 on line " . i - continue - endif - - if i_line =~ s:doc_begin_regex && ! has_open_docstring - " echom "case 01 on line " . 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 - continue else let has_open_docstring = 1 - let number_of_folding = number_of_folding + 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:doc_end_regex && has_open_docstring - " echom "case 02 on line " . i + elseif i_line =~ s:docstring_end_regex && has_open_docstring let has_open_docstring = 0 - let number_of_folding = number_of_folding + 1 + let fold_odd_even = fold_odd_even + 1 - elseif i_line =~ s:doc_general_regex - " echom "extra docstrings on line " . i + elseif i_line =~ s:docstring_general_regex let extra_docstrings = extra_docstrings + 1 - endif - endfor + endif + + call add(b:fold_cache, fold_odd_even % 2) - call add(b:fold_cache, number_of_folding % 2) + endfor return b:fold_cache[a:lnum] + endfunction "}}} " vim: fdm=marker:fdl=0 diff --git a/autoload/pymode/indent.vim b/autoload/pymode/indent.vim index efd41f29..e964f378 100644 --- a/autoload/pymode/indent.vim +++ b/autoload/pymode/indent.vim @@ -24,7 +24,9 @@ function! pymode#indent#get_indent(lnum) if closing_paren return indent(parlnum) else - return indent(parlnum) + &shiftwidth + let l:indent_width = (g:pymode_indent_hanging_width > 0 ? + \ g:pymode_indent_hanging_width : &shiftwidth) + return indent(parlnum) + l:indent_width endif else return parcol diff --git a/autoload/pymode/lint.vim b/autoload/pymode/lint.vim index e7dba8b5..29dd6168 100644 --- a/autoload/pymode/lint.vim +++ b/autoload/pymode/lint.vim @@ -5,7 +5,7 @@ call pymode#tools#loclist#init() fun! pymode#lint#auto() "{{{ - if !pymode#save() + if ! pymode#save() return 0 endif PymodePython from pymode import auto @@ -42,7 +42,7 @@ fun! pymode#lint#toggle() "{{{ call pymode#wide_message("Code checking is enabled.") else call pymode#wide_message("Code checking is disabled.") - end + endif endfunction "}}} @@ -61,6 +61,9 @@ fun! pymode#lint#check() "{{{ if loclist.is_empty() call pymode#wide_message('Code checking is completed. No errors found.') + call g:PymodeSigns.refresh(loclist) + call loclist.show() + return endif call g:PymodeSigns.refresh(loclist) @@ -68,7 +71,7 @@ fun! pymode#lint#check() "{{{ call loclist.show() call pymode#lint#show_errormessage() - call pymode#wide_message('Found errors and warnings: ' . len(loclist._loclist)) + call pymode#wide_message('Found ' . loclist.num_errors() . ' error(s) and ' . loclist.num_warnings() . ' warning(s)') endfunction " }}} diff --git a/autoload/pymode/motion.vim b/autoload/pymode/motion.vim index 67e99e6b..c88fb913 100644 --- a/autoload/pymode/motion.vim +++ b/autoload/pymode/motion.vim @@ -21,64 +21,72 @@ fun! pymode#motion#vmove(pattern, flags) range "{{{ call cursor(a:firstline, 0) normal! v call cursor(end) -endfunction "}}} +endfunction "}}} fun! pymode#motion#pos_le(pos1, pos2) "{{{ return ((a:pos1[0] < a:pos2[0]) || (a:pos1[0] == a:pos2[0] && a:pos1[1] <= a:pos2[1])) endfunction "}}} - -fun! pymode#motion#select(pattern, inner) "{{{ +fun! pymode#motion#select(first_pattern, second_pattern, inner) "{{{ let cnt = v:count1 - 1 let orig = getpos('.')[1:2] - let snum = s:BlockStart(orig[0], a:pattern) - if getline(snum) !~ a:pattern + let posns = s:BlockStart(orig[0], a:first_pattern, a:second_pattern) + if getline(posns[0]) !~ a:first_pattern && getline(posns[0]) !~ a:second_pattern return 0 endif - let enum = s:BlockEnd(snum, indent(snum)) + let snum = posns[0] + let enum = s:BlockEnd(posns[1], indent(posns[1])) while cnt - let lnum = search(a:pattern, 'nW') + let lnum = search(a:second_pattern, 'nW') if lnum let enum = s:BlockEnd(lnum, indent(lnum)) call cursor(enum, 1) endif let cnt = cnt - 1 endwhile - if pymode#motion#pos_le([snum, 0], orig) && pymode#motion#pos_le(orig, [enum, 1]) + if pymode#motion#pos_le([snum, 0], orig) && pymode#motion#pos_le(orig, [enum+1, 0]) if a:inner - let snum = snum + 1 - let enum = prevnonblank(enum) + let snum = posns[1] + 1 endif call cursor(snum, 1) - normal! v + normal! V call cursor(enum, len(getline(enum))) endif endfunction "}}} +fun! pymode#motion#select_c(pattern, inner) "{{{ + call pymode#motion#select(a:pattern, a:pattern, a:inner) +endfunction "}}} -fun! s:BlockStart(lnum, ...) "{{{ - let pattern = a:0 ? a:1 : '^\s*\(@\|class\s.*:\|def\s\)' +fun! s:BlockStart(lnum, first_pattern, second_pattern) "{{{ let lnum = a:lnum + 1 let indent = 100 while lnum let lnum = prevnonblank(lnum - 1) let test = indent(lnum) let line = getline(lnum) - if line =~ '^\s*#' " Skip comments + " Skip comments, deeper or equal lines + if line =~ '^\s*#' || test >= indent continue - elseif !test " Zero-level regular line - return lnum - elseif test >= indent " Skip deeper or equal lines - continue - " Indent is strictly less at this point: check for def/class - elseif line =~ pattern && line !~ '^\s*@' - return lnum endif let indent = indent(lnum) + + " Indent is strictly less at this point: check for def/class/@ + if line =~ a:first_pattern || line =~ a:second_pattern + while getline(lnum-1) =~ a:first_pattern + let lnum = lnum - 1 + endwhile + let first_pos = lnum + while getline(lnum) !~ a:second_pattern + let lnum = lnum + 1 + endwhile + let second_pos = lnum + return [first_pos, second_pos] + endif endwhile - return 0 + return [0, 0] endfunction "}}} @@ -89,7 +97,7 @@ fun! s:BlockEnd(lnum, ...) "{{{ let lnum = nextnonblank(lnum + 1) if getline(lnum) =~ '^\s*#' | continue elseif lnum && indent(lnum) <= indent - return lnum - 1 + return prevnonblank(lnum - 1) endif endwhile return line('$') diff --git a/autoload/pymode/rope.vim b/autoload/pymode/rope.vim index a82a46d9..36344d0a 100644 --- a/autoload/pymode/rope.vim +++ b/autoload/pymode/rope.vim @@ -1,5 +1,9 @@ " Python-mode Rope support -" + +if ! g:pymode_rope + finish +endif + PymodePython from pymode import rope call pymode#tools#loclist#init() @@ -11,24 +15,28 @@ endfunction fun! pymode#rope#complete(dot) if pumvisible() - return "\" - end + if stridx('noselect', &completeopt) != -1 + return "\" + else + return "" + endif + endif if a:dot PymodePython rope.complete(True) else PymodePython rope.complete() - end - return pumvisible() ? "\\" : "" + endif + return pumvisible() && stridx('noselect', &completeopt) != -1 ? "\\" : "" endfunction fun! pymode#rope#complete_on_dot() "{{{ if !exists("*synstack") return "" - end + endif for group in map(synstack(line('.'), col('.') - 1), 'synIDattr(v:val, "name")') for name in ['pythonString', 'pythonComment', 'pythonNumber', 'pythonDocstring'] if group == name - return "" + return "" endif endfor endfor @@ -72,8 +80,11 @@ fun! pymode#rope#show_doc() setlocal nomodifiable setlocal nomodified setlocal filetype=rst + + normal gg + wincmd p - end + endif endfunction @@ -183,3 +194,7 @@ fun! pymode#rope#generate_package() "{{{ endif PymodePython rope.GenerateElementRefactoring('package').run() endfunction "}}} + +fun! pymode#rope#select_logical_line() "{{{ + PymodePython rope.select_logical_line() +endfunction "}}} diff --git a/autoload/pymode/run.vim b/autoload/pymode/run.vim index 24c8729c..356409b6 100644 --- a/autoload/pymode/run.vim +++ b/autoload/pymode/run.vim @@ -74,8 +74,8 @@ fun! pymode#run#code_run(line1, line2) "{{{ cgetexpr(l:traceback) - " If a range is run (starting other than at line 1), fix the reported error line numbers for - " the current buffer + " If a range is run (starting other than at line 1), fix the reported + " error line numbers for the current buffer if a:line1 > 1 let qflist = getqflist() for i in qflist diff --git a/autoload/pymode/tools/loclist.vim b/autoload/pymode/tools/loclist.vim index 18b6d294..b9121bdf 100644 --- a/autoload/pymode/tools/loclist.vim +++ b/autoload/pymode/tools/loclist.vim @@ -24,19 +24,37 @@ endfunction "}}} fun! g:PymodeLocList.is_empty() "{{{ - return empty(self._loclist) + return empty(self._errlist) && empty(self._warnlist) +endfunction "}}} + +fun! g:PymodeLocList.loclist() "{{{ + let loclist = copy(self._errlist) + call extend(loclist, self._warnlist) + return loclist +endfunction "}}} + +fun! g:PymodeLocList.num_errors() "{{{ + return len(self._errlist) +endfunction "}}} + +fun! g:PymodeLocList.num_warnings() "{{{ + return len(self._warnlist) endfunction "}}} fun! g:PymodeLocList.clear() "{{{ - let self._loclist = [] + let self._errlist = [] + let self._warnlist = [] let self._messages = {} let self._name = expand('%:t') endfunction "}}} fun! g:PymodeLocList.extend(raw_list) "{{{ - call extend(self._loclist, a:raw_list) + let err_list = filter(copy(a:raw_list), 'v:val["type"] == "E"') + let warn_list = filter(copy(a:raw_list), 'v:val["type"] != "E"') + call extend(self._errlist, err_list) + call extend(self._warnlist, warn_list) for issue in a:raw_list let self._messages[issue.lnum] = issue.text endfor @@ -46,7 +64,7 @@ endfunction "}}} fun! g:PymodeLocList.filter(filters) "{{{ let loclist = [] - for error in self._loclist + for error in self.loclist() let passes_filters = 1 for key in keys(a:filters) if get(error, key, '') !=? a:filters[key] @@ -65,7 +83,7 @@ endfunction "}}} fun! g:PymodeLocList.show() "{{{ - call setloclist(0, self._loclist) + call setloclist(0, self.loclist()) if self.is_empty() lclose elseif g:pymode_lint_cwindow diff --git a/autoload/pymode/tools/signs.vim b/autoload/pymode/tools/signs.vim index 3487cf85..579573ed 100644 --- a/autoload/pymode/tools/signs.vim +++ b/autoload/pymode/tools/signs.vim @@ -46,7 +46,7 @@ endfunction "}}} fun! g:PymodeSigns.place(loclist) "{{{ let seen = {} - for issue in a:loclist._loclist + for issue in a:loclist.loclist() if !has_key(seen, issue.lnum) let seen[issue.lnum] = 1 call add(self._sign_ids, self._next_id) diff --git a/autoload/pymode/troubleshooting.vim b/autoload/pymode/troubleshooting.vim deleted file mode 100644 index 915a5c5e..00000000 --- a/autoload/pymode/troubleshooting.vim +++ /dev/null @@ -1,89 +0,0 @@ -" DESC: Get debug information about pymode problem -fun! pymode#troubleshooting#test() "{{{ - new - setlocal buftype=nofile bufhidden=delete noswapfile nowrap - - let os = "Unknown" - if has('win16') || has('win32') || has('win64') - let os = "Windows" - else - let os = substitute(system('uname'), "\n", "", "") - endif - - if !pymode#default('g:pymode_init', 1) - call pymode#init(expand(':p:h'), g:pymode_paths) - call pymode#virtualenv#init() - call pymode#breakpoint#init() - endif - - call append('0', ['Pymode diagnostic', - \ '===================', - \ 'VIM:' . v:version . ', OS: ' . os .', multi_byte:' . has('multi_byte') . ', pymode: ' . g:pymode_version . ', pymode-python: ' . g:pymode_python, - \ '']) - - if !exists('#filetypeplugin') - call append('$', ['WARNING: ', 'Python-mode required :filetype plugin indent on', '']) - endif - - call append('$', ['+python: ' . has('python')]) - call append('$', ['+python3: ' . has('python3'), '']) - - if g:pymode_python == 'disable' - - if !has('python') && !has('python3') - - call append('$', ['WARNING: Python-mode required vim compiled with +python or +python3.', - \ '"lint, rope, run, doc, virtualenv" features disabled.', '']) - - else - - call append('$', ['WARNING: Python is disabled by `pymode_python` option.', - \ '"lint, rope, run, doc, virtualenv" features disabled.', '']) - - endif - - else - - call append('$', 'VIM python paths:') - call append('$', '-----------------') - PymodePython << EOF -import vim -vim.command('let l:output = %s' % repr(sys.path)) -EOF - call append('$', output) - call append('$', '') - - endif - - call append('$', 'Pymode variables:') - call append('$', '-------------------') - call append('$', 'let pymode = ' . string(g:pymode)) - call append('$', 'let pymode_breakpoint = ' . string(g:pymode_breakpoint)) - call append('$', 'let pymode_breakpoint_bind = ' . string(g:pymode_breakpoint_bind)) - call append('$', 'let pymode_doc = ' . string(g:pymode_doc)) - call append('$', 'let pymode_doc_bind = ' . string(g:pymode_doc_bind)) - call append('$', 'let pymode_folding = ' . string(g:pymode_folding)) - call append('$', 'let pymode_indent = ' . string(g:pymode_indent)) - call append('$', 'let pymode_lint = ' . string(g:pymode_lint)) - call append('$', 'let pymode_lint_checkers = ' . string(g:pymode_lint_checkers)) - call append('$', 'let pymode_lint_cwindow = ' . string(g:pymode_lint_cwindow)) - call append('$', 'let pymode_lint_ignore = ' . string(g:pymode_lint_ignore)) - call append('$', 'let pymode_lint_message = ' . string(g:pymode_lint_message)) - call append('$', 'let pymode_lint_on_fly = ' . string(g:pymode_lint_on_fly)) - call append('$', 'let pymode_lint_on_write = ' . string(g:pymode_lint_on_write)) - call append('$', 'let pymode_lint_select = ' . string(g:pymode_lint_select)) - call append('$', 'let pymode_lint_signs = ' . string(g:pymode_lint_signs)) - call append('$', 'let pymode_motion = ' . string(g:pymode_motion)) - call append('$', 'let pymode_options = ' . string(g:pymode_options)) - call append('$', 'let pymode_paths = ' . string(g:pymode_paths)) - call append('$', 'let pymode_quickfix_maxheight = ' . string(g:pymode_quickfix_maxheight)) - call append('$', 'let pymode_quickfix_minheight = ' . string(g:pymode_quickfix_minheight)) - call append('$', 'let pymode_rope = ' . string(g:pymode_rope)) - call append('$', 'let pymode_run = ' . string(g:pymode_run)) - call append('$', 'let pymode_run_bind = ' . string(g:pymode_run_bind)) - call append('$', 'let pymode_trim_whitespaces = ' . string(g:pymode_trim_whitespaces)) - call append('$', 'let pymode_virtualenv = ' . string(g:pymode_virtualenv)) - call append('$', 'let pymode_virtualenv_enabled = ' . string(g:pymode_virtualenv_enabled)) - call append('$', 'let pymode_virtualenv_path = ' . string(g:pymode_virtualenv_path)) - -endfunction "}}} diff --git a/debug.vim b/debug.vim deleted file mode 100644 index c7d32661..00000000 --- a/debug.vim +++ /dev/null @@ -1,13 +0,0 @@ -" Use this settings for testing the plugin. -" Run vim with command -" -" $ vim -u debug.py -" -" Only python-mode will be loaded. - - -execute('set rtp+='. expand(':p:h')) -set rtp -=$HOME/.vim -set rtp -=$HOME/.vim/after -set nocp -syntax enable diff --git a/debugvimrc.vim b/debugvimrc.vim new file mode 100644 index 00000000..a8b3f188 --- /dev/null +++ b/debugvimrc.vim @@ -0,0 +1,32 @@ +" Use this settings for testing the plugin. +" +" Run vim with command: +" +" $ vim -u ./debug.vim /my/py/file.py +" +" Only python-mode will be loaded. + +" Disable all persistence between sessions. +let skip_defaults_vim=1 +" TODO XXX: this nevertheless keeps viminfo enabled. As a workaround the flag +" '-i NONE' should be added to vim's loading. +set viminfo= +set nobackup +set noswapfile + +" Modify vimrc configuration. +execute('set rtp+='. expand(':p:h')) +set rtp -=$HOME/.vim +set rtp -=$HOME/.vim/after +set nocompatible + +" Activate debugging. +let g:pymode_debug = 1 + +" Define a common shell for non Windows systems. +if ! (has('win16') || has('win32') || has('win64')) + set shell=/bin/bash +endif + +" IMPORTANT: Do note that the history of this session is saved on the log file. +" See the augroup in ./ftplugin/python/pymode.vim file. diff --git a/doc/pymode.txt b/doc/pymode.txt index 1551f119..7235b5d5 100644 --- a/doc/pymode.txt +++ b/doc/pymode.txt @@ -1,4 +1,4 @@ -*pymode.txt* *python-mode.txt* *pymode* *python-mode* +*pymode.txt* For Vim Version 8.0 Last change: 2019 March 08 ____ _ _ ____ _ _ _____ _ _ __ __ _____ ____ ____ ~ ( _ \( \/ )(_ _)( )_( )( _ )( \( )___( \/ )( _ )( _ \( ___) ~ @@ -6,36 +6,42 @@ (__) (__) (__) (_) (_)(_____)(_)\_) (_/\/\_)(_____)(____/(____) ~ - Version: 0.9.2 - -============================================================================== -CONTENTS *pymode-contents* - - 1.Intro.......................................................|pymode-intro| - 2.Common functionality.......................................|pymode-common| - 2.1 Python version...............................|pymode-python-version| - 2.2 Python indentation...................................|pymode-indent| - 2.3 Python folding......................................|pymode-folding| - 2.4 Vim motion...........................................|pymode-motion| - 2.5 Show documentation............................|pymode-documentation| - 2.6 Support virtualenv...............................|pymode-virtualenv| - 2.7 Run code................................................|pymode-run| - 2.8 Breakpoints.....................................|pymode-breakpoints| - 3. Code checking...............................................|pymode-lint| - 3.1 Code checkers options..........................|pymode-lint-options| - 4. Rope support................................................|pymode-rope| - 4.1 Code completion..................................|pymode-completion| - 4.2 Find definition.................................|pymode-rope-findit| - 4.3 Refactoring................................|pymode-rope-refactoring| - 4.4 Undo/Redo changes.................................|pymode-rope-undo| - 5. Syntax....................................................|pymode-syntax| - 6.FAQ...........................................................|pymode-faq| - 7.Credits...................................................|pymode-credits| - 8.License...................................................|pymode-license| - -============================================================================== + Version: 0.14.0 + +=============================================================================== +CONTENTS *pymode-contents* + +1. Intro...........................................................|pymode-intro| +2. Common functionality...........................................|pymode-common| + 2.1 Python version....................................|pymode-python-version| + 2.2 Python indentation........................................|pymode-indent| + 2.3 Python folding...........................................|pymode-folding| + 2.4 Vim motion................................................|pymode-motion| + 2.5 Show documentation.................................|pymode-documentation| + 2.6 Support virtualenv....................................|pymode-virtualenv| + 2.7 Run code.....................................................|pymode-run| + 2.8 Breakpoints..........................................|pymode-breakpoints| +3. Code checking....................................................|pymode-lint| + 3.1 Code checkers options...............................|pymode-lint-options| +4. Rope support.....................................................|pymode-rope| + 4.1 Code completion.......................................|pymode-completion| + 4.2 Find definition......................................|pymode-rope-findit| + 4.3 Refactoring.....................................|pymode-rope-refactoring| + 4.4 Undo/Redo changes......................................|pymode-rope-undo| +5. Syntax.........................................................|pymode-syntax| +6. FAQ...............................................................|pymode-faq| +7. Development...............................................|pymode-development| +8. Credits.......................................................|pymode-credits| +9. License.......................................................|pymode-license| + +=============================================================================== 1. Intro ~ - *pymode-intro* + *pymode-intro* + +XXX IMPORTANT: As of 2017-11-18 python-mode is going through a major redesign. +Thus some of its functionality may not work as expected. Please be patient and +do report bugs or inconsistencies in its documentation. But remember to look +for already openned bug reports for the same issue before creating a new one. Python-mode is a vim plugin that allows you to use the pylint, rope, and pydoc libraries in vim to provide features like python code bug checking, @@ -46,7 +52,7 @@ need to install the pylint or rope libraries on your system. Python-mode contains all you need to develop python applications in Vim. -Features: *pymode-features* +Features: *pymode-features* - Support Python version 2.6+ and 3.2+ - Syntax highlighting @@ -67,34 +73,34 @@ Features: *pymode-features* - And more, more ... -============================================================================== +=============================================================================== 2. Common functionality ~ - *pymode-common* + *pymode-common* This script provides the following options that can customizes the behavior of -PythonMode. These options should be set in your |vimrc|. +python-mode. These options should be set in your |vimrc|. - Below shows the default values. +Find below the default values: -Turn on the whole plugin *'g:pymode'* +Turn on the whole plugin. *'g:pymode'* > let g:pymode = 1 -Turn off plugin's warnings *'g:pymode_warnings'* +Turn off plugin's warnings. *'g:pymode_warnings'* > let g:pymode_warnings = 1 -Add paths to `sys.path` *'g:pymode_paths'* -Value is list of path's strings. +Add paths to `sys.path` *'g:pymode_paths'* +Value is list of path's strings. > let g:pymode_paths = [] -Trim unused white spaces on save *'g:pymode_trim_whitespaces'* +Trim unused white spaces on save. *'g:pymode_trim_whitespaces'* > let g:pymode_trim_whitespaces = 1 -Setup default python options *'g:pymode_options'* +Setup default python options. *'g:pymode_options'* > let g:pymode_options = 1 @@ -111,105 +117,130 @@ python buffers: > setlocal commentstring=#%s setlocal define=^\s*\\(def\\\\|class\\) -Setup max line length *'g:pymode_options_max_line_length'* +Setup max line length *'g:pymode_options_max_line_length'* > let g:pymode_options_max_line_length = 79 -Enable colorcolumn display at max_line_length *'g:pymode_options_colorcolumn'* +Enable colorcolumn display at max_line_length. *'g:pymode_options_colorcolumn'* > let g:pymode_options_colorcolumn = 1 -Setup pymode |quickfix| window +Setup pymode |quickfix| window. - *'g:pymode_quickfix_maxheight'* *'g:pymode_quickfix_minheight'* + *'g:pymode_quickfix_maxheight'* *'g:pymode_quickfix_minheight'* > let g:pymode_quickfix_minheight = 3 let g:pymode_quickfix_maxheight = 6 ------------------------------------------------------------------------------- +Set pymode |preview| window height. *'g:pymode_preview_height'* +Preview window is used to show documentation and ouput from |pymode-run|. +> + let g:pymode_preview_height = &previewheight + +Set where pymode |preview| window will appear. *'g:pymode_preview_position'* +> + let g:pymode_preview_position = 'botright' + +Value is command which can influcece where new window created by `:new` command +will appear, eg. `:botright`. + +------------------------------------------------------------------------------- 2.1. Python version ~ - *pymode-python-version* + *pymode-python-version* -By default pymode looks for current python version supported in your Vim. -You could choose prefer version, but value will be tested on loading. +By default pymode will attempt to use Python 3, if available. However, you can +also disable all Python features of pymode. - *'g:pymode_python'* + *'g:pymode_python'* > - let g:pymode_python = 'python' + let g:pymode_python = 'python3' -Values are `python`, `python3`, `disable`. If value set to `disable` most +Values are `python3`, `disable`. If value set to `disable` most python-features of **pymode** will be disabled. Set value to `python3` if you are working with python3 projects. You could use |exrc| ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.2 Python indentation ~ - *pymode-indent* + *pymode-indent* Pymode supports PEP8-compatible python indent. -Enable pymode indentation *'g:pymode_indent'* +Enable pymode indentation *'g:pymode_indent'* > let g:pymode_indent = 1 ------------------------------------------------------------------------------- + +Customization: + +Hanging indent size after an open parenthesis or bracket (but nothing after the +parenthesis), when vertical alignment is not used. Defaults to `&shiftwidth`. + *'g:pymode_indent_hanging_width'* +> + let g:pymode_indent_hanging_width = &shiftwidth + let g:pymode_indent_hanging_width = 4 + +------------------------------------------------------------------------------- 2.3 Python folding ~ - *pymode-folding* + *pymode-folding* -Fast and usual python folding in Vim. -Enable pymode folding *'g:pymode_folding'* +Enable pymode folding *'g:pymode_folding'* > - let g:pymode_folding = 1 + let g:pymode_folding = 0 ------------------------------------------------------------------------------- +Currently folding is considered experimental. There are several issues with +its implementation. + +------------------------------------------------------------------------------- 2.4 Vim motion ~ - *pymode-motion* + *pymode-motion* Support Vim motion (See |operator|) for python objects (such as functions, class and methods). `C` — means class `M` — means method or function - *pymode-motion-keys* - -================ ============================ -Key Command -================ ============================ -[[ Jump to previous class or function (normal, visual, operator modes) -]] Jump to next class or function (normal, visual, operator modes) -[M Jump to previous class or method (normal, visual, operator modes) -]M Jump to next class or method (normal, visual, operator modes) -aC Select a class. Ex: vaC, daC, yaC, caC (normal, operator modes) -iC Select inner class. Ex: viC, diC, yiC, ciC (normal, operator modes) -aM Select a function or method. Ex: vaM, daM, yaM, caM (normal, operator modes) -iM Select inner function or method. Ex: viM, diM, yiM, ciM (normal, operator modes) -================ ============================ - -Enable pymode-motion *'g:pymode_motion'* + *pymode-motion-keys* + +==== ============================ +Key Command +==== ============================ +[[ Jump to previous class or function (normal, visual, operator modes) +]] Jump to next class or function (normal, visual, operator modes) +[M Jump to previous class or method (normal, visual, operator modes) +]M Jump to next class or method (normal, visual, operator modes) +aC Select a class. Ex: vaC, daC, yaC, caC (operator modes) +iC Select inner class. Ex: viC, diC, yiC, ciC (operator modes) +aM Select a function or method. Ex: vaM, daM, yaM, caM (operator modes) +iM Select inner function or method. Ex: viM, diM, yiM, ciM (operator modes) +V Select logical line. Ex: dV, yV, cV (operator modes), also works with count +==== ============================ + +Enable pymode-motion *'g:pymode_motion'* > let g:pymode_motion = 1 ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.5 Show documentation ~ - *pymode-documentation* + *pymode-documentation* Pymode could show documentation for current word by `pydoc`. Commands: -*:PymodeDoc* — show documentation +*:PymodeDoc* — show documentation -Turns on the documentation script *'g:pymode_doc'* +Turns on the documentation script *'g:pymode_doc'* > let g:pymode_doc = 1 Bind keys to show documentation for current word (selection) - *'g:pymode_doc_bind'* + *'g:pymode_doc_bind'* > let g:pymode_doc_bind = 'K' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.6 Support virtualenv ~ - *pymode-virtualenv* + *pymode-virtualenv* Commands: *:PymodeVirtualenv* -- Activate virtualenv (path can be absolute or @@ -219,33 +250,33 @@ Enable automatic virtualenv detection *'g:pymode_virtualenv' > let g:pymode_virtualenv = 1 -Set path to virtualenv manually *'g:pymode_virtualenv_path'* +Set path to virtualenv manually *'g:pymode_virtualenv_path'* > let g:pymode_virtualenv_path = $VIRTUAL_ENV ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.7 Run code ~ - *pymode-run* + *pymode-run* Commands: *:PymodeRun* -- Run current buffer or selection -Turn on the run code script *'g:pymode_run'* +Turn on the run code script *'g:pymode_run'* > let g:pymode_run = 1 -Binds keys to run python code *'g:pymode_run_bind'* +Binds keys to run python code *'g:pymode_run_bind'* > let g:pymode_run_bind = 'r' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 2.8 Breakpoints ~ - *pymode-breakpoints* + *pymode-breakpoints* Pymode automatically detects available debugger (like pdb, ipdb, pudb) and user can set/unset breakpoint with one key and without code checking and etc. -Enable functionality *'g:pymode_breakpoint'* +Enable functionality *'g:pymode_breakpoint'* > let g:pymode_breakpoint = 1 @@ -258,11 +289,11 @@ Manually set breakpoint command (leave empty for automatic detection) let g:pymode_breakpoint_cmd = '' -============================================================================== +=============================================================================== 3. Code checking ~ - *pymode-lint* + *pymode-lint* -Pymode supports `pylint`, `pep257`, `pep8`, `pyflakes`, `mccabe` code +Pymode supports `pylint`, `pep257`, `pycodestyle`, `pyflakes`, `mccabe` code checkers. You could run several similar checkers. Pymode uses Pylama library for code checking. Many options like skip @@ -277,44 +308,44 @@ Commands: *:PymodeLintToggle* -- Toggle code checking *:PymodeLintAuto* -- Fix PEP8 errors in current buffer automatically -Turn on code checking *'g:pymode_lint'* +Turn on code checking *'g:pymode_lint'* > let g:pymode_lint = 1 -Check code on every save (if file has been modified) *'g:pymode_lint_on_write'* +Check code on every save (if file has been modified) *'g:pymode_lint_on_write'* > let g:pymode_lint_on_write = 1 -Check code on every save (every) *'g:pymode_lint_unmodified'* +Check code on every save (every) *'g:pymode_lint_unmodified'* > let g:pymode_lint_unmodified = 0 -Check code when editing (on the fly) *'g:pymode_lint_on_fly'* +Check code when editing (on the fly) *'g:pymode_lint_on_fly'* > let g:pymode_lint_on_fly = 0 -Show error message if cursor placed at the error line *'g:pymode_lint_message'* +Show error message if cursor placed at the error line *'g:pymode_lint_message'* > let g:pymode_lint_message = 1 -Default code checkers (you could set several) *'g:pymode_lint_checkers'* +Default code checkers (you could set several) *'g:pymode_lint_checkers'* > - let g:pymode_lint_checkers = ['pyflakes', 'pep8', 'mccabe'] + let g:pymode_lint_checkers = ['pyflakes', 'pycodestyle', 'mccabe'] -Values may be chosen from: `pylint`, `pep8`, `mccabe`, `pep257`, `pyflakes`. +Values may be chosen from: `pylint`, `pycodestyle`, `mccabe`, `pep257`, `pyflakes`. -Skip errors and warnings *'g:pymode_lint_ignore'* -E.g. "E501,W002", "E2,W" (Skip all Warnings and Errors that starts with E2) and etc +Skip errors and warnings *'g:pymode_lint_ignore'* +E.g. ["W", "E2"] (Skip all Warnings and the Errors starting with E2) etc. > - let g:pymode_lint_ignore = "E501,W" + let g:pymode_lint_ignore = ["E501", "W",] -Select some error or warnings. *'g:pymode_lint_select'* +Select some error or warnings. *'g:pymode_lint_select'* By example you disable all warnings starting from 'W', but want to see warning 'W0011' and warning 'W430' > - let g:pymode_lint_select = "E501,W0011,W430" + let g:pymode_lint_select = ["E501", "W0011", "W430"] -Sort errors by relevance *'g:pymode_lint_sort'* +Sort errors by relevance *'g:pymode_lint_sort'* If not empty, errors will be sort by defined relevance E.g. let g:pymode_lint_sort = ['E', 'C', 'I'] " Errors first 'E', after them 'C' and ... @@ -322,11 +353,11 @@ after them 'C' and ... let g:pymode_lint_sort = [] Auto open cwindow (quickfix) if any errors have been found - *'g:pymode_lint_cwindow'* + *'g:pymode_lint_cwindow'* > let g:pymode_lint_cwindow = 1 -Place error |signs| *'g:pymode_signs'* +Place error |signs| *'g:pymode_signs'* > let g:pymode_lint_signs = 1 @@ -339,44 +370,43 @@ Definitions for |signs| let g:pymode_lint_info_symbol = 'II' let g:pymode_lint_pyflakes_symbol = 'FF' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 3.1 Set code checkers options ~ - *pymode-lint-options* + *pymode-lint-options* Pymode has the ability to set code checkers options from pymode variables: -Set PEP8 options *'g:pymode_lint_options_pep8'* +Set PEP8 options *'g:pymode_lint_options_pycodestyle'* > - let g:pymode_lint_options_pep8 = - \ {'max_line_length': g:pymode_options_max_line_length}) + let g:pymode_lint_options_pycodestyle = + \ {'max_line_length': g:pymode_options_max_line_length} See https://pep8.readthedocs.org/en/1.4.6/intro.html#configuration for more info. -Set Pyflakes options *'g:pymode_lint_options_pyflakes'* +Set Pyflakes options *'g:pymode_lint_options_pyflakes'* > let g:pymode_lint_options_pyflakes = { 'builtins': '_' } -Set mccabe options *'g:pymode_lint_options_mccabe'* +Set mccabe options *'g:pymode_lint_options_mccabe'* > let g:pymode_lint_options_mccabe = { 'complexity': 12 } -Set pep257 options *'g:pymode_lint_options_pep257'* +Set pep257 options *'g:pymode_lint_options_pep257'* > let g:pymode_lint_options_pep257 = {} -Set pylint options *'g:pymode_lint_options_pylint'* +Set pylint options *'g:pymode_lint_options_pylint'* > let g:pymode_lint_options_pylint = - \ {'max-line-length': g:pymode_options_max_line_length}) + \ {'max-line-length': g:pymode_options_max_line_length} See http://docs.pylint.org/features.html#options for more info. - -============================================================================== -3. Rope support ~ - *pymode-rope* +=============================================================================== +4. Rope support ~ + *pymode-rope* Pymode supports Rope refactoring operations, code completion and code assists. @@ -390,12 +420,16 @@ Commands: |:PymodeRopeUndo| -- Undo changes from last refactoring -Turn on the rope script *'g:pymode_rope'* +Turn on the rope script *'g:pymode_rope'* > let g:pymode_rope = 1 +Set the prefix for rope commands *'g:pymode_rope_prefix'* +> + let g:pymode_rope_refix = '' + .ropeproject Folder ~ - *.ropeproject* + *.ropeproject* *:PymodeRopeNewProject* [] -- Open new Rope project in the given path *:PymodeRopeRegenerate* -- Regenerate the project cache @@ -422,13 +456,13 @@ all its child directories, which may slow scanning down (because of many, possibly unrelated, files) Enable searching for |.ropeproject| in parent directories - *'g:pymode_rope_lookup_project'* + *'g:pymode_rope_lookup_project'* > let g:pymode_rope_lookup_project = 0 You can also manually set the rope project directory. If not specified rope will use the current directory. - *'g:pymode_rope_project_root'* + *'g:pymode_rope_project_root'* > let g:pymode_rope_project_root = "" @@ -438,15 +472,14 @@ keep it outside of your project root. The rope library treats this folder as a project resource, so the path will always be relative to your project root (a leading '/' will be ignored). You may use `'..'` path segments to place the folder outside of your project root. - *'g:pymode_rope_ropefolder'* + *'g:pymode_rope_ropefolder'* > let g:pymode_rope_ropefolder='.ropeproject' - Show documentation for element under cursor ~ -Show documentation for object under cursor. *'g:pymode_rope_show_doc_bind'* +Show documentation for object under cursor. *'g:pymode_rope_show_doc_bind'* Leave empty to disable the key binding. > let g:pymode_rope_show_doc_bind = 'd' @@ -455,9 +488,9 @@ Regenerate project cache on every save (if file has been modified) > let g:pymode_rope_regenerate_on_write = 1 ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.1 Completion ~ - *pymode-completion* + *pymode-completion* By default you can use for autocompletion. The first entry will be automatically selected and you can press to insert the entry in @@ -465,26 +498,32 @@ your code. and / works too. Autocompletion is also called by typing a period in |Insert| mode by default. +If there's only one complete item, vim may be inserting it automatically +instead of using a popup menu. If the complete item which inserted is not +your wanted, you can roll it back use '' in |Insert| mode or setup +'completeopt' with `menuone` and `noinsert` in your vimrc. .e.g. +> + set completeopt=menuone,noinsert -Turn on code completion support in the plugin *'g:pymode_rope_completion'* +Turn on code completion support in the plugin *'g:pymode_rope_completion'* > let g:pymode_rope_completion = 1 Turn on autocompletion when typing a period - *'g:pymode_rope_complete_on_dot'* + *'g:pymode_rope_complete_on_dot'* > let g:pymode_rope_complete_on_dot = 1 -Keymap for autocomplete *'g:pymode_rope_completion_bind'* +Keymap for autocomplete *'g:pymode_rope_completion_bind'* > let g:pymode_rope_completion_bind = '' Extended autocompletion (rope could complete objects which have not been -imported) from project *'g:pymode_rope_autoimport'* +imported) from project *'g:pymode_rope_autoimport'* > let g:pymode_rope_autoimport = 0 -Load modules to autoimport by default *'g:pymode_rope_autoimport_modules'* +Load modules to autoimport by default *'g:pymode_rope_autoimport_modules'* > let g:pymode_rope_autoimport_modules = ['os', 'shutil', 'datetime'] @@ -493,24 +532,24 @@ Offer to unresolved import object after completion. let g:pymode_rope_autoimport_import_after_complete = 0 ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.2 Find definition ~ - *pymode-rope-findit* + *pymode-rope-findit* By default when you press *g* on any object in your code you will be moved -to definition. -Leave empty for disable key binding. *'g:pymode_rope_goto_definition_bind'* +to definition. +Leave empty for disable key binding. *'g:pymode_rope_goto_definition_bind'* > let g:pymode_rope_goto_definition_bind = 'g' Command for open window when definition has been found -Values are (`e`, `new`, `vnew`) *'g:pymode_rope_goto_definition_cmd'* +Values are (`e`, `new`, `vnew`) *'g:pymode_rope_goto_definition_cmd'* > let g:pymode_rope_goto_definition_cmd = 'new' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.3 Refactoring ~ - *pymode-rope-refactoring* + *pymode-rope-refactoring* Rename method/function/class/variable in the project ~ @@ -518,7 +557,7 @@ Pymode can rename everything: classes, functions, modules, packages, methods, variables and keyword arguments. Keymap for rename method/function/class/variables under cursor - *'g:pymode_rope_rename_bind'* + *'g:pymode_rope_rename_bind'* > let g:pymode_rope_rename_bind = 'rr' @@ -527,7 +566,7 @@ Rename a current module/package ~ *:PymodeRopeRenameModule* -- Rename current module -Keymap for rename current module *'g:pymode_rope_rename_module_bind'* +Keymap for rename current module *'g:pymode_rope_rename_module_bind'* > let g:pymode_rope_rename_module_bind = 'r1r' @@ -538,18 +577,18 @@ Imports ~ Organize imports sorts imports, too. It does that according to PEP8. Unused imports will be dropped. -Keymap *'g:pymode_rope_organize_imports_bind'* +Keymap *'g:pymode_rope_organize_imports_bind'* > let g:pymode_rope_organize_imports_bind = 'ro' -Insert import for current word under cursor *'g:pymode_rope_autoimport_bind'* +Insert import for current word under cursor *'g:pymode_rope_autoimport_bind'* Should be enabled |'g:pymode_rope_autoimport'| > let g:pymode_rope_autoimport_bind = 'ra' Convert module to package ~ - *'g:pymode_rope_module_to_package_bind'* + *'g:pymode_rope_module_to_package_bind'* *:PymodeRopeModuleToPackage* -- convert current module to package @@ -559,19 +598,19 @@ Keybinding: Extract method/variable ~ - *pymode-rope-extract* + *pymode-rope-extract* Extract method/variable from selected lines. - *'g:pymode_rope_extract_method_bind'* - *'g:pymode_rope_extract_variable_bind'* + *'g:pymode_rope_extract_method_bind'* + *'g:pymode_rope_extract_variable_bind'* > let g:pymode_rope_extract_method_bind = 'rm' let g:pymode_rope_extract_variable_bind = 'rl' Use function ~ - *pymode-rope-use* + *pymode-rope-use* It tries to find the places in which a function can be used and changes the code to call it instead. @@ -579,14 +618,31 @@ code to call it instead. let g:pymode_rope_use_function_bind = 'ru' -Move method/fields ~ - *pymode-rope-move* +Move refactoring ~ + *pymode-rope-move* + +Moving method/fields It happens when you perform move refactoring on a method of a class. In this refactoring, a method of a class is moved to the class of one of its attributes. The old method will call the new method. If you want to change all of the occurrences of the old method to use the new method you can inline it afterwards. + +Moving global variable/class/function into another module + +It happens when you perform move refactoring on global variable/class/function. +In this refactoring, the object being refactored will be moved to a destination +module. All references to the object being moved will be updated to point to +the new location. + +Moving module variable/class/function into a package + +It happens when you perform move refactoring on a name referencing a module. +In this refactoring, the module being refactored will be moved to a destination +package. All references to the object being moved will be updated to point to +the new location. + > let g:pymode_rope_move_bind = 'rv' @@ -595,10 +651,10 @@ Change function signature ~ let g:pymode_rope_change_signature_bind = 'rs' ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- 4.4 Undo/Redo changes ~ - *pymode-rope-undo* - *pymode-rope-redo* + *pymode-rope-undo* + *pymode-rope-redo* Commands: @@ -606,94 +662,118 @@ Commands: *:PymodeRopeRedo* -- Redo last changes in the project -============================================================================== +=============================================================================== 5. Syntax ~ - *pymode-syntax* + *pymode-syntax* -Turn on pymode syntax *'g:pymode_syntax'* +Turn on pymode syntax *'g:pymode_syntax'* > let g:pymode_syntax = 1 Slower syntax synchronization that is better at handling code blocks in docstrings. Consider disabling this on slower hardware. - *'g:pymode_syntax_slow_sync'* + *'g:pymode_syntax_slow_sync'* > let g:pymode_syntax_slow_sync = 1 -Enable all python highlights *'g:pymode_syntax_all'* +Enable all python highlights *'g:pymode_syntax_all'* > let g:pymode_syntax_all = 1 -Highlight "print" as a function *'g:pymode_syntax_print_as_function'* +Highlight "print" as a function *'g:pymode_syntax_print_as_function'* > let g:pymode_syntax_print_as_function = 0 -Highlight "async/await" keywords *'g:pymode_syntax_highlight_async_await'* +Highlight "async/await" keywords *'g:pymode_syntax_highlight_async_await'* > let g:pymode_syntax_highlight_async_await = g:pymode_syntax_all -Highlight '=' operator *'g:pymode_syntax_highlight_equal_operator'* +Highlight '=' operator *'g:pymode_syntax_highlight_equal_operator'* > let g:pymode_syntax_highlight_equal_operator = g:pymode_syntax_all -Highlight '*' operator *'g:pymode_syntax_highlight_stars_operator'* +Highlight ':=' operator *'g:pymode_syntax_highlight_walrus_operator'* +> + let g:pymode_syntax_highlight_walrus_operator = g:pymode_syntax_all + +Highlight '*' operator *'g:pymode_syntax_highlight_stars_operator'* > let g:pymode_syntax_highlight_stars_operator = g:pymode_syntax_all -Highlight 'self' keyword *'g:pymode_syntax_highlight_self'* +Highlight 'self' keyword *'g:pymode_syntax_highlight_self'* > let g:pymode_syntax_highlight_self = g:pymode_syntax_all -Highlight indent's errors *'g:pymode_syntax_indent_errors'* +Highlight indent's errors *'g:pymode_syntax_indent_errors'* > let g:pymode_syntax_indent_errors = g:pymode_syntax_all -Highlight space's errors *'g:pymode_syntax_space_errors'* +Highlight space's errors *'g:pymode_syntax_space_errors'* > let g:pymode_syntax_space_errors = g:pymode_syntax_all Highlight string formatting *'g:pymode_syntax_string_formatting'* - *'g:pymode_syntax_string_format'* - *'g:pymode_syntax_string_templates'* - *'g:pymode_syntax_doctests'* + *'g:pymode_syntax_string_format'* + *'g:pymode_syntax_string_templates'* + *'g:pymode_syntax_doctests'* > let g:pymode_syntax_string_formatting = g:pymode_syntax_all let g:pymode_syntax_string_format = g:pymode_syntax_all let g:pymode_syntax_string_templates = g:pymode_syntax_all let g:pymode_syntax_doctests = g:pymode_syntax_all -Highlight builtin objects (True, False, ...) *'g:pymode_syntax_builtin_objs'* +Highlight builtin objects (True, False, ...) *'g:pymode_syntax_builtin_objs'* > let g:pymode_syntax_builtin_objs = g:pymode_syntax_all -Highlight builtin types (str, list, ...) *'g:pymode_syntax_builtin_types'* +Highlight builtin types (str, list, ...) *'g:pymode_syntax_builtin_types'* > let g:pymode_syntax_builtin_types = g:pymode_syntax_all -Highlight exceptions (TypeError, ValueError, ...) - *'g:pymode_syntax_highlight_exceptions'* +Highlight exceptions (TypeError, ValueError, ...) + *'g:pymode_syntax_highlight_exceptions'* > let g:pymode_syntax_highlight_exceptions = g:pymode_syntax_all Highlight docstrings as pythonDocstring (otherwise as pythonString) - *'g:pymode_syntax_docstrings'* + *'g:pymode_syntax_docstrings'* > let g:pymode_syntax_docstrings = g:pymode_syntax_all -============================================================================== +=============================================================================== 6. FAQ ~ - *pymode-faq* + *pymode-faq* -Python-mode doesn't work ------------------------- +1. Python-mode doesn't work +--------------------------- -Open any python file and run ":call pymode#troubleshooting#test()", -fix the warning or send me the output. +First remember to get the latest and updated version of the project source +code and also update the project submodules. +Clear all python cache/compiled files (`*.pyc` files and `__pycache__` +directory and everything under it). In Linux/Unix/MacOS you can run: + +`find . -type f -name '*.pyc' -delete && find . -type d -name '__pycache__' -delete` + +Then start python mode with: +`vim -i NONE -u /debugvimrc.vim` + +Reproduce the error and submit your python mode debug file. You can check its +location with `:messages` for something like: + +`pymode debug msg 1: Starting debug on: 2017-11-18 16:44:13 with file /tmp/pymode_debug_file.txt` + +Please submit the entire content of the file along with a reasoning of why the +plugin seems broken. + + *Underlined do check for sensitive information in the file before + *Underlined submitting! -Rope completion is very slow *pymode-rope-slow* ----------------------------- + + +2. Rope completion is very slow *pymode-rope-slow* +------------------------------- Rope creates a project-level service directory in |.ropeproject| @@ -715,9 +795,8 @@ You may also set |'g:pymode_rope_project_root'| to manually specify the project root path. - -Pylint check is very slow -------------------------- +3. Pylint check is very slow +---------------------------- In some projects pylint may check slowly, because it also scans imported modules if possible. Try using another code checker: see @@ -726,9 +805,8 @@ modules if possible. Try using another code checker: see You may set |exrc| and |secure| in your |vimrc| to auto-set custom settings from `.vimrc` from your projects directories. - -OSX cannot import urandom -------------------------- +4. OSX cannot import urandom +---------------------------- See: https://groups.google.com/forum/?fromgroups=#!topic/vim_dev/2NXKF6kDONo @@ -740,11 +818,53 @@ The sequence of commands that fixed this: brew install -v --force macvim brew link macvim brew link python -< -============================================================================== -7. Credits ~ - *pymode-credits* +5. Folding is slow +------------------ + +Python mode adds folding for definitions and multi line docstrings. These may +be costly to compute on large files. To disable them one simple has to to add: + + let g:pymode_folding = 1 + +to their vimrc file. + +Beware that when editing python files in multiple windows vim computes the +folding for every typed character. Thus it may be useful to define: + + augroup unset_folding_in_insert_mode + autocmd! + autocmd InsertEnter *.py setlocal foldmethod=marker + autocmd InsertLeave *.py setlocal foldmethod=expr + augroup END + +=============================================================================== +7. Development~ + *pymode-development* + +This section briefly defines development guidelines for python-mode. + +1. This help file uses vim's conventions defined at |help-writing|. +2. The name of the plugin shall be referred to as 'python-mode' throughout +documentation (except as a first word in a sentence in which case is +'Python-mode'). +3. All defined functions should use vim's conventions and start with 'Pymode'. +4. Special marks for project development are `XXX` and `TODO`. They provide a +easy way for developers to check pending issues. +5. If submitting a pull request then a test should be added which smartly +covers the found bug/new feature. Check out the `tests/test.sh` (1) file and +other executed files. +A suggested structure is the following: add your test to +`tests/test_bash` (2) and a vim script to be sourced at +`tests/test_procedures_vimscript` (3). Try to make use of the already existing +files at `tests/test_python_sample_code` (4). File (1) should be trigger the +newly added file (2). This latter file should invoke vim which in turn sources +file (3). File (3) may then read (4) as a first part of its assertion +structure and then execute the remaning of the instructions/assertions. + +=============================================================================== +8. Credits ~ + *pymode-credits* Kirill Klenov http://klen.github.com/ http://github.com/klen/ @@ -758,8 +878,9 @@ The sequence of commands that fixed this: http://www.logilab.fr/ Pyflakes: - Copyright (c) 2005 Divmod, Inc. - http://www.divmod.com/ + Copyright (c) 2005-2011 Divmod, Inc. + Copyright (c) 2013-2014 Florent Xicluna + https://github.com/PyCQA/pyflakes PEP8: Copyright (c) 2006 Johann C. Rocholl @@ -778,19 +899,20 @@ The sequence of commands that fixed this: http://github.com/hynek/vim-python-pep8-indent -============================================================================== -8. License ~ - *pymode-license* +=============================================================================== +9. License ~ + *pymode-license* Python-mode is released under the GNU lesser general public license. See: http://www.gnu.org/copyleft/lesser.html -If you like this plugin, I would very appreciated if you kindly send me a postcard :) +If you like this plugin, I would very appreciated if you kindly send me a +postcard :) -My address is: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill Klenov". -Thanks for your support! +My address is: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill +Klenov". Thanks for your support! ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- - vim:tw=78:ts=8:ft=help:norl: + vim:tw=79:ts=8:ft=help:norl: diff --git a/ftplugin/python/pymode.vim b/ftplugin/python/pymode.vim index 97daecca..a1370669 100644 --- a/ftplugin/python/pymode.vim +++ b/ftplugin/python/pymode.vim @@ -5,7 +5,7 @@ endif if g:pymode_python == 'disable' if g:pymode_warning - call pymode#error("Pymode requires vim compiled with +python. Most of features will be disabled.") + call pymode#error("Pymode requires vim compiled with +python3 (exclusively). Most of features will be disabled.") endif finish @@ -110,7 +110,7 @@ if g:pymode_lint " let &l:updatetime = g:pymode_lint_async_updatetime " au! BufEnter call pymode#lint#start() " au! BufLeave call pymode#lint#stop() - end + endif endif @@ -124,7 +124,7 @@ if g:pymode_doc exe "nnoremap " g:pymode_doc_bind ":call pymode#doc#find()" exe "vnoremap " g:pymode_doc_bind ":call pymode#doc#show(@*)" -end +endif " Rope support if g:pymode_rope @@ -134,69 +134,69 @@ if g:pymode_rope endif if g:pymode_rope_show_doc_bind != "" exe "noremap " . g:pymode_rope_show_doc_bind . " :call pymode#rope#show_doc()" - end + endif if g:pymode_rope_find_it_bind != "" exe "noremap " . g:pymode_rope_find_it_bind . " :call pymode#rope#find_it()" - end + endif if g:pymode_rope_organize_imports_bind != "" exe "noremap " . g:pymode_rope_organize_imports_bind . " :call pymode#rope#organize_imports()" - end + endif if g:pymode_rope_rename_bind != "" exe "noremap " . g:pymode_rope_rename_bind . " :call pymode#rope#rename()" - end + endif if g:pymode_rope_rename_module_bind != "" exe "noremap " . g:pymode_rope_rename_module_bind . " :call pymode#rope#rename_module()" - end + endif if g:pymode_rope_extract_method_bind != "" exe "vnoremap " . g:pymode_rope_extract_method_bind . " :call pymode#rope#extract_method()" - end + endif if g:pymode_rope_extract_variable_bind != "" exe "vnoremap " . g:pymode_rope_extract_variable_bind . " :call pymode#rope#extract_variable()" - end + endif if g:pymode_rope_inline_bind != "" exe "noremap " . g:pymode_rope_inline_bind . " :call pymode#rope#inline()" - end + endif if g:pymode_rope_move_bind != "" exe "noremap " . g:pymode_rope_move_bind . " :call pymode#rope#move()" - end + endif if g:pymode_rope_change_signature_bind != "" exe "noremap " . g:pymode_rope_change_signature_bind . " :call pymode#rope#signature()" - end + endif if g:pymode_rope_use_function_bind != "" exe "noremap " . g:pymode_rope_use_function_bind . " :call pymode#rope#use_function()" - end + endif if g:pymode_rope_generate_function_bind != "" exe "noremap " . g:pymode_rope_generate_function_bind . " :call pymode#rope#generate_function()" - end + endif if g:pymode_rope_generate_package_bind != "" exe "noremap " . g:pymode_rope_generate_package_bind . " :call pymode#rope#generate_package()" - end + endif if g:pymode_rope_generate_class_bind != "" exe "noremap " . g:pymode_rope_generate_class_bind . " :call pymode#rope#generate_class()" - end + endif if g:pymode_rope_module_to_package_bind != "" exe "noremap " . g:pymode_rope_module_to_package_bind . " :call pymode#rope#module_to_package()" - end + endif if g:pymode_rope_autoimport_bind != "" exe "noremap " . g:pymode_rope_autoimport_bind . " :PymodeRopeAutoImport" - end + endif if g:pymode_rope_completion && g:pymode_rope_complete_on_dot inoremap . .=pymode#rope#complete_on_dot() - end + endif command! -buffer -nargs=? PymodeRopeNewProject call pymode#rope#new() command! -buffer PymodeRopeUndo call pymode#rope#undo() @@ -207,6 +207,52 @@ if g:pymode_rope if g:pymode_rope_autoimport command! -buffer PymodeRopeAutoImport call pymode#rope#autoimport(expand('')) - end + endif -end +endif + + +if g:pymode_debug + " Redefine functions to be debugged here functions here. + + " NOTE: The redraw seems to be necessary to force messages to get echoed to + " the screen. See: + " https://groups.google.com/forum/#!topic/vim_use/EfcXOjq_rKE + " for details. + " silent! redraw! + " TODO: when loading with 'vim -u ./debug.vim' the messages shown in vim + " are unduly cleared. Need a fix. + + " Start debbuging environment. {{{ + if ! &verbosefile + " Get a system independent temporary filename. The 'marker' variable is + " used to get rid of a null character getting inserted at position. + " substitute() was not able to remove it. + " TODO: see https://superuser.com/questions/935574/get-rid-of-null-character-in-vim-variable + let g:pymode_debug_tempfile=matchstr( + \ execute( + \ g:pymode_python + \ . " import os;import tempfile; marker='|';" + \ . " print(marker, tempfile.gettempdir(), os.sep, " + \ . "'pymode_debug_file.txt', marker, sep='', end='')"), + \ '|\zs.*\ze|') + execute "set verbosefile=" . g:pymode_debug_tempfile + endif + call pymode#debug('Starting debug on: ' + \ . strftime("\%Y-\%m-\%d \%H:\%M:\%S") + \ . ' with file ' . &verbosefile) + " }}} + " Redefine folding expression. {{{ + if g:pymode_folding + setlocal foldexpr=pymode#debug#foldingexpr(v:lnum) + endif + call pymode#debug#sysinfo() + " }}} + " Define auto commands for vim. {{{ + augroup augroup_save_issue_commands + autocmd! + autocmd VimLeave *.py | call pymode#debug('Session history:') | silent! history + augroup END + " }}} + + endif diff --git a/plugin/pymode.vim b/plugin/pymode.vim index 30078ce6..b0d99270 100644 --- a/plugin/pymode.vim +++ b/plugin/pymode.vim @@ -1,15 +1,16 @@ " vi: fdl=1 -let g:pymode_version = "0.9.2" +let g:pymode_version = "0.14.0" -com! PymodeVersion echomsg "Current python-mode version: " . g:pymode_version -com! PymodeTroubleshooting call pymode#troubleshooting#test() " Enable pymode by default :) call pymode#default('g:pymode', 1) call pymode#default('g:pymode_debug', 0) " DESC: Disable script loading -if !g:pymode || &cp +if !g:pymode || &cp || &diff + " Update pymode status to prevent loading in other files and adding this + " condition to all of them. + let g:pymode = 0 finish endif @@ -19,7 +20,11 @@ filetype plugin on " OPTIONS: {{{ " Vim Python interpreter. Set to 'disable' for remove python features. -call pymode#default('g:pymode_python', '') +if has("python3") && executable('python3') + call pymode#default('g:pymode_python', 'python3') +else + call pymode#default('g:pymode_python', 'disable') +endif " Disable pymode warnings call pymode#default('g:pymode_warning', 1) @@ -34,12 +39,18 @@ call pymode#default('g:pymode_doc_bind', 'K') " Enable/Disable pymode PEP8 indentation call pymode#default("g:pymode_indent", 1) +" Customize hanging indent size different than &shiftwidth +call pymode#default("g:pymode_indent_hanging_width", -1) + +" TODO: currently folding suffers from a bad performance and incorrect +" implementation. This feature should be considered experimental. " Enable/disable pymode folding for pyfiles. -call pymode#default("g:pymode_folding", 1) +call pymode#default("g:pymode_folding", 0) " Maximum file length to check for nested class/def statements call pymode#default("g:pymode_folding_nest_limit", 1000) " Change for folding customization (by example enable fold for 'if', 'for') call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\|async\s\+def\) .\+\(:\s\+\w\)\@!') +" call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\|async\s\+def\)') " Enable/disable python motion operators call pymode#default("g:pymode_motion", 1) @@ -49,7 +60,7 @@ call pymode#default("g:pymode_trim_whitespaces", 1) " Set recomended python options call pymode#default("g:pymode_options", 1) -call pymode#default("g:pymode_options_max_line_length", 80) +call pymode#default("g:pymode_options_max_line_length", 79) call pymode#default("g:pymode_options_colorcolumn", 1) " Enable/disable vertical display of python documentation @@ -61,6 +72,12 @@ call pymode#default('g:pymode_quickfix_maxheight', 6) " Maximal height of pymode quickfix window call pymode#default('g:pymode_quickfix_minheight', 3) +" Height of preview window +call pymode#default('g:pymode_preview_height', &previewheight) + +" Position of preview window +call pymode#default('g:pymode_preview_position', 'botright') + " LOAD VIRTUALENV {{{ " " Enable virtualenv support @@ -105,14 +122,14 @@ call pymode#default("g:pymode_lint_on_fly", 0) " Show message about error in command line call pymode#default("g:pymode_lint_message", 1) -" Choices are: pylint, pyflakes, pep8, mccabe -call pymode#default("g:pymode_lint_checkers", ['pyflakes', 'pep8', 'mccabe']) +" Choices are: pylint, pyflakes, pycodestyle, mccabe and pep257 +call pymode#default("g:pymode_lint_checkers", ['pyflakes', 'pycodestyle', 'mccabe']) " Skip errors and warnings (e.g. E4,W) -call pymode#default("g:pymode_lint_ignore", "") +call pymode#default("g:pymode_lint_ignore", []) " Select errors and warnings (e.g. E4,W) -call pymode#default("g:pymode_lint_select", "") +call pymode#default("g:pymode_lint_select", []) " Auto open cwindow if any errors has been finded call pymode#default("g:pymode_lint_cwindow", 1) @@ -135,6 +152,7 @@ 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}) @@ -167,96 +185,99 @@ call pymode#default('g:pymode_breakpoint_cmd', '') " ROPE (refactoring, codeassist) {{{ " " Rope support -call pymode#default('g:pymode_rope', 1) +call pymode#default('g:pymode_rope', 0) +call pymode#default('g:pymode_rope_prefix', '') " System plugin variable -call pymode#default('g:pymode_rope_current', '') +if g:pymode_rope + call pymode#default('g:pymode_rope_current', '') -" Configurable rope project root -call pymode#default('g:pymode_rope_project_root', '') + " Configurable rope project root + call pymode#default('g:pymode_rope_project_root', '') -" Configurable rope project folder (always relative to project root) -call pymode#default('g:pymode_rope_ropefolder', '.ropeproject') + " Configurable rope project folder (always relative to project root) + call pymode#default('g:pymode_rope_ropefolder', '.ropeproject') -" If project hasnt been finded in current working directory, look at parents directory -call pymode#default('g:pymode_rope_lookup_project', 0) + " If project hasnt been finded in current working directory, look at parents directory + call pymode#default('g:pymode_rope_lookup_project', 0) -" Enable Rope completion -call pymode#default('g:pymode_rope_completion', 1) + " Enable Rope completion + call pymode#default('g:pymode_rope_completion', 1) -" Complete keywords from not imported modules (could make completion slower) -" Enable autoimport used modules -call pymode#default('g:pymode_rope_autoimport', 0) + " Complete keywords from not imported modules (could make completion slower) + " Enable autoimport used modules + call pymode#default('g:pymode_rope_autoimport', 0) -" Offer to import object after complete (if that not be imported before) -call pymode#default('g:pymode_rope_autoimport_import_after_complete', 0) + " Offer to import object after complete (if that not be imported before) + call pymode#default('g:pymode_rope_autoimport_import_after_complete', 0) -" Autoimported modules -call pymode#default('g:pymode_rope_autoimport_modules', ['os', 'shutil', 'datetime']) + " Autoimported modules + call pymode#default('g:pymode_rope_autoimport_modules', ['os', 'shutil', 'datetime']) -" Bind keys to autoimport module for object under cursor -call pymode#default('g:pymode_rope_autoimport_bind', 'ra') + " Bind keys to autoimport module for object under cursor + call pymode#default('g:pymode_rope_autoimport_bind', g:pymode_rope_prefix . 'ra') -" Automatic completion on dot -call pymode#default('g:pymode_rope_complete_on_dot', 1) + " Automatic completion on dot + call pymode#default('g:pymode_rope_complete_on_dot', 1) -" Bind keys for autocomplete (leave empty for disable) -call pymode#default('g:pymode_rope_completion_bind', '') + " Bind keys for autocomplete (leave empty for disable) + call pymode#default('g:pymode_rope_completion_bind', '') -" Bind keys for goto definition (leave empty for disable) -call pymode#default('g:pymode_rope_goto_definition_bind', 'g') + " Bind keys for goto definition (leave empty for disable) + call pymode#default('g:pymode_rope_goto_definition_bind', g:pymode_rope_prefix . 'g') -" set command for open definition (e, new, vnew) -call pymode#default('g:pymode_rope_goto_definition_cmd', 'new') + " set command for open definition (e, new, vnew) + call pymode#default('g:pymode_rope_goto_definition_cmd', 'new') -" Bind keys for show documentation (leave empty for disable) -call pymode#default('g:pymode_rope_show_doc_bind', 'd') + " Bind keys for show documentation (leave empty for disable) + call pymode#default('g:pymode_rope_show_doc_bind', g:pymode_rope_prefix . 'd') -" Bind keys for find occurencies (leave empty for disable) -call pymode#default('g:pymode_rope_find_it_bind', 'f') + " Bind keys for find occurencies (leave empty for disable) + call pymode#default('g:pymode_rope_find_it_bind', g:pymode_rope_prefix . 'f') -" Bind keys for organize imports (leave empty for disable) -call pymode#default('g:pymode_rope_organize_imports_bind', 'ro') + " Bind keys for organize imports (leave empty for disable) + call pymode#default('g:pymode_rope_organize_imports_bind', g:pymode_rope_prefix . 'ro') -" Bind keys for rename variable/method/class in the project (leave empty for disable) -call pymode#default('g:pymode_rope_rename_bind', 'rr') + " Bind keys for rename variable/method/class in the project (leave empty for disable) + call pymode#default('g:pymode_rope_rename_bind', g:pymode_rope_prefix . 'rr') -" Bind keys for rename module -call pymode#default('g:pymode_rope_rename_module_bind', 'r1r') + " Bind keys for rename module + call pymode#default('g:pymode_rope_rename_module_bind', g:pymode_rope_prefix . 'r1r') -" Bind keys for convert module to package -call pymode#default('g:pymode_rope_module_to_package_bind', 'r1p') + " Bind keys for convert module to package + call pymode#default('g:pymode_rope_module_to_package_bind', g:pymode_rope_prefix . 'r1p') -" Creates a new function or method (depending on the context) from the selected lines -call pymode#default('g:pymode_rope_extract_method_bind', 'rm') + " Creates a new function or method (depending on the context) from the selected lines + call pymode#default('g:pymode_rope_extract_method_bind', g:pymode_rope_prefix . 'rm') -" Creates a variable from the selected lines -call pymode#default('g:pymode_rope_extract_variable_bind', 'rl') + " Creates a variable from the selected lines + call pymode#default('g:pymode_rope_extract_variable_bind', g:pymode_rope_prefix . 'rl') -" Inline refactoring -call pymode#default('g:pymode_rope_inline_bind', 'ri') + " Inline refactoring + call pymode#default('g:pymode_rope_inline_bind', g:pymode_rope_prefix . 'ri') -" Move refactoring -call pymode#default('g:pymode_rope_move_bind', 'rv') + " Move refactoring + call pymode#default('g:pymode_rope_move_bind', g:pymode_rope_prefix . 'rv') -" Generate function -call pymode#default('g:pymode_rope_generate_function_bind', 'rnf') + " Generate function + call pymode#default('g:pymode_rope_generate_function_bind', g:pymode_rope_prefix . 'rnf') -" Generate class -call pymode#default('g:pymode_rope_generate_class_bind', 'rnc') + " Generate class + call pymode#default('g:pymode_rope_generate_class_bind', g:pymode_rope_prefix . 'rnc') -" Generate package -call pymode#default('g:pymode_rope_generate_package_bind', 'rnp') + " Generate package + call pymode#default('g:pymode_rope_generate_package_bind', g:pymode_rope_prefix . 'rnp') -" Change signature -call pymode#default('g:pymode_rope_change_signature_bind', 'rs') + " Change signature + call pymode#default('g:pymode_rope_change_signature_bind', g:pymode_rope_prefix . 'rs') -" Tries to find the places in which a function can be used and changes the -" code to call it instead -call pymode#default('g:pymode_rope_use_function_bind', 'ru') + " Tries to find the places in which a function can be used and changes the + " code to call it instead + call pymode#default('g:pymode_rope_use_function_bind', g:pymode_rope_prefix . 'ru') -" Regenerate project cache on every save -call pymode#default('g:pymode_rope_regenerate_on_write', 1) + " Regenerate project cache on every save + call pymode#default('g:pymode_rope_regenerate_on_write', 1) +endif " }}} @@ -268,31 +289,16 @@ if &compatible endif filetype plugin on -" Disable python-related functionality -" let g:pymode_python = 'disable' -" let g:pymode_python = 'python3' - " UltiSnips Fixes if !len(g:pymode_python) - if exists('g:_uspy') && g:_uspy == ':py' - let g:pymode_python = 'python' - elseif exists('g:_uspy') && g:_uspy == ':py3' - let g:pymode_python = 'python3' - elseif has("python") - let g:pymode_python = 'python' - elseif has("python3") + if (exists('g:_uspy') && g:_uspy == ':py3') || has("python3") let g:pymode_python = 'python3' else let g:pymode_python = 'disable' endif endif -if g:pymode_python == 'python' - - command! -nargs=1 PymodePython python - let g:UltiSnipsUsePythonVersion = 2 - -elseif g:pymode_python == 'python3' +if g:pymode_python == 'python3' command! -nargs=1 PymodePython python3 let g:UltiSnipsUsePythonVersion = 3 @@ -310,7 +316,6 @@ else endif - command! PymodeVersion echomsg "Pymode version: " . g:pymode_version . " interpreter: " . g:pymode_python . " lint: " . g:pymode_lint . " rope: " . g:pymode_rope augroup pymode diff --git a/pymode/__init__.py b/pymode/__init__.py index d5e63ba3..906d7059 100644 --- a/pymode/__init__.py +++ b/pymode/__init__.py @@ -1,23 +1,37 @@ -""" Pymode support functions. """ - -from __future__ import absolute_import +"""Pymode support functions.""" import sys +from importlib.machinery import PathFinder as _PathFinder + import vim # noqa +if not hasattr(vim, 'find_module'): + try: + vim.find_module = _PathFinder.find_module # deprecated + except AttributeError: + def _find_module(package_name): + spec = _PathFinder.find_spec(package_name) + return spec.loader if spec else None + vim.find_module = _find_module + def auto(): - """ Fix PEP8 erorrs in current buffer. """ + """Fix PEP8 erorrs in current buffer. + + pymode: uses it in command PymodeLintAuto with pymode#lint#auto() + + """ from .autopep8 import fix_file class Options(object): - aggressive = 2 + aggressive = 1 diff = False experimental = True ignore = vim.eval('g:pymode_lint_ignore') in_place = True indent_size = int(vim.eval('&tabstop')) line_range = None + hang_closing = False max_line_length = int(vim.eval('g:pymode_options_max_line_length')) pep8_passes = 100 recursive = False @@ -28,8 +42,8 @@ class Options(object): def get_documentation(): - """ Search documentation and append to current buffer. """ - from ._compat import StringIO + """Search documentation and append to current buffer.""" + from io import StringIO sys.stdout, _ = StringIO(), sys.stdout help(vim.eval('a:word')) diff --git a/pymode/_compat.py b/pymode/_compat.py deleted file mode 100644 index d859f152..00000000 --- a/pymode/_compat.py +++ /dev/null @@ -1,98 +0,0 @@ -""" Compatibility. - - Some py2/py3 compatibility support based on a stripped down - version of six so we don't have to depend on a specific version - of it. - - :copyright: (c) 2014 by Armin Ronacher. - :license: BSD -""" -import sys - -PY2 = sys.version_info[0] == 2 -_identity = lambda x: x - - -if not PY2: - text_type = str - string_types = (str,) - integer_types = (int, ) - - iterkeys = lambda d: iter(d.keys()) - itervalues = lambda d: iter(d.values()) - iteritems = lambda d: iter(d.items()) - - from io import StringIO - from queue import Queue # noqa - - def reraise(tp, value, tb=None): - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - - implements_to_string = _identity - -else: - text_type = unicode - string_types = (str, unicode) - integer_types = (int, long) - - iterkeys = lambda d: d.iterkeys() - itervalues = lambda d: d.itervalues() - iteritems = lambda d: d.iteritems() - - from cStringIO import StringIO - from Queue import Queue - - exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') - - def implements_to_string(cls): - cls.__unicode__ = cls.__str__ - cls.__str__ = lambda x: x.__unicode__().encode('utf-8') - return cls - - -def with_metaclass(meta, *bases): - # This requires a bit of explanation: the basic idea is to make a - # dummy metaclass for one level of class instantiation that replaces - # itself with the actual metaclass. Because of internal type checks - # we also need to make sure that we downgrade the custom metaclass - # for one level to something closer to type (that's why __call__ and - # __init__ comes back from type etc.). - # - # This has the advantage over six.with_metaclass in that it does not - # introduce dummy classes into the final MRO. - class metaclass(meta): - __call__ = type.__call__ - __init__ = type.__init__ - def __new__(cls, name, this_bases, d): - if this_bases is None: - return type.__new__(cls, name, (), d) - return meta(name, bases, d) - return metaclass('temporary_class', None, {}) - - -# Certain versions of pypy have a bug where clearing the exception stack -# breaks the __exit__ function in a very peculiar way. This is currently -# true for pypy 2.2.1 for instance. The second level of exception blocks -# is necessary because pypy seems to forget to check if an exception -# happend until the next bytecode instruction? -BROKEN_PYPY_CTXMGR_EXIT = False -if hasattr(sys, 'pypy_version_info'): - class _Mgr(object): - def __enter__(self): - return self - def __exit__(self, *args): - sys.exc_clear() - try: - try: - with _Mgr(): - raise AssertionError() - except: - raise - except TypeError: - BROKEN_PYPY_CTXMGR_EXIT = True - except AssertionError: - pass - -# pylama:skip=1 diff --git a/pymode/async.py b/pymode/async.py index dd314d76..d211ac4a 100644 --- a/pymode/async.py +++ b/pymode/async.py @@ -1,6 +1,6 @@ """ Python-mode async support. """ -from ._compat import Queue +from queue import Queue # noqa RESULTS = Queue() diff --git a/pymode/autopep8.py b/pymode/autopep8.py deleted file mode 100644 index 62e5832f..00000000 --- a/pymode/autopep8.py +++ /dev/null @@ -1,3885 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2010-2011 Hideo Hattori -# Copyright (C) 2011-2013 Hideo Hattori, Steven Myint -# Copyright (C) 2013-2016 Hideo Hattori, Steven Myint, Bill Wendling -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Automatically formats Python code to conform to the PEP 8 style guide. - -Fixes that only need be done once can be added by adding a function of the form -"fix_(source)" to this module. They should return the fixed source code. -These fixes are picked up by apply_global_fixes(). - -Fixes that depend on pycodestyle should be added as methods to FixPEP8. See the -class documentation for more information. - -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import codecs -import collections -import copy -import difflib -import fnmatch -import inspect -import io -import keyword -import locale -import os -import re -import signal -import sys -import textwrap -import token -import tokenize - -import pycodestyle - - -try: - unicode -except NameError: - unicode = str - - -__version__ = '1.3.2' - - -CR = '\r' -LF = '\n' -CRLF = '\r\n' - - -PYTHON_SHEBANG_REGEX = re.compile(r'^#!.*\bpython[23]?\b\s*$') -LAMBDA_REGEX = re.compile(r'([\w.]+)\s=\slambda\s*([\(\)\w,\s.]*):') -COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+([^][)(}{]+)\s+(in|is)\s') -BARE_EXCEPT_REGEX = re.compile(r'except\s*:') -STARTSWITH_DEF_REGEX = re.compile(r'^(async\s+def|def)\s.*\):') - - -# For generating line shortening candidates. -SHORTEN_OPERATOR_GROUPS = frozenset([ - frozenset([',']), - frozenset(['%']), - frozenset([',', '(', '[', '{']), - frozenset(['%', '(', '[', '{']), - frozenset([',', '(', '[', '{', '%', '+', '-', '*', '/', '//']), - frozenset(['%', '+', '-', '*', '/', '//']), -]) - - -DEFAULT_IGNORE = 'E24,W503' -DEFAULT_INDENT_SIZE = 4 - - -# W602 is handled separately due to the need to avoid "with_traceback". -CODE_TO_2TO3 = { - 'E231': ['ws_comma'], - 'E721': ['idioms'], - 'W601': ['has_key'], - 'W603': ['ne'], - 'W604': ['repr'], - 'W690': ['apply', - 'except', - 'exitfunc', - 'numliterals', - 'operator', - 'paren', - 'reduce', - 'renames', - 'standarderror', - 'sys_exc', - 'throw', - 'tuple_params', - 'xreadlines']} - - -if sys.platform == 'win32': # pragma: no cover - DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8') -else: - DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or - os.path.expanduser('~/.config'), 'pep8') -PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8') - - -MAX_PYTHON_FILE_DETECTION_BYTES = 1024 - - -def open_with_encoding(filename, - encoding=None, mode='r', limit_byte_check=-1): - """Return opened file with a specific encoding.""" - if not encoding: - encoding = detect_encoding(filename, limit_byte_check=limit_byte_check) - - return io.open(filename, mode=mode, encoding=encoding, - newline='') # Preserve line endings - - -def detect_encoding(filename, limit_byte_check=-1): - """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] - - with open_with_encoding(filename, encoding) as test_file: - test_file.read(limit_byte_check) - - return encoding - except (LookupError, SyntaxError, UnicodeDecodeError): - return 'latin-1' - - -def readlines_from_file(filename): - """Return contents of file.""" - with open_with_encoding(filename) as input_file: - return input_file.readlines() - - -def extended_blank_lines(logical_line, - blank_lines, - blank_before, - indent_level, - previous_logical): - """Check for missing blank lines after class declaration.""" - if previous_logical.startswith('def '): - if blank_lines and pycodestyle.DOCSTRING_REGEX.match(logical_line): - yield (0, 'E303 too many blank lines ({0})'.format(blank_lines)) - elif pycodestyle.DOCSTRING_REGEX.match(previous_logical): - # Missing blank line between class docstring and method declaration. - if ( - indent_level and - not blank_lines and - not blank_before and - logical_line.startswith(('def ')) and - '(self' in logical_line - ): - yield (0, 'E301 expected 1 blank line, found 0') - - -pycodestyle.register_check(extended_blank_lines) - - -def continued_indentation(logical_line, tokens, indent_level, indent_char, - noqa): - """Override pycodestyle's function to provide indentation information.""" - first_row = tokens[0][2][0] - nrows = 1 + tokens[-1][2][0] - first_row - if noqa or nrows == 1: - return - - # indent_next tells us whether the next block is indented. Assuming - # that it is indented by 4 spaces, then we should not allow 4-space - # indents on the final continuation line. In turn, some other - # indents are allowed to have an extra 4 spaces. - indent_next = logical_line.endswith(':') - - row = depth = 0 - valid_hangs = ( - (DEFAULT_INDENT_SIZE,) - if indent_char != '\t' else (DEFAULT_INDENT_SIZE, - 2 * DEFAULT_INDENT_SIZE) - ) - - # Remember how many brackets were opened on each line. - parens = [0] * nrows - - # Relative indents of physical lines. - rel_indent = [0] * nrows - - # For each depth, collect a list of opening rows. - open_rows = [[0]] - # For each depth, memorize the hanging indentation. - hangs = [None] - - # Visual indents. - indent_chances = {} - last_indent = tokens[0][2] - indent = [last_indent[1]] - - last_token_multiline = None - line = None - last_line = '' - last_line_begins_with_multiline = False - for token_type, text, start, end, line in tokens: - - newline = row < start[0] - first_row - if newline: - row = start[0] - first_row - newline = (not last_token_multiline and - token_type not in (tokenize.NL, tokenize.NEWLINE)) - last_line_begins_with_multiline = last_token_multiline - - if newline: - # This is the beginning of a continuation line. - last_indent = start - - # Record the initial indent. - rel_indent[row] = pycodestyle.expand_indent(line) - indent_level - - # Identify closing bracket. - close_bracket = (token_type == tokenize.OP and text in ']})') - - # Is the indent relative to an opening bracket line? - for open_row in reversed(open_rows[depth]): - hang = rel_indent[row] - rel_indent[open_row] - hanging_indent = hang in valid_hangs - if hanging_indent: - break - if hangs[depth]: - hanging_indent = (hang == hangs[depth]) - - visual_indent = (not close_bracket and hang > 0 and - indent_chances.get(start[1])) - - if close_bracket and indent[depth]: - # Closing bracket for visual indent. - if start[1] != indent[depth]: - yield (start, 'E124 {0}'.format(indent[depth])) - elif close_bracket and not hang: - pass - elif indent[depth] and start[1] < indent[depth]: - # Visual indent is broken. - yield (start, 'E128 {0}'.format(indent[depth])) - elif (hanging_indent or - (indent_next and - rel_indent[row] == 2 * DEFAULT_INDENT_SIZE)): - # Hanging indent is verified. - if close_bracket: - yield (start, 'E123 {0}'.format(indent_level + - rel_indent[open_row])) - hangs[depth] = hang - elif visual_indent is True: - # Visual indent is verified. - indent[depth] = start[1] - elif visual_indent in (text, unicode): - # Ignore token lined up with matching one from a previous line. - pass - else: - one_indented = (indent_level + rel_indent[open_row] + - DEFAULT_INDENT_SIZE) - # Indent is broken. - if hang <= 0: - error = ('E122', one_indented) - elif indent[depth]: - error = ('E127', indent[depth]) - elif not close_bracket and hangs[depth]: - error = ('E131', one_indented) - elif hang > DEFAULT_INDENT_SIZE: - error = ('E126', one_indented) - else: - hangs[depth] = hang - error = ('E121', one_indented) - - yield (start, '{0} {1}'.format(*error)) - - # Look for visual indenting. - if ( - parens[row] and - token_type not in (tokenize.NL, tokenize.COMMENT) and - not indent[depth] - ): - indent[depth] = start[1] - indent_chances[start[1]] = True - # Deal with implicit string concatenation. - elif (token_type in (tokenize.STRING, tokenize.COMMENT) or - text in ('u', 'ur', 'b', 'br')): - indent_chances[start[1]] = unicode - # Special case for the "if" statement because len("if (") is equal to - # 4. - elif not indent_chances and not row and not depth and text == 'if': - indent_chances[end[1] + 1] = True - elif text == ':' and line[end[1]:].isspace(): - open_rows[depth].append(row) - - # Keep track of bracket depth. - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - hangs.append(None) - if len(open_rows) == depth: - open_rows.append([]) - open_rows[depth].append(row) - parens[row] += 1 - elif text in ')]}' and depth > 0: - # Parent indents should not be more than this one. - prev_indent = indent.pop() or last_indent[1] - hangs.pop() - for d in range(depth): - if indent[d] > prev_indent: - indent[d] = 0 - for ind in list(indent_chances): - if ind >= prev_indent: - del indent_chances[ind] - del open_rows[depth + 1:] - depth -= 1 - if depth: - indent_chances[indent[depth]] = True - for idx in range(row, -1, -1): - if parens[idx]: - parens[idx] -= 1 - break - assert len(indent) == depth + 1 - if ( - start[1] not in indent_chances and - # This is for purposes of speeding up E121 (GitHub #90). - not last_line.rstrip().endswith(',') - ): - # Allow to line up tokens. - indent_chances[start[1]] = text - - last_token_multiline = (start[0] != end[0]) - if last_token_multiline: - rel_indent[end[0] - first_row] = rel_indent[row] - - last_line = line - - if ( - indent_next and - not last_line_begins_with_multiline and - pycodestyle.expand_indent(line) == indent_level + DEFAULT_INDENT_SIZE - ): - pos = (start[0], indent[0] + 4) - desired_indent = indent_level + 2 * DEFAULT_INDENT_SIZE - if visual_indent: - yield (pos, 'E129 {0}'.format(desired_indent)) - else: - yield (pos, 'E125 {0}'.format(desired_indent)) - - -del pycodestyle._checks['logical_line'][pycodestyle.continued_indentation] -pycodestyle.register_check(continued_indentation) - - -class FixPEP8(object): - - """Fix invalid code. - - Fixer methods are prefixed "fix_". The _fix_source() method looks for these - automatically. - - The fixer method can take either one or two arguments (in addition to - self). The first argument is "result", which is the error information from - pycodestyle. 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 pycodestyle 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 pycodestyle error - information. - - [fixed method list] - - e111,e114,e115,e116 - - 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,e304,e306 - - e401 - - e502 - - e701,e702,e703,e704 - - e711,e712,e713,e714 - - e722 - - e731 - - w291 - - w503 - - """ - - def __init__(self, filename, - options, - contents=None, - long_line_ignore_cache=None): - self.filename = filename - if contents is None: - self.source = readlines_from_file(filename) - else: - sio = io.StringIO(contents) - self.source = sio.readlines() - self.options = options - self.indent_word = _get_indentword(''.join(self.source)) - - self.long_line_ignore_cache = ( - set() if long_line_ignore_cache is None - else long_line_ignore_cache) - - # Many fixers are the same even though pycodestyle categorizes them - # differently. - self.fix_e115 = self.fix_e112 - self.fix_e116 = self.fix_e113 - self.fix_e121 = self._fix_reindent - self.fix_e122 = self._fix_reindent - self.fix_e123 = self._fix_reindent - self.fix_e124 = self._fix_reindent - self.fix_e126 = self._fix_reindent - self.fix_e127 = self._fix_reindent - self.fix_e128 = self._fix_reindent - self.fix_e129 = self._fix_reindent - self.fix_e202 = self.fix_e201 - self.fix_e203 = self.fix_e201 - self.fix_e211 = self.fix_e201 - self.fix_e221 = self.fix_e271 - self.fix_e222 = self.fix_e271 - self.fix_e223 = self.fix_e271 - self.fix_e226 = self.fix_e225 - self.fix_e227 = self.fix_e225 - self.fix_e228 = self.fix_e225 - self.fix_e241 = self.fix_e271 - self.fix_e242 = self.fix_e224 - self.fix_e261 = self.fix_e262 - self.fix_e272 = self.fix_e271 - self.fix_e273 = self.fix_e271 - self.fix_e274 = self.fix_e271 - self.fix_e306 = self.fix_e301 - self.fix_e501 = ( - self.fix_long_line_logically if - options and (options.aggressive >= 2 or options.experimental) else - self.fix_long_line_physically) - self.fix_e703 = self.fix_e702 - self.fix_w293 = self.fix_w291 - - def _fix_source(self, results): - try: - (logical_start, logical_end) = _find_logical(self.source) - logical_support = True - except (SyntaxError, tokenize.TokenError): # pragma: no cover - logical_support = False - - completed_lines = set() - for result in sorted(results, key=_priority_key): - if result['line'] in completed_lines: - continue - - fixed_methodname = 'fix_' + result['id'].lower() - if hasattr(self, fixed_methodname): - fix = getattr(self, fixed_methodname) - - line_index = result['line'] - 1 - original_line = self.source[line_index] - - is_logical_fix = len(_get_parameters(fix)) > 2 - if is_logical_fix: - logical = None - if logical_support: - logical = _get_logical(self.source, - result, - logical_start, - logical_end) - if logical and set(range( - logical[0][0] + 1, - logical[1][0] + 1)).intersection( - completed_lines): - continue - - modified_lines = fix(result, logical) - else: - modified_lines = fix(result) - - if modified_lines is None: - # Force logical fixes to report what they modified. - assert not is_logical_fix - - if self.source[line_index] == original_line: - modified_lines = [] - - if modified_lines: - completed_lines.update(modified_lines) - elif modified_lines == []: # Empty list means no fix - if self.options.verbose >= 2: - print( - '---> Not fixing {error} on line {line}'.format( - error=result['id'], line=result['line']), - file=sys.stderr) - else: # We assume one-line fix when None. - completed_lines.add(result['line']) - else: - if self.options.verbose >= 3: - print( - "---> '{0}' is not defined.".format(fixed_methodname), - file=sys.stderr) - - info = result['info'].strip() - print('---> {0}:{1}:{2}:{3}'.format(self.filename, - result['line'], - result['column'], - info), - file=sys.stderr) - - def fix(self): - """Return a version of the source code with PEP 8 violations fixed.""" - pep8_options = { - 'ignore': self.options.ignore, - 'select': self.options.select, - 'max_line_length': self.options.max_line_length, - } - results = _execute_pep8(pep8_options, self.source) - - if self.options.verbose: - progress = {} - for r in results: - if r['id'] not in progress: - progress[r['id']] = set() - progress[r['id']].add(r['line']) - print('---> {n} issue(s) to fix {progress}'.format( - n=len(results), progress=progress), file=sys.stderr) - - if self.options.line_range: - start, end = self.options.line_range - results = [r for r in results - if start <= r['line'] <= end] - - self._fix_source(filter_results(source=''.join(self.source), - results=results, - aggressive=self.options.aggressive)) - - if self.options.line_range: - # If number of lines has changed then change line_range. - count = sum(sline.count('\n') - for sline in self.source[start - 1:end]) - self.options.line_range[1] = start + count - 1 - - return ''.join(self.source) - - def _fix_reindent(self, result): - """Fix a badly indented line. - - This is done by adding or removing from its initial indent only. - - """ - num_indent_spaces = int(result['info'].split()[1]) - line_index = result['line'] - 1 - target = self.source[line_index] - - self.source[line_index] = ' ' * num_indent_spaces + target.lstrip() - - def fix_e112(self, result): - """Fix under-indented comments.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - if not target.lstrip().startswith('#'): - # Don't screw with invalid syntax. - return [] - - self.source[line_index] = self.indent_word + target - - def fix_e113(self, result): - """Fix over-indented comments.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - indent = _get_indentation(target) - stripped = target.lstrip() - - if not stripped.startswith('#'): - # Don't screw with invalid syntax. - return [] - - self.source[line_index] = indent[1:] + stripped - - def fix_e125(self, result): - """Fix indentation undistinguish from the next logical line.""" - num_indent_spaces = int(result['info'].split()[1]) - line_index = result['line'] - 1 - target = self.source[line_index] - - spaces_to_add = num_indent_spaces - len(_get_indentation(target)) - indent = len(_get_indentation(target)) - modified_lines = [] - - while len(_get_indentation(self.source[line_index])) >= indent: - self.source[line_index] = (' ' * spaces_to_add + - self.source[line_index]) - modified_lines.append(1 + line_index) # Line indexed at 1. - line_index -= 1 - - return modified_lines - - def fix_e131(self, result): - """Fix indentation undistinguish from the next logical line.""" - num_indent_spaces = int(result['info'].split()[1]) - line_index = result['line'] - 1 - target = self.source[line_index] - - spaces_to_add = num_indent_spaces - len(_get_indentation(target)) - - if spaces_to_add >= 0: - self.source[line_index] = (' ' * spaces_to_add + - self.source[line_index]) - else: - offset = abs(spaces_to_add) - self.source[line_index] = self.source[line_index][offset:] - - def fix_e201(self, result): - """Remove extraneous whitespace.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - fixed = fix_whitespace(target, - offset=offset, - replacement='') - - self.source[line_index] = fixed - - def fix_e224(self, result): - """Remove extraneous whitespace around operator.""" - target = self.source[result['line'] - 1] - offset = result['column'] - 1 - fixed = target[:offset] + target[offset:].replace('\t', ' ') - self.source[result['line'] - 1] = fixed - - def fix_e225(self, result): - """Fix missing whitespace around operator.""" - target = self.source[result['line'] - 1] - offset = result['column'] - 1 - fixed = target[:offset] + ' ' + target[offset:] - - # Only proceed if non-whitespace characters match. - # And make sure we don't break the indentation. - if ( - fixed.replace(' ', '') == target.replace(' ', '') and - _get_indentation(fixed) == _get_indentation(target) - ): - self.source[result['line'] - 1] = fixed - error_code = result.get('id', 0) - try: - ts = generate_tokens(fixed) - except tokenize.TokenError: - return - if not check_syntax(fixed.lstrip()): - return - errors = list( - pycodestyle.missing_whitespace_around_operator(fixed, ts)) - for e in reversed(errors): - if error_code != e[1].split()[0]: - continue - offset = e[0][1] - fixed = fixed[:offset] + ' ' + fixed[offset:] - self.source[result['line'] - 1] = fixed - else: - return [] - - def fix_e231(self, result): - """Add missing whitespace.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - fixed = target[:offset].rstrip() + ' ' + target[offset:].lstrip() - 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 pycodestyle sometimes reports columns that - # goes past the end of the physical line. This happens in cases like, - # foo(bar\n=None) - c = min(result['column'] - 1, - len(target) - 1) - - if target[c].strip(): - fixed = target - else: - fixed = target[:c].rstrip() + target[c:].lstrip() - - # There could be an escaped newline - # - # def foo(a=\ - # 1) - if fixed.endswith(('=\\\n', '=\\\r\n', '=\\\r')): - self.source[line_index] = fixed.rstrip('\n\r \t\\') - self.source[line_index + 1] = self.source[line_index + 1].lstrip() - return [line_index + 1, line_index + 2] # Line indexed at 1 - - self.source[result['line'] - 1] = fixed - - def fix_e262(self, result): - """Fix spacing after comment hash.""" - target = self.source[result['line'] - 1] - offset = result['column'] - - code = target[:offset].rstrip(' \t#') - comment = target[offset:].lstrip(' \t#') - - fixed = code + (' # ' + comment if comment.strip() else '\n') - - self.source[result['line'] - 1] = fixed - - def fix_e271(self, result): - """Fix extraneous whitespace around keywords.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - fixed = fix_whitespace(target, - offset=offset, - replacement=' ') - - if fixed == target: - return [] - else: - self.source[line_index] = fixed - - def fix_e301(self, result): - """Add missing blank line.""" - cr = '\n' - self.source[result['line'] - 1] = cr + self.source[result['line'] - 1] - - def fix_e302(self, result): - """Add missing 2 blank lines.""" - add_linenum = 2 - int(result['info'].split()[-1]) - cr = '\n' * add_linenum - self.source[result['line'] - 1] = cr + self.source[result['line'] - 1] - - def fix_e303(self, result): - """Remove extra blank lines.""" - delete_linenum = int(result['info'].split('(')[1].split(')')[0]) - 2 - delete_linenum = max(1, delete_linenum) - - # We need to count because pycodestyle 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_e305(self, result): - """Add missing 2 blank lines after end of function or class.""" - cr = '\n' - # check comment line - offset = result['line'] - 2 - while True: - if offset < 0: - break - line = self.source[offset].lstrip() - if not line: - break - if line[0] != '#': - break - offset -= 1 - offset += 1 - self.source[offset] = cr + self.source[offset] - - def fix_e401(self, result): - """Put imports on separate lines.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - if not target.lstrip().startswith('import'): - return [] - - indentation = re.split(pattern=r'\bimport\b', - string=target, maxsplit=1)[0] - fixed = (target[:offset].rstrip('\t ,') + '\n' + - indentation + 'import ' + target[offset:].lstrip('\t ,')) - self.source[line_index] = fixed - - def fix_long_line_logically(self, result, logical): - """Try to make lines fit within --max-line-length characters.""" - if ( - not logical or - len(logical[2]) == 1 or - self.source[result['line'] - 1].lstrip().startswith('#') - ): - return self.fix_long_line_physically(result) - - start_line_index = logical[0][0] - end_line_index = logical[1][0] - logical_lines = logical[2] - - previous_line = get_item(self.source, start_line_index - 1, default='') - next_line = get_item(self.source, end_line_index + 1, default='') - - single_line = join_logical_line(''.join(logical_lines)) - - try: - fixed = self.fix_long_line( - target=single_line, - previous_line=previous_line, - next_line=next_line, - original=''.join(logical_lines)) - except (SyntaxError, tokenize.TokenError): - return self.fix_long_line_physically(result) - - if fixed: - for line_index in range(start_line_index, end_line_index + 1): - self.source[line_index] = '' - self.source[start_line_index] = fixed - return range(start_line_index + 1, end_line_index + 1) - - return [] - - def fix_long_line_physically(self, result): - """Try to make lines fit within --max-line-length characters.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - previous_line = get_item(self.source, line_index - 1, default='') - next_line = get_item(self.source, line_index + 1, default='') - - try: - fixed = self.fix_long_line( - target=target, - previous_line=previous_line, - next_line=next_line, - original=target) - except (SyntaxError, tokenize.TokenError): - return [] - - if fixed: - self.source[line_index] = fixed - return [line_index + 1] - - return [] - - def fix_long_line(self, target, previous_line, - next_line, original): - cache_entry = (target, previous_line, next_line) - if cache_entry in self.long_line_ignore_cache: - return [] - - if target.lstrip().startswith('#'): - if self.options.aggressive: - # Wrap commented lines. - return shorten_comment( - line=target, - max_line_length=self.options.max_line_length, - last_comment=not next_line.lstrip().startswith('#')) - else: - return [] - - fixed = get_fixed_long_line( - target=target, - previous_line=previous_line, - original=original, - indent_word=self.indent_word, - max_line_length=self.options.max_line_length, - aggressive=self.options.aggressive, - experimental=self.options.experimental, - verbose=self.options.verbose) - - if fixed and not code_almost_equal(original, fixed): - return fixed - - self.long_line_ignore_cache.add(cache_entry) - return None - - def fix_e502(self, result): - """Remove extraneous escape of newline.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - self.source[line_index] = target.rstrip('\n\r \t\\') + '\n' - - def fix_e701(self, result): - """Put colon-separated compound statement on separate lines.""" - line_index = result['line'] - 1 - target = self.source[line_index] - c = result['column'] - - fixed_source = (target[:c] + '\n' + - _get_indentation(target) + self.indent_word + - target[c:].lstrip('\n\r \t\\')) - self.source[result['line'] - 1] = fixed_source - return [result['line'], result['line'] + 1] - - def fix_e702(self, result, logical): - """Put semicolon-separated compound statement on separate lines.""" - if not logical: - return [] # pragma: no cover - logical_lines = logical[2] - - # Avoid applying this when indented. - # https://docs.python.org/reference/compound_stmts.html - for line in logical_lines: - if ':' in line: - return [] - - line_index = result['line'] - 1 - target = self.source[line_index] - - if target.rstrip().endswith('\\'): - # Normalize '1; \\\n2' into '1; 2'. - self.source[line_index] = target.rstrip('\n \r\t\\') - self.source[line_index + 1] = self.source[line_index + 1].lstrip() - return [line_index + 1, line_index + 2] - - if target.rstrip().endswith(';'): - self.source[line_index] = target.rstrip('\n \r\t;') + '\n' - return [line_index + 1] - - offset = result['column'] - 1 - first = target[:offset].rstrip(';').rstrip() - second = (_get_indentation(logical_lines[0]) + - target[offset:].lstrip(';').lstrip()) - - # Find inline comment. - inline_comment = None - if target[offset:].lstrip(';').lstrip()[:2] == '# ': - inline_comment = target[offset:].lstrip(';') - - if inline_comment: - self.source[line_index] = first + inline_comment - else: - self.source[line_index] = first + '\n' + second - return [line_index + 1] - - def fix_e704(self, result): - """Fix multiple statements on one line def""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - match = STARTSWITH_DEF_REGEX.match(target) - if match: - self.source[line_index] = '{0}\n{1}{2}'.format( - match.group(0), - _get_indentation(target) + self.indent_word, - target[match.end(0):].lstrip()) - - def fix_e711(self, result): - """Fix comparison with None.""" - (line_index, offset, target) = get_index_offset_contents(result, - self.source) - - right_offset = offset + 2 - if right_offset >= len(target): - return [] - - left = target[:offset].rstrip() - center = target[offset:right_offset] - right = target[right_offset:].lstrip() - - if not right.startswith('None'): - return [] - - if center.strip() == '==': - new_center = 'is' - elif center.strip() == '!=': - new_center = 'is not' - else: - return [] - - self.source[line_index] = ' '.join([left, new_center, right]) - - def fix_e712(self, result): - """Fix (trivial case of) comparison with boolean.""" - (line_index, offset, target) = get_index_offset_contents(result, - self.source) - - # Handle very easy "not" special cases. - if re.match(r'^\s*if [\w.]+ == False:$', target): - self.source[line_index] = re.sub(r'if ([\w.]+) == False:', - r'if not \1:', target, count=1) - elif re.match(r'^\s*if [\w.]+ != True:$', target): - self.source[line_index] = re.sub(r'if ([\w.]+) != True:', - r'if not \1:', target, count=1) - else: - right_offset = offset + 2 - if right_offset >= len(target): - return [] - - left = target[:offset].rstrip() - center = target[offset:right_offset] - right = target[right_offset:].lstrip() - - # Handle simple cases only. - new_right = None - if center.strip() == '==': - if re.match(r'\bTrue\b', right): - new_right = re.sub(r'\bTrue\b *', '', right, count=1) - elif center.strip() == '!=': - if re.match(r'\bFalse\b', right): - new_right = re.sub(r'\bFalse\b *', '', right, count=1) - - if new_right is None: - return [] - - if new_right[0].isalnum(): - new_right = ' ' + new_right - - self.source[line_index] = left + new_right - - def fix_e713(self, result): - """Fix (trivial case of) non-membership check.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - - match = COMPARE_NEGATIVE_REGEX.search(target) - if match: - if match.group(3) == 'in': - pos_start = match.start(1) - self.source[line_index] = '{0}{1} {2} {3} {4}'.format( - target[:pos_start], match.group(2), match.group(1), - match.group(3), target[match.end():]) - - def fix_e714(self, result): - """Fix object identity should be 'is not' case.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - - match = COMPARE_NEGATIVE_REGEX.search(target) - if match: - if match.group(3) == 'is': - pos_start = match.start(1) - self.source[line_index] = '{0}{1} {2} {3} {4}'.format( - target[:pos_start], match.group(2), match.group(3), - match.group(1), target[match.end():]) - - def fix_e722(self, result): - """fix bare except""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - match = BARE_EXCEPT_REGEX.search(target) - if match: - self.source[line_index] = '{0}{1}{2}'.format( - target[:result['column'] - 1], "except BaseException:", - target[match.end():]) - - def fix_e731(self, result): - """Fix do not assign a lambda expression check.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - match = LAMBDA_REGEX.search(target) - if match: - end = match.end() - self.source[line_index] = '{0}def {1}({2}): return {3}'.format( - target[:match.start(0)], match.group(1), match.group(2), - target[end:].lstrip()) - - def fix_w291(self, result): - """Remove trailing whitespace.""" - fixed_line = self.source[result['line'] - 1].rstrip() - self.source[result['line'] - 1] = fixed_line + '\n' - - def fix_w391(self, _): - """Remove trailing blank lines.""" - blank_count = 0 - for line in reversed(self.source): - line = line.rstrip() - if line: - break - else: - blank_count += 1 - - original_length = len(self.source) - self.source = self.source[:original_length - blank_count] - return range(1, 1 + original_length) - - def fix_w503(self, result): - (line_index, _, target) = get_index_offset_contents(result, - self.source) - one_string_token = target.split()[0] - try: - ts = generate_tokens(one_string_token) - except tokenize.TokenError: - return - if not _is_binary_operator(ts[0][0], one_string_token): - return - # find comment - comment_index = None - for i in range(5): - # NOTE: try to parse code in 5 times - if (line_index - i) < 0: - break - from_index = line_index - i - 1 - to_index = line_index + 1 - try: - ts = generate_tokens("".join(self.source[from_index:to_index])) - except Exception: - continue - newline_count = 0 - newline_index = [] - for i, t in enumerate(ts): - if t[0] in (tokenize.NEWLINE, tokenize.NL): - newline_index.append(i) - newline_count += 1 - if newline_count > 2: - tts = ts[newline_index[-3]:] - else: - tts = ts - old = None - for t in tts: - if tokenize.COMMENT == t[0]: - if old is None: - comment_index = 0 - else: - comment_index = old[3][1] - break - old = t - break - i = target.index(one_string_token) - self.source[line_index] = '{0}{1}'.format( - target[:i], target[i + len(one_string_token):]) - nl = find_newline(self.source[line_index - 1:line_index]) - before_line = self.source[line_index - 1] - bl = before_line.index(nl) - if comment_index: - self.source[line_index - 1] = '{0} {1} {2}'.format( - before_line[:comment_index], one_string_token, - before_line[comment_index + 1:]) - else: - self.source[line_index - 1] = '{0} {1}{2}'.format( - before_line[:bl], one_string_token, before_line[bl:]) - - -def get_index_offset_contents(result, source): - """Return (line_index, column_offset, line_contents).""" - line_index = result['line'] - 1 - return (line_index, - result['column'] - 1, - source[line_index]) - - -def get_fixed_long_line(target, previous_line, original, - indent_word=' ', max_line_length=79, - aggressive=False, experimental=False, verbose=False): - """Break up long line and return result. - - Do this by generating multiple reformatted candidates and then - ranking the candidates to heuristically select the best option. - - """ - indent = _get_indentation(target) - source = target[len(indent):] - assert source.lstrip() == source - assert not target.lstrip().startswith('#') - - # Check for partial multiline. - tokens = list(generate_tokens(source)) - - candidates = shorten_line( - tokens, source, indent, - indent_word, - max_line_length, - aggressive=aggressive, - experimental=experimental, - previous_line=previous_line) - - # Also sort alphabetically as a tie breaker (for determinism). - candidates = sorted( - sorted(set(candidates).union([target, original])), - key=lambda x: line_shortening_rank( - x, - indent_word, - max_line_length, - experimental=experimental)) - - if verbose >= 4: - print(('-' * 79 + '\n').join([''] + candidates + ['']), - file=wrap_output(sys.stderr, 'utf-8')) - - if candidates: - best_candidate = candidates[0] - - # Don't allow things to get longer. - if longest_line_length(best_candidate) > longest_line_length(original): - return None - - return best_candidate - - -def longest_line_length(code): - """Return length of longest line.""" - return max(len(line) for line in code.splitlines()) - - -def join_logical_line(logical_line): - """Return single line based on logical line input.""" - indentation = _get_indentation(logical_line) - - return indentation + untokenize_without_newlines( - generate_tokens(logical_line.lstrip())) + '\n' - - -def untokenize_without_newlines(tokens): - """Return source code based on tokens.""" - text = '' - last_row = 0 - last_column = -1 - - for t in tokens: - token_string = t[1] - (start_row, start_column) = t[2] - (end_row, end_column) = t[3] - - if start_row > last_row: - last_column = 0 - if ( - (start_column > last_column or token_string == '\n') and - not text.endswith(' ') - ): - text += ' ' - - if token_string != '\n': - text += token_string - - last_row = end_row - last_column = end_column - - return text.rstrip() - - -def _find_logical(source_lines): - # Make a variable which is the index of all the starts of lines. - logical_start = [] - logical_end = [] - last_newline = True - parens = 0 - for t in generate_tokens(''.join(source_lines)): - if t[0] in [tokenize.COMMENT, tokenize.DEDENT, - tokenize.INDENT, tokenize.NL, - tokenize.ENDMARKER]: - continue - if not parens and t[0] in [tokenize.NEWLINE, tokenize.SEMI]: - last_newline = True - logical_end.append((t[3][0] - 1, t[2][1])) - continue - if last_newline and not parens: - logical_start.append((t[2][0] - 1, t[2][1])) - last_newline = False - if t[0] == tokenize.OP: - if t[1] in '([{': - parens += 1 - elif t[1] in '}])': - parens -= 1 - return (logical_start, logical_end) - - -def _get_logical(source_lines, result, logical_start, logical_end): - """Return the logical line corresponding to the result. - - Assumes input is already E702-clean. - - """ - row = result['line'] - 1 - col = result['column'] - 1 - ls = None - le = None - for i in range(0, len(logical_start), 1): - assert logical_end - x = logical_end[i] - if x[0] > row or (x[0] == row and x[1] > col): - le = x - ls = logical_start[i] - break - if ls is None: - return None - original = source_lines[ls[0]:le[0] + 1] - return ls, le, original - - -def get_item(items, index, default=None): - if 0 <= index < len(items): - return items[index] - - return default - - -def reindent(source, indent_size): - """Reindent all lines.""" - reindenter = Reindenter(source) - return reindenter.run(indent_size) - - -def code_almost_equal(a, b): - """Return True if code is similar. - - Ignore whitespace when comparing specific line. - - """ - split_a = split_and_strip_non_empty_lines(a) - split_b = split_and_strip_non_empty_lines(b) - - if len(split_a) != len(split_b): - return False - - for (index, _) in enumerate(split_a): - if ''.join(split_a[index].split()) != ''.join(split_b[index].split()): - return False - - return True - - -def split_and_strip_non_empty_lines(text): - """Return lines split by newline. - - Ignore empty lines. - - """ - return [line.strip() for line in text.splitlines() if line.strip()] - - -def fix_e265(source, aggressive=False): # pylint: disable=unused-argument - """Format block comments.""" - if '#' not in source: - # Optimization. - return source - - ignored_line_numbers = multiline_string_lines( - source, - include_docstrings=True) | set(commented_out_code_lines(source)) - - fixed_lines = [] - sio = io.StringIO(source) - for (line_number, line) in enumerate(sio.readlines(), start=1): - if ( - line.lstrip().startswith('#') and - line_number not in ignored_line_numbers and - not pycodestyle.noqa(line) - ): - indentation = _get_indentation(line) - line = line.lstrip() - - # Normalize beginning if not a shebang. - if len(line) > 1: - pos = next((index for index, c in enumerate(line) - if c != '#')) - if ( - # Leave multiple spaces like '# ' alone. - (line[:pos].count('#') > 1 or line[1].isalnum()) and - # Leave stylistic outlined blocks alone. - not line.rstrip().endswith('#') - ): - line = '# ' + line.lstrip('# \t') - - fixed_lines.append(indentation + line) - else: - fixed_lines.append(line) - - return ''.join(fixed_lines) - - -def refactor(source, fixer_names, ignore=None, filename=''): - """Return refactored code using lib2to3. - - Skip if ignore string is produced in the refactored code. - - """ - from lib2to3 import pgen2 - try: - new_text = refactor_with_2to3(source, - fixer_names=fixer_names, - filename=filename) - except (pgen2.parse.ParseError, - SyntaxError, - UnicodeDecodeError, - UnicodeEncodeError): - return source - - if ignore: - if ignore in new_text and ignore not in source: - return source - - return new_text - - -def code_to_2to3(select, ignore): - fixes = set() - for code, fix in CODE_TO_2TO3.items(): - if code_match(code, select=select, ignore=ignore): - fixes |= set(fix) - return fixes - - -def fix_2to3(source, - aggressive=True, select=None, ignore=None, filename=''): - """Fix various deprecated code (via lib2to3).""" - if not aggressive: - return source - - select = select or [] - ignore = ignore or [] - - return refactor(source, - code_to_2to3(select=select, - ignore=ignore), - filename=filename) - - -def fix_w602(source, aggressive=True): - """Fix deprecated form of raising exception.""" - if not aggressive: - return source - - return refactor(source, ['raise'], - ignore='with_traceback') - - -def find_newline(source): - """Return type of newline used in source. - - Input is a list of lines. - - """ - assert not isinstance(source, unicode) - - counter = collections.defaultdict(int) - for line in source: - if line.endswith(CRLF): - counter[CRLF] += 1 - elif line.endswith(CR): - counter[CR] += 1 - elif line.endswith(LF): - counter[LF] += 1 - - return (sorted(counter, key=counter.get, reverse=True) or [LF])[0] - - -def _get_indentword(source): - """Return indentation type.""" - indent_word = ' ' # Default in case source has no indentation - try: - for t in generate_tokens(source): - if t[0] == token.INDENT: - indent_word = t[1] - break - except (SyntaxError, tokenize.TokenError): - pass - return indent_word - - -def _get_indentation(line): - """Return leading whitespace.""" - if line.strip(): - non_whitespace_index = len(line) - len(line.lstrip()) - return line[:non_whitespace_index] - - return '' - - -def get_diff_text(old, new, filename): - """Return text of unified diff between old and new.""" - newline = '\n' - diff = difflib.unified_diff( - old, new, - 'original/' + filename, - 'fixed/' + filename, - lineterm=newline) - - text = '' - for line in diff: - text += line - - # Work around missing newline (http://bugs.python.org/issue2142). - if text and not line.endswith(newline): - text += newline + r'\ No newline at end of file' + newline - - return text - - -def _priority_key(pep8_result): - """Key for sorting PEP8 results. - - Global fixes should be done first. This is important for things like - indentation. - - """ - priority = [ - # Fix multiline colon-based before semicolon based. - 'e701', - # Break multiline statements early. - 'e702', - # Things that make lines longer. - 'e225', 'e231', - # Remove extraneous whitespace before breaking lines. - 'e201', - # Shorten whitespace in comment before resorting to wrapping. - 'e262' - ] - middle_index = 10000 - lowest_priority = [ - # We need to shorten lines last since the logical fixer can get in a - # loop, which causes us to exit early. - 'e501', - 'w503' - ] - key = pep8_result['id'].lower() - try: - return priority.index(key) - except ValueError: - try: - return middle_index + lowest_priority.index(key) + 1 - except ValueError: - return middle_index - - -def shorten_line(tokens, source, indentation, indent_word, max_line_length, - aggressive=False, experimental=False, previous_line=''): - """Separate line at OPERATOR. - - Multiple candidates will be yielded. - - """ - for candidate in _shorten_line(tokens=tokens, - source=source, - indentation=indentation, - indent_word=indent_word, - aggressive=aggressive, - previous_line=previous_line): - yield candidate - - if aggressive: - for key_token_strings in SHORTEN_OPERATOR_GROUPS: - shortened = _shorten_line_at_tokens( - tokens=tokens, - source=source, - indentation=indentation, - indent_word=indent_word, - key_token_strings=key_token_strings, - aggressive=aggressive) - - if shortened is not None and shortened != source: - yield shortened - - if experimental: - for shortened in _shorten_line_at_tokens_new( - tokens=tokens, - source=source, - indentation=indentation, - max_line_length=max_line_length): - - yield shortened - - -def _shorten_line(tokens, source, indentation, indent_word, - aggressive=False, previous_line=''): - """Separate line at OPERATOR. - - The input is expected to be free of newlines except for inside multiline - strings and at the end. - - Multiple candidates will be yielded. - - """ - for (token_type, - token_string, - start_offset, - end_offset) in token_offsets(tokens): - - if ( - token_type == tokenize.COMMENT and - not is_probably_part_of_multiline(previous_line) and - not is_probably_part_of_multiline(source) and - not source[start_offset + 1:].strip().lower().startswith( - ('noqa', 'pragma:', 'pylint:')) - ): - # Move inline comments to previous line. - first = source[:start_offset] - second = source[start_offset:] - yield (indentation + second.strip() + '\n' + - indentation + first.strip() + '\n') - elif token_type == token.OP and token_string != '=': - # Don't break on '=' after keyword as this violates PEP 8. - - assert token_type != token.INDENT - - first = source[:end_offset] - - second_indent = indentation - if first.rstrip().endswith('('): - second_indent += indent_word - elif '(' in first: - second_indent += ' ' * (1 + first.find('(')) - else: - second_indent += indent_word - - second = (second_indent + source[end_offset:].lstrip()) - if ( - not second.strip() or - second.lstrip().startswith('#') - ): - continue - - # Do not begin a line with a comma - if second.lstrip().startswith(','): - continue - # Do end a line with a dot - if first.rstrip().endswith('.'): - continue - if token_string in '+-*/': - fixed = first + ' \\' + '\n' + second - else: - fixed = first + '\n' + second - - # Only fix if syntax is okay. - if check_syntax(normalize_multiline(fixed) - if aggressive else fixed): - yield indentation + fixed - - -def _is_binary_operator(token_type, text): - return ((token_type == tokenize.OP or text in ['and', 'or']) and - text not in '()[]{},:.;@=%~') - - -# A convenient way to handle tokens. -Token = collections.namedtuple('Token', ['token_type', 'token_string', - 'spos', 'epos', 'line']) - - -class ReformattedLines(object): - - """The reflowed lines of atoms. - - Each part of the line is represented as an "atom." They can be moved - around when need be to get the optimal formatting. - - """ - - ########################################################################### - # Private Classes - - class _Indent(object): - - """Represent an indentation in the atom stream.""" - - def __init__(self, indent_amt): - self._indent_amt = indent_amt - - def emit(self): - return ' ' * self._indent_amt - - @property - def size(self): - return self._indent_amt - - class _Space(object): - - """Represent a space in the atom stream.""" - - def emit(self): - return ' ' - - @property - def size(self): - return 1 - - class _LineBreak(object): - - """Represent a line break in the atom stream.""" - - def emit(self): - return '\n' - - @property - def size(self): - return 0 - - def __init__(self, max_line_length): - self._max_line_length = max_line_length - self._lines = [] - self._bracket_depth = 0 - self._prev_item = None - self._prev_prev_item = None - - def __repr__(self): - return self.emit() - - ########################################################################### - # Public Methods - - def add(self, obj, indent_amt, break_after_open_bracket): - if isinstance(obj, Atom): - self._add_item(obj, indent_amt) - return - - self._add_container(obj, indent_amt, break_after_open_bracket) - - def add_comment(self, item): - num_spaces = 2 - if len(self._lines) > 1: - if isinstance(self._lines[-1], self._Space): - num_spaces -= 1 - if len(self._lines) > 2: - if isinstance(self._lines[-2], self._Space): - num_spaces -= 1 - - while num_spaces > 0: - self._lines.append(self._Space()) - num_spaces -= 1 - self._lines.append(item) - - def add_indent(self, indent_amt): - self._lines.append(self._Indent(indent_amt)) - - def add_line_break(self, indent): - self._lines.append(self._LineBreak()) - self.add_indent(len(indent)) - - def add_line_break_at(self, index, indent_amt): - self._lines.insert(index, self._LineBreak()) - self._lines.insert(index + 1, self._Indent(indent_amt)) - - def add_space_if_needed(self, curr_text, equal=False): - if ( - not self._lines or isinstance( - self._lines[-1], (self._LineBreak, self._Indent, self._Space)) - ): - return - - prev_text = unicode(self._prev_item) - prev_prev_text = ( - unicode(self._prev_prev_item) if self._prev_prev_item else '') - - if ( - # The previous item was a keyword or identifier and the current - # item isn't an operator that doesn't require a space. - ((self._prev_item.is_keyword or self._prev_item.is_string or - self._prev_item.is_name or self._prev_item.is_number) and - (curr_text[0] not in '([{.,:}])' or - (curr_text[0] == '=' and equal))) or - - # Don't place spaces around a '.', unless it's in an 'import' - # statement. - ((prev_prev_text != 'from' and prev_text[-1] != '.' and - curr_text != 'import') and - - # Don't place a space before a colon. - curr_text[0] != ':' and - - # Don't split up ending brackets by spaces. - ((prev_text[-1] in '}])' and curr_text[0] not in '.,}])') or - - # Put a space after a colon or comma. - prev_text[-1] in ':,' or - - # Put space around '=' if asked to. - (equal and prev_text == '=') or - - # Put spaces around non-unary arithmetic operators. - ((self._prev_prev_item and - (prev_text not in '+-' and - (self._prev_prev_item.is_name or - self._prev_prev_item.is_number or - self._prev_prev_item.is_string)) and - prev_text in ('+', '-', '%', '*', '/', '//', '**', 'in'))))) - ): - self._lines.append(self._Space()) - - def previous_item(self): - """Return the previous non-whitespace item.""" - return self._prev_item - - def fits_on_current_line(self, item_extent): - return self.current_size() + item_extent <= self._max_line_length - - def current_size(self): - """The size of the current line minus the indentation.""" - size = 0 - for item in reversed(self._lines): - size += item.size - if isinstance(item, self._LineBreak): - break - - return size - - def line_empty(self): - return (self._lines and - isinstance(self._lines[-1], - (self._LineBreak, self._Indent))) - - def emit(self): - string = '' - for item in self._lines: - if isinstance(item, self._LineBreak): - string = string.rstrip() - string += item.emit() - - return string.rstrip() + '\n' - - ########################################################################### - # Private Methods - - def _add_item(self, item, indent_amt): - """Add an item to the line. - - Reflow the line to get the best formatting after the item is - inserted. The bracket depth indicates if the item is being - inserted inside of a container or not. - - """ - if self._prev_item and self._prev_item.is_string and item.is_string: - # Place consecutive string literals on separate lines. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - - item_text = unicode(item) - if self._lines and self._bracket_depth: - # Adding the item into a container. - self._prevent_default_initializer_splitting(item, indent_amt) - - if item_text in '.,)]}': - self._split_after_delimiter(item, indent_amt) - - elif self._lines and not self.line_empty(): - # Adding the item outside of a container. - if self.fits_on_current_line(len(item_text)): - self._enforce_space(item) - - else: - # Line break for the new item. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - - self._lines.append(item) - self._prev_item, self._prev_prev_item = item, self._prev_item - - if item_text in '([{': - self._bracket_depth += 1 - - elif item_text in '}])': - self._bracket_depth -= 1 - assert self._bracket_depth >= 0 - - def _add_container(self, container, indent_amt, break_after_open_bracket): - actual_indent = indent_amt + 1 - - if ( - unicode(self._prev_item) != '=' and - not self.line_empty() and - not self.fits_on_current_line( - container.size + self._bracket_depth + 2) - ): - - if unicode(container)[0] == '(' and self._prev_item.is_name: - # Don't split before the opening bracket of a call. - break_after_open_bracket = True - actual_indent = indent_amt + 4 - elif ( - break_after_open_bracket or - unicode(self._prev_item) not in '([{' - ): - # If the container doesn't fit on the current line and the - # current line isn't empty, place the container on the next - # line. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - break_after_open_bracket = False - else: - actual_indent = self.current_size() + 1 - break_after_open_bracket = False - - if isinstance(container, (ListComprehension, IfExpression)): - actual_indent = indent_amt - - # Increase the continued indentation only if recursing on a - # container. - container.reflow(self, ' ' * actual_indent, - break_after_open_bracket=break_after_open_bracket) - - def _prevent_default_initializer_splitting(self, item, indent_amt): - """Prevent splitting between a default initializer. - - When there is a default initializer, it's best to keep it all on - the same line. It's nicer and more readable, even if it goes - over the maximum allowable line length. This goes back along the - current line to determine if we have a default initializer, and, - if so, to remove extraneous whitespaces and add a line - break/indent before it if needed. - - """ - if unicode(item) == '=': - # This is the assignment in the initializer. Just remove spaces for - # now. - self._delete_whitespace() - return - - if (not self._prev_item or not self._prev_prev_item or - unicode(self._prev_item) != '='): - return - - self._delete_whitespace() - prev_prev_index = self._lines.index(self._prev_prev_item) - - if ( - isinstance(self._lines[prev_prev_index - 1], self._Indent) or - self.fits_on_current_line(item.size + 1) - ): - # The default initializer is already the only item on this line. - # Don't insert a newline here. - return - - # Replace the space with a newline/indent combo. - if isinstance(self._lines[prev_prev_index - 1], self._Space): - del self._lines[prev_prev_index - 1] - - self.add_line_break_at(self._lines.index(self._prev_prev_item), - indent_amt) - - def _split_after_delimiter(self, item, indent_amt): - """Split the line only after a delimiter.""" - self._delete_whitespace() - - if self.fits_on_current_line(item.size): - return - - last_space = None - for current_item in reversed(self._lines): - if ( - last_space and - (not isinstance(current_item, Atom) or - not current_item.is_colon) - ): - break - else: - last_space = None - if isinstance(current_item, self._Space): - last_space = current_item - if isinstance(current_item, (self._LineBreak, self._Indent)): - return - - if not last_space: - return - - self.add_line_break_at(self._lines.index(last_space), indent_amt) - - def _enforce_space(self, item): - """Enforce a space in certain situations. - - There are cases where we will want a space where normally we - wouldn't put one. This just enforces the addition of a space. - - """ - if isinstance(self._lines[-1], - (self._Space, self._LineBreak, self._Indent)): - return - - if not self._prev_item: - return - - item_text = unicode(item) - prev_text = unicode(self._prev_item) - - # Prefer a space around a '.' in an import statement, and between the - # 'import' and '('. - if ( - (item_text == '.' and prev_text == 'from') or - (item_text == 'import' and prev_text == '.') or - (item_text == '(' and prev_text == 'import') - ): - self._lines.append(self._Space()) - - def _delete_whitespace(self): - """Delete all whitespace from the end of the line.""" - while isinstance(self._lines[-1], (self._Space, self._LineBreak, - self._Indent)): - del self._lines[-1] - - -class Atom(object): - - """The smallest unbreakable unit that can be reflowed.""" - - def __init__(self, atom): - self._atom = atom - - def __repr__(self): - return self._atom.token_string - - def __len__(self): - return self.size - - def reflow( - self, reflowed_lines, continued_indent, extent, - break_after_open_bracket=False, - is_list_comp_or_if_expr=False, - next_is_dot=False - ): - if self._atom.token_type == tokenize.COMMENT: - reflowed_lines.add_comment(self) - return - - total_size = extent if extent else self.size - - if self._atom.token_string not in ',:([{}])': - # Some atoms will need an extra 1-sized space token after them. - total_size += 1 - - prev_item = reflowed_lines.previous_item() - if ( - not is_list_comp_or_if_expr and - not reflowed_lines.fits_on_current_line(total_size) and - not (next_is_dot and - reflowed_lines.fits_on_current_line(self.size + 1)) and - not reflowed_lines.line_empty() and - not self.is_colon and - not (prev_item and prev_item.is_name and - unicode(self) == '(') - ): - # Start a new line if there is already something on the line and - # adding this atom would make it go over the max line length. - reflowed_lines.add_line_break(continued_indent) - else: - reflowed_lines.add_space_if_needed(unicode(self)) - - reflowed_lines.add(self, len(continued_indent), - break_after_open_bracket) - - def emit(self): - return self.__repr__() - - @property - def is_keyword(self): - return keyword.iskeyword(self._atom.token_string) - - @property - def is_string(self): - return self._atom.token_type == tokenize.STRING - - @property - def is_name(self): - return self._atom.token_type == tokenize.NAME - - @property - def is_number(self): - return self._atom.token_type == tokenize.NUMBER - - @property - def is_comma(self): - return self._atom.token_string == ',' - - @property - def is_colon(self): - return self._atom.token_string == ':' - - @property - def size(self): - return len(self._atom.token_string) - - -class Container(object): - - """Base class for all container types.""" - - def __init__(self, items): - self._items = items - - def __repr__(self): - string = '' - last_was_keyword = False - - for item in self._items: - if item.is_comma: - string += ', ' - elif item.is_colon: - string += ': ' - else: - item_string = unicode(item) - if ( - string and - (last_was_keyword or - (not string.endswith(tuple('([{,.:}]) ')) and - not item_string.startswith(tuple('([{,.:}])')))) - ): - string += ' ' - string += item_string - - last_was_keyword = item.is_keyword - return string - - def __iter__(self): - for element in self._items: - yield element - - def __getitem__(self, idx): - return self._items[idx] - - def reflow(self, reflowed_lines, continued_indent, - break_after_open_bracket=False): - last_was_container = False - for (index, item) in enumerate(self._items): - next_item = get_item(self._items, index + 1) - - if isinstance(item, Atom): - is_list_comp_or_if_expr = ( - isinstance(self, (ListComprehension, IfExpression))) - item.reflow(reflowed_lines, continued_indent, - self._get_extent(index), - is_list_comp_or_if_expr=is_list_comp_or_if_expr, - next_is_dot=(next_item and - unicode(next_item) == '.')) - if last_was_container and item.is_comma: - reflowed_lines.add_line_break(continued_indent) - last_was_container = False - else: # isinstance(item, Container) - reflowed_lines.add(item, len(continued_indent), - break_after_open_bracket) - last_was_container = not isinstance(item, (ListComprehension, - IfExpression)) - - if ( - break_after_open_bracket and index == 0 and - # Prefer to keep empty containers together instead of - # separating them. - unicode(item) == self.open_bracket and - (not next_item or unicode(next_item) != self.close_bracket) and - (len(self._items) != 3 or not isinstance(next_item, Atom)) - ): - reflowed_lines.add_line_break(continued_indent) - break_after_open_bracket = False - else: - next_next_item = get_item(self._items, index + 2) - if ( - unicode(item) not in ['.', '%', 'in'] and - next_item and not isinstance(next_item, Container) and - unicode(next_item) != ':' and - next_next_item and (not isinstance(next_next_item, Atom) or - unicode(next_item) == 'not') and - not reflowed_lines.line_empty() and - not reflowed_lines.fits_on_current_line( - self._get_extent(index + 1) + 2) - ): - reflowed_lines.add_line_break(continued_indent) - - def _get_extent(self, index): - """The extent of the full element. - - E.g., the length of a function call or keyword. - - """ - extent = 0 - prev_item = get_item(self._items, index - 1) - seen_dot = prev_item and unicode(prev_item) == '.' - while index < len(self._items): - item = get_item(self._items, index) - index += 1 - - if isinstance(item, (ListComprehension, IfExpression)): - break - - if isinstance(item, Container): - if prev_item and prev_item.is_name: - if seen_dot: - extent += 1 - else: - extent += item.size - - prev_item = item - continue - elif (unicode(item) not in ['.', '=', ':', 'not'] and - not item.is_name and not item.is_string): - break - - if unicode(item) == '.': - seen_dot = True - - extent += item.size - prev_item = item - - return extent - - @property - def is_string(self): - return False - - @property - def size(self): - return len(self.__repr__()) - - @property - def is_keyword(self): - return False - - @property - def is_name(self): - return False - - @property - def is_comma(self): - return False - - @property - def is_colon(self): - return False - - @property - def open_bracket(self): - return None - - @property - def close_bracket(self): - return None - - -class Tuple(Container): - - """A high-level representation of a tuple.""" - - @property - def open_bracket(self): - return '(' - - @property - def close_bracket(self): - return ')' - - -class List(Container): - - """A high-level representation of a list.""" - - @property - def open_bracket(self): - return '[' - - @property - def close_bracket(self): - return ']' - - -class DictOrSet(Container): - - """A high-level representation of a dictionary or set.""" - - @property - def open_bracket(self): - return '{' - - @property - def close_bracket(self): - return '}' - - -class ListComprehension(Container): - - """A high-level representation of a list comprehension.""" - - @property - def size(self): - length = 0 - for item in self._items: - if isinstance(item, IfExpression): - break - length += item.size - return length - - -class IfExpression(Container): - - """A high-level representation of an if-expression.""" - - -def _parse_container(tokens, index, for_or_if=None): - """Parse a high-level container, such as a list, tuple, etc.""" - - # Store the opening bracket. - items = [Atom(Token(*tokens[index]))] - index += 1 - - num_tokens = len(tokens) - while index < num_tokens: - tok = Token(*tokens[index]) - - if tok.token_string in ',)]}': - # First check if we're at the end of a list comprehension or - # if-expression. Don't add the ending token as part of the list - # comprehension or if-expression, because they aren't part of those - # constructs. - if for_or_if == 'for': - return (ListComprehension(items), index - 1) - - elif for_or_if == 'if': - return (IfExpression(items), index - 1) - - # We've reached the end of a container. - items.append(Atom(tok)) - - # If not, then we are at the end of a container. - if tok.token_string == ')': - # The end of a tuple. - return (Tuple(items), index) - - elif tok.token_string == ']': - # The end of a list. - return (List(items), index) - - elif tok.token_string == '}': - # The end of a dictionary or set. - return (DictOrSet(items), index) - - elif tok.token_string in '([{': - # A sub-container is being defined. - (container, index) = _parse_container(tokens, index) - items.append(container) - - elif tok.token_string == 'for': - (container, index) = _parse_container(tokens, index, 'for') - items.append(container) - - elif tok.token_string == 'if': - (container, index) = _parse_container(tokens, index, 'if') - items.append(container) - - else: - items.append(Atom(tok)) - - index += 1 - - return (None, None) - - -def _parse_tokens(tokens): - """Parse the tokens. - - This converts the tokens into a form where we can manipulate them - more easily. - - """ - - index = 0 - parsed_tokens = [] - - num_tokens = len(tokens) - while index < num_tokens: - tok = Token(*tokens[index]) - - assert tok.token_type != token.INDENT - if tok.token_type == tokenize.NEWLINE: - # There's only one newline and it's at the end. - break - - if tok.token_string in '([{': - (container, index) = _parse_container(tokens, index) - if not container: - return None - parsed_tokens.append(container) - else: - parsed_tokens.append(Atom(tok)) - - index += 1 - - return parsed_tokens - - -def _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line): - """Reflow the lines so that it looks nice.""" - - if unicode(parsed_tokens[0]) == 'def': - # A function definition gets indented a bit more. - continued_indent = indentation + ' ' * 2 * DEFAULT_INDENT_SIZE - else: - continued_indent = indentation + ' ' * DEFAULT_INDENT_SIZE - - break_after_open_bracket = not start_on_prefix_line - - lines = ReformattedLines(max_line_length) - lines.add_indent(len(indentation.lstrip('\r\n'))) - - if not start_on_prefix_line: - # If splitting after the opening bracket will cause the first element - # to be aligned weirdly, don't try it. - first_token = get_item(parsed_tokens, 0) - second_token = get_item(parsed_tokens, 1) - - if ( - first_token and second_token and - unicode(second_token)[0] == '(' and - len(indentation) + len(first_token) + 1 == len(continued_indent) - ): - return None - - for item in parsed_tokens: - lines.add_space_if_needed(unicode(item), equal=True) - - save_continued_indent = continued_indent - if start_on_prefix_line and isinstance(item, Container): - start_on_prefix_line = False - continued_indent = ' ' * (lines.current_size() + 1) - - item.reflow(lines, continued_indent, break_after_open_bracket) - continued_indent = save_continued_indent - - return lines.emit() - - -def _shorten_line_at_tokens_new(tokens, source, indentation, - max_line_length): - """Shorten the line taking its length into account. - - The input is expected to be free of newlines except for inside - multiline strings and at the end. - - """ - # Yield the original source so to see if it's a better choice than the - # shortened candidate lines we generate here. - yield indentation + source - - parsed_tokens = _parse_tokens(tokens) - - if parsed_tokens: - # Perform two reflows. The first one starts on the same line as the - # prefix. The second starts on the line after the prefix. - fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line=True) - if fixed and check_syntax(normalize_multiline(fixed.lstrip())): - yield fixed - - fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line=False) - if fixed and check_syntax(normalize_multiline(fixed.lstrip())): - yield fixed - - -def _shorten_line_at_tokens(tokens, source, indentation, indent_word, - key_token_strings, aggressive): - """Separate line by breaking at tokens in key_token_strings. - - The input is expected to be free of newlines except for inside - multiline strings and at the end. - - """ - offsets = [] - for (index, _t) in enumerate(token_offsets(tokens)): - (token_type, - token_string, - start_offset, - end_offset) = _t - - assert token_type != token.INDENT - - if token_string in key_token_strings: - # Do not break in containers with zero or one items. - unwanted_next_token = { - '(': ')', - '[': ']', - '{': '}'}.get(token_string) - if unwanted_next_token: - if ( - get_item(tokens, - index + 1, - default=[None, None])[1] == unwanted_next_token or - get_item(tokens, - index + 2, - default=[None, None])[1] == unwanted_next_token - ): - continue - - if ( - index > 2 and token_string == '(' and - tokens[index - 1][1] in ',(%[' - ): - # Don't split after a tuple start, or before a tuple start if - # the tuple is in a list. - continue - - if end_offset < len(source) - 1: - # Don't split right before newline. - offsets.append(end_offset) - else: - # Break at adjacent strings. These were probably meant to be on - # separate lines in the first place. - previous_token = get_item(tokens, index - 1) - if ( - token_type == tokenize.STRING and - previous_token and previous_token[0] == tokenize.STRING - ): - offsets.append(start_offset) - - current_indent = None - fixed = None - for line in split_at_offsets(source, offsets): - if fixed: - fixed += '\n' + current_indent + line - - for symbol in '([{': - if line.endswith(symbol): - current_indent += indent_word - else: - # First line. - fixed = line - assert not current_indent - current_indent = indent_word - - assert fixed is not None - - if check_syntax(normalize_multiline(fixed) - if aggressive > 1 else fixed): - return indentation + fixed - - return None - - -def token_offsets(tokens): - """Yield tokens and offsets.""" - end_offset = 0 - previous_end_row = 0 - previous_end_column = 0 - for t in tokens: - token_type = t[0] - token_string = t[1] - (start_row, start_column) = t[2] - (end_row, end_column) = t[3] - - # Account for the whitespace between tokens. - end_offset += start_column - if previous_end_row == start_row: - end_offset -= previous_end_column - - # Record the start offset of the token. - start_offset = end_offset - - # Account for the length of the token itself. - end_offset += len(token_string) - - yield (token_type, - token_string, - start_offset, - end_offset) - - previous_end_row = end_row - previous_end_column = end_column - - -def normalize_multiline(line): - """Normalize multiline-related code that will cause syntax error. - - This is for purposes of checking syntax. - - """ - if line.startswith('def ') and line.rstrip().endswith(':'): - return line + ' pass' - elif line.startswith('return '): - return 'def _(): ' + line - elif line.startswith('@'): - return line + 'def _(): pass' - elif line.startswith('class '): - return line + ' pass' - elif line.startswith(('if ', 'elif ', 'for ', 'while ')): - return line + ' pass' - - 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 - - return left + replacement + right - - -def _execute_pep8(pep8_options, source): - """Execute pycodestyle via python method calls.""" - class QuietReport(pycodestyle.BaseReport): - - """Version of checker that does not print.""" - - def __init__(self, options): - super(QuietReport, self).__init__(options) - self.__full_error_results = [] - - def error(self, line_number, offset, text, check): - """Collect errors.""" - code = super(QuietReport, self).error(line_number, - offset, - text, - check) - if code: - self.__full_error_results.append( - {'id': code, - 'line': line_number, - 'column': offset + 1, - 'info': text}) - - def full_error_results(self): - """Return error results in detail. - - Results are in the form of a list of dictionaries. Each - dictionary contains 'id', 'line', 'column', and 'info'. - - """ - return self.__full_error_results - - checker = pycodestyle.Checker('', lines=source, reporter=QuietReport, - **pep8_options) - checker.check_all() - return checker.report.full_error_results() - - -def _remove_leading_and_normalize(line): - return line.lstrip().rstrip(CR + LF) + '\n' - - -class Reindenter(object): - - """Reindents badly-indented code to uniformly use four-space indentation. - - Released to the public domain, by Tim Peters, 03 October 2000. - - """ - - def __init__(self, input_text): - sio = io.StringIO(input_text) - source_lines = sio.readlines() - - self.string_content_line_numbers = multiline_string_lines(input_text) - - # File lines, rstripped & tab-expanded. Dummy at start is so - # that we can use tokenize's 1-based line numbering easily. - # Note that a line is all-blank iff it is a newline. - self.lines = [] - for line_number, line in enumerate(source_lines, start=1): - # Do not modify if inside a multiline string. - if line_number in self.string_content_line_numbers: - self.lines.append(line) - else: - # Only expand leading tabs. - self.lines.append(_get_indentation(line).expandtabs() + - _remove_leading_and_normalize(line)) - - self.lines.insert(0, None) - self.index = 1 # index into self.lines of next line - self.input_text = input_text - - def run(self, indent_size=DEFAULT_INDENT_SIZE): - """Fix indentation and return modified line numbers. - - Line numbers are indexed at 1. - - """ - if indent_size < 1: - return self.input_text - - try: - stats = _reindent_stats(tokenize.generate_tokens(self.getline)) - except (SyntaxError, tokenize.TokenError): - return self.input_text - # Remove trailing empty lines. - lines = self.lines - # Sentinel. - stats.append((len(lines), 0)) - # Map count of leading spaces to # we want. - have2want = {} - # Program after transformation. - after = [] - # Copy over initial empty lines -- there's nothing to do until - # we see a line with *something* on it. - i = stats[0][0] - after.extend(lines[1:i]) - for i in range(len(stats) - 1): - thisstmt, thislevel = stats[i] - nextstmt = stats[i + 1][0] - have = _leading_space_count(lines[thisstmt]) - want = thislevel * indent_size - if want < 0: - # A comment line. - if have: - # An indented comment line. If we saw the same - # indentation before, reuse what it most recently - # mapped to. - want = have2want.get(have, -1) - if want < 0: - # Then it probably belongs to the next real stmt. - for j in range(i + 1, len(stats) - 1): - jline, jlevel = stats[j] - if jlevel >= 0: - if have == _leading_space_count(lines[jline]): - want = jlevel * indent_size - break - if want < 0: # Maybe it's a hanging - # comment like this one, - # in which case we should shift it like its base - # line got shifted. - for j in range(i - 1, -1, -1): - jline, jlevel = stats[j] - if jlevel >= 0: - want = (have + _leading_space_count( - after[jline - 1]) - - _leading_space_count(lines[jline])) - break - if want < 0: - # Still no luck -- leave it alone. - want = have - else: - want = 0 - assert want >= 0 - have2want[have] = want - diff = want - have - if diff == 0 or have == 0: - after.extend(lines[thisstmt:nextstmt]) - else: - for line_number, line in enumerate(lines[thisstmt:nextstmt], - start=thisstmt): - if line_number in self.string_content_line_numbers: - after.append(line) - elif diff > 0: - if line == '\n': - after.append(line) - else: - after.append(' ' * diff + line) - else: - remove = min(_leading_space_count(line), -diff) - after.append(line[remove:]) - - return ''.join(after) - - def getline(self): - """Line-getter for tokenize.""" - if self.index >= len(self.lines): - line = '' - else: - line = self.lines[self.index] - self.index += 1 - return line - - -def _reindent_stats(tokens): - """Return list of (lineno, indentlevel) pairs. - - One for each stmt and comment line. indentlevel is -1 for comment - lines, as a signal that tokenize doesn't know what to do about them; - indeed, they're our headache! - - """ - find_stmt = 1 # Next token begins a fresh stmt? - level = 0 # Current indent level. - stats = [] - - for t in tokens: - token_type = t[0] - sline = t[2][0] - line = t[4] - - if token_type == tokenize.NEWLINE: - # A program statement, or ENDMARKER, will eventually follow, - # after some (possibly empty) run of tokens of the form - # (NL | COMMENT)* (INDENT | DEDENT+)? - find_stmt = 1 - - elif token_type == tokenize.INDENT: - find_stmt = 1 - level += 1 - - elif token_type == tokenize.DEDENT: - find_stmt = 1 - level -= 1 - - elif token_type == tokenize.COMMENT: - if find_stmt: - stats.append((sline, -1)) - # But we're still looking for a new stmt, so leave - # find_stmt alone. - - elif token_type == tokenize.NL: - pass - - elif find_stmt: - # This is the first "real token" following a NEWLINE, so it - # must be the first token of the next program statement, or an - # ENDMARKER. - find_stmt = 0 - if line: # Not endmarker. - stats.append((sline, level)) - - return stats - - -def _leading_space_count(line): - """Return number of leading spaces in line.""" - i = 0 - while i < len(line) and line[i] == ' ': - i += 1 - return i - - -def refactor_with_2to3(source_text, fixer_names, filename=''): - """Use lib2to3 to refactor the source. - - Return the refactored source code. - - """ - from lib2to3.refactor import RefactoringTool - fixers = ['lib2to3.fixes.fix_' + name for name in fixer_names] - tool = RefactoringTool(fixer_names=fixers, explicit=fixers) - - from lib2to3.pgen2 import tokenize as lib2to3_tokenize - try: - # The name parameter is necessary particularly for the "import" fixer. - return unicode(tool.refactor_string(source_text, name=filename)) - except lib2to3_tokenize.TokenError: - return source_text - - -def check_syntax(code): - """Return True if syntax is okay.""" - try: - return compile(code, '', 'exec') - except (SyntaxError, TypeError, UnicodeDecodeError): - return False - - -def filter_results(source, results, aggressive): - """Filter out spurious reports from pycodestyle. - - If aggressive is True, we allow possibly unsafe fixes (E711, E712). - - """ - non_docstring_string_line_numbers = multiline_string_lines( - source, include_docstrings=False) - all_string_line_numbers = multiline_string_lines( - source, include_docstrings=True) - - commented_out_code_line_numbers = commented_out_code_lines(source) - - has_e901 = any(result['id'].lower() == 'e901' for result in results) - - for r in results: - issue_id = r['id'].lower() - - if r['line'] in non_docstring_string_line_numbers: - if issue_id.startswith(('e1', 'e501', 'w191')): - continue - - if r['line'] in all_string_line_numbers: - if issue_id in ['e501']: - continue - - # We must offset by 1 for lines that contain the trailing contents of - # multiline strings. - if not aggressive and (r['line'] + 1) in all_string_line_numbers: - # Do not modify multiline strings in non-aggressive mode. Remove - # trailing whitespace could break doctests. - if issue_id.startswith(('w29', 'w39')): - continue - - if aggressive <= 0: - if issue_id.startswith(('e711', 'e72', 'w6')): - continue - - if aggressive <= 1: - if issue_id.startswith(('e712', 'e713', 'e714', 'w5')): - continue - - if aggressive <= 2: - if issue_id.startswith(('e704', 'w5')): - continue - - if r['line'] in commented_out_code_line_numbers: - if issue_id.startswith(('e26', 'e501')): - continue - - # Do not touch indentation if there is a token error caused by - # incomplete multi-line statement. Otherwise, we risk screwing up the - # indentation. - if has_e901: - if issue_id.startswith(('e1', 'e7')): - continue - - yield r - - -def multiline_string_lines(source, include_docstrings=False): - """Return line numbers that are within multiline strings. - - The line numbers are indexed at 1. - - Docstrings are ignored. - - """ - line_numbers = set() - previous_token_type = '' - try: - for t in generate_tokens(source): - token_type = t[0] - start_row = t[2][0] - end_row = t[3][0] - - if token_type == tokenize.STRING and start_row != end_row: - if ( - include_docstrings or - previous_token_type != tokenize.INDENT - ): - # We increment by one since we want the contents of the - # string. - line_numbers |= set(range(1 + start_row, 1 + end_row)) - - previous_token_type = token_type - except (SyntaxError, tokenize.TokenError): - pass - - return line_numbers - - -def commented_out_code_lines(source): - """Return line numbers of comments that are likely code. - - Commented-out code is bad practice, but modifying it just adds even - more clutter. - - """ - line_numbers = [] - try: - for t in generate_tokens(source): - token_type = t[0] - token_string = t[1] - start_row = t[2][0] - line = t[4] - - # Ignore inline comments. - if not line.lstrip().startswith('#'): - continue - - if token_type == tokenize.COMMENT: - stripped_line = token_string.lstrip('#').strip() - if ( - ' ' in stripped_line and - '#' not in stripped_line and - check_syntax(stripped_line) - ): - line_numbers.append(start_row) - except (SyntaxError, tokenize.TokenError): - pass - - return line_numbers - - -def shorten_comment(line, max_line_length, last_comment=False): - """Return trimmed or split long comment line. - - If there are no comments immediately following it, do a text wrap. - Doing this wrapping on all comments in general would lead to jagged - comment text. - - """ - assert len(line) > max_line_length - line = line.rstrip() - - # PEP 8 recommends 72 characters for comment text. - indentation = _get_indentation(line) + '# ' - max_line_length = min(max_line_length, - len(indentation) + 72) - - MIN_CHARACTER_REPEAT = 5 - if ( - len(line) - len(line.rstrip(line[-1])) >= MIN_CHARACTER_REPEAT and - not line[-1].isalnum() - ): - # Trim comments that end with things like --------- - return line[:max_line_length] + '\n' - elif last_comment and re.match(r'\s*#+\s*\w+', line): - split_lines = textwrap.wrap(line.lstrip(' \t#'), - initial_indent=indentation, - subsequent_indent=indentation, - width=max_line_length, - break_long_words=False, - break_on_hyphens=False) - return '\n'.join(split_lines) + '\n' - - return line + '\n' - - -def normalize_line_endings(lines, newline): - """Return fixed line endings. - - All lines will be modified to use the most common line ending. - - """ - return [line.rstrip('\n\r') + newline for line in lines] - - -def mutual_startswith(a, b): - return b.startswith(a) or a.startswith(b) - - -def code_match(code, select, ignore): - if ignore: - assert not isinstance(ignore, unicode) - for ignored_code in [c.strip() for c in ignore]: - if mutual_startswith(code.lower(), ignored_code.lower()): - return False - - if select: - assert not isinstance(select, unicode) - for selected_code in [c.strip() for c in select]: - if mutual_startswith(code.lower(), selected_code.lower()): - return True - return False - - return True - - -def fix_code(source, options=None, encoding=None, apply_config=False): - """Return fixed source code. - - "encoding" will be used to decode "source" if it is a byte string. - - """ - options = _get_options(options, apply_config) - - if not isinstance(source, unicode): - source = source.decode(encoding or get_encoding()) - - sio = io.StringIO(source) - return fix_lines(sio.readlines(), options=options) - - -def _get_options(raw_options, apply_config): - """Return parsed options.""" - if not raw_options: - return parse_args([''], apply_config=apply_config) - - if isinstance(raw_options, dict): - options = parse_args([''], apply_config=apply_config) - for name, value in raw_options.items(): - if not hasattr(options, name): - raise ValueError("No such option '{}'".format(name)) - - # Check for very basic type errors. - expected_type = type(getattr(options, name)) - if not isinstance(expected_type, (str, unicode)): - if isinstance(value, (str, unicode)): - raise ValueError( - "Option '{}' should not be a string".format(name)) - setattr(options, name, value) - else: - options = raw_options - - return options - - -def fix_lines(source_lines, options, filename=''): - """Return fixed source code.""" - # Transform everything to line feed. Then change them back to original - # before returning fixed source code. - original_newline = find_newline(source_lines) - tmp_source = ''.join(normalize_line_endings(source_lines, '\n')) - - # Keep a history to break out of cycles. - previous_hashes = set() - - if options.line_range: - # Disable "apply_local_fixes()" for now due to issue #175. - fixed_source = tmp_source - else: - # Apply global fixes only once (for efficiency). - fixed_source = apply_global_fixes(tmp_source, - options, - filename=filename) - - passes = 0 - long_line_ignore_cache = set() - while hash(fixed_source) not in previous_hashes: - if options.pep8_passes >= 0 and passes > options.pep8_passes: - break - passes += 1 - - previous_hashes.add(hash(fixed_source)) - - tmp_source = copy.copy(fixed_source) - - fix = FixPEP8( - filename, - options, - contents=tmp_source, - long_line_ignore_cache=long_line_ignore_cache) - - fixed_source = fix.fix() - - sio = io.StringIO(fixed_source) - return ''.join(normalize_line_endings(sio.readlines(), original_newline)) - - -def fix_file(filename, options=None, output=None, apply_config=False): - if not options: - options = parse_args([filename], apply_config=apply_config) - - original_source = readlines_from_file(filename) - - fixed_source = original_source - - if options.in_place or output: - encoding = detect_encoding(filename) - - if output: - output = LineEndingWrapper(wrap_output(output, encoding=encoding)) - - fixed_source = fix_lines(fixed_source, options, filename=filename) - - if options.diff: - new = io.StringIO(fixed_source) - new = new.readlines() - diff = get_diff_text(original_source, new, filename) - if output: - output.write(diff) - output.flush() - else: - return diff - elif options.in_place: - fp = open_with_encoding(filename, encoding=encoding, mode='w') - fp.write(fixed_source) - fp.close() - else: - if output: - output.write(fixed_source) - output.flush() - else: - return fixed_source - - -def global_fixes(): - """Yield multiple (code, function) tuples.""" - for function in list(globals().values()): - if inspect.isfunction(function): - arguments = _get_parameters(function) - if arguments[:1] != ['source']: - continue - - code = extract_code_from_function(function) - if code: - yield (code, function) - - -def _get_parameters(function): - # pylint: disable=deprecated-method - if sys.version_info >= (3, 3): - # We need to match "getargspec()", which includes "self" as the first - # value for methods. - # https://bugs.python.org/issue17481#msg209469 - if inspect.ismethod(function): - function = function.__func__ - - return list(inspect.signature(function).parameters) - else: - return inspect.getargspec(function)[0] - - -def apply_global_fixes(source, options, where='global', filename=''): - """Run global fixes on source code. - - These are fixes that only need be done once (unlike those in - FixPEP8, which are dependent on pycodestyle). - - """ - if any(code_match(code, select=options.select, ignore=options.ignore) - for code in ['E101', 'E111']): - source = reindent(source, - indent_size=options.indent_size) - - for (code, function) in global_fixes(): - if code_match(code, select=options.select, ignore=options.ignore): - if options.verbose: - print('---> Applying {0} fix for {1}'.format(where, - code.upper()), - file=sys.stderr) - source = function(source, - aggressive=options.aggressive) - - source = fix_2to3(source, - aggressive=options.aggressive, - select=options.select, - ignore=options.ignore, - filename=filename) - - return source - - -def extract_code_from_function(function): - """Return code handled by function.""" - if not function.__name__.startswith('fix_'): - return None - - code = re.sub('^fix_', '', function.__name__) - if not code: - return None - - try: - int(code[1:]) - except ValueError: - return None - - return code - - -def _get_package_version(): - packages = ["pycodestyle: {0}".format(pycodestyle.__version__)] - return ", ".join(packages) - - -def create_parser(): - """Return command-line parser.""" - # Do import locally to be friendly to those who use autopep8 as a library - # and are supporting Python 2.6. - import argparse - - parser = argparse.ArgumentParser(description=docstring_summary(__doc__), - prog='autopep8') - parser.add_argument('--version', action='version', - version='%(prog)s {0} ({1})'.format( - __version__, _get_package_version())) - parser.add_argument('-v', '--verbose', action='count', - default=0, - help='print verbose messages; ' - 'multiple -v result in more verbose messages') - parser.add_argument('-d', '--diff', action='store_true', - help='print the diff for the fixed source') - parser.add_argument('-i', '--in-place', action='store_true', - help='make changes to files in place') - parser.add_argument('--global-config', metavar='filename', - default=DEFAULT_CONFIG, - help='path to a global pep8 config file; if this file ' - 'does not exist then this is ignored ' - '(default: {0})'.format(DEFAULT_CONFIG)) - parser.add_argument('--ignore-local-config', action='store_true', - help="don't look for and apply local config files; " - 'if not passed, defaults are updated with any ' - "config files in the project's root directory") - parser.add_argument('-r', '--recursive', action='store_true', - help='run recursively over directories; ' - 'must be used with --in-place or --diff') - parser.add_argument('-j', '--jobs', type=int, metavar='n', default=1, - help='number of parallel jobs; ' - 'match CPU count if value is less than 1') - parser.add_argument('-p', '--pep8-passes', metavar='n', - default=-1, type=int, - help='maximum number of additional pep8 passes ' - '(default: infinite)') - parser.add_argument('-a', '--aggressive', action='count', default=0, - help='enable non-whitespace changes; ' - 'multiple -a result in more aggressive changes') - parser.add_argument('--experimental', action='store_true', - help='enable experimental fixes') - parser.add_argument('--exclude', metavar='globs', - help='exclude file/directory names that match these ' - 'comma-separated globs') - parser.add_argument('--list-fixes', action='store_true', - help='list codes for fixes; ' - 'used by --ignore and --select') - parser.add_argument('--ignore', metavar='errors', default='', - help='do not fix these errors/warnings ' - '(default: {0})'.format(DEFAULT_IGNORE)) - parser.add_argument('--select', metavar='errors', default='', - help='fix only these errors/warnings (e.g. E4,W)') - parser.add_argument('--max-line-length', metavar='n', default=79, type=int, - help='set maximum allowed line length ' - '(default: %(default)s)') - parser.add_argument('--line-range', '--range', metavar='line', - default=None, type=int, nargs=2, - help='only fix errors found within this inclusive ' - 'range of line numbers (e.g. 1 99); ' - 'line numbers are indexed at 1') - parser.add_argument('--indent-size', default=DEFAULT_INDENT_SIZE, - type=int, help=argparse.SUPPRESS) - parser.add_argument('files', nargs='*', - help="files to format or '-' for standard in") - - return parser - - -def parse_args(arguments, apply_config=False): - """Parse command-line options.""" - parser = create_parser() - args = parser.parse_args(arguments) - - if not args.files and not args.list_fixes: - parser.error('incorrect number of arguments') - - args.files = [decode_filename(name) for name in args.files] - - if apply_config: - parser = read_config(args, parser) - args = parser.parse_args(arguments) - args.files = [decode_filename(name) for name in args.files] - - if '-' in args.files: - if len(args.files) > 1: - parser.error('cannot mix stdin and regular files') - - if args.diff: - parser.error('--diff cannot be used with standard input') - - if args.in_place: - parser.error('--in-place cannot be used with standard input') - - if args.recursive: - parser.error('--recursive cannot be used with standard input') - - if len(args.files) > 1 and not (args.in_place or args.diff): - parser.error('autopep8 only takes one filename as argument ' - 'unless the "--in-place" or "--diff" args are ' - 'used') - - if args.recursive and not (args.in_place or args.diff): - parser.error('--recursive must be used with --in-place or --diff') - - if args.in_place and args.diff: - parser.error('--in-place and --diff are mutually exclusive') - - if args.max_line_length <= 0: - parser.error('--max-line-length must be greater than 0') - - if args.select: - args.select = _split_comma_separated(args.select) - - if args.ignore: - args.ignore = _split_comma_separated(args.ignore) - elif not args.select: - if args.aggressive: - # Enable everything by default if aggressive. - args.select = set(['E', 'W']) - else: - args.ignore = _split_comma_separated(DEFAULT_IGNORE) - - if args.exclude: - args.exclude = _split_comma_separated(args.exclude) - else: - args.exclude = set([]) - - if args.jobs < 1: - # Do not import multiprocessing globally in case it is not supported - # on the platform. - import multiprocessing - args.jobs = multiprocessing.cpu_count() - - if args.jobs > 1 and not args.in_place: - parser.error('parallel jobs requires --in-place') - - if args.line_range: - if args.line_range[0] <= 0: - parser.error('--range must be positive numbers') - if args.line_range[0] > args.line_range[1]: - parser.error('First value of --range should be less than or equal ' - 'to the second') - - return args - - -def read_config(args, parser): - """Read both user configuration and local configuration.""" - try: - from configparser import ConfigParser as SafeConfigParser - from configparser import Error - except ImportError: - from ConfigParser import SafeConfigParser - from ConfigParser import Error - - config = SafeConfigParser() - - try: - config.read(args.global_config) - - if not args.ignore_local_config: - parent = tail = args.files and os.path.abspath( - os.path.commonprefix(args.files)) - while tail: - if config.read([os.path.join(parent, fn) - for fn in PROJECT_CONFIG]): - break - (parent, tail) = os.path.split(parent) - - defaults = dict() - option_list = dict([(o.dest, o.type or type(o.default)) - for o in parser._actions]) - - for section in ['pep8', 'pycodestyle']: - if not config.has_section(section): - continue - for (k, _) in config.items(section): - norm_opt = k.lstrip('-').replace('-', '_') - opt_type = option_list[norm_opt] - if opt_type is int: - value = config.getint(section, k) - elif opt_type is bool: - value = config.getboolean(section, k) - else: - value = config.get(section, k) - defaults[norm_opt] = value - - parser.set_defaults(**defaults) - except Error: - # Ignore for now. - pass - - return parser - - -def _split_comma_separated(string): - """Return a set of strings.""" - return set(text.strip() for text in string.split(',') if text.strip()) - - -def decode_filename(filename): - """Return Unicode filename.""" - if isinstance(filename, unicode): - return filename - - return filename.decode(sys.getfilesystemencoding()) - - -def supported_fixes(): - """Yield pep8 error codes that autopep8 fixes. - - Each item we yield is a tuple of the code followed by its - description. - - """ - yield ('E101', docstring_summary(reindent.__doc__)) - - instance = FixPEP8(filename=None, options=None, contents='') - for attribute in dir(instance): - code = re.match('fix_([ew][0-9][0-9][0-9])', attribute) - if code: - yield ( - code.group(1).upper(), - re.sub(r'\s+', ' ', - docstring_summary(getattr(instance, attribute).__doc__)) - ) - - for (code, function) in sorted(global_fixes()): - yield (code.upper() + (4 - len(code)) * ' ', - re.sub(r'\s+', ' ', docstring_summary(function.__doc__))) - - for code in sorted(CODE_TO_2TO3): - yield (code.upper() + (4 - len(code)) * ' ', - re.sub(r'\s+', ' ', docstring_summary(fix_2to3.__doc__))) - - -def docstring_summary(docstring): - """Return summary of docstring.""" - return docstring.split('\n')[0] if docstring else '' - - -def line_shortening_rank(candidate, indent_word, max_line_length, - experimental=False): - """Return rank of candidate. - - This is for sorting candidates. - - """ - if not candidate.strip(): - return 0 - - rank = 0 - lines = candidate.rstrip().split('\n') - - offset = 0 - if ( - not lines[0].lstrip().startswith('#') and - lines[0].rstrip()[-1] not in '([{' - ): - for (opening, closing) in ('()', '[]', '{}'): - # Don't penalize empty containers that aren't split up. Things like - # this "foo(\n )" aren't particularly good. - opening_loc = lines[0].find(opening) - closing_loc = lines[0].find(closing) - if opening_loc >= 0: - if closing_loc < 0 or closing_loc != opening_loc + 1: - offset = max(offset, 1 + opening_loc) - - current_longest = max(offset + len(x.strip()) for x in lines) - - rank += 4 * max(0, current_longest - max_line_length) - - rank += len(lines) - - # Too much variation in line length is ugly. - rank += 2 * standard_deviation(len(line) for line in lines) - - bad_staring_symbol = { - '(': ')', - '[': ']', - '{': '}'}.get(lines[0][-1]) - - if len(lines) > 1: - if ( - bad_staring_symbol and - lines[1].lstrip().startswith(bad_staring_symbol) - ): - rank += 20 - - for lineno, current_line in enumerate(lines): - current_line = current_line.strip() - - if current_line.startswith('#'): - continue - - for bad_start in ['.', '%', '+', '-', '/']: - if current_line.startswith(bad_start): - rank += 100 - - # Do not tolerate operators on their own line. - if current_line == bad_start: - rank += 1000 - - if ( - current_line.endswith(('.', '%', '+', '-', '/')) and - "': " in current_line - ): - rank += 1000 - - if current_line.endswith(('(', '[', '{', '.')): - # Avoid lonely opening. They result in longer lines. - if len(current_line) <= len(indent_word): - rank += 100 - - # Avoid the ugliness of ", (\n". - if ( - current_line.endswith('(') and - current_line[:-1].rstrip().endswith(',') - ): - rank += 100 - - # Avoid the ugliness of "something[\n" and something[index][\n. - if ( - current_line.endswith('[') and - len(current_line) > 1 and - (current_line[-2].isalnum() or current_line[-2] in ']') - ): - rank += 300 - - # Also avoid the ugliness of "foo.\nbar" - if current_line.endswith('.'): - rank += 100 - - if has_arithmetic_operator(current_line): - rank += 100 - - # Avoid breaking at unary operators. - if re.match(r'.*[(\[{]\s*[\-\+~]$', current_line.rstrip('\\ ')): - rank += 1000 - - if re.match(r'.*lambda\s*\*$', current_line.rstrip('\\ ')): - rank += 1000 - - if current_line.endswith(('%', '(', '[', '{')): - rank -= 20 - - # Try to break list comprehensions at the "for". - if current_line.startswith('for '): - rank -= 50 - - if current_line.endswith('\\'): - # If a line ends in \-newline, it may be part of a - # multiline string. In that case, we would like to know - # how long that line is without the \-newline. If it's - # longer than the maximum, or has comments, then we assume - # that the \-newline is an okay candidate and only - # penalize it a bit. - total_len = len(current_line) - lineno += 1 - while lineno < len(lines): - total_len += len(lines[lineno]) - - if lines[lineno].lstrip().startswith('#'): - total_len = max_line_length - break - - if not lines[lineno].endswith('\\'): - break - - lineno += 1 - - if total_len < max_line_length: - rank += 10 - else: - rank += 100 if experimental else 1 - - # Prefer breaking at commas rather than colon. - if ',' in current_line and current_line.endswith(':'): - rank += 10 - - # Avoid splitting dictionaries between key and value. - if current_line.endswith(':'): - rank += 100 - - rank += 10 * count_unbalanced_brackets(current_line) - - return max(0, rank) - - -def standard_deviation(numbers): - """Return standard devation.""" - numbers = list(numbers) - if not numbers: - return 0 - mean = sum(numbers) / len(numbers) - return (sum((n - mean) ** 2 for n in numbers) / - len(numbers)) ** .5 - - -def has_arithmetic_operator(line): - """Return True if line contains any arithmetic operators.""" - for operator in pycodestyle.ARITHMETIC_OP: - if operator in line: - return True - - return False - - -def count_unbalanced_brackets(line): - """Return number of unmatched open/close brackets.""" - count = 0 - for opening, closing in ['()', '[]', '{}']: - count += abs(line.count(opening) - line.count(closing)) - - return count - - -def split_at_offsets(line, offsets): - """Split line at offsets. - - Return list of strings. - - """ - result = [] - - previous_offset = 0 - current_offset = 0 - for current_offset in sorted(offsets): - if current_offset < len(line) and previous_offset != current_offset: - result.append(line[previous_offset:current_offset].strip()) - previous_offset = current_offset - - result.append(line[current_offset:]) - - return result - - -class LineEndingWrapper(object): - - r"""Replace line endings to work with sys.stdout. - - It seems that sys.stdout expects only '\n' as the line ending, no matter - the platform. Otherwise, we get repeated line endings. - - """ - - def __init__(self, output): - self.__output = output - - def write(self, s): - self.__output.write(s.replace('\r\n', '\n').replace('\r', '\n')) - - def flush(self): - self.__output.flush() - - -def match_file(filename, exclude): - """Return True if file is okay for modifying/recursing.""" - base_name = os.path.basename(filename) - - if base_name.startswith('.'): - return False - - for pattern in exclude: - if fnmatch.fnmatch(base_name, pattern): - return False - if fnmatch.fnmatch(filename, pattern): - return False - - if not os.path.isdir(filename) and not is_python_file(filename): - return False - - return True - - -def find_files(filenames, recursive, exclude): - """Yield filenames.""" - while filenames: - name = filenames.pop(0) - if recursive and os.path.isdir(name): - for root, directories, children in os.walk(name): - filenames += [os.path.join(root, f) for f in children - if match_file(os.path.join(root, f), - exclude)] - directories[:] = [d for d in directories - if match_file(os.path.join(root, d), - exclude)] - else: - yield name - - -def _fix_file(parameters): - """Helper function for optionally running fix_file() in parallel.""" - if parameters[1].verbose: - print('[file:{0}]'.format(parameters[0]), file=sys.stderr) - try: - fix_file(*parameters) - except IOError as error: - print(unicode(error), file=sys.stderr) - - -def fix_multiple_files(filenames, options, output=None): - """Fix list of files. - - Optionally fix files recursively. - - """ - filenames = find_files(filenames, options.recursive, options.exclude) - if options.jobs > 1: - import multiprocessing - pool = multiprocessing.Pool(options.jobs) - pool.map(_fix_file, - [(name, options) for name in filenames]) - else: - for name in filenames: - _fix_file((name, options, output)) - - -def is_python_file(filename): - """Return True if filename is Python file.""" - if filename.endswith('.py'): - return True - - try: - with open_with_encoding( - filename, - limit_byte_check=MAX_PYTHON_FILE_DETECTION_BYTES) as f: - text = f.read(MAX_PYTHON_FILE_DETECTION_BYTES) - if not text: - return False - first_line = text.splitlines()[0] - except (IOError, IndexError): - return False - - if not PYTHON_SHEBANG_REGEX.match(first_line): - return False - - return True - - -def is_probably_part_of_multiline(line): - """Return True if line is likely part of a multiline string. - - When multiline strings are involved, pep8 reports the error as being - at the start of the multiline string, which doesn't work for us. - - """ - return ( - '"""' in line or - "'''" in line or - line.rstrip().endswith('\\') - ) - - -def wrap_output(output, encoding): - """Return output with specified encoding.""" - return codecs.getwriter(encoding)(output.buffer - if hasattr(output, 'buffer') - else output) - - -def get_encoding(): - """Return preferred encoding.""" - return locale.getpreferredencoding() or sys.getdefaultencoding() - - -def main(argv=None, apply_config=True): - """Command-line entry.""" - if argv is None: - argv = sys.argv - - try: - # Exit on broken pipe. - signal.signal(signal.SIGPIPE, signal.SIG_DFL) - except AttributeError: # pragma: no cover - # SIGPIPE is not available on Windows. - pass - - try: - args = parse_args(argv[1:], apply_config=apply_config) - - if args.list_fixes: - for code, description in sorted(supported_fixes()): - print('{code} - {description}'.format( - code=code, description=description)) - return 0 - - if args.files == ['-']: - assert not args.in_place - - encoding = sys.stdin.encoding or get_encoding() - - # LineEndingWrapper is unnecessary here due to the symmetry between - # standard in and standard out. - wrap_output(sys.stdout, encoding=encoding).write( - fix_code(sys.stdin.read(), args, encoding=encoding)) - else: - if args.in_place or args.diff: - args.files = list(set(args.files)) - else: - assert len(args.files) == 1 - assert not args.recursive - - fix_multiple_files(args.files, args, sys.stdout) - except KeyboardInterrupt: - return 1 # pragma: no cover - - -class CachedTokenizer(object): - - """A one-element cache around tokenize.generate_tokens(). - - Original code written by Ned Batchelder, in coverage.py. - - """ - - def __init__(self): - self.last_text = None - self.last_tokens = None - - def generate_tokens(self, text): - """A stand-in for tokenize.generate_tokens().""" - if text != self.last_text: - string_io = io.StringIO(text) - self.last_tokens = list( - tokenize.generate_tokens(string_io.readline) - ) - self.last_text = text - return self.last_tokens - - -_cached_tokenizer = CachedTokenizer() -generate_tokens = _cached_tokenizer.generate_tokens - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/pymode/autopep8.py b/pymode/autopep8.py new file mode 120000 index 00000000..0a26e017 --- /dev/null +++ b/pymode/autopep8.py @@ -0,0 +1 @@ +../submodules/autopep8/autopep8.py \ No newline at end of file diff --git a/pymode/environment.py b/pymode/environment.py index 56f49b4a..86527f56 100644 --- a/pymode/environment.py +++ b/pymode/environment.py @@ -1,14 +1,10 @@ """Define interfaces.""" -from __future__ import print_function - import json import os.path import time import vim # noqa -from ._compat import PY2 - class VimPymodeEnviroment(object): @@ -53,10 +49,7 @@ def lines(self): :return list: """ - if not PY2: - return self.curbuf - - return [l.decode(self.options.get('encoding')) for l in self.curbuf] + return self.curbuf @staticmethod def var(name, to_bool=False, silence=False, default=None): @@ -91,24 +84,31 @@ def message(msg, history=False): return vim.command('call pymode#wide_message("%s")' % str(msg)) - def user_input(self, msg, default=''): + def user_input(self, msg='', default=''): """Return user input or default. :return str: """ - msg = '%s %s ' % (self.prefix, msg) + prompt = [] + prompt.append(str(self.prefix.strip())) + prompt.append(str(msg).strip()) if default != '': - msg += '[%s] ' % default + prompt.append('[%s]' % default) + + prompt.append('> ') + prompt = ' '.join([s for s in prompt if s]) + + vim.command('echohl Debug') try: - vim.command('echohl Debug') - input_str = vim.eval('input("%s> ")' % msg) - vim.command('echohl none') + input_str = vim.eval('input(%r)' % (prompt,)) except KeyboardInterrupt: input_str = '' + vim.command('echohl none') + return input_str or default def user_confirm(self, msg, yes=False): @@ -201,9 +201,6 @@ def prepare_value(self, value, dumps=True): if dumps: value = json.dumps(value) - if PY2: - value = value.decode('utf-8').encode(self.options.get('encoding')) - return value def get_offset_params(self, cursor=None, base=""): @@ -245,5 +242,8 @@ def goto_buffer(bufnr): if str(bufnr) != '-1': vim.command('buffer %s' % bufnr) + def select_line(self, start, end): + vim.command('normal %sggV%sgg' % (start, end)) + env = VimPymodeEnviroment() diff --git a/pymode/libs/astroid/interpreter/__init__.py b/pymode/libs/__init__.py similarity index 100% rename from pymode/libs/astroid/interpreter/__init__.py rename to pymode/libs/__init__.py diff --git a/pymode/libs/appdirs.py b/pymode/libs/appdirs.py new file mode 120000 index 00000000..da7cbf20 --- /dev/null +++ b/pymode/libs/appdirs.py @@ -0,0 +1 @@ +../../submodules/appdirs/appdirs.py \ No newline at end of file diff --git a/pymode/libs/astroid b/pymode/libs/astroid new file mode 120000 index 00000000..492d8fbc --- /dev/null +++ b/pymode/libs/astroid @@ -0,0 +1 @@ +../../submodules/astroid/astroid \ No newline at end of file diff --git a/pymode/libs/astroid/__init__.py b/pymode/libs/astroid/__init__.py deleted file mode 100644 index 69b2ff0c..00000000 --- a/pymode/libs/astroid/__init__.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2006-2013, 2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Python Abstract Syntax Tree New Generation - -The aim of this module is to provide a common base representation of -python source code for projects such as pychecker, pyreverse, -pylint... Well, actually the development of this library is essentially -governed by pylint's needs. - -It extends class defined in the python's _ast module with some -additional methods and attributes. Instance attributes are added by a -builder object, which can either generate extended ast (let's call -them astroid ;) by visiting an existent ast tree or by inspecting living -object. Methods are added by monkey patching ast classes. - -Main modules are: - -* nodes and scoped_nodes for more information about methods and - attributes added to different node classes - -* the manager contains a high level object to get astroid trees from - source files and living objects. It maintains a cache of previously - constructed tree for quick access - -* builder contains the class responsible to build astroid trees -""" - -import os -import sys -import re -from operator import attrgetter - -import enum - - -_Context = enum.Enum('Context', 'Load Store Del') -Load = _Context.Load -Store = _Context.Store -Del = _Context.Del -del _Context - - -from .__pkginfo__ import version as __version__ -# WARNING: internal imports order matters ! - -# pylint: disable=redefined-builtin, wildcard-import - -# make all exception classes accessible from astroid package -from astroid.exceptions import * - -# make all node classes accessible from astroid package -from astroid.nodes import * - -# trigger extra monkey-patching -from astroid import inference - -# more stuff available -from astroid import raw_building -from astroid.bases import BaseInstance, Instance, BoundMethod, UnboundMethod -from astroid.node_classes import are_exclusive, unpack_infer -from astroid.scoped_nodes import builtin_lookup -from astroid.builder import parse, extract_node -from astroid.util import Uninferable, YES - -# make a manager instance (borg) accessible from astroid package -from astroid.manager import AstroidManager -MANAGER = AstroidManager() -del AstroidManager - -# transform utilities (filters and decorator) - -class AsStringRegexpPredicate(object): - """ClassDef to be used as predicate that may be given to `register_transform` - - First argument is a regular expression that will be searched against the `as_string` - representation of the node onto which it's applied. - - If specified, the second argument is an `attrgetter` expression that will be - applied on the node first to get the actual node on which `as_string` should - be called. - - WARNING: This can be fairly slow, as it has to convert every AST node back - to Python code; you should consider examining the AST directly instead. - """ - def __init__(self, regexp, expression=None): - self.regexp = re.compile(regexp) - self.expression = expression - - def __call__(self, node): - if self.expression is not None: - node = attrgetter(self.expression)(node) - # pylint: disable=no-member; github.com/pycqa/astroid/126 - return self.regexp.search(node.as_string()) - -def inference_tip(infer_function): - """Given an instance specific inference function, return a function to be - given to MANAGER.register_transform to set this inference function. - - Typical usage - - .. sourcecode:: python - - MANAGER.register_transform(Call, inference_tip(infer_named_tuple), - predicate) - """ - def transform(node, infer_function=infer_function): - node._explicit_inference = infer_function - return node - return transform - - -def register_module_extender(manager, module_name, get_extension_mod): - def transform(node): - extension_module = get_extension_mod() - for name, objs in extension_module.locals.items(): - node.locals[name] = objs - for obj in objs: - if obj.parent is extension_module: - obj.parent = node - - manager.register_transform(Module, transform, lambda n: n.name == module_name) - - -# load brain plugins -BRAIN_MODULES_DIR = os.path.join(os.path.dirname(__file__), 'brain') -if BRAIN_MODULES_DIR not in sys.path: - # add it to the end of the list so user path take precedence - sys.path.append(BRAIN_MODULES_DIR) -# load modules in this directory -for module in os.listdir(BRAIN_MODULES_DIR): - if module.endswith('.py'): - __import__(module[:-3]) diff --git a/pymode/libs/astroid/__pkginfo__.py b/pymode/libs/astroid/__pkginfo__.py deleted file mode 100644 index 96c2dcba..00000000 --- a/pymode/libs/astroid/__pkginfo__.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""astroid packaging information""" - -from sys import version_info as py_version - -from pkg_resources import parse_version -from setuptools import __version__ as setuptools_version - -distname = 'astroid' - -modname = 'astroid' - -version = '1.5.3' -numversion = tuple(map(int, version.split('.'))) - -extras_require = {} -install_requires = ['lazy_object_proxy', 'six', 'wrapt'] - - -def has_environment_marker_range_operators_support(): - """Code extracted from 'pytest/setup.py' - https://github.com/pytest-dev/pytest/blob/7538680c/setup.py#L31 - - The first known release to support environment marker with range operators - it is 17.1, see: https://setuptools.readthedocs.io/en/latest/history.html#id113 - """ - return parse_version(setuptools_version) >= parse_version('17.1') - - -if has_environment_marker_range_operators_support(): - extras_require[':python_version<"3.4"'] = ['enum34>=1.1.3', 'singledispatch'] - extras_require[':python_version<"3.3"'] = ['backports.functools_lru_cache'] -else: - if py_version < (3, 4): - install_requires.extend(['enum34', 'singledispatch']) - if py_version < (3, 3): - install_requires.append('backports.functools_lru_cache') - - -# pylint: disable=redefined-builtin; why license is a builtin anyway? -license = 'LGPL' - -author = 'Python Code Quality Authority' -author_email = 'code-quality@python.org' -mailinglist = "mailto://%s" % author_email -web = 'https://github.com/PyCQA/astroid' - -description = "A abstract syntax tree for Python with inference support." - -classifiers = ["Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: Quality Assurance", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 3", - ] diff --git a/pymode/libs/astroid/arguments.py b/pymode/libs/astroid/arguments.py deleted file mode 100644 index f81ccbd6..00000000 --- a/pymode/libs/astroid/arguments.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -import six - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import nodes -from astroid import util - - - -class CallSite(object): - """Class for understanding arguments passed into a call site - - It needs a call context, which contains the arguments and the - keyword arguments that were passed into a given call site. - In order to infer what an argument represents, call - :meth:`infer_argument` with the corresponding function node - and the argument name. - """ - - def __init__(self, callcontext): - args = callcontext.args - keywords = callcontext.keywords - self.duplicated_keywords = set() - self._unpacked_args = self._unpack_args(args) - self._unpacked_kwargs = self._unpack_keywords(keywords) - - self.positional_arguments = [ - arg for arg in self._unpacked_args - if arg is not util.Uninferable - ] - self.keyword_arguments = { - key: value for key, value in self._unpacked_kwargs.items() - if value is not util.Uninferable - } - - @classmethod - def from_call(cls, call_node): - """Get a CallSite object from the given Call node.""" - callcontext = contextmod.CallContext(call_node.args, - call_node.keywords) - return cls(callcontext) - - def has_invalid_arguments(self): - """Check if in the current CallSite were passed *invalid* arguments - - This can mean multiple things. For instance, if an unpacking - of an invalid object was passed, then this method will return True. - Other cases can be when the arguments can't be inferred by astroid, - for example, by passing objects which aren't known statically. - """ - return len(self.positional_arguments) != len(self._unpacked_args) - - def has_invalid_keywords(self): - """Check if in the current CallSite were passed *invalid* keyword arguments - - For instance, unpacking a dictionary with integer keys is invalid - (**{1:2}), because the keys must be strings, which will make this - method to return True. Other cases where this might return True if - objects which can't be inferred were passed. - """ - return len(self.keyword_arguments) != len(self._unpacked_kwargs) - - def _unpack_keywords(self, keywords): - values = {} - context = contextmod.InferenceContext() - for name, value in keywords: - if name is None: - # Then it's an unpacking operation (**) - try: - inferred = next(value.infer(context=context)) - except exceptions.InferenceError: - values[name] = util.Uninferable - continue - - if not isinstance(inferred, nodes.Dict): - # Not something we can work with. - values[name] = util.Uninferable - continue - - for dict_key, dict_value in inferred.items: - try: - dict_key = next(dict_key.infer(context=context)) - except exceptions.InferenceError: - values[name] = util.Uninferable - continue - if not isinstance(dict_key, nodes.Const): - values[name] = util.Uninferable - continue - if not isinstance(dict_key.value, six.string_types): - values[name] = util.Uninferable - continue - if dict_key.value in values: - # The name is already in the dictionary - values[dict_key.value] = util.Uninferable - self.duplicated_keywords.add(dict_key.value) - continue - values[dict_key.value] = dict_value - else: - values[name] = value - return values - - @staticmethod - def _unpack_args(args): - values = [] - context = contextmod.InferenceContext() - for arg in args: - if isinstance(arg, nodes.Starred): - try: - inferred = next(arg.value.infer(context=context)) - except exceptions.InferenceError: - values.append(util.Uninferable) - continue - - if inferred is util.Uninferable: - values.append(util.Uninferable) - continue - if not hasattr(inferred, 'elts'): - values.append(util.Uninferable) - continue - values.extend(inferred.elts) - else: - values.append(arg) - return values - - def infer_argument(self, funcnode, name, context): - """infer a function argument value according to the call context - - Arguments: - funcnode: The function being called. - name: The name of the argument whose value is being inferred. - context: TODO - """ - if name in self.duplicated_keywords: - raise exceptions.InferenceError('The arguments passed to {func!r} ' - ' have duplicate keywords.', - call_site=self, func=funcnode, - arg=name, context=context) - - # Look into the keywords first, maybe it's already there. - try: - return self.keyword_arguments[name].infer(context) - except KeyError: - pass - - # Too many arguments given and no variable arguments. - if len(self.positional_arguments) > len(funcnode.args.args): - if not funcnode.args.vararg: - raise exceptions.InferenceError('Too many positional arguments ' - 'passed to {func!r} that does ' - 'not have *args.', - call_site=self, func=funcnode, - arg=name, context=context) - - positional = self.positional_arguments[:len(funcnode.args.args)] - vararg = self.positional_arguments[len(funcnode.args.args):] - argindex = funcnode.args.find_argname(name)[0] - kwonlyargs = set(arg.name for arg in funcnode.args.kwonlyargs) - kwargs = { - key: value for key, value in self.keyword_arguments.items() - if key not in kwonlyargs - } - # If there are too few positionals compared to - # what the function expects to receive, check to see - # if the missing positional arguments were passed - # as keyword arguments and if so, place them into the - # positional args list. - if len(positional) < len(funcnode.args.args): - for func_arg in funcnode.args.args: - if func_arg.name in kwargs: - arg = kwargs.pop(func_arg.name) - positional.append(arg) - - if argindex is not None: - # 2. first argument of instance/class method - if argindex == 0 and funcnode.type in ('method', 'classmethod'): - if context.boundnode is not None: - boundnode = context.boundnode - else: - # XXX can do better ? - boundnode = funcnode.parent.frame() - - if isinstance(boundnode, nodes.ClassDef): - # Verify that we're accessing a method - # of the metaclass through a class, as in - # `cls.metaclass_method`. In this case, the - # first argument is always the class. - method_scope = funcnode.parent.scope() - if method_scope is boundnode.metaclass(): - return iter((boundnode, )) - - if funcnode.type == 'method': - if not isinstance(boundnode, bases.Instance): - boundnode = bases.Instance(boundnode) - return iter((boundnode,)) - if funcnode.type == 'classmethod': - return iter((boundnode,)) - # if we have a method, extract one position - # from the index, so we'll take in account - # the extra parameter represented by `self` or `cls` - if funcnode.type in ('method', 'classmethod'): - argindex -= 1 - # 2. search arg index - try: - return self.positional_arguments[argindex].infer(context) - except IndexError: - pass - - if funcnode.args.kwarg == name: - # It wants all the keywords that were passed into - # the call site. - if self.has_invalid_keywords(): - raise exceptions.InferenceError( - "Inference failed to find values for all keyword arguments " - "to {func!r}: {unpacked_kwargs!r} doesn't correspond to " - "{keyword_arguments!r}.", - keyword_arguments=self.keyword_arguments, - unpacked_kwargs=self._unpacked_kwargs, - call_site=self, func=funcnode, arg=name, context=context) - kwarg = nodes.Dict(lineno=funcnode.args.lineno, - col_offset=funcnode.args.col_offset, - parent=funcnode.args) - kwarg.postinit([(nodes.const_factory(key), value) - for key, value in kwargs.items()]) - return iter((kwarg, )) - elif funcnode.args.vararg == name: - # It wants all the args that were passed into - # the call site. - if self.has_invalid_arguments(): - raise exceptions.InferenceError( - "Inference failed to find values for all positional " - "arguments to {func!r}: {unpacked_args!r} doesn't " - "correspond to {positional_arguments!r}.", - positional_arguments=self.positional_arguments, - unpacked_args=self._unpacked_args, - call_site=self, func=funcnode, arg=name, context=context) - args = nodes.Tuple(lineno=funcnode.args.lineno, - col_offset=funcnode.args.col_offset, - parent=funcnode.args) - args.postinit(vararg) - return iter((args, )) - - # Check if it's a default parameter. - try: - return funcnode.args.default_value(name).infer(context) - except exceptions.NoDefault: - pass - raise exceptions.InferenceError('No value found for argument {name} to ' - '{func!r}', call_site=self, - func=funcnode, arg=name, context=context) diff --git a/pymode/libs/astroid/as_string.py b/pymode/libs/astroid/as_string.py deleted file mode 100644 index 85afdc30..00000000 --- a/pymode/libs/astroid/as_string.py +++ /dev/null @@ -1,527 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""This module renders Astroid nodes as string: - -* :func:`to_code` function return equivalent (hopefully valid) python string - -* :func:`dump` function return an internal representation of nodes found - in the tree, useful for debugging or understanding the tree structure -""" -import sys - -import six - - -# pylint: disable=unused-argument - - -class AsStringVisitor(object): - """Visitor to render an Astroid node as a valid python code string""" - - def __init__(self, indent): - self.indent = indent - - def __call__(self, node): - """Makes this visitor behave as a simple function""" - return node.accept(self) - - def _stmt_list(self, stmts): - """return a list of nodes to string""" - stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr]) - return self.indent + stmts.replace('\n', '\n'+self.indent) - - - ## visit_ methods ########################################### - - def visit_arguments(self, node): - """return an astroid.Function node as string""" - return node.format_args() - - def visit_assignattr(self, node): - """return an astroid.AssAttr node as string""" - return self.visit_attribute(node) - - def visit_assert(self, node): - """return an astroid.Assert node as string""" - if node.fail: - return 'assert %s, %s' % (node.test.accept(self), - node.fail.accept(self)) - return 'assert %s' % node.test.accept(self) - - def visit_assignname(self, node): - """return an astroid.AssName node as string""" - return node.name - - def visit_assign(self, node): - """return an astroid.Assign node as string""" - lhs = ' = '.join([n.accept(self) for n in node.targets]) - return '%s = %s' % (lhs, node.value.accept(self)) - - def visit_augassign(self, node): - """return an astroid.AugAssign node as string""" - return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) - - def visit_annassign(self, node): - """Return an astroid.AugAssign node as string""" - - target = node.target.accept(self) - annotation = node.annotation.accept(self) - if node.value is None: - return '%s: %s' % (target, annotation) - return '%s: %s = %s' % (target, annotation, node.value.accept(self)) - - def visit_repr(self, node): - """return an astroid.Repr node as string""" - return '`%s`' % node.value.accept(self) - - def visit_binop(self, node): - """return an astroid.BinOp node as string""" - return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self)) - - def visit_boolop(self, node): - """return an astroid.BoolOp node as string""" - return (' %s ' % node.op).join(['(%s)' % n.accept(self) - for n in node.values]) - - def visit_break(self, node): - """return an astroid.Break node as string""" - return 'break' - - def visit_call(self, node): - """return an astroid.Call node as string""" - expr_str = node.func.accept(self) - args = [arg.accept(self) for arg in node.args] - if node.keywords: - keywords = [kwarg.accept(self) for kwarg in node.keywords] - else: - keywords = [] - - args.extend(keywords) - return '%s(%s)' % (expr_str, ', '.join(args)) - - def visit_classdef(self, node): - """return an astroid.ClassDef node as string""" - decorate = node.decorators.accept(self) if node.decorators else '' - bases = ', '.join([n.accept(self) for n in node.bases]) - if sys.version_info[0] == 2: - bases = '(%s)' % bases if bases else '' - else: - metaclass = node.metaclass() - if metaclass and not node.has_metaclass_hack(): - if bases: - bases = '(%s, metaclass=%s)' % (bases, metaclass.name) - else: - bases = '(metaclass=%s)' % metaclass.name - else: - bases = '(%s)' % bases if bases else '' - docs = '\n%s"""%s"""' % (self.indent, node.doc) if node.doc else '' - return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, - self._stmt_list(node.body)) - - def visit_compare(self, node): - """return an astroid.Compare node as string""" - rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) - for op, expr in node.ops]) - return '%s %s' % (node.left.accept(self), rhs_str) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - ifs = ''.join([' if %s' % n.accept(self) for n in node.ifs]) - return 'for %s in %s%s' % (node.target.accept(self), - node.iter.accept(self), ifs) - - def visit_const(self, node): - """return an astroid.Const node as string""" - return repr(node.value) - - def visit_continue(self, node): - """return an astroid.Continue node as string""" - return 'continue' - - def visit_delete(self, node): # XXX check if correct - """return an astroid.Delete node as string""" - return 'del %s' % ', '.join([child.accept(self) - for child in node.targets]) - - def visit_delattr(self, node): - """return an astroid.DelAttr node as string""" - return self.visit_attribute(node) - - def visit_delname(self, node): - """return an astroid.DelName node as string""" - return node.name - - def visit_decorators(self, node): - """return an astroid.Decorators node as string""" - return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) - - def visit_dict(self, node): - """return an astroid.Dict node as string""" - return '{%s}' % ', '.join(self._visit_dict(node)) - - def _visit_dict(self, node): - for key, value in node.items: - key = key.accept(self) - value = value.accept(self) - if key == '**': - # It can only be a DictUnpack node. - yield key + value - else: - yield '%s: %s' % (key, value) - - def visit_dictunpack(self, node): - return '**' - - def visit_dictcomp(self, node): - """return an astroid.DictComp node as string""" - return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_expr(self, node): - """return an astroid.Discard node as string""" - return node.value.accept(self) - - def visit_emptynode(self, node): - """dummy method for visiting an Empty node""" - return '' - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s, %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_ellipsis(self, node): - """return an astroid.Ellipsis node as string""" - return '...' - - def visit_empty(self, node): - """return an Empty node as string""" - return '' - - def visit_exec(self, node): - """return an astroid.Exec node as string""" - if node.locals: - return 'exec %s in %s, %s' % (node.expr.accept(self), - node.locals.accept(self), - node.globals.accept(self)) - if node.globals: - return 'exec %s in %s' % (node.expr.accept(self), - node.globals.accept(self)) - return 'exec %s' % node.expr.accept(self) - - def visit_extslice(self, node): - """return an astroid.ExtSlice node as string""" - return ','.join([dim.accept(self) for dim in node.dims]) - - def visit_for(self, node): - """return an astroid.For node as string""" - fors = 'for %s in %s:\n%s' % (node.target.accept(self), - node.iter.accept(self), - self._stmt_list(node.body)) - if node.orelse: - fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) - return fors - - def visit_importfrom(self, node): - """return an astroid.ImportFrom node as string""" - return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, - _import_string(node.names)) - - def visit_functiondef(self, node): - """return an astroid.Function node as string""" - decorate = node.decorators.accept(self) if node.decorators else '' - docs = '\n%s"""%s"""' % (self.indent, node.doc) if node.doc else '' - return_annotation = '' - if six.PY3 and node.returns: - return_annotation = '->' + node.returns.as_string() - trailer = return_annotation + ":" - else: - trailer = ":" - def_format = "\n%sdef %s(%s)%s%s\n%s" - return def_format % (decorate, node.name, - node.args.accept(self), - trailer, docs, - self._stmt_list(node.body)) - - def visit_generatorexp(self, node): - """return an astroid.GeneratorExp node as string""" - return '(%s %s)' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_attribute(self, node): - """return an astroid.Getattr node as string""" - return '%s.%s' % (node.expr.accept(self), node.attrname) - - def visit_global(self, node): - """return an astroid.Global node as string""" - return 'global %s' % ', '.join(node.names) - - def visit_if(self, node): - """return an astroid.If node as string""" - ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))] - if node.orelse:# XXX use elif ??? - ifs.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(ifs) - - def visit_ifexp(self, node): - """return an astroid.IfExp node as string""" - return '%s if %s else %s' % (node.body.accept(self), - node.test.accept(self), - node.orelse.accept(self)) - - def visit_import(self, node): - """return an astroid.Import node as string""" - return 'import %s' % _import_string(node.names) - - def visit_keyword(self, node): - """return an astroid.Keyword node as string""" - if node.arg is None: - return '**%s' % node.value.accept(self) - return '%s=%s' % (node.arg, node.value.accept(self)) - - def visit_lambda(self, node): - """return an astroid.Lambda node as string""" - return 'lambda %s: %s' % (node.args.accept(self), - node.body.accept(self)) - - def visit_list(self, node): - """return an astroid.List node as string""" - return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_listcomp(self, node): - """return an astroid.ListComp node as string""" - return '[%s %s]' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_module(self, node): - """return an astroid.Module node as string""" - docs = '"""%s"""\n\n' % node.doc if node.doc else '' - return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' - - def visit_name(self, node): - """return an astroid.Name node as string""" - return node.name - - def visit_pass(self, node): - """return an astroid.Pass node as string""" - return 'pass' - - def visit_print(self, node): - """return an astroid.Print node as string""" - nodes = ', '.join([n.accept(self) for n in node.values]) - if not node.nl: - nodes = '%s,' % nodes - if node.dest: - return 'print >> %s, %s' % (node.dest.accept(self), nodes) - return 'print %s' % nodes - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.inst: - if node.tback: - return 'raise %s, %s, %s' % (node.exc.accept(self), - node.inst.accept(self), - node.tback.accept(self)) - return 'raise %s, %s' % (node.exc.accept(self), - node.inst.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_return(self, node): - """return an astroid.Return node as string""" - if node.value: - return 'return %s' % node.value.accept(self) - - return 'return' - - def visit_index(self, node): - """return a astroid.Index node as string""" - return node.value.accept(self) - - def visit_set(self, node): - """return an astroid.Set node as string""" - return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_setcomp(self, node): - """return an astroid.SetComp node as string""" - return '{%s %s}' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_slice(self, node): - """return a astroid.Slice node as string""" - lower = node.lower.accept(self) if node.lower else '' - upper = node.upper.accept(self) if node.upper else'' - step = node.step.accept(self) if node.step else '' - if step: - return '%s:%s:%s' % (lower, upper, step) - return '%s:%s' % (lower, upper) - - def visit_subscript(self, node): - """return an astroid.Subscript node as string""" - return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) - - def visit_tryexcept(self, node): - """return an astroid.TryExcept node as string""" - trys = ['try:\n%s' % self._stmt_list(node.body)] - for handler in node.handlers: - trys.append(handler.accept(self)) - if node.orelse: - trys.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(trys) - - def visit_tryfinally(self, node): - """return an astroid.TryFinally node as string""" - return 'try:\n%s\nfinally:\n%s' % (self._stmt_list(node.body), - self._stmt_list(node.finalbody)) - - def visit_tuple(self, node): - """return an astroid.Tuple node as string""" - if len(node.elts) == 1: - return '(%s, )' % node.elts[0].accept(self) - return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_unaryop(self, node): - """return an astroid.UnaryOp node as string""" - if node.op == 'not': - operator = 'not ' - else: - operator = node.op - return '%s%s' % (operator, node.operand.accept(self)) - - def visit_while(self, node): - """return an astroid.While node as string""" - whiles = 'while %s:\n%s' % (node.test.accept(self), - self._stmt_list(node.body)) - if node.orelse: - whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) - return whiles - - def visit_with(self, node): # 'with' without 'as' is possible - """return an astroid.With node as string""" - items = ', '.join(('(%s)' % expr.accept(self)) + - (vars and ' as (%s)' % (vars.accept(self)) or '') - for expr, vars in node.items) - return 'with %s:\n%s' % (items, self._stmt_list(node.body)) - - def visit_yield(self, node): - """yield an ast.Yield node as string""" - yi_val = (" " + node.value.accept(self)) if node.value else "" - expr = 'yield' + yi_val - if node.parent.is_statement: - return expr - - return "(%s)" % (expr,) - - def visit_starred(self, node): - """return Starred node as string""" - return "*" + node.value.accept(self) - - # These aren't for real AST nodes, but for inference objects. - - def visit_frozenset(self, node): - return node.parent.accept(self) - - def visit_super(self, node): - return node.parent.accept(self) - - def visit_uninferable(self, node): - return str(node) - - -class AsStringVisitor3(AsStringVisitor): - """AsStringVisitor3 overwrites some AsStringVisitor methods""" - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s as %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_nonlocal(self, node): - """return an astroid.Nonlocal node as string""" - return 'nonlocal %s' % ', '.join(node.names) - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.cause: - return 'raise %s from %s' % (node.exc.accept(self), - node.cause.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_yieldfrom(self, node): - """ Return an astroid.YieldFrom node as string. """ - yi_val = (" " + node.value.accept(self)) if node.value else "" - expr = 'yield from' + yi_val - if node.parent.is_statement: - return expr - - return "(%s)" % (expr,) - - def visit_asyncfunctiondef(self, node): - function = super(AsStringVisitor3, self).visit_functiondef(node) - return 'async ' + function.strip() - - def visit_await(self, node): - return 'await %s' % node.value.accept(self) - - def visit_asyncwith(self, node): - return 'async %s' % self.visit_with(node) - - def visit_asyncfor(self, node): - return 'async %s' % self.visit_for(node) - - def visit_joinedstr(self, node): - # Special treatment for constants, - # as we want to join literals not reprs - string = ''.join( - value.value if type(value).__name__ == 'Const' - else value.accept(self) - for value in node.values - ) - return "f'%s'" % string - - def visit_formattedvalue(self, node): - return '{%s}' % node.value.accept(self) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - return '%s%s' % ('async ' if node.is_async else '', - super(AsStringVisitor3, self).visit_comprehension(node)) - - -def _import_string(names): - """return a list of (name, asname) formatted as a string""" - _names = [] - for name, asname in names: - if asname is not None: - _names.append('%s as %s' % (name, asname)) - else: - _names.append(name) - return ', '.join(_names) - - -if sys.version_info >= (3, 0): - AsStringVisitor = AsStringVisitor3 - -# This sets the default indent to 4 spaces. -to_code = AsStringVisitor(' ') diff --git a/pymode/libs/astroid/astpeephole.py b/pymode/libs/astroid/astpeephole.py deleted file mode 100644 index dde5dca1..00000000 --- a/pymode/libs/astroid/astpeephole.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Small AST optimizations.""" - -import _ast - -from astroid import nodes - - -__all__ = ('ASTPeepholeOptimizer', ) - - -try: - _TYPES = (_ast.Str, _ast.Bytes) -except AttributeError: - _TYPES = (_ast.Str, ) - - -class ASTPeepholeOptimizer(object): - """Class for applying small optimizations to generate new AST.""" - - def optimize_binop(self, node, parent=None): - """Optimize BinOps with string Const nodes on the lhs. - - This fixes an infinite recursion crash, where multiple - strings are joined using the addition operator. With a - sufficient number of such strings, astroid will fail - with a maximum recursion limit exceeded. The - function will return a Const node with all the strings - already joined. - Return ``None`` if no AST node can be obtained - through optimization. - """ - ast_nodes = [] - current = node - while isinstance(current, _ast.BinOp): - # lhs must be a BinOp with the addition operand. - if not isinstance(current.left, _ast.BinOp): - return - if (not isinstance(current.left.op, _ast.Add) - or not isinstance(current.op, _ast.Add)): - return - - # rhs must a str / bytes. - if not isinstance(current.right, _TYPES): - return - - ast_nodes.append(current.right.s) - current = current.left - - if (isinstance(current, _ast.BinOp) - and isinstance(current.left, _TYPES) - and isinstance(current.right, _TYPES)): - # Stop early if we are at the last BinOp in - # the operation - ast_nodes.append(current.right.s) - ast_nodes.append(current.left.s) - break - - if not ast_nodes: - return - - # If we have inconsistent types, bail out. - known = type(ast_nodes[0]) - if any(not isinstance(element, known) - for element in ast_nodes[1:]): - return - - value = known().join(reversed(ast_nodes)) - newnode = nodes.Const(value, node.lineno, node.col_offset, parent) - return newnode diff --git a/pymode/libs/astroid/bases.py b/pymode/libs/astroid/bases.py deleted file mode 100644 index aefbaa1a..00000000 --- a/pymode/libs/astroid/bases.py +++ /dev/null @@ -1,450 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""This module contains base classes and functions for the nodes and some -inference utils. -""" - -import collections -import sys - -import six - -from astroid import context as contextmod -from astroid import exceptions -from astroid import util - -objectmodel = util.lazy_import('interpreter.objectmodel') -BUILTINS = six.moves.builtins.__name__ -manager = util.lazy_import('manager') -MANAGER = manager.AstroidManager() - -if sys.version_info >= (3, 0): - BUILTINS = 'builtins' - BOOL_SPECIAL_METHOD = '__bool__' -else: - BUILTINS = '__builtin__' - BOOL_SPECIAL_METHOD = '__nonzero__' -PROPERTIES = {BUILTINS + '.property', 'abc.abstractproperty'} -# List of possible property names. We use this list in order -# to see if a method is a property or not. This should be -# pretty reliable and fast, the alternative being to check each -# decorator to see if its a real property-like descriptor, which -# can be too complicated. -# Also, these aren't qualified, because each project can -# define them, we shouldn't expect to know every possible -# property-like decorator! -# TODO(cpopa): just implement descriptors already. -POSSIBLE_PROPERTIES = {"cached_property", "cachedproperty", - "lazyproperty", "lazy_property", "reify", - "lazyattribute", "lazy_attribute", - "LazyProperty", "lazy"} - - -def _is_property(meth): - if PROPERTIES.intersection(meth.decoratornames()): - return True - stripped = {name.split(".")[-1] for name in meth.decoratornames() - if name is not util.Uninferable} - return any(name in stripped for name in POSSIBLE_PROPERTIES) - - -class Proxy(object): - """a simple proxy object""" - - _proxied = None # proxied object may be set by class or by instance - - def __init__(self, proxied=None): - if proxied is not None: - self._proxied = proxied - - def __getattr__(self, name): - if name == '_proxied': - return getattr(self.__class__, '_proxied') - if name in self.__dict__: - return self.__dict__[name] - return getattr(self._proxied, name) - - def infer(self, context=None): - yield self - - -def _infer_stmts(stmts, context, frame=None): - """Return an iterator on statements inferred by each statement in *stmts*.""" - stmt = None - inferred = False - if context is not None: - name = context.lookupname - context = context.clone() - else: - name = None - context = contextmod.InferenceContext() - - for stmt in stmts: - if stmt is util.Uninferable: - yield stmt - inferred = True - continue - context.lookupname = stmt._infer_name(frame, name) - try: - for inferred in stmt.infer(context=context): - yield inferred - inferred = True - except exceptions.NameInferenceError: - continue - except exceptions.InferenceError: - yield util.Uninferable - inferred = True - if not inferred: - raise exceptions.InferenceError( - 'Inference failed for all members of {stmts!r}.', - stmts=stmts, frame=frame, context=context) - - -def _infer_method_result_truth(instance, method_name, context): - # Get the method from the instance and try to infer - # its return's truth value. - meth = next(instance.igetattr(method_name, context=context), None) - if meth and hasattr(meth, 'infer_call_result'): - if not meth.callable(): - return util.Uninferable - for value in meth.infer_call_result(instance, context=context): - if value is util.Uninferable: - return value - - inferred = next(value.infer(context=context)) - return inferred.bool_value() - return util.Uninferable - - -class BaseInstance(Proxy): - """An instance base class, which provides lookup methods for potential instances.""" - - special_attributes = None - - def display_type(self): - return 'Instance of' - - def getattr(self, name, context=None, lookupclass=True): - try: - values = self._proxied.instance_attr(name, context) - except exceptions.AttributeInferenceError: - if self.special_attributes and name in self.special_attributes: - return [self.special_attributes.lookup(name)] - - if lookupclass: - # Class attributes not available through the instance - # unless they are explicitly defined. - return self._proxied.getattr(name, context, - class_context=False) - - util.reraise(exceptions.AttributeInferenceError(target=self, - attribute=name, - context=context)) - # since we've no context information, return matching class members as - # well - if lookupclass: - try: - return values + self._proxied.getattr(name, context, - class_context=False) - except exceptions.AttributeInferenceError: - pass - return values - - def igetattr(self, name, context=None): - """inferred getattr""" - if not context: - context = contextmod.InferenceContext() - try: - # avoid recursively inferring the same attr on the same class - if context.push((self._proxied, name)): - return - - # XXX frame should be self._proxied, or not ? - get_attr = self.getattr(name, context, lookupclass=False) - for stmt in _infer_stmts(self._wrap_attr(get_attr, context), - context, frame=self): - yield stmt - except exceptions.AttributeInferenceError: - try: - # fallback to class.igetattr since it has some logic to handle - # descriptors - attrs = self._proxied.igetattr(name, context, class_context=False) - for stmt in self._wrap_attr(attrs, context): - yield stmt - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError(**vars(error))) - - def _wrap_attr(self, attrs, context=None): - """wrap bound methods of attrs in a InstanceMethod proxies""" - for attr in attrs: - if isinstance(attr, UnboundMethod): - if _is_property(attr): - for inferred in attr.infer_call_result(self, context): - yield inferred - else: - yield BoundMethod(attr, self) - elif hasattr(attr, 'name') and attr.name == '': - # This is a lambda function defined at class level, - # since its scope is the underlying _proxied class. - # Unfortunately, we can't do an isinstance check here, - # because of the circular dependency between astroid.bases - # and astroid.scoped_nodes. - if attr.statement().scope() == self._proxied: - if attr.args.args and attr.args.args[0].name == 'self': - yield BoundMethod(attr, self) - continue - yield attr - else: - yield attr - - def infer_call_result(self, caller, context=None): - """infer what a class instance is returning when called""" - inferred = False - for node in self._proxied.igetattr('__call__', context): - if node is util.Uninferable or not node.callable(): - continue - for res in node.infer_call_result(caller, context): - inferred = True - yield res - if not inferred: - raise exceptions.InferenceError(node=self, caller=caller, - context=context) - - -class Instance(BaseInstance): - """A special node representing a class instance.""" - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.InstanceModel()) - - def __repr__(self): - return '' % (self._proxied.root().name, - self._proxied.name, - id(self)) - def __str__(self): - return 'Instance of %s.%s' % (self._proxied.root().name, - self._proxied.name) - - def callable(self): - try: - self._proxied.getattr('__call__', class_context=False) - return True - except exceptions.AttributeInferenceError: - return False - - def pytype(self): - return self._proxied.qname() - - def display_type(self): - return 'Instance of' - - def bool_value(self): - """Infer the truth value for an Instance - - The truth value of an instance is determined by these conditions: - - * if it implements __bool__ on Python 3 or __nonzero__ - on Python 2, then its bool value will be determined by - calling this special method and checking its result. - * when this method is not defined, __len__() is called, if it - is defined, and the object is considered true if its result is - nonzero. If a class defines neither __len__() nor __bool__(), - all its instances are considered true. - """ - context = contextmod.InferenceContext() - context.callcontext = contextmod.CallContext(args=[]) - context.boundnode = self - - try: - result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context) - except (exceptions.InferenceError, exceptions.AttributeInferenceError): - # Fallback to __len__. - try: - result = _infer_method_result_truth(self, '__len__', context) - except (exceptions.AttributeInferenceError, exceptions.InferenceError): - return True - return result - - # TODO(cpopa): this is set in inference.py - # The circular dependency hell goes deeper and deeper. - def getitem(self, index, context=None): - pass - - -class UnboundMethod(Proxy): - """a special node representing a method not bound to an instance""" - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.UnboundMethodModel()) - - def __repr__(self): - frame = self._proxied.parent.frame() - return '<%s %s of %s at 0x%s' % (self.__class__.__name__, - self._proxied.name, - frame.qname(), id(self)) - - def is_bound(self): - return False - - def getattr(self, name, context=None): - if name in self.special_attributes: - return [self.special_attributes.lookup(name)] - return self._proxied.getattr(name, context) - - def igetattr(self, name, context=None): - if name in self.special_attributes: - return iter((self.special_attributes.lookup(name), )) - return self._proxied.igetattr(name, context) - - def infer_call_result(self, caller, context): - # If we're unbound method __new__ of builtin object, the result is an - # instance of the class given as first argument. - if (self._proxied.name == '__new__' and - self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - infer = caller.args[0].infer() if caller.args else [] - return (Instance(x) if x is not util.Uninferable else x for x in infer) - return self._proxied.infer_call_result(caller, context) - - def bool_value(self): - return True - - -class BoundMethod(UnboundMethod): - """a special node representing a method bound to an instance""" - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.BoundMethodModel()) - - def __init__(self, proxy, bound): - UnboundMethod.__init__(self, proxy) - self.bound = bound - - def is_bound(self): - return True - - def _infer_type_new_call(self, caller, context): - """Try to infer what type.__new__(mcs, name, bases, attrs) returns. - - In order for such call to be valid, the metaclass needs to be - a subtype of ``type``, the name needs to be a string, the bases - needs to be a tuple of classes and the attributes a dictionary - of strings to values. - """ - from astroid import node_classes - # Verify the metaclass - mcs = next(caller.args[0].infer(context=context)) - if mcs.__class__.__name__ != 'ClassDef': - # Not a valid first argument. - return - if not mcs.is_subtype_of("%s.type" % BUILTINS): - # Not a valid metaclass. - return - - # Verify the name - name = next(caller.args[1].infer(context=context)) - if name.__class__.__name__ != 'Const': - # Not a valid name, needs to be a const. - return - if not isinstance(name.value, str): - # Needs to be a string. - return - - # Verify the bases - bases = next(caller.args[2].infer(context=context)) - if bases.__class__.__name__ != 'Tuple': - # Needs to be a tuple. - return - inferred_bases = [next(elt.infer(context=context)) - for elt in bases.elts] - if any(base.__class__.__name__ != 'ClassDef' - for base in inferred_bases): - # All the bases needs to be Classes - return - - # Verify the attributes. - attrs = next(caller.args[3].infer(context=context)) - if attrs.__class__.__name__ != 'Dict': - # Needs to be a dictionary. - return - cls_locals = collections.defaultdict(list) - for key, value in attrs.items: - key = next(key.infer(context=context)) - value = next(value.infer(context=context)) - if key.__class__.__name__ != 'Const': - # Something invalid as an attribute. - return - if not isinstance(key.value, str): - # Not a proper attribute. - return - cls_locals[key.value].append(value) - - # Build the class from now. - cls = mcs.__class__(name=name.value, lineno=caller.lineno, - col_offset=caller.col_offset, - parent=caller) - empty = node_classes.Pass() - cls.postinit(bases=bases.elts, body=[empty], decorators=[], - newstyle=True, metaclass=mcs, keywords=[]) - cls.locals = cls_locals - return cls - - def infer_call_result(self, caller, context=None): - if context is None: - context = contextmod.InferenceContext() - context = context.clone() - context.boundnode = self.bound - - if (self.bound.__class__.__name__ == 'ClassDef' - and self.bound.name == 'type' - and self.name == '__new__' - and len(caller.args) == 4 - # TODO(cpopa): this check shouldn't be needed. - and self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - - # Check if we have an ``type.__new__(mcs, name, bases, attrs)`` call. - new_cls = self._infer_type_new_call(caller, context) - if new_cls: - return iter((new_cls, )) - - return super(BoundMethod, self).infer_call_result(caller, context) - - def bool_value(self): - return True - - -class Generator(BaseInstance): - """a special node representing a generator. - - Proxied class is set once for all in raw_building. - """ - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.GeneratorModel()) - - # pylint: disable=super-init-not-called - def __init__(self, parent=None): - self.parent = parent - - def callable(self): - return False - - def pytype(self): - return '%s.generator' % BUILTINS - - def display_type(self): - return 'Generator' - - def bool_value(self): - return True - - def __repr__(self): - return '' % (self._proxied.name, self.lineno, id(self)) - - def __str__(self): - return 'Generator(%s)' % (self._proxied.name) diff --git a/pymode/libs/astroid/brain/brain_builtin_inference.py b/pymode/libs/astroid/brain/brain_builtin_inference.py deleted file mode 100644 index 717dc78c..00000000 --- a/pymode/libs/astroid/brain/brain_builtin_inference.py +++ /dev/null @@ -1,496 +0,0 @@ -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Astroid hooks for various builtins.""" - -from functools import partial -import sys -from textwrap import dedent - -import six -from astroid import (MANAGER, UseInferenceDefault, AttributeInferenceError, - inference_tip, InferenceError, NameInferenceError) -from astroid import arguments -from astroid.builder import AstroidBuilder -from astroid import helpers -from astroid import nodes -from astroid import objects -from astroid import scoped_nodes -from astroid import util - -def _extend_str(class_node, rvalue): - """function to extend builtin str/unicode class""" - # TODO(cpopa): this approach will make astroid to believe - # that some arguments can be passed by keyword, but - # unfortunately, strings and bytes don't accept keyword arguments. - code = dedent(''' - class whatever(object): - def join(self, iterable): - return {rvalue} - def replace(self, old, new, count=None): - return {rvalue} - def format(self, *args, **kwargs): - return {rvalue} - def encode(self, encoding='ascii', errors=None): - return '' - def decode(self, encoding='ascii', errors=None): - return u'' - def capitalize(self): - return {rvalue} - def title(self): - return {rvalue} - def lower(self): - return {rvalue} - def upper(self): - return {rvalue} - def swapcase(self): - return {rvalue} - def index(self, sub, start=None, end=None): - return 0 - def find(self, sub, start=None, end=None): - return 0 - def count(self, sub, start=None, end=None): - return 0 - def strip(self, chars=None): - return {rvalue} - def lstrip(self, chars=None): - return {rvalue} - def rstrip(self, chars=None): - return {rvalue} - def rjust(self, width, fillchar=None): - return {rvalue} - def center(self, width, fillchar=None): - return {rvalue} - def ljust(self, width, fillchar=None): - return {rvalue} - ''') - code = code.format(rvalue=rvalue) - fake = AstroidBuilder(MANAGER).string_build(code)['whatever'] - for method in fake.mymethods(): - class_node.locals[method.name] = [method] - method.parent = class_node - -def extend_builtins(class_transforms): - from astroid.bases import BUILTINS - builtin_ast = MANAGER.astroid_cache[BUILTINS] - for class_name, transform in class_transforms.items(): - transform(builtin_ast[class_name]) - -if sys.version_info > (3, 0): - extend_builtins({'bytes': partial(_extend_str, rvalue="b''"), - 'str': partial(_extend_str, rvalue="''")}) -else: - extend_builtins({'str': partial(_extend_str, rvalue="''"), - 'unicode': partial(_extend_str, rvalue="u''")}) - - -def register_builtin_transform(transform, builtin_name): - """Register a new transform function for the given *builtin_name*. - - The transform function must accept two parameters, a node and - an optional context. - """ - def _transform_wrapper(node, context=None): - result = transform(node, context=context) - if result: - if not result.parent: - # Let the transformation function determine - # the parent for its result. Otherwise, - # we set it to be the node we transformed from. - result.parent = node - - result.lineno = node.lineno - result.col_offset = node.col_offset - return iter([result]) - - MANAGER.register_transform(nodes.Call, - inference_tip(_transform_wrapper), - lambda n: (isinstance(n.func, nodes.Name) and - n.func.name == builtin_name)) - - -def _generic_inference(node, context, node_type, transform): - args = node.args - if not args: - return node_type() - if len(node.args) > 1: - raise UseInferenceDefault() - - arg, = args - transformed = transform(arg) - if not transformed: - try: - inferred = next(arg.infer(context=context)) - except (InferenceError, StopIteration): - raise UseInferenceDefault() - if inferred is util.Uninferable: - raise UseInferenceDefault() - transformed = transform(inferred) - if not transformed or transformed is util.Uninferable: - raise UseInferenceDefault() - return transformed - - -def _generic_transform(arg, klass, iterables, build_elts): - if isinstance(arg, klass): - return arg - elif isinstance(arg, iterables): - if not all(isinstance(elt, nodes.Const) - for elt in arg.elts): - # TODO(cpopa): Don't support heterogenous elements. - # Not yet, though. - raise UseInferenceDefault() - elts = [elt.value for elt in arg.elts] - elif isinstance(arg, nodes.Dict): - if not all(isinstance(elt[0], nodes.Const) - for elt in arg.items): - raise UseInferenceDefault() - elts = [item[0].value for item in arg.items] - elif (isinstance(arg, nodes.Const) and - isinstance(arg.value, (six.string_types, six.binary_type))): - elts = arg.value - else: - return - return klass.from_constants(elts=build_elts(elts)) - - -def _infer_builtin(node, context, - klass=None, iterables=None, - build_elts=None): - transform_func = partial( - _generic_transform, - klass=klass, - iterables=iterables, - build_elts=build_elts) - - return _generic_inference(node, context, klass, transform_func) - -# pylint: disable=invalid-name -infer_tuple = partial( - _infer_builtin, - klass=nodes.Tuple, - iterables=(nodes.List, nodes.Set, objects.FrozenSet, - objects.DictItems, objects.DictKeys, - objects.DictValues), - build_elts=tuple) - -infer_list = partial( - _infer_builtin, - klass=nodes.List, - iterables=(nodes.Tuple, nodes.Set, objects.FrozenSet, - objects.DictItems, objects.DictKeys, - objects.DictValues), - build_elts=list) - -infer_set = partial( - _infer_builtin, - klass=nodes.Set, - iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, - objects.DictKeys), - build_elts=set) - -infer_frozenset = partial( - _infer_builtin, - klass=objects.FrozenSet, - iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, - objects.DictKeys), - build_elts=frozenset) - - -def _get_elts(arg, context): - is_iterable = lambda n: isinstance(n, - (nodes.List, nodes.Tuple, nodes.Set)) - try: - inferred = next(arg.infer(context)) - except (InferenceError, NameInferenceError): - raise UseInferenceDefault() - if isinstance(inferred, nodes.Dict): - items = inferred.items - elif is_iterable(inferred): - items = [] - for elt in inferred.elts: - # If an item is not a pair of two items, - # then fallback to the default inference. - # Also, take in consideration only hashable items, - # tuples and consts. We are choosing Names as well. - if not is_iterable(elt): - raise UseInferenceDefault() - if len(elt.elts) != 2: - raise UseInferenceDefault() - if not isinstance(elt.elts[0], - (nodes.Tuple, nodes.Const, nodes.Name)): - raise UseInferenceDefault() - items.append(tuple(elt.elts)) - else: - raise UseInferenceDefault() - return items - -def infer_dict(node, context=None): - """Try to infer a dict call to a Dict node. - - The function treats the following cases: - - * dict() - * dict(mapping) - * dict(iterable) - * dict(iterable, **kwargs) - * dict(mapping, **kwargs) - * dict(**kwargs) - - If a case can't be inferred, we'll fallback to default inference. - """ - call = arguments.CallSite.from_call(node) - if call.has_invalid_arguments() or call.has_invalid_keywords(): - raise UseInferenceDefault - - args = call.positional_arguments - kwargs = list(call.keyword_arguments.items()) - - if not args and not kwargs: - # dict() - return nodes.Dict() - elif kwargs and not args: - # dict(a=1, b=2, c=4) - items = [(nodes.Const(key), value) for key, value in kwargs] - elif len(args) == 1 and kwargs: - # dict(some_iterable, b=2, c=4) - elts = _get_elts(args[0], context) - keys = [(nodes.Const(key), value) for key, value in kwargs] - items = elts + keys - elif len(args) == 1: - items = _get_elts(args[0], context) - else: - raise UseInferenceDefault() - - value = nodes.Dict(col_offset=node.col_offset, - lineno=node.lineno, - parent=node.parent) - value.postinit(items) - return value - - -def infer_super(node, context=None): - """Understand super calls. - - There are some restrictions for what can be understood: - - * unbounded super (one argument form) is not understood. - - * if the super call is not inside a function (classmethod or method), - then the default inference will be used. - - * if the super arguments can't be inferred, the default inference - will be used. - """ - if len(node.args) == 1: - # Ignore unbounded super. - raise UseInferenceDefault - - scope = node.scope() - if not isinstance(scope, nodes.FunctionDef): - # Ignore non-method uses of super. - raise UseInferenceDefault - if scope.type not in ('classmethod', 'method'): - # Not interested in staticmethods. - raise UseInferenceDefault - - cls = scoped_nodes.get_wrapping_class(scope) - if not len(node.args): - mro_pointer = cls - # In we are in a classmethod, the interpreter will fill - # automatically the class as the second argument, not an instance. - if scope.type == 'classmethod': - mro_type = cls - else: - mro_type = cls.instantiate_class() - else: - # TODO(cpopa): support flow control (multiple inference values). - try: - mro_pointer = next(node.args[0].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - try: - mro_type = next(node.args[1].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - - if mro_pointer is util.Uninferable or mro_type is util.Uninferable: - # No way we could understand this. - raise UseInferenceDefault - - super_obj = objects.Super(mro_pointer=mro_pointer, - mro_type=mro_type, - self_class=cls, - scope=scope) - super_obj.parent = node - return super_obj - - -def _infer_getattr_args(node, context): - if len(node.args) not in (2, 3): - # Not a valid getattr call. - raise UseInferenceDefault - - try: - # TODO(cpopa): follow all the values of the first argument? - obj = next(node.args[0].infer(context=context)) - attr = next(node.args[1].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - - if obj is util.Uninferable or attr is util.Uninferable: - # If one of the arguments is something we can't infer, - # then also make the result of the getattr call something - # which is unknown. - return util.Uninferable, util.Uninferable - - is_string = (isinstance(attr, nodes.Const) and - isinstance(attr.value, six.string_types)) - if not is_string: - raise UseInferenceDefault - - return obj, attr.value - - -def infer_getattr(node, context=None): - """Understand getattr calls - - If one of the arguments is an Uninferable object, then the - result will be an Uninferable object. Otherwise, the normal attribute - lookup will be done. - """ - obj, attr = _infer_getattr_args(node, context) - if obj is util.Uninferable or attr is util.Uninferable or not hasattr(obj, 'igetattr'): - return util.Uninferable - - try: - return next(obj.igetattr(attr, context=context)) - except (StopIteration, InferenceError, AttributeInferenceError): - if len(node.args) == 3: - # Try to infer the default and return it instead. - try: - return next(node.args[2].infer(context=context)) - except InferenceError: - raise UseInferenceDefault - - raise UseInferenceDefault - - -def infer_hasattr(node, context=None): - """Understand hasattr calls - - This always guarantees three possible outcomes for calling - hasattr: Const(False) when we are sure that the object - doesn't have the intended attribute, Const(True) when - we know that the object has the attribute and Uninferable - when we are unsure of the outcome of the function call. - """ - try: - obj, attr = _infer_getattr_args(node, context) - if obj is util.Uninferable or attr is util.Uninferable or not hasattr(obj, 'getattr'): - return util.Uninferable - obj.getattr(attr, context=context) - except UseInferenceDefault: - # Can't infer something from this function call. - return util.Uninferable - except AttributeInferenceError: - # Doesn't have it. - return nodes.Const(False) - return nodes.Const(True) - - -def infer_callable(node, context=None): - """Understand callable calls - - This follows Python's semantics, where an object - is callable if it provides an attribute __call__, - even though that attribute is something which can't be - called. - """ - if len(node.args) != 1: - # Invalid callable call. - raise UseInferenceDefault - - argument = node.args[0] - try: - inferred = next(argument.infer(context=context)) - except InferenceError: - return util.Uninferable - if inferred is util.Uninferable: - return util.Uninferable - return nodes.Const(inferred.callable()) - - -def infer_bool(node, context=None): - """Understand bool calls.""" - if len(node.args) > 1: - # Invalid bool call. - raise UseInferenceDefault - - if not node.args: - return nodes.Const(False) - - argument = node.args[0] - try: - inferred = next(argument.infer(context=context)) - except InferenceError: - return util.Uninferable - if inferred is util.Uninferable: - return util.Uninferable - - bool_value = inferred.bool_value() - if bool_value is util.Uninferable: - return util.Uninferable - return nodes.Const(bool_value) - - -def infer_type(node, context=None): - """Understand the one-argument form of *type*.""" - if len(node.args) != 1: - raise UseInferenceDefault - - return helpers.object_type(node.args[0], context) - - -def infer_slice(node, context=None): - """Understand `slice` calls.""" - args = node.args - if not 0 < len(args) <= 3: - raise UseInferenceDefault - - args = list(map(helpers.safe_infer, args)) - for arg in args: - if not arg or arg is util.Uninferable: - raise UseInferenceDefault - if not isinstance(arg, nodes.Const): - raise UseInferenceDefault - if not isinstance(arg.value, (type(None), int)): - raise UseInferenceDefault - - if len(args) < 3: - # Make sure we have 3 arguments. - args.extend([None] * (3 - len(args))) - - slice_node = nodes.Slice(lineno=node.lineno, - col_offset=node.col_offset, - parent=node.parent) - slice_node.postinit(*args) - return slice_node - - -# Builtins inference -register_builtin_transform(infer_bool, 'bool') -register_builtin_transform(infer_super, 'super') -register_builtin_transform(infer_callable, 'callable') -register_builtin_transform(infer_getattr, 'getattr') -register_builtin_transform(infer_hasattr, 'hasattr') -register_builtin_transform(infer_tuple, 'tuple') -register_builtin_transform(infer_set, 'set') -register_builtin_transform(infer_list, 'list') -register_builtin_transform(infer_dict, 'dict') -register_builtin_transform(infer_frozenset, 'frozenset') -register_builtin_transform(infer_type, 'type') -register_builtin_transform(infer_slice, 'slice') diff --git a/pymode/libs/astroid/brain/brain_collections.py b/pymode/libs/astroid/brain/brain_collections.py deleted file mode 100644 index 77343377..00000000 --- a/pymode/libs/astroid/brain/brain_collections.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -import astroid - - -def _collections_transform(): - return astroid.parse(''' - class defaultdict(dict): - default_factory = None - def __missing__(self, key): pass - def __getitem__(self, key): return default_factory - - class deque(object): - maxlen = 0 - def __init__(self, iterable=None, maxlen=None): - self.iterable = iterable - def append(self, x): pass - def appendleft(self, x): pass - def clear(self): pass - def count(self, x): return 0 - def extend(self, iterable): pass - def extendleft(self, iterable): pass - def pop(self): pass - def popleft(self): pass - def remove(self, value): pass - def reverse(self): pass - def rotate(self, n): pass - def __iter__(self): return self - def __reversed__(self): return self.iterable[::-1] - def __getitem__(self, index): pass - def __setitem__(self, index, value): pass - def __delitem__(self, index): pass - - def OrderedDict(dict): - def __reversed__(self): return self[::-1] - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'collections', _collections_transform) - diff --git a/pymode/libs/astroid/brain/brain_dateutil.py b/pymode/libs/astroid/brain/brain_dateutil.py deleted file mode 100644 index e9aafd25..00000000 --- a/pymode/libs/astroid/brain/brain_dateutil.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa -# Copyright (c) 2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Astroid hooks for dateutil""" - -import textwrap - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - -def dateutil_transform(): - return AstroidBuilder(MANAGER).string_build(textwrap.dedent(''' - import datetime - def parse(timestr, parserinfo=None, **kwargs): - return datetime.datetime() - ''')) - -register_module_extender(MANAGER, 'dateutil.parser', dateutil_transform) diff --git a/pymode/libs/astroid/brain/brain_fstrings.py b/pymode/libs/astroid/brain/brain_fstrings.py deleted file mode 100644 index e4c93571..00000000 --- a/pymode/libs/astroid/brain/brain_fstrings.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2017 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -import sys - -import astroid - - -def _clone_node_with_lineno(node, parent, lineno): - cls = node.__class__ - other_fields = node._other_fields - _astroid_fields = node._astroid_fields - init_params = { - 'lineno': lineno, - 'col_offset': node.col_offset, - 'parent': parent - } - postinit_params = { - param: getattr(node, param) - for param in _astroid_fields - } - if other_fields: - init_params.update({ - param: getattr(node, param) - for param in other_fields - }) - new_node = cls(**init_params) - if hasattr(node, 'postinit'): - new_node.postinit(**postinit_params) - return new_node - - -def _transform_formatted_value(node): - if node.value and node.value.lineno == 1: - if node.lineno != node.value.lineno: - new_node = astroid.FormattedValue( - lineno=node.lineno, - col_offset=node.col_offset, - parent=node.parent - ) - new_value = _clone_node_with_lineno( - node=node.value, - lineno=node.lineno, - parent=new_node - ) - new_node.postinit(value=new_value, - format_spec=node.format_spec) - return new_node - - -if sys.version_info[:2] >= (3, 6): - # TODO: this fix tries to *patch* http://bugs.python.org/issue29051 - # The problem is that FormattedValue.value, which is a Name node, - # has wrong line numbers, usually 1. This creates problems for pylint, - # which expects correct line numbers for things such as message control. - astroid.MANAGER.register_transform( - astroid.FormattedValue, - _transform_formatted_value) - diff --git a/pymode/libs/astroid/brain/brain_functools.py b/pymode/libs/astroid/brain/brain_functools.py deleted file mode 100644 index 6dcf7cd9..00000000 --- a/pymode/libs/astroid/brain/brain_functools.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -"""Astroid hooks for understanding functools library module.""" - -import astroid -from astroid import BoundMethod -from astroid import extract_node -from astroid import helpers -from astroid.interpreter import objectmodel -from astroid import MANAGER - - -LRU_CACHE = 'functools.lru_cache' - - -class LruWrappedModel(objectmodel.FunctionModel): - """Special attribute model for functions decorated with functools.lru_cache. - - The said decorators patches at decoration time some functions onto - the decorated function. - """ - - @property - def py__wrapped__(self): - return self._instance - - @property - def pycache_info(self): - cache_info = extract_node(''' - from functools import _CacheInfo - _CacheInfo(0, 0, 0, 0) - ''') - class CacheInfoBoundMethod(BoundMethod): - def infer_call_result(self, caller, context=None): - yield helpers.safe_infer(cache_info) - - return CacheInfoBoundMethod(proxy=self._instance, bound=self._instance) - - @property - def pycache_clear(self): - node = extract_node('''def cache_clear(self): pass''') - return BoundMethod(proxy=node, bound=self._instance.parent.scope()) - - -def _transform_lru_cache(node, context=None): - # TODO: this is not ideal, since the node should be immutable, - # but due to https://github.com/PyCQA/astroid/issues/354, - # there's not much we can do now. - # Replacing the node would work partially, because, - # in pylint, the old node would still be available, leading - # to spurious false positives. - node.special_attributes = LruWrappedModel()(node) - return - - -def _looks_like_lru_cache(node): - """Check if the given function node is decorated with lru_cache.""" - if not node.decorators: - return False - - for decorator in node.decorators.nodes: - if not isinstance(decorator, astroid.Call): - continue - - func = helpers.safe_infer(decorator.func) - if func in (None, astroid.Uninferable): - continue - - if isinstance(func, astroid.FunctionDef) and func.qname() == LRU_CACHE: - return True - return False - - -MANAGER.register_transform(astroid.FunctionDef, _transform_lru_cache, - _looks_like_lru_cache) diff --git a/pymode/libs/astroid/brain/brain_gi.py b/pymode/libs/astroid/brain/brain_gi.py deleted file mode 100644 index 03c19c0a..00000000 --- a/pymode/libs/astroid/brain/brain_gi.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Astroid hooks for the Python 2 GObject introspection bindings. - -Helps with understanding everything imported from 'gi.repository' -""" - -import inspect -import itertools -import sys -import re -import warnings - -from astroid import MANAGER, AstroidBuildingError, nodes -from astroid.builder import AstroidBuilder - - -_inspected_modules = {} - -_identifier_re = r'^[A-Za-z_]\w*$' - -def _gi_build_stub(parent): - """ - Inspect the passed module recursively and build stubs for functions, - classes, etc. - """ - classes = {} - functions = {} - constants = {} - methods = {} - for name in dir(parent): - if name.startswith("__"): - continue - - # Check if this is a valid name in python - if not re.match(_identifier_re, name): - continue - - try: - obj = getattr(parent, name) - except: - continue - - if inspect.isclass(obj): - classes[name] = obj - elif (inspect.isfunction(obj) or - inspect.isbuiltin(obj)): - functions[name] = obj - elif (inspect.ismethod(obj) or - inspect.ismethoddescriptor(obj)): - methods[name] = obj - elif (str(obj).startswith(", ) - # Only accept function calls with two constant arguments - if len(node.args) != 2: - return False - - if not all(isinstance(arg, nodes.Const) for arg in node.args): - return False - - func = node.func - if isinstance(func, nodes.Attribute): - if func.attrname != 'require_version': - return False - if isinstance(func.expr, nodes.Name) and func.expr.name == 'gi': - return True - - return False - - if isinstance(func, nodes.Name): - return func.name == 'require_version' - - return False - -def _register_require_version(node): - # Load the gi.require_version locally - try: - import gi - gi.require_version(node.args[0].value, node.args[1].value) - except Exception: - pass - - return node - -MANAGER.register_failed_import_hook(_import_gi_module) -MANAGER.register_transform(nodes.Call, _register_require_version, _looks_like_require_version) diff --git a/pymode/libs/astroid/brain/brain_hashlib.py b/pymode/libs/astroid/brain/brain_hashlib.py deleted file mode 100644 index aacd1743..00000000 --- a/pymode/libs/astroid/brain/brain_hashlib.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -import six - -import astroid - - -def _hashlib_transform(): - template = ''' - class %(name)s(object): - def __init__(self, value=''): pass - def digest(self): - return %(digest)s - def copy(self): - return self - def update(self, value): pass - def hexdigest(self): - return '' - @property - def name(self): - return %(name)r - @property - def block_size(self): - return 1 - @property - def digest_size(self): - return 1 - ''' - algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') - classes = "".join( - template % {'name': hashfunc, 'digest': 'b""' if six.PY3 else '""'} - for hashfunc in algorithms) - return astroid.parse(classes) - - -astroid.register_module_extender(astroid.MANAGER, 'hashlib', _hashlib_transform) - diff --git a/pymode/libs/astroid/brain/brain_io.py b/pymode/libs/astroid/brain/brain_io.py deleted file mode 100644 index be8d30c8..00000000 --- a/pymode/libs/astroid/brain/brain_io.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -'''Astroid brain hints for some of the _io C objects.''' - -import astroid - - -BUFFERED = {'BufferedWriter', 'BufferedReader'} -TextIOWrapper = 'TextIOWrapper' -FileIO = 'FileIO' -BufferedWriter = 'BufferedWriter' - - -def _generic_io_transform(node, name, cls): - '''Transform the given name, by adding the given *class* as a member of the node.''' - - io_module = astroid.MANAGER.ast_from_module_name('_io') - attribute_object = io_module[cls] - instance = attribute_object.instantiate_class() - node.locals[name] = [instance] - - -def _transform_text_io_wrapper(node): - # This is not always correct, since it can vary with the type of the descriptor, - # being stdout, stderr or stdin. But we cannot get access to the name of the - # stream, which is why we are using the BufferedWriter class as a default - # value - return _generic_io_transform(node, name='buffer', cls=BufferedWriter) - - -def _transform_buffered(node): - return _generic_io_transform(node, name='raw', cls=FileIO) - - -astroid.MANAGER.register_transform(astroid.ClassDef, - _transform_buffered, - lambda node: node.name in BUFFERED) -astroid.MANAGER.register_transform(astroid.ClassDef, - _transform_text_io_wrapper, - lambda node: node.name == TextIOWrapper) diff --git a/pymode/libs/astroid/brain/brain_mechanize.py b/pymode/libs/astroid/brain/brain_mechanize.py deleted file mode 100644 index afc65059..00000000 --- a/pymode/libs/astroid/brain/brain_mechanize.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - -def mechanize_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -class Browser(object): - def open(self, url, data=None, timeout=None): - return None - def open_novisit(self, url, data=None, timeout=None): - return None - def open_local_file(self, filename): - return None - -''') - - -register_module_extender(MANAGER, 'mechanize', mechanize_transform) diff --git a/pymode/libs/astroid/brain/brain_multiprocessing.py b/pymode/libs/astroid/brain/brain_multiprocessing.py deleted file mode 100644 index a19bac69..00000000 --- a/pymode/libs/astroid/brain/brain_multiprocessing.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -import sys - -import astroid -from astroid import exceptions - - -PY34 = sys.version_info >= (3, 4) - - -def _multiprocessing_transform(): - module = astroid.parse(''' - from multiprocessing.managers import SyncManager - def Manager(): - return SyncManager() - ''') - if not PY34: - return module - - # On Python 3.4, multiprocessing uses a getattr lookup inside contexts, - # in order to get the attributes they need. Since it's extremely - # dynamic, we use this approach to fake it. - node = astroid.parse(''' - from multiprocessing.context import DefaultContext, BaseContext - default = DefaultContext() - base = BaseContext() - ''') - try: - context = next(node['default'].infer()) - base = next(node['base'].infer()) - except exceptions.InferenceError: - return module - - for node in (context, base): - for key, value in node.locals.items(): - if key.startswith("_"): - continue - - value = value[0] - if isinstance(value, astroid.FunctionDef): - # We need to rebound this, since otherwise - # it will have an extra argument (self). - value = astroid.BoundMethod(value, node) - module[key] = value - return module - - -def _multiprocessing_managers_transform(): - return astroid.parse(''' - import array - import threading - import multiprocessing.pool as pool - - import six - - class Namespace(object): - pass - - class Value(object): - def __init__(self, typecode, value, lock=True): - self._typecode = typecode - self._value = value - def get(self): - return self._value - def set(self, value): - self._value = value - def __repr__(self): - return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value) - value = property(get, set) - - def Array(typecode, sequence, lock=True): - return array.array(typecode, sequence) - - class SyncManager(object): - Queue = JoinableQueue = six.moves.queue.Queue - Event = threading.Event - RLock = threading.RLock - BoundedSemaphore = threading.BoundedSemaphore - Condition = threading.Condition - Barrier = threading.Barrier - Pool = pool.Pool - list = list - dict = dict - Value = Value - Array = Array - Namespace = Namespace - __enter__ = lambda self: self - __exit__ = lambda *args: args - - def start(self, initializer=None, initargs=None): - pass - def shutdown(self): - pass - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'multiprocessing.managers', - _multiprocessing_managers_transform) -astroid.register_module_extender(astroid.MANAGER, 'multiprocessing', - _multiprocessing_transform) diff --git a/pymode/libs/astroid/brain/brain_namedtuple_enum.py b/pymode/libs/astroid/brain/brain_namedtuple_enum.py deleted file mode 100644 index b99cb761..00000000 --- a/pymode/libs/astroid/brain/brain_namedtuple_enum.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) 2012-2015 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Astroid hooks for the Python standard library.""" - -import functools -import sys -import keyword -from textwrap import dedent - -from astroid import ( - MANAGER, UseInferenceDefault, inference_tip, - InferenceError) -from astroid import arguments -from astroid import exceptions -from astroid import nodes -from astroid.builder import AstroidBuilder, extract_node -from astroid import util - -PY3K = sys.version_info > (3, 0) -PY33 = sys.version_info >= (3, 3) -PY34 = sys.version_info >= (3, 4) - -# general function - -def infer_func_form(node, base_type, context=None, enum=False): - """Specific inference function for namedtuple or Python 3 enum. """ - def infer_first(node): - if node is util.Uninferable: - raise UseInferenceDefault - try: - value = next(node.infer(context=context)) - if value is util.Uninferable: - raise UseInferenceDefault() - else: - return value - except StopIteration: - raise InferenceError() - - # node is a Call node, class name as first argument and generated class - # attributes as second argument - if len(node.args) != 2: - # something weird here, go back to class implementation - raise UseInferenceDefault() - # namedtuple or enums list of attributes can be a list of strings or a - # whitespace-separate string - try: - name = infer_first(node.args[0]).value - names = infer_first(node.args[1]) - try: - attributes = names.value.replace(',', ' ').split() - except AttributeError: - if not enum: - attributes = [infer_first(const).value for const in names.elts] - else: - # Enums supports either iterator of (name, value) pairs - # or mappings. - # TODO: support only list, tuples and mappings. - if hasattr(names, 'items') and isinstance(names.items, list): - attributes = [infer_first(const[0]).value - for const in names.items - if isinstance(const[0], nodes.Const)] - elif hasattr(names, 'elts'): - # Enums can support either ["a", "b", "c"] - # or [("a", 1), ("b", 2), ...], but they can't - # be mixed. - if all(isinstance(const, nodes.Tuple) - for const in names.elts): - attributes = [infer_first(const.elts[0]).value - for const in names.elts - if isinstance(const, nodes.Tuple)] - else: - attributes = [infer_first(const).value - for const in names.elts] - else: - raise AttributeError - if not attributes: - raise AttributeError - except (AttributeError, exceptions.InferenceError): - raise UseInferenceDefault() - - # If we can't infer the name of the class, don't crash, up to this point - # we know it is a namedtuple anyway. - name = name or 'Uninferable' - # we want to return a Class node instance with proper attributes set - class_node = nodes.ClassDef(name, 'docstring') - class_node.parent = node.parent - # set base class=tuple - class_node.bases.append(base_type) - # XXX add __init__(*attributes) method - for attr in attributes: - fake_node = nodes.EmptyNode() - fake_node.parent = class_node - fake_node.attrname = attr - class_node.instance_attrs[attr] = [fake_node] - return class_node, name, attributes - - -def _looks_like(node, name): - func = node.func - if isinstance(func, nodes.Attribute): - return func.attrname == name - if isinstance(func, nodes.Name): - return func.name == name - return False - -_looks_like_namedtuple = functools.partial(_looks_like, name='namedtuple') -_looks_like_enum = functools.partial(_looks_like, name='Enum') - - -def infer_named_tuple(node, context=None): - """Specific inference function for namedtuple Call node""" - class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, - context=context) - call_site = arguments.CallSite.from_call(node) - func = next(extract_node('import collections; collections.namedtuple').infer()) - try: - rename = next(call_site.infer_argument(func, 'rename', context)).bool_value() - except InferenceError: - rename = False - - if rename: - attributes = _get_renamed_namedtuple_atributes(attributes) - - field_def = (" {name} = property(lambda self: self[{index:d}], " - "doc='Alias for field number {index:d}')") - field_defs = '\n'.join(field_def.format(name=name, index=index) - for index, name in enumerate(attributes)) - fake = AstroidBuilder(MANAGER).string_build(''' -class %(name)s(tuple): - __slots__ = () - _fields = %(fields)r - def _asdict(self): - return self.__dict__ - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - return new(cls, iterable) - def _replace(self, **kwds): - return self - def __getnewargs__(self): - return tuple(self) -%(field_defs)s - ''' % {'name': name, 'fields': attributes, 'field_defs': field_defs}) - class_node.locals['_asdict'] = fake.body[0].locals['_asdict'] - class_node.locals['_make'] = fake.body[0].locals['_make'] - class_node.locals['_replace'] = fake.body[0].locals['_replace'] - class_node.locals['_fields'] = fake.body[0].locals['_fields'] - for attr in attributes: - class_node.locals[attr] = fake.body[0].locals[attr] - # we use UseInferenceDefault, we can't be a generator so return an iterator - return iter([class_node]) - - -def _get_renamed_namedtuple_atributes(field_names): - names = list(field_names) - seen = set() - for i, name in enumerate(field_names): - if (not all(c.isalnum() or c == '_' for c in name) or keyword.iskeyword(name) - or not name or name[0].isdigit() or name.startswith('_') or name in seen): - names[i] = '_%d' % i - seen.add(name) - return tuple(names) - - -def infer_enum(node, context=None): - """ Specific inference function for enum Call node. """ - enum_meta = extract_node(''' - class EnumMeta(object): - 'docstring' - def __call__(self, node): - class EnumAttribute(object): - name = '' - value = 0 - return EnumAttribute() - ''') - class_node = infer_func_form(node, enum_meta, - context=context, enum=True)[0] - return iter([class_node.instantiate_class()]) - - -def infer_enum_class(node): - """ Specific inference for enums. """ - names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) - for basename in node.basenames: - # TODO: doesn't handle subclasses yet. This implementation - # is a hack to support enums. - if basename not in names: - continue - if node.root().name == 'enum': - # Skip if the class is directly from enum module. - break - for local, values in node.locals.items(): - if any(not isinstance(value, nodes.AssignName) - for value in values): - continue - - stmt = values[0].statement() - if isinstance(stmt.targets[0], nodes.Tuple): - targets = stmt.targets[0].itered() - else: - targets = stmt.targets - - new_targets = [] - for target in targets: - # Replace all the assignments with our mocked class. - classdef = dedent(''' - class %(name)s(%(types)s): - @property - def value(self): - # Not the best return. - return None - @property - def name(self): - return %(name)r - ''' % {'name': target.name, 'types': ', '.join(node.basenames)}) - fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] - fake.parent = target.parent - for method in node.mymethods(): - fake.locals[method.name] = [method] - new_targets.append(fake.instantiate_class()) - node.locals[local] = new_targets - break - return node - - -MANAGER.register_transform(nodes.Call, inference_tip(infer_named_tuple), - _looks_like_namedtuple) -MANAGER.register_transform(nodes.Call, inference_tip(infer_enum), - _looks_like_enum) -MANAGER.register_transform(nodes.ClassDef, infer_enum_class) diff --git a/pymode/libs/astroid/brain/brain_nose.py b/pymode/libs/astroid/brain/brain_nose.py deleted file mode 100644 index cc8939bd..00000000 --- a/pymode/libs/astroid/brain/brain_nose.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -"""Hooks for nose library.""" - -import re -import textwrap - -import astroid -import astroid.builder - -_BUILDER = astroid.builder.AstroidBuilder(astroid.MANAGER) - - -def _pep8(name, caps=re.compile('([A-Z])')): - return caps.sub(lambda m: '_' + m.groups()[0].lower(), name) - - -def _nose_tools_functions(): - """Get an iterator of names and bound methods.""" - module = _BUILDER.string_build(textwrap.dedent(''' - import unittest - - class Test(unittest.TestCase): - pass - a = Test() - ''')) - try: - case = next(module['a'].infer()) - except astroid.InferenceError: - return - for method in case.methods(): - if method.name.startswith('assert') and '_' not in method.name: - pep8_name = _pep8(method.name) - yield pep8_name, astroid.BoundMethod(method, case) - if method.name == 'assertEqual': - # nose also exports assert_equals. - yield 'assert_equals', astroid.BoundMethod(method, case) - - -def _nose_tools_transform(node): - for method_name, method in _nose_tools_functions(): - node.locals[method_name] = [method] - - -def _nose_tools_trivial_transform(): - """Custom transform for the nose.tools module.""" - stub = _BUILDER.string_build('''__all__ = []''') - all_entries = ['ok_', 'eq_'] - - for pep8_name, method in _nose_tools_functions(): - all_entries.append(pep8_name) - stub[pep8_name] = method - - # Update the __all__ variable, since nose.tools - # does this manually with .append. - all_assign = stub['__all__'].parent - all_object = astroid.List(all_entries) - all_object.parent = all_assign - all_assign.value = all_object - return stub - - -astroid.register_module_extender(astroid.MANAGER, 'nose.tools.trivial', - _nose_tools_trivial_transform) -astroid.MANAGER.register_transform(astroid.Module, _nose_tools_transform, - lambda n: n.name == 'nose.tools') diff --git a/pymode/libs/astroid/brain/brain_numpy.py b/pymode/libs/astroid/brain/brain_numpy.py deleted file mode 100644 index 8acfe053..00000000 --- a/pymode/libs/astroid/brain/brain_numpy.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -"""Astroid hooks for numpy.""" - -import astroid - - -# TODO(cpopa): drop when understanding augmented assignments - -def numpy_core_transform(): - return astroid.parse(''' - from numpy.core import numeric - from numpy.core import fromnumeric - from numpy.core import defchararray - from numpy.core import records - from numpy.core import function_base - from numpy.core import machar - from numpy.core import getlimits - from numpy.core import shape_base - __all__ = (['char', 'rec', 'memmap', 'chararray'] + numeric.__all__ + - fromnumeric.__all__ + - records.__all__ + - function_base.__all__ + - machar.__all__ + - getlimits.__all__ + - shape_base.__all__) - ''') - - -def numpy_transform(): - return astroid.parse(''' - from numpy import core - from numpy import matrixlib as _mat - from numpy import lib - __all__ = ['add_newdocs', - 'ModuleDeprecationWarning', - 'VisibleDeprecationWarning', 'linalg', 'fft', 'random', - 'ctypeslib', 'ma', - '__version__', 'pkgload', 'PackageLoader', - 'show_config'] + core.__all__ + _mat.__all__ + lib.__all__ - - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'numpy.core', numpy_core_transform) -astroid.register_module_extender(astroid.MANAGER, 'numpy', numpy_transform) diff --git a/pymode/libs/astroid/brain/brain_pkg_resources.py b/pymode/libs/astroid/brain/brain_pkg_resources.py deleted file mode 100644 index bd60ecd3..00000000 --- a/pymode/libs/astroid/brain/brain_pkg_resources.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -import astroid -from astroid import parse -from astroid import inference_tip -from astroid import register_module_extender -from astroid import MANAGER - - -def pkg_resources_transform(): - return parse(''' -def require(*requirements): - return pkg_resources.working_set.require(*requirements) - -def run_script(requires, script_name): - return pkg_resources.working_set.run_script(requires, script_name) - -def iter_entry_points(group, name=None): - return pkg_resources.working_set.iter_entry_points(group, name) - -def resource_exists(package_or_requirement, resource_name): - return get_provider(package_or_requirement).has_resource(resource_name) - -def resource_isdir(package_or_requirement, resource_name): - return get_provider(package_or_requirement).resource_isdir( - resource_name) - -def resource_filename(package_or_requirement, resource_name): - return get_provider(package_or_requirement).get_resource_filename( - self, resource_name) - -def resource_stream(package_or_requirement, resource_name): - return get_provider(package_or_requirement).get_resource_stream( - self, resource_name) - -def resource_string(package_or_requirement, resource_name): - return get_provider(package_or_requirement).get_resource_string( - self, resource_name) - -def resource_listdir(package_or_requirement, resource_name): - return get_provider(package_or_requirement).resource_listdir( - resource_name) - -def extraction_error(): - pass - -def get_cache_path(archive_name, names=()): - extract_path = self.extraction_path or get_default_cache() - target_path = os.path.join(extract_path, archive_name+'-tmp', *names) - return target_path - -def postprocess(tempname, filename): - pass - -def set_extraction_path(path): - pass - -def cleanup_resources(force=False): - pass - -def get_distribution(dist): - return Distribution(dist) - -''') - -register_module_extender(MANAGER, 'pkg_resources', pkg_resources_transform) diff --git a/pymode/libs/astroid/brain/brain_pytest.py b/pymode/libs/astroid/brain/brain_pytest.py deleted file mode 100644 index 23e2db8e..00000000 --- a/pymode/libs/astroid/brain/brain_pytest.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Astroid hooks for pytest.""" -from __future__ import absolute_import -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - - -def pytest_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -try: - import _pytest.mark - import _pytest.recwarn - import _pytest.runner - import _pytest.python - import _pytest.skipping - import _pytest.assertion -except ImportError: - pass -else: - deprecated_call = _pytest.recwarn.deprecated_call - warns = _pytest.recwarn.warns - - exit = _pytest.runner.exit - fail = _pytest.runner.fail - skip = _pytest.runner.skip - importorskip = _pytest.runner.importorskip - - xfail = _pytest.skipping.xfail - mark = _pytest.mark.MarkGenerator() - raises = _pytest.python.raises - - # New in pytest 3.0 - try: - approx = _pytest.python.approx - register_assert_rewrite = _pytest.assertion.register_assert_rewrite - except AttributeError: - pass - - -# Moved in pytest 3.0 - -try: - import _pytest.freeze_support - freeze_includes = _pytest.freeze_support.freeze_includes -except ImportError: - try: - import _pytest.genscript - freeze_includes = _pytest.genscript.freeze_includes - except ImportError: - pass - -try: - import _pytest.debugging - set_trace = _pytest.debugging.pytestPDB().set_trace -except ImportError: - try: - import _pytest.pdb - set_trace = _pytest.pdb.pytestPDB().set_trace - except ImportError: - pass - -try: - import _pytest.fixtures - fixture = _pytest.fixtures.fixture - yield_fixture = _pytest.fixtures.yield_fixture -except ImportError: - try: - import _pytest.python - fixture = _pytest.python.fixture - yield_fixture = _pytest.python.yield_fixture - except ImportError: - pass -''') - -register_module_extender(MANAGER, 'pytest', pytest_transform) -register_module_extender(MANAGER, 'py.test', pytest_transform) diff --git a/pymode/libs/astroid/brain/brain_qt.py b/pymode/libs/astroid/brain/brain_qt.py deleted file mode 100644 index bb1565fe..00000000 --- a/pymode/libs/astroid/brain/brain_qt.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Astroid hooks for the PyQT library.""" - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid import nodes -from astroid import parse - - -def _looks_like_signal(node, signal_name='pyqtSignal'): - if '__class__' in node.instance_attrs: - try: - cls = node.instance_attrs['__class__'][0] - return cls.name == signal_name - except AttributeError: - # return False if the cls does not have a name attribute - pass - return False - - -def transform_pyqt_signal(node): - module = parse(''' - class pyqtSignal(object): - def connect(self, slot, type=None, no_receiver_check=False): - pass - def disconnect(self, slot): - pass - def emit(self, *args): - pass - ''') - signal_cls = module['pyqtSignal'] - node.instance_attrs['emit'] = signal_cls['emit'] - node.instance_attrs['disconnect'] = signal_cls['disconnect'] - node.instance_attrs['connect'] = signal_cls['connect'] - - -def pyqt4_qtcore_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -def SIGNAL(signal_name): pass - -class QObject(object): - def emit(self, signal): pass -''') - - -register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform) -MANAGER.register_transform(nodes.FunctionDef, transform_pyqt_signal, - _looks_like_signal) \ No newline at end of file diff --git a/pymode/libs/astroid/brain/brain_re.py b/pymode/libs/astroid/brain/brain_re.py deleted file mode 100644 index f6e1befd..00000000 --- a/pymode/libs/astroid/brain/brain_re.py +++ /dev/null @@ -1,34 +0,0 @@ -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER -import sys -import astroid - -PY36 = sys.version_info >= (3, 6) - -if PY36: - # Since Python 3.6 there is the RegexFlag enum - # where every entry will be exposed via updating globals() - - def _re_transform(): - return astroid.parse(''' - import sre_compile - ASCII = sre_compile.SRE_FLAG_ASCII - IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE - LOCALE = sre_compile.SRE_FLAG_LOCALE - UNICODE = sre_compile.SRE_FLAG_UNICODE - MULTILINE = sre_compile.SRE_FLAG_MULTILINE - DOTALL = sre_compile.SRE_FLAG_DOTALL - VERBOSE = sre_compile.SRE_FLAG_VERBOSE - A = ASCII - I = IGNORECASE - L = LOCALE - U = UNICODE - M = MULTILINE - S = DOTALL - X = VERBOSE - TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE - T = TEMPLATE - DEBUG = sre_compile.SRE_FLAG_DEBUG - ''') - - astroid.register_module_extender(astroid.MANAGER, 're', _re_transform) diff --git a/pymode/libs/astroid/brain/brain_six.py b/pymode/libs/astroid/brain/brain_six.py deleted file mode 100644 index f16a2938..00000000 --- a/pymode/libs/astroid/brain/brain_six.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) 2014-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -"""Astroid hooks for six module.""" - -import sys -from textwrap import dedent - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid.exceptions import AstroidBuildingError, InferenceError -from astroid import nodes - - -SIX_ADD_METACLASS = 'six.add_metaclass' - - -def _indent(text, prefix, predicate=None): - """Adds 'prefix' to the beginning of selected lines in 'text'. - - If 'predicate' is provided, 'prefix' will only be added to the lines - where 'predicate(line)' is True. If 'predicate' is not provided, - it will default to adding 'prefix' to all non-empty lines that do not - consist solely of whitespace characters. - """ - if predicate is None: - predicate = lambda line: line.strip() - - def prefixed_lines(): - for line in text.splitlines(True): - yield prefix + line if predicate(line) else line - return ''.join(prefixed_lines()) - - -if sys.version_info[0] == 2: - _IMPORTS_2 = """ - import BaseHTTPServer - import CGIHTTPServer - import SimpleHTTPServer - - from StringIO import StringIO - from cStringIO import StringIO as cStringIO - from UserDict import UserDict - from UserList import UserList - from UserString import UserString - - import __builtin__ as builtins - import thread as _thread - import dummy_thread as _dummy_thread - import ConfigParser as configparser - import copy_reg as copyreg - from itertools import (imap as map, - ifilter as filter, - ifilterfalse as filterfalse, - izip_longest as zip_longest, - izip as zip) - import htmlentitydefs as html_entities - import HTMLParser as html_parser - import httplib as http_client - import cookielib as http_cookiejar - import Cookie as http_cookies - import Queue as queue - import repr as reprlib - from pipes import quote as shlex_quote - import SocketServer as socketserver - import SimpleXMLRPCServer as xmlrpc_server - import xmlrpclib as xmlrpc_client - import _winreg as winreg - import robotparser as urllib_robotparser - import Tkinter as tkinter - import tkFileDialog as tkinter_tkfiledialog - - input = raw_input - intern = intern - range = xrange - xrange = xrange - reduce = reduce - reload_module = reload - - class UrllibParse(object): - import urlparse as _urlparse - import urllib as _urllib - ParseResult = _urlparse.ParseResult - SplitResult = _urlparse.SplitResult - parse_qs = _urlparse.parse_qs - parse_qsl = _urlparse.parse_qsl - urldefrag = _urlparse.urldefrag - urljoin = _urlparse.urljoin - urlparse = _urlparse.urlparse - urlsplit = _urlparse.urlsplit - urlunparse = _urlparse.urlunparse - urlunsplit = _urlparse.urlunsplit - quote = _urllib.quote - quote_plus = _urllib.quote_plus - unquote = _urllib.unquote - unquote_plus = _urllib.unquote_plus - urlencode = _urllib.urlencode - splitquery = _urllib.splitquery - splittag = _urllib.splittag - splituser = _urllib.splituser - uses_fragment = _urlparse.uses_fragment - uses_netloc = _urlparse.uses_netloc - uses_params = _urlparse.uses_params - uses_query = _urlparse.uses_query - uses_relative = _urlparse.uses_relative - - class UrllibError(object): - import urllib2 as _urllib2 - import urllib as _urllib - URLError = _urllib2.URLError - HTTPError = _urllib2.HTTPError - ContentTooShortError = _urllib.ContentTooShortError - - class DummyModule(object): - pass - - class UrllibRequest(object): - import urlparse as _urlparse - import urllib2 as _urllib2 - import urllib as _urllib - urlopen = _urllib2.urlopen - install_opener = _urllib2.install_opener - build_opener = _urllib2.build_opener - pathname2url = _urllib.pathname2url - url2pathname = _urllib.url2pathname - getproxies = _urllib.getproxies - Request = _urllib2.Request - OpenerDirector = _urllib2.OpenerDirector - HTTPDefaultErrorHandler = _urllib2.HTTPDefaultErrorHandler - HTTPRedirectHandler = _urllib2.HTTPRedirectHandler - HTTPCookieProcessor = _urllib2.HTTPCookieProcessor - ProxyHandler = _urllib2.ProxyHandler - BaseHandler = _urllib2.BaseHandler - HTTPPasswordMgr = _urllib2.HTTPPasswordMgr - HTTPPasswordMgrWithDefaultRealm = _urllib2.HTTPPasswordMgrWithDefaultRealm - AbstractBasicAuthHandler = _urllib2.AbstractBasicAuthHandler - HTTPBasicAuthHandler = _urllib2.HTTPBasicAuthHandler - ProxyBasicAuthHandler = _urllib2.ProxyBasicAuthHandler - AbstractDigestAuthHandler = _urllib2.AbstractDigestAuthHandler - HTTPDigestAuthHandler = _urllib2.HTTPDigestAuthHandler - ProxyDigestAuthHandler = _urllib2.ProxyDigestAuthHandler - HTTPHandler = _urllib2.HTTPHandler - HTTPSHandler = _urllib2.HTTPSHandler - FileHandler = _urllib2.FileHandler - FTPHandler = _urllib2.FTPHandler - CacheFTPHandler = _urllib2.CacheFTPHandler - UnknownHandler = _urllib2.UnknownHandler - HTTPErrorProcessor = _urllib2.HTTPErrorProcessor - urlretrieve = _urllib.urlretrieve - urlcleanup = _urllib.urlcleanup - proxy_bypass = _urllib.proxy_bypass - - urllib_parse = UrllibParse() - urllib_error = UrllibError() - urllib = DummyModule() - urllib.request = UrllibRequest() - urllib.parse = UrllibParse() - urllib.error = UrllibError() - """ -else: - _IMPORTS_3 = """ - import _io - cStringIO = _io.StringIO - filter = filter - from itertools import filterfalse - input = input - from sys import intern - map = map - range = range - from imp import reload as reload_module - from functools import reduce - from shlex import quote as shlex_quote - from io import StringIO - from collections import UserDict, UserList, UserString - xrange = range - zip = zip - from itertools import zip_longest - import builtins - import configparser - import copyreg - import _dummy_thread - import http.cookiejar as http_cookiejar - import http.cookies as http_cookies - import html.entities as html_entities - import html.parser as html_parser - import http.client as http_client - import http.server - BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server - import pickle as cPickle - import queue - import reprlib - import socketserver - import _thread - import winreg - import xmlrpc.server as xmlrpc_server - import xmlrpc.client as xmlrpc_client - import urllib.robotparser as urllib_robotparser - import email.mime.multipart as email_mime_multipart - import email.mime.nonmultipart as email_mime_nonmultipart - import email.mime.text as email_mime_text - import email.mime.base as email_mime_base - import urllib.parse as urllib_parse - import urllib.error as urllib_error - import tkinter - import tkinter.dialog as tkinter_dialog - import tkinter.filedialog as tkinter_filedialog - import tkinter.scrolledtext as tkinter_scrolledtext - import tkinter.simpledialog as tkinder_simpledialog - import tkinter.tix as tkinter_tix - import tkinter.ttk as tkinter_ttk - import tkinter.constants as tkinter_constants - import tkinter.dnd as tkinter_dnd - import tkinter.colorchooser as tkinter_colorchooser - import tkinter.commondialog as tkinter_commondialog - import tkinter.filedialog as tkinter_tkfiledialog - import tkinter.font as tkinter_font - import tkinter.messagebox as tkinter_messagebox - import urllib.request - import urllib.robotparser as urllib_robotparser - import urllib.parse as urllib_parse - import urllib.error as urllib_error - """ -if sys.version_info[0] == 2: - _IMPORTS = dedent(_IMPORTS_2) -else: - _IMPORTS = dedent(_IMPORTS_3) - - -def six_moves_transform(): - code = dedent(''' - class Moves(object): - {} - moves = Moves() - ''').format(_indent(_IMPORTS, " ")) - module = AstroidBuilder(MANAGER).string_build(code) - module.name = 'six.moves' - return module - - -def _six_fail_hook(modname): - if modname != 'six.moves': - raise AstroidBuildingError(modname=modname) - module = AstroidBuilder(MANAGER).string_build(_IMPORTS) - module.name = 'six.moves' - return module - -def transform_six_add_metaclass(node): - """Check if the given class node is decorated with *six.add_metaclass* - - If so, inject its argument as the metaclass of the underlying class. - """ - if not node.decorators: - return - - for decorator in node.decorators.nodes: - if not isinstance(decorator, nodes.Call): - continue - - try: - func = next(decorator.func.infer()) - except InferenceError: - continue - if func.qname() == SIX_ADD_METACLASS and decorator.args: - metaclass = decorator.args[0] - node._metaclass = metaclass - return node - - -register_module_extender(MANAGER, 'six', six_moves_transform) -register_module_extender(MANAGER, 'requests.packages.urllib3.packages.six', - six_moves_transform) -MANAGER.register_failed_import_hook(_six_fail_hook) -MANAGER.register_transform(nodes.ClassDef, transform_six_add_metaclass) diff --git a/pymode/libs/astroid/brain/brain_ssl.py b/pymode/libs/astroid/brain/brain_ssl.py deleted file mode 100644 index 78c20c99..00000000 --- a/pymode/libs/astroid/brain/brain_ssl.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Astroid hooks for the ssl library.""" - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid import nodes -from astroid import parse - - -def ssl_transform(): - return parse(''' - from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION - from _ssl import _SSLContext, MemoryBIO - from _ssl import ( - SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, - SSLSyscallError, SSLEOFError, - ) - from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED - from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj - from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes - try: - from _ssl import RAND_egd - except ImportError: - # LibreSSL does not provide RAND_egd - pass - from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE, - OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3, - OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2, - OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE) - - from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE, - ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE, - ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE, - ALERT_DESCRIPTION_BAD_RECORD_MAC, - ALERT_DESCRIPTION_CERTIFICATE_EXPIRED, - ALERT_DESCRIPTION_CERTIFICATE_REVOKED, - ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN, - ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE, - ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR, - ALERT_DESCRIPTION_DECOMPRESSION_FAILURE, - ALERT_DESCRIPTION_DECRYPT_ERROR, - ALERT_DESCRIPTION_HANDSHAKE_FAILURE, - ALERT_DESCRIPTION_ILLEGAL_PARAMETER, - ALERT_DESCRIPTION_INSUFFICIENT_SECURITY, - ALERT_DESCRIPTION_INTERNAL_ERROR, - ALERT_DESCRIPTION_NO_RENEGOTIATION, - ALERT_DESCRIPTION_PROTOCOL_VERSION, - ALERT_DESCRIPTION_RECORD_OVERFLOW, - ALERT_DESCRIPTION_UNEXPECTED_MESSAGE, - ALERT_DESCRIPTION_UNKNOWN_CA, - ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY, - ALERT_DESCRIPTION_UNRECOGNIZED_NAME, - ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE, - ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION, - ALERT_DESCRIPTION_USER_CANCELLED) - from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL, - SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ, - SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN) - from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT - from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN - from _ssl import _OPENSSL_API_VERSION - from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 - ''') - - -register_module_extender(MANAGER, 'ssl', ssl_transform) diff --git a/pymode/libs/astroid/brain/brain_subprocess.py b/pymode/libs/astroid/brain/brain_subprocess.py deleted file mode 100644 index db800e78..00000000 --- a/pymode/libs/astroid/brain/brain_subprocess.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -import sys -import textwrap - -import six - -import astroid - - -PY33 = sys.version_info >= (3, 3) -PY36 = sys.version_info >= (3, 6) - - -def _subprocess_transform(): - if six.PY3: - communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) - communicate_signature = 'def communicate(self, input=None, timeout=None)' - if PY36: - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=(), *, - encoding=None, errors=None): - pass - """ - else: - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=()): - pass - """ - else: - communicate = ('string', 'string') - communicate_signature = 'def communicate(self, input=None)' - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - pass - """ - if PY33: - wait_signature = 'def wait(self, timeout=None)' - else: - wait_signature = 'def wait(self)' - if six.PY3: - ctx_manager = ''' - def __enter__(self): return self - def __exit__(self, *args): pass - ''' - else: - ctx_manager = '' - code = textwrap.dedent(''' - class Popen(object): - returncode = pid = 0 - stdin = stdout = stderr = file() - - %(communicate_signature)s: - return %(communicate)r - %(wait_signature)s: - return self.returncode - def poll(self): - return self.returncode - def send_signal(self, signal): - pass - def terminate(self): - pass - def kill(self): - pass - %(ctx_manager)s - ''' % {'communicate': communicate, - 'communicate_signature': communicate_signature, - 'wait_signature': wait_signature, - 'ctx_manager': ctx_manager}) - - init_lines = textwrap.dedent(init).splitlines() - indented_init = '\n'.join([' ' * 4 + line for line in init_lines]) - code += indented_init - return astroid.parse(code) - - -astroid.register_module_extender(astroid.MANAGER, 'subprocess', _subprocess_transform) diff --git a/pymode/libs/astroid/brain/brain_threading.py b/pymode/libs/astroid/brain/brain_threading.py deleted file mode 100644 index bbc0c612..00000000 --- a/pymode/libs/astroid/brain/brain_threading.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -import astroid - - -def _thread_transform(): - return astroid.parse(''' - class lock(object): - def acquire(self, blocking=True): - pass - def release(self): - pass - def __enter__(self): - return True - def __exit__(self, *args): - pass - - def Lock(): - return lock() - ''') - - -astroid.register_module_extender(astroid.MANAGER, 'threading', _thread_transform) diff --git a/pymode/libs/astroid/brain/brain_typing.py b/pymode/libs/astroid/brain/brain_typing.py deleted file mode 100644 index 711066cb..00000000 --- a/pymode/libs/astroid/brain/brain_typing.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) 2016 David Euresti - -"""Astroid hooks for typing.py support.""" -import textwrap - -from astroid import ( - MANAGER, UseInferenceDefault, extract_node, inference_tip, - nodes, InferenceError) -from astroid.nodes import List, Tuple - - -TYPING_NAMEDTUPLE_BASENAMES = { - 'NamedTuple', - 'typing.NamedTuple' -} - - -def infer_typing_namedtuple(node, context=None): - """Infer a typing.NamedTuple(...) call.""" - # This is essentially a namedtuple with different arguments - # so we extract the args and infer a named tuple. - try: - func = next(node.func.infer()) - except InferenceError: - raise UseInferenceDefault - - if func.qname() != 'typing.NamedTuple': - raise UseInferenceDefault - - if len(node.args) != 2: - raise UseInferenceDefault - - if not isinstance(node.args[1], (List, Tuple)): - raise UseInferenceDefault - - names = [] - for elt in node.args[1].elts: - if not isinstance(elt, (List, Tuple)): - raise UseInferenceDefault - if len(elt.elts) != 2: - raise UseInferenceDefault - names.append(elt.elts[0].as_string()) - - typename = node.args[0].as_string() - node = extract_node('namedtuple(%(typename)s, (%(fields)s,)) ' % - {'typename': typename, 'fields': ",".join(names)}) - return node.infer(context=context) - - -def infer_typing_namedtuple_class(node, context=None): - """Infer a subclass of typing.NamedTuple""" - - # Check if it has the corresponding bases - if not set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES: - raise UseInferenceDefault - - annassigns_fields = [ - annassign.target.name for annassign in node.body - if isinstance(annassign, nodes.AnnAssign) - ] - code = textwrap.dedent(''' - from collections import namedtuple - namedtuple({typename!r}, {fields!r}) - ''').format( - typename=node.name, - fields=",".join(annassigns_fields) - ) - node = extract_node(code) - return node.infer(context=context) - - -def looks_like_typing_namedtuple(node): - func = node.func - if isinstance(func, nodes.Attribute): - return func.attrname == 'NamedTuple' - if isinstance(func, nodes.Name): - return func.name == 'NamedTuple' - return False - - -MANAGER.register_transform( - nodes.Call, - inference_tip(infer_typing_namedtuple), - looks_like_typing_namedtuple -) -MANAGER.register_transform( - nodes.ClassDef, - inference_tip(infer_typing_namedtuple_class) -) diff --git a/pymode/libs/astroid/builder.py b/pymode/libs/astroid/builder.py deleted file mode 100644 index afb9daf1..00000000 --- a/pymode/libs/astroid/builder.py +++ /dev/null @@ -1,437 +0,0 @@ -# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2015 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""The AstroidBuilder makes astroid from living object and / or from _ast - -The builder is not thread safe and can't be used to parse different sources -at the same time. -""" - -import re -import os -import sys -import textwrap -import _ast - -from astroid import bases -from astroid import exceptions -from astroid import manager -from astroid import modutils -from astroid import raw_building -from astroid import rebuilder -from astroid import nodes -from astroid import util - -# The name of the transient function that is used to -# wrap expressions to be extracted when calling -# extract_node. -_TRANSIENT_FUNCTION = '__' - -# The comment used to select a statement to be extracted -# when calling extract_node. -_STATEMENT_SELECTOR = '#@' - - -def _parse(string): - return compile(string, "", 'exec', _ast.PyCF_ONLY_AST) - - -if sys.version_info >= (3, 0): - from tokenize import detect_encoding - - def open_source_file(filename): - with open(filename, 'rb') as byte_stream: - encoding = detect_encoding(byte_stream.readline)[0] - stream = open(filename, 'r', newline=None, encoding=encoding) - data = stream.read() - return stream, encoding, data - -else: - _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)") - - def _guess_encoding(string): - """get encoding from a python file as string or return None if not found""" - # check for UTF-8 byte-order mark - if string.startswith('\xef\xbb\xbf'): - return 'UTF-8' - for line in string.split('\n', 2)[:2]: - # check for encoding declaration - match = _ENCODING_RGX.match(line) - if match is not None: - return match.group(1) - - def open_source_file(filename): - """get data for parsing a file""" - stream = open(filename, 'U') - data = stream.read() - encoding = _guess_encoding(data) - return stream, encoding, data - - -MANAGER = manager.AstroidManager() - - -def _can_assign_attr(node, attrname): - try: - slots = node.slots() - except NotImplementedError: - pass - else: - if slots and attrname not in set(slot.value for slot in slots): - return False - return True - - -class AstroidBuilder(raw_building.InspectBuilder): - """Class for building an astroid tree from source code or from a live module. - - The param *manager* specifies the manager class which should be used. - If no manager is given, then the default one will be used. The - param *apply_transforms* determines if the transforms should be - applied after the tree was built from source or from a live object, - by default being True. - """ - # pylint: disable=redefined-outer-name - def __init__(self, manager=None, apply_transforms=True): - super(AstroidBuilder, self).__init__() - self._manager = manager or MANAGER - self._apply_transforms = apply_transforms - - def module_build(self, module, modname=None): - """Build an astroid from a living module instance.""" - node = None - path = getattr(module, '__file__', None) - if path is not None: - path_, ext = os.path.splitext(modutils._path_from_filename(path)) - if ext in ('.py', '.pyc', '.pyo') and os.path.exists(path_ + '.py'): - node = self.file_build(path_ + '.py', modname) - if node is None: - # this is a built-in module - # get a partial representation by introspection - node = self.inspect_build(module, modname=modname, path=path) - if self._apply_transforms: - # We have to handle transformation by ourselves since the - # rebuilder isn't called for builtin nodes - node = self._manager.visit_transforms(node) - return node - - def file_build(self, path, modname=None): - """Build astroid from a source code file (i.e. from an ast) - - *path* is expected to be a python source file - """ - try: - stream, encoding, data = open_source_file(path) - except IOError as exc: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to load file {path}:\n{error}', - modname=modname, path=path, error=exc)) - except (SyntaxError, LookupError) as exc: - util.reraise(exceptions.AstroidSyntaxError( - 'Python 3 encoding specification error or unknown encoding:\n' - '{error}', modname=modname, path=path, error=exc)) - except UnicodeError: # wrong encoding - # detect_encoding returns utf-8 if no encoding specified - util.reraise(exceptions.AstroidBuildingError( - 'Wrong or no encoding specified for {filename}.', - filename=path)) - with stream: - # get module name if necessary - if modname is None: - try: - modname = '.'.join(modutils.modpath_from_file(path)) - except ImportError: - modname = os.path.splitext(os.path.basename(path))[0] - # build astroid representation - module = self._data_build(data, modname, path) - return self._post_build(module, encoding) - - def string_build(self, data, modname='', path=None): - """Build astroid from source code string.""" - module = self._data_build(data, modname, path) - module.file_bytes = data.encode('utf-8') - return self._post_build(module, 'utf-8') - - def _post_build(self, module, encoding): - """Handles encoding and delayed nodes after a module has been built""" - module.file_encoding = encoding - self._manager.cache_module(module) - # post tree building steps after we stored the module in the cache: - for from_node in module._import_from_nodes: - if from_node.modname == '__future__': - for symbol, _ in from_node.names: - module.future_imports.add(symbol) - self.add_from_names_to_locals(from_node) - # handle delayed assattr nodes - for delayed in module._delayed_assattr: - self.delayed_assattr(delayed) - - # Visit the transforms - if self._apply_transforms: - module = self._manager.visit_transforms(module) - return module - - def _data_build(self, data, modname, path): - """Build tree node from data and add some informations""" - try: - node = _parse(data + '\n') - except (TypeError, ValueError, SyntaxError) as exc: - util.reraise(exceptions.AstroidSyntaxError( - 'Parsing Python code failed:\n{error}', - source=data, modname=modname, path=path, error=exc)) - if path is not None: - node_file = os.path.abspath(path) - else: - node_file = '' - if modname.endswith('.__init__'): - modname = modname[:-9] - package = True - else: - package = path is not None and os.path.splitext(os.path.basename(path))[0] == '__init__' - builder = rebuilder.TreeRebuilder(self._manager) - module = builder.visit_module(node, modname, node_file, package) - module._import_from_nodes = builder._import_from_nodes - module._delayed_assattr = builder._delayed_assattr - return module - - def add_from_names_to_locals(self, node): - """Store imported names to the locals - - Resort the locals if coming from a delayed node - """ - _key_func = lambda node: node.fromlineno - def sort_locals(my_list): - my_list.sort(key=_key_func) - - for (name, asname) in node.names: - if name == '*': - try: - imported = node.do_import_module() - except exceptions.AstroidBuildingError: - continue - for name in imported.public_names(): - node.parent.set_local(name, node) - sort_locals(node.parent.scope().locals[name]) - else: - node.parent.set_local(asname or name, node) - sort_locals(node.parent.scope().locals[asname or name]) - - def delayed_assattr(self, node): - """Visit a AssAttr node - - This adds name to locals and handle members definition. - """ - try: - frame = node.frame() - for inferred in node.expr.infer(): - if inferred is util.Uninferable: - continue - try: - if inferred.__class__ is bases.Instance: - inferred = inferred._proxied - iattrs = inferred.instance_attrs - if not _can_assign_attr(inferred, node.attrname): - continue - elif isinstance(inferred, bases.Instance): - # Const, Tuple, ... we may be wrong, may be not, but - # anyway we don't want to pollute builtin's namespace - continue - elif inferred.is_function: - iattrs = inferred.instance_attrs - else: - iattrs = inferred.locals - except AttributeError: - # XXX log error - continue - values = iattrs.setdefault(node.attrname, []) - if node in values: - continue - # get assign in __init__ first XXX useful ? - if (frame.name == '__init__' and values and - values[0].frame().name != '__init__'): - values.insert(0, node) - else: - values.append(node) - except exceptions.InferenceError: - pass - - -def build_namespace_package_module(name, path): - return nodes.Module(name, doc='', path=path, package=True) - - -def parse(code, module_name='', path=None, apply_transforms=True): - """Parses a source string in order to obtain an astroid AST from it - - :param str code: The code for the module. - :param str module_name: The name for the module, if any - :param str path: The path for the module - :param bool apply_transforms: - Apply the transforms for the give code. Use it if you - don't want the default transforms to be applied. - """ - code = textwrap.dedent(code) - builder = AstroidBuilder(manager=MANAGER, - apply_transforms=apply_transforms) - return builder.string_build(code, modname=module_name, path=path) - - -def _extract_expressions(node): - """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. - - The function walks the AST recursively to search for expressions that - are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an - expression, it completely removes the function call node from the tree, - replacing it by the wrapped expression inside the parent. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :yields: The sequence of wrapped expressions on the modified tree - expression can be found. - """ - if (isinstance(node, nodes.Call) - and isinstance(node.func, nodes.Name) - and node.func.name == _TRANSIENT_FUNCTION): - real_expr = node.args[0] - real_expr.parent = node.parent - # Search for node in all _astng_fields (the fields checked when - # get_children is called) of its parent. Some of those fields may - # be lists or tuples, in which case the elements need to be checked. - # When we find it, replace it by real_expr, so that the AST looks - # like no call to _TRANSIENT_FUNCTION ever took place. - for name in node.parent._astroid_fields: - child = getattr(node.parent, name) - if isinstance(child, (list, tuple)): - for idx, compound_child in enumerate(child): - if compound_child is node: - child[idx] = real_expr - elif child is node: - setattr(node.parent, name, real_expr) - yield real_expr - else: - for child in node.get_children(): - for result in _extract_expressions(child): - yield result - - -def _find_statement_by_line(node, line): - """Extracts the statement on a specific line from an AST. - - If the line number of node matches line, it will be returned; - otherwise its children are iterated and the function is called - recursively. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :param line: The line number of the statement to extract. - :type line: int - :returns: The statement on the line, or None if no statement for the line - can be found. - :rtype: astroid.bases.NodeNG or None - """ - if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)): - # This is an inaccuracy in the AST: the nodes that can be - # decorated do not carry explicit information on which line - # the actual definition (class/def), but .fromline seems to - # be close enough. - node_line = node.fromlineno - else: - node_line = node.lineno - - if node_line == line: - return node - - for child in node.get_children(): - result = _find_statement_by_line(child, line) - if result: - return result - - return None - - -def extract_node(code, module_name=''): - """Parses some Python code as a module and extracts a designated AST node. - - Statements: - To extract one or more statement nodes, append #@ to the end of the line - - Examples: - >>> def x(): - >>> def y(): - >>> return 1 #@ - - The return statement will be extracted. - - >>> class X(object): - >>> def meth(self): #@ - >>> pass - - The function object 'meth' will be extracted. - - Expressions: - To extract arbitrary expressions, surround them with the fake - function call __(...). After parsing, the surrounded expression - will be returned and the whole AST (accessible via the returned - node's parent attribute) will look like the function call was - never there in the first place. - - Examples: - >>> a = __(1) - - The const node will be extracted. - - >>> def x(d=__(foo.bar)): pass - - The node containing the default argument will be extracted. - - >>> def foo(a, b): - >>> return 0 < __(len(a)) < b - - The node containing the function call 'len' will be extracted. - - If no statements or expressions are selected, the last toplevel - statement will be returned. - - If the selected statement is a discard statement, (i.e. an expression - turned into a statement), the wrapped expression is returned instead. - - For convenience, singleton lists are unpacked. - - :param str code: A piece of Python code that is parsed as - a module. Will be passed through textwrap.dedent first. - :param str module_name: The name of the module. - :returns: The designated node from the parse tree, or a list of nodes. - :rtype: astroid.bases.NodeNG, or a list of nodes. - """ - def _extract(node): - if isinstance(node, nodes.Expr): - return node.value - - return node - - requested_lines = [] - for idx, line in enumerate(code.splitlines()): - if line.strip().endswith(_STATEMENT_SELECTOR): - requested_lines.append(idx + 1) - - tree = parse(code, module_name=module_name) - extracted = [] - if requested_lines: - for line in requested_lines: - extracted.append(_find_statement_by_line(tree, line)) - - # Modifies the tree. - extracted.extend(_extract_expressions(tree)) - - if not extracted: - extracted.append(tree.body[-1]) - - extracted = [_extract(node) for node in extracted] - if len(extracted) == 1: - return extracted[0] - return extracted diff --git a/pymode/libs/astroid/context.py b/pymode/libs/astroid/context.py deleted file mode 100644 index 627bae5d..00000000 --- a/pymode/libs/astroid/context.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Various context related utilities, including inference and call contexts.""" - -import contextlib -import pprint - - -class InferenceContext(object): - __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'inferred') - - def __init__(self, path=None, inferred=None): - self.path = path or set() - self.lookupname = None - self.callcontext = None - self.boundnode = None - self.inferred = inferred or {} - - def push(self, node): - name = self.lookupname - if (node, name) in self.path: - return True - - self.path.add((node, name)) - return False - - def clone(self): - # XXX copy lookupname/callcontext ? - clone = InferenceContext(self.path, inferred=self.inferred) - clone.callcontext = self.callcontext - clone.boundnode = self.boundnode - return clone - - def cache_generator(self, key, generator): - results = [] - for result in generator: - results.append(result) - yield result - - self.inferred[key] = tuple(results) - return - - @contextlib.contextmanager - def restore_path(self): - path = set(self.path) - yield - self.path = path - - def __str__(self): - state = ('%s=%s' % (field, pprint.pformat(getattr(self, field), - width=80 - len(field))) - for field in self.__slots__) - return '%s(%s)' % (type(self).__name__, ',\n '.join(state)) - - -class CallContext(object): - """Holds information for a call site.""" - - __slots__ = ('args', 'keywords') - - def __init__(self, args, keywords=None): - self.args = args - if keywords: - keywords = [(arg.arg, arg.value) for arg in keywords] - else: - keywords = [] - self.keywords = keywords - - -def copy_context(context): - if context is not None: - return context.clone() - - return InferenceContext() diff --git a/pymode/libs/astroid/decorators.py b/pymode/libs/astroid/decorators.py deleted file mode 100644 index a59cab22..00000000 --- a/pymode/libs/astroid/decorators.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015 Florian Bruhin -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -""" A few useful function/method decorators.""" - -import functools - -import wrapt - -from astroid import context as contextmod -from astroid import exceptions -from astroid import util - - -@wrapt.decorator -def cached(func, instance, args, kwargs): - """Simple decorator to cache result of method calls without args.""" - cache = getattr(instance, '__cache', None) - if cache is None: - instance.__cache = cache = {} - try: - return cache[func] - except KeyError: - cache[func] = result = func(*args, **kwargs) - return result - - -class cachedproperty(object): - """ Provides a cached property equivalent to the stacking of - @cached and @property, but more efficient. - - After first usage, the becomes part of the object's - __dict__. Doing: - - del obj. empties the cache. - - Idea taken from the pyramid_ framework and the mercurial_ project. - - .. _pyramid: http://pypi.python.org/pypi/pyramid - .. _mercurial: http://pypi.python.org/pypi/Mercurial - """ - __slots__ = ('wrapped',) - - def __init__(self, wrapped): - try: - wrapped.__name__ - except AttributeError: - util.reraise(TypeError('%s must have a __name__ attribute' - % wrapped)) - self.wrapped = wrapped - - @property - def __doc__(self): - doc = getattr(self.wrapped, '__doc__', None) - return ('%s' - % ('\n%s' % doc if doc else '')) - - def __get__(self, inst, objtype=None): - if inst is None: - return self - val = self.wrapped(inst) - setattr(inst, self.wrapped.__name__, val) - return val - - -def path_wrapper(func): - """return the given infer function wrapped to handle the path""" - # TODO: switch this to wrapt after the monkey-patching is fixed (ceridwen) - @functools.wraps(func) - def wrapped(node, context=None, _func=func, **kwargs): - """wrapper function handling context""" - if context is None: - context = contextmod.InferenceContext() - if context.push(node): - return - - yielded = set() - generator = _func(node, context, **kwargs) - try: - while True: - res = next(generator) - # unproxy only true instance, not const, tuple, dict... - if res.__class__.__name__ == 'Instance': - ares = res._proxied - else: - ares = res - if ares not in yielded: - yield res - yielded.add(ares) - except StopIteration as error: - # Explicit StopIteration to return error information, see - # comment in raise_if_nothing_inferred. - if error.args: - raise StopIteration(error.args[0]) - else: - raise StopIteration - - return wrapped - - -@wrapt.decorator -def yes_if_nothing_inferred(func, instance, args, kwargs): - inferred = False - for node in func(*args, **kwargs): - inferred = True - yield node - if not inferred: - yield util.Uninferable - - -@wrapt.decorator -def raise_if_nothing_inferred(func, instance, args, kwargs): - '''All generators wrapped with raise_if_nothing_inferred *must* - explicitly raise StopIteration with information to create an - appropriate structured InferenceError. - - ''' - # TODO: Explicitly raising StopIteration in a generator will cause - # a RuntimeError in Python >=3.7, as per - # http://legacy.python.org/dev/peps/pep-0479/ . Before 3.7 is - # released, this code will need to use one of four possible - # solutions: a decorator that restores the current behavior as - # described in - # http://legacy.python.org/dev/peps/pep-0479/#sub-proposal-decorator-to-explicitly-request-current-behaviour - # , dynamic imports or exec to generate different code for - # different versions, drop support for all Python versions <3.3, - # or refactoring to change how these decorators work. In any - # event, after dropping support for Python <3.3 this code should - # be refactored to use `yield from`. - inferred = False - try: - generator = func(*args, **kwargs) - while True: - yield next(generator) - inferred = True - except StopIteration as error: - if not inferred: - if error.args: - # pylint: disable=not-a-mapping - raise exceptions.InferenceError(**error.args[0]) - else: - raise exceptions.InferenceError( - 'StopIteration raised without any error information.') diff --git a/pymode/libs/astroid/exceptions.py b/pymode/libs/astroid/exceptions.py deleted file mode 100644 index 8b3f2522..00000000 --- a/pymode/libs/astroid/exceptions.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (c) 2007, 2009-2010, 2013 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""this module contains exceptions used in the astroid library -""" -from astroid import util - - -class AstroidError(Exception): - """base exception class for all astroid related exceptions - - AstroidError and its subclasses are structured, intended to hold - objects representing state when the exception is thrown. Field - values are passed to the constructor as keyword-only arguments. - Each subclass has its own set of standard fields, but use your - best judgment to decide whether a specific exception instance - needs more or fewer fields for debugging. Field values may be - used to lazily generate the error message: self.message.format() - will be called with the field names and values supplied as keyword - arguments. - """ - def __init__(self, message='', **kws): - super(AstroidError, self).__init__(message) - self.message = message - for key, value in kws.items(): - setattr(self, key, value) - - def __str__(self): - return self.message.format(**vars(self)) - - -class AstroidBuildingError(AstroidError): - """exception class when we are unable to build an astroid representation - - Standard attributes: - modname: Name of the module that AST construction failed for. - error: Exception raised during construction. - """ - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='Failed to import module {modname}.', **kws): - super(AstroidBuildingError, self).__init__(message, **kws) - - -class AstroidImportError(AstroidBuildingError): - """Exception class used when a module can't be imported by astroid.""" - - -class TooManyLevelsError(AstroidImportError): - """Exception class which is raised when a relative import was beyond the top-level. - - Standard attributes: - level: The level which was attempted. - name: the name of the module on which the relative import was attempted. - """ - level = None - name = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='Relative import with too many levels ' - '({level}) for module {name!r}', **kws): - super(TooManyLevelsError, self).__init__(message, **kws) - - -class AstroidSyntaxError(AstroidBuildingError): - """Exception class used when a module can't be parsed.""" - - -class NoDefault(AstroidError): - """raised by function's `default_value` method when an argument has - no default value - - Standard attributes: - func: Function node. - name: Name of argument without a default. - """ - func = None - name = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='{func!r} has no default for {name!r}.', **kws): - super(NoDefault, self).__init__(message, **kws) - - -class ResolveError(AstroidError): - """Base class of astroid resolution/inference error. - - ResolveError is not intended to be raised. - - Standard attributes: - context: InferenceContext object. - """ - context = None - - -class MroError(ResolveError): - """Error raised when there is a problem with method resolution of a class. - - Standard attributes: - mros: A sequence of sequences containing ClassDef nodes. - cls: ClassDef node whose MRO resolution failed. - context: InferenceContext object. - """ - mros = () - cls = None - - def __str__(self): - mro_names = ", ".join("({})".format(", ".join(b.name for b in m)) - for m in self.mros) - return self.message.format(mros=mro_names, cls=self.cls) - - -class DuplicateBasesError(MroError): - """Error raised when there are duplicate bases in the same class bases.""" - - -class InconsistentMroError(MroError): - """Error raised when a class's MRO is inconsistent.""" - - -class SuperError(ResolveError): - - """Error raised when there is a problem with a super call. - - Standard attributes: - super_: The Super instance that raised the exception. - context: InferenceContext object. - """ - super_ = None - - def __str__(self): - return self.message.format(**vars(self.super_)) - - -class InferenceError(ResolveError): - """raised when we are unable to infer a node - - Standard attributes: - node: The node inference was called on. - context: InferenceContext object. - """ - node = None - context = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='Inference failed for {node!r}.', **kws): - super(InferenceError, self).__init__(message, **kws) - - -# Why does this inherit from InferenceError rather than ResolveError? -# Changing it causes some inference tests to fail. -class NameInferenceError(InferenceError): - """Raised when a name lookup fails, corresponds to NameError. - - Standard attributes: - name: The name for which lookup failed, as a string. - scope: The node representing the scope in which the lookup occurred. - context: InferenceContext object. - """ - name = None - scope = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='{name!r} not found in {scope!r}.', **kws): - super(NameInferenceError, self).__init__(message, **kws) - - -class AttributeInferenceError(ResolveError): - """Raised when an attribute lookup fails, corresponds to AttributeError. - - Standard attributes: - target: The node for which lookup failed. - attribute: The attribute for which lookup failed, as a string. - context: InferenceContext object. - """ - target = None - attribute = None - - # pylint: disable=useless-super-delegation; https://github.com/PyCQA/pylint/issues/1085 - def __init__(self, message='{attribute!r} not found on {target!r}.', **kws): - super(AttributeInferenceError, self).__init__(message, **kws) - - -class UseInferenceDefault(Exception): - """exception to be raised in custom inference function to indicate that it - should go back to the default behaviour - """ - - -class _NonDeducibleTypeHierarchy(Exception): - """Raised when is_subtype / is_supertype can't deduce the relation between two types.""" - - -class AstroidIndexError(AstroidError): - """Raised when an Indexable / Mapping does not have an index / key.""" - - -class AstroidTypeError(AstroidError): - """Raised when a TypeError would be expected in Python code.""" - - -# Backwards-compatibility aliases -OperationError = util.BadOperationMessage -UnaryOperationError = util.BadUnaryOperationMessage -BinaryOperationError = util.BadBinaryOperationMessage - -SuperArgumentTypeError = SuperError -UnresolvableName = NameInferenceError -NotFoundError = AttributeInferenceError -AstroidBuildingException = AstroidBuildingError diff --git a/pymode/libs/astroid/helpers.py b/pymode/libs/astroid/helpers.py deleted file mode 100644 index 62da56ea..00000000 --- a/pymode/libs/astroid/helpers.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -""" -Various helper utilities. -""" - -import six - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import manager -from astroid import nodes -from astroid import raw_building -from astroid import scoped_nodes -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ - - -def _build_proxy_class(cls_name, builtins): - proxy = raw_building.build_class(cls_name) - proxy.parent = builtins - return proxy - - -def _function_type(function, builtins): - if isinstance(function, scoped_nodes.Lambda): - if function.root().name == BUILTINS: - cls_name = 'builtin_function_or_method' - else: - cls_name = 'function' - elif isinstance(function, bases.BoundMethod): - if six.PY2: - cls_name = 'instancemethod' - else: - cls_name = 'method' - elif isinstance(function, bases.UnboundMethod): - if six.PY2: - cls_name = 'instancemethod' - else: - cls_name = 'function' - return _build_proxy_class(cls_name, builtins) - - -def _object_type(node, context=None): - astroid_manager = manager.AstroidManager() - builtins = astroid_manager.astroid_cache[BUILTINS] - context = context or contextmod.InferenceContext() - - for inferred in node.infer(context=context): - if isinstance(inferred, scoped_nodes.ClassDef): - if inferred.newstyle: - metaclass = inferred.metaclass() - if metaclass: - yield metaclass - continue - yield builtins.getattr('type')[0] - elif isinstance(inferred, (scoped_nodes.Lambda, bases.UnboundMethod)): - yield _function_type(inferred, builtins) - elif isinstance(inferred, scoped_nodes.Module): - yield _build_proxy_class('module', builtins) - else: - yield inferred._proxied - - -def object_type(node, context=None): - """Obtain the type of the given node - - This is used to implement the ``type`` builtin, which means that it's - used for inferring type calls, as well as used in a couple of other places - in the inference. - The node will be inferred first, so this function can support all - sorts of objects, as long as they support inference. - """ - - try: - types = set(_object_type(node, context)) - except exceptions.InferenceError: - return util.Uninferable - if len(types) > 1 or not types: - return util.Uninferable - return list(types)[0] - - -def safe_infer(node, context=None): - """Return the inferred value for the given node. - - Return None if inference failed or if there is some ambiguity (more than - one node has been inferred). - """ - try: - inferit = node.infer(context=context) - value = next(inferit) - except exceptions.InferenceError: - return - try: - next(inferit) - return # None if there is ambiguity on the inferred node - except exceptions.InferenceError: - return # there is some kind of ambiguity - except StopIteration: - return value - - -def has_known_bases(klass, context=None): - """Return true if all base classes of a class could be inferred.""" - try: - return klass._all_bases_known - except AttributeError: - pass - for base in klass.bases: - result = safe_infer(base, context=context) - # TODO: check for A->B->A->B pattern in class structure too? - if (not isinstance(result, scoped_nodes.ClassDef) or - result is klass or - not has_known_bases(result, context=context)): - klass._all_bases_known = False - return False - klass._all_bases_known = True - return True - - -def _type_check(type1, type2): - if not all(map(has_known_bases, (type1, type2))): - raise exceptions._NonDeducibleTypeHierarchy - - if not all([type1.newstyle, type2.newstyle]): - return False - try: - return type1 in type2.mro()[:-1] - except exceptions.MroError: - # The MRO is invalid. - raise exceptions._NonDeducibleTypeHierarchy - - -def is_subtype(type1, type2): - """Check if *type1* is a subtype of *typ2*.""" - return _type_check(type2, type1) - - -def is_supertype(type1, type2): - """Check if *type2* is a supertype of *type1*.""" - return _type_check(type1, type2) - - -def class_instance_as_index(node): - """Get the value as an index for the given instance. - - If an instance provides an __index__ method, then it can - be used in some scenarios where an integer is expected, - for instance when multiplying or subscripting a list. - """ - context = contextmod.InferenceContext() - context.callcontext = contextmod.CallContext(args=[node]) - - try: - for inferred in node.igetattr('__index__', context=context): - if not isinstance(inferred, bases.BoundMethod): - continue - - for result in inferred.infer_call_result(node, context=context): - if (isinstance(result, nodes.Const) - and isinstance(result.value, int)): - return result - except exceptions.InferenceError: - pass diff --git a/pymode/libs/astroid/inference.py b/pymode/libs/astroid/inference.py deleted file mode 100644 index f77ad19a..00000000 --- a/pymode/libs/astroid/inference.py +++ /dev/null @@ -1,816 +0,0 @@ -# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""this module contains a set of functions to handle inference on astroid trees -""" - -import functools -import itertools -import operator - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import decorators -from astroid import helpers -from astroid import manager -from astroid import nodes -from astroid.interpreter import dunder_lookup -from astroid import protocols -from astroid import util - - -MANAGER = manager.AstroidManager() - - -# .infer method ############################################################### - - -def infer_end(self, context=None): - """inference's end for node such as Module, ClassDef, FunctionDef, - Const... - - """ - yield self -nodes.Module._infer = infer_end -nodes.ClassDef._infer = infer_end -nodes.FunctionDef._infer = infer_end -nodes.Lambda._infer = infer_end -nodes.Const._infer = infer_end -nodes.Slice._infer = infer_end - - -def infer_seq(self, context=None): - if not any(isinstance(e, nodes.Starred) for e in self.elts): - yield self - else: - values = _infer_seq(self, context) - new_seq = type(self)(self.lineno, self.col_offset, self.parent) - new_seq.postinit(values) - yield new_seq - - -def _infer_seq(node, context=None): - """Infer all values based on _BaseContainer.elts""" - values = [] - - for elt in node.elts: - if isinstance(elt, nodes.Starred): - starred = helpers.safe_infer(elt.value, context) - if starred in (None, util.Uninferable): - raise exceptions.InferenceError(node=node, - context=context) - if not hasattr(starred, 'elts'): - raise exceptions.InferenceError(node=node, - context=context) - values.extend(_infer_seq(starred)) - else: - values.append(elt) - return values - - -nodes.List._infer = infer_seq -nodes.Tuple._infer = infer_seq -nodes.Set._infer = infer_seq - - -def infer_map(self, context=None): - if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items): - yield self - else: - items = _infer_map(self, context) - new_seq = type(self)(self.lineno, self.col_offset, self.parent) - new_seq.postinit(list(items.items())) - yield new_seq - - -def _infer_map(node, context): - """Infer all values based on Dict.items""" - values = {} - for name, value in node.items: - if isinstance(name, nodes.DictUnpack): - double_starred = helpers.safe_infer(value, context) - if double_starred in (None, util.Uninferable): - raise exceptions.InferenceError - if not isinstance(double_starred, nodes.Dict): - raise exceptions.InferenceError(node=node, - context=context) - values.update(_infer_map(double_starred, context)) - else: - key = helpers.safe_infer(name, context=context) - value = helpers.safe_infer(value, context=context) - if key is None or value is None: - raise exceptions.InferenceError(node=node, - context=context) - values[key] = value - return values - - -nodes.Dict._infer = infer_map - - -def _higher_function_scope(node): - """ Search for the first function which encloses the given - scope. This can be used for looking up in that function's - scope, in case looking up in a lower scope for a particular - name fails. - - :param node: A scope node. - :returns: - ``None``, if no parent function scope was found, - otherwise an instance of :class:`astroid.scoped_nodes.Function`, - which encloses the given node. - """ - current = node - while current.parent and not isinstance(current.parent, nodes.FunctionDef): - current = current.parent - if current and current.parent: - return current.parent - -def infer_name(self, context=None): - """infer a Name: use name lookup rules""" - frame, stmts = self.lookup(self.name) - if not stmts: - # Try to see if the name is enclosed in a nested function - # and use the higher (first function) scope for searching. - # TODO: should this be promoted to other nodes as well? - parent_function = _higher_function_scope(self.scope()) - if parent_function: - _, stmts = parent_function.lookup(self.name) - - if not stmts: - raise exceptions.NameInferenceError(name=self.name, - scope=self.scope(), - context=context) - context = context.clone() - context.lookupname = self.name - return bases._infer_stmts(stmts, context, frame) -nodes.Name._infer = decorators.path_wrapper(infer_name) -nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper - - -@decorators.raise_if_nothing_inferred -@decorators.path_wrapper -def infer_call(self, context=None): - """infer a Call node by trying to guess what the function returns""" - callcontext = context.clone() - callcontext.callcontext = contextmod.CallContext(args=self.args, - keywords=self.keywords) - callcontext.boundnode = None - for callee in self.func.infer(context): - if callee is util.Uninferable: - yield callee - continue - try: - if hasattr(callee, 'infer_call_result'): - for inferred in callee.infer_call_result(self, callcontext): - yield inferred - except exceptions.InferenceError: - ## XXX log error ? - continue - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) -nodes.Call._infer = infer_call - - -@decorators.path_wrapper -def infer_import(self, context=None, asname=True): - """infer an Import node: return the imported module/object""" - name = context.lookupname - if name is None: - raise exceptions.InferenceError(node=self, context=context) - - try: - if asname: - yield self.do_import_module(self.real_name(name)) - else: - yield self.do_import_module(name) - except exceptions.AstroidBuildingError as exc: - util.reraise(exceptions.InferenceError(node=self, error=exc, - context=context)) - -nodes.Import._infer = infer_import - - -def infer_name_module(self, name): - context = contextmod.InferenceContext() - context.lookupname = name - return self.infer(context, asname=False) -nodes.Import.infer_name_module = infer_name_module - - -@decorators.path_wrapper -def infer_import_from(self, context=None, asname=True): - """infer a ImportFrom node: return the imported module/object""" - name = context.lookupname - if name is None: - raise exceptions.InferenceError(node=self, context=context) - if asname: - name = self.real_name(name) - - try: - module = self.do_import_module() - except exceptions.AstroidBuildingError as exc: - util.reraise(exceptions.InferenceError(node=self, error=exc, - context=context)) - - try: - context = contextmod.copy_context(context) - context.lookupname = name - stmts = module.getattr(name, ignore_locals=module is self.root()) - return bases._infer_stmts(stmts, context) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) -nodes.ImportFrom._infer = infer_import_from - - -@decorators.raise_if_nothing_inferred -def infer_attribute(self, context=None): - """infer an Attribute node by using getattr on the associated object""" - for owner in self.expr.infer(context): - if owner is util.Uninferable: - yield owner - continue - - if context and context.boundnode: - # This handles the situation where the attribute is accessed through a subclass - # of a base class and the attribute is defined at the base class's level, - # by taking in consideration a redefinition in the subclass. - if (isinstance(owner, bases.Instance) - and isinstance(context.boundnode, bases.Instance)): - try: - if helpers.is_subtype(helpers.object_type(context.boundnode), - helpers.object_type(owner)): - owner = context.boundnode - except exceptions._NonDeducibleTypeHierarchy: - # Can't determine anything useful. - pass - - try: - context.boundnode = owner - for obj in owner.igetattr(self.attrname, context): - yield obj - context.boundnode = None - except (exceptions.AttributeInferenceError, exceptions.InferenceError): - context.boundnode = None - except AttributeError: - # XXX method / function - context.boundnode = None - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) -nodes.Attribute._infer = decorators.path_wrapper(infer_attribute) -nodes.AssignAttr.infer_lhs = infer_attribute # # won't work with a path wrapper - - -@decorators.path_wrapper -def infer_global(self, context=None): - if context.lookupname is None: - raise exceptions.InferenceError(node=self, context=context) - try: - return bases._infer_stmts(self.root().getattr(context.lookupname), - context) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=context.lookupname, - context=context)) -nodes.Global._infer = infer_global - - -_SUBSCRIPT_SENTINEL = object() - - -@decorators.raise_if_nothing_inferred -def infer_subscript(self, context=None): - """Inference for subscripts - - We're understanding if the index is a Const - or a slice, passing the result of inference - to the value's `getitem` method, which should - handle each supported index type accordingly. - """ - - value = next(self.value.infer(context)) - if value is util.Uninferable: - yield util.Uninferable - return - - index = next(self.slice.infer(context)) - if index is util.Uninferable: - yield util.Uninferable - return - - # Try to deduce the index value. - index_value = _SUBSCRIPT_SENTINEL - if value.__class__ == bases.Instance: - index_value = index - else: - if index.__class__ == bases.Instance: - instance_as_index = helpers.class_instance_as_index(index) - if instance_as_index: - index_value = instance_as_index - else: - index_value = index - if index_value is _SUBSCRIPT_SENTINEL: - raise exceptions.InferenceError(node=self, context=context) - - try: - assigned = value.getitem(index_value, context) - except (exceptions.AstroidTypeError, - exceptions.AstroidIndexError, - exceptions.AttributeInferenceError, - AttributeError) as exc: - util.reraise(exceptions.InferenceError(node=self, error=exc, - context=context)) - - # Prevent inferring if the inferred subscript - # is the same as the original subscripted object. - if self is assigned or assigned is util.Uninferable: - yield util.Uninferable - return - for inferred in assigned.infer(context): - yield inferred - - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) - -nodes.Subscript._infer = decorators.path_wrapper(infer_subscript) -nodes.Subscript.infer_lhs = infer_subscript - - -@decorators.raise_if_nothing_inferred -@decorators.path_wrapper -def _infer_boolop(self, context=None): - """Infer a boolean operation (and / or / not). - - The function will calculate the boolean operation - for all pairs generated through inference for each component - node. - """ - values = self.values - if self.op == 'or': - predicate = operator.truth - else: - predicate = operator.not_ - - try: - values = [value.infer(context=context) for value in values] - except exceptions.InferenceError: - yield util.Uninferable - return - - for pair in itertools.product(*values): - if any(item is util.Uninferable for item in pair): - # Can't infer the final result, just yield Uninferable. - yield util.Uninferable - continue - - bool_values = [item.bool_value() for item in pair] - if any(item is util.Uninferable for item in bool_values): - # Can't infer the final result, just yield Uninferable. - yield util.Uninferable - continue - - # Since the boolean operations are short circuited operations, - # this code yields the first value for which the predicate is True - # and if no value respected the predicate, then the last value will - # be returned (or Uninferable if there was no last value). - # This is conforming to the semantics of `and` and `or`: - # 1 and 0 -> 1 - # 0 and 1 -> 0 - # 1 or 0 -> 1 - # 0 or 1 -> 1 - value = util.Uninferable - for value, bool_value in zip(pair, bool_values): - if predicate(bool_value): - yield value - break - else: - yield value - - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) - -nodes.BoolOp._infer = _infer_boolop - - -# UnaryOp, BinOp and AugAssign inferences - -def _filter_operation_errors(self, infer_callable, context, error): - for result in infer_callable(self, context): - if isinstance(result, error): - # For the sake of .infer(), we don't care about operation - # errors, which is the job of pylint. So return something - # which shows that we can't infer the result. - yield util.Uninferable - else: - yield result - - -def _infer_unaryop(self, context=None): - """Infer what an UnaryOp should return when evaluated.""" - for operand in self.operand.infer(context): - try: - yield operand.infer_unary_op(self.op) - except TypeError as exc: - # The operand doesn't support this operation. - yield util.BadUnaryOperationMessage(operand, self.op, exc) - except AttributeError as exc: - meth = protocols.UNARY_OP_METHOD[self.op] - if meth is None: - # `not node`. Determine node's boolean - # value and negate its result, unless it is - # Uninferable, which will be returned as is. - bool_value = operand.bool_value() - if bool_value is not util.Uninferable: - yield nodes.const_factory(not bool_value) - else: - yield util.Uninferable - else: - if not isinstance(operand, (bases.Instance, nodes.ClassDef)): - # The operation was used on something which - # doesn't support it. - yield util.BadUnaryOperationMessage(operand, self.op, exc) - continue - - try: - try: - methods = dunder_lookup.lookup(operand, meth) - except exceptions.AttributeInferenceError: - yield util.BadUnaryOperationMessage(operand, self.op, exc) - continue - - meth = methods[0] - inferred = next(meth.infer(context=context)) - if inferred is util.Uninferable or not inferred.callable(): - continue - - context = contextmod.copy_context(context) - context.callcontext = contextmod.CallContext(args=[operand]) - call_results = inferred.infer_call_result(self, context=context) - result = next(call_results, None) - if result is None: - # Failed to infer, return the same type. - yield operand - else: - yield result - except exceptions.AttributeInferenceError as exc: - # The unary operation special method was not found. - yield util.BadUnaryOperationMessage(operand, self.op, exc) - except exceptions.InferenceError: - yield util.Uninferable - - -@decorators.raise_if_nothing_inferred -@decorators.path_wrapper -def infer_unaryop(self, context=None): - """Infer what an UnaryOp should return when evaluated.""" - for inferred in _filter_operation_errors(self, _infer_unaryop, context, - util.BadUnaryOperationMessage): - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, context=context)) - -nodes.UnaryOp._infer_unaryop = _infer_unaryop -nodes.UnaryOp._infer = infer_unaryop - - -def _is_not_implemented(const): - """Check if the given const node is NotImplemented.""" - return isinstance(const, nodes.Const) and const.value is NotImplemented - - -def _invoke_binop_inference(instance, opnode, op, other, context, method_name): - """Invoke binary operation inference on the given instance.""" - methods = dunder_lookup.lookup(instance, method_name) - method = methods[0] - inferred = next(method.infer(context=context)) - return instance.infer_binary_op(opnode, op, other, context, inferred) - - -def _aug_op(instance, opnode, op, other, context, reverse=False): - """Get an inference callable for an augmented binary operation.""" - method_name = protocols.AUGMENTED_OP_METHOD[op] - return functools.partial(_invoke_binop_inference, - instance=instance, - op=op, opnode=opnode, other=other, - context=context, - method_name=method_name) - - -def _bin_op(instance, opnode, op, other, context, reverse=False): - """Get an inference callable for a normal binary operation. - - If *reverse* is True, then the reflected method will be used instead. - """ - if reverse: - method_name = protocols.REFLECTED_BIN_OP_METHOD[op] - else: - method_name = protocols.BIN_OP_METHOD[op] - return functools.partial(_invoke_binop_inference, - instance=instance, - op=op, opnode=opnode, other=other, - context=context, - method_name=method_name) - - -def _get_binop_contexts(context, left, right): - """Get contexts for binary operations. - - This will return two inferrence contexts, the first one - for x.__op__(y), the other one for y.__rop__(x), where - only the arguments are inversed. - """ - # The order is important, since the first one should be - # left.__op__(right). - for arg in (right, left): - new_context = context.clone() - new_context.callcontext = contextmod.CallContext(args=[arg]) - new_context.boundnode = None - yield new_context - - -def _same_type(type1, type2): - """Check if type1 is the same as type2.""" - return type1.qname() == type2.qname() - - -def _get_binop_flow(left, left_type, binary_opnode, right, right_type, - context, reverse_context): - """Get the flow for binary operations. - - The rules are a bit messy: - - * if left and right have the same type, then only one - method will be called, left.__op__(right) - * if left and right are unrelated typewise, then first - left.__op__(right) is tried and if this does not exist - or returns NotImplemented, then right.__rop__(left) is tried. - * if left is a subtype of right, then only left.__op__(right) - is tried. - * if left is a supertype of right, then right.__rop__(left) - is first tried and then left.__op__(right) - """ - op = binary_opnode.op - if _same_type(left_type, right_type): - methods = [_bin_op(left, binary_opnode, op, right, context)] - elif helpers.is_subtype(left_type, right_type): - methods = [_bin_op(left, binary_opnode, op, right, context)] - elif helpers.is_supertype(left_type, right_type): - methods = [_bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), - _bin_op(left, binary_opnode, op, right, context)] - else: - methods = [_bin_op(left, binary_opnode, op, right, context), - _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True)] - return methods - - -def _get_aug_flow(left, left_type, aug_opnode, right, right_type, - context, reverse_context): - """Get the flow for augmented binary operations. - - The rules are a bit messy: - - * if left and right have the same type, then left.__augop__(right) - is first tried and then left.__op__(right). - * if left and right are unrelated typewise, then - left.__augop__(right) is tried, then left.__op__(right) - is tried and then right.__rop__(left) is tried. - * if left is a subtype of right, then left.__augop__(right) - is tried and then left.__op__(right). - * if left is a supertype of right, then left.__augop__(right) - is tried, then right.__rop__(left) and then - left.__op__(right) - """ - bin_op = aug_opnode.op.strip("=") - aug_op = aug_opnode.op - if _same_type(left_type, right_type): - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(left, aug_opnode, bin_op, right, context)] - elif helpers.is_subtype(left_type, right_type): - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(left, aug_opnode, bin_op, right, context)] - elif helpers.is_supertype(left_type, right_type): - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True), - _bin_op(left, aug_opnode, bin_op, right, context)] - else: - methods = [_aug_op(left, aug_opnode, aug_op, right, context), - _bin_op(left, aug_opnode, bin_op, right, context), - _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True)] - return methods - - -def _infer_binary_operation(left, right, binary_opnode, context, flow_factory): - """Infer a binary operation between a left operand and a right operand - - This is used by both normal binary operations and augmented binary - operations, the only difference is the flow factory used. - """ - - context, reverse_context = _get_binop_contexts(context, left, right) - left_type = helpers.object_type(left) - right_type = helpers.object_type(right) - methods = flow_factory(left, left_type, binary_opnode, right, right_type, - context, reverse_context) - for method in methods: - try: - results = list(method()) - except AttributeError: - continue - except exceptions.AttributeInferenceError: - continue - except exceptions.InferenceError: - yield util.Uninferable - return - else: - if any(result is util.Uninferable for result in results): - yield util.Uninferable - return - - # TODO(cpopa): since the inference engine might return - # more values than are actually possible, we decide - # to return util.Uninferable if we have union types. - if all(map(_is_not_implemented, results)): - continue - not_implemented = sum(1 for result in results - if _is_not_implemented(result)) - if not_implemented and not_implemented != len(results): - # Can't decide yet what this is, not yet though. - yield util.Uninferable - return - - for result in results: - yield result - return - # TODO(cpopa): yield a BadBinaryOperationMessage here, - # since the operation is not supported - yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type) - - -def _infer_binop(self, context): - """Binary operation inferrence logic.""" - if context is None: - context = contextmod.InferenceContext() - left = self.left - right = self.right - - # we use two separate contexts for evaluating lhs and rhs because - # 1. evaluating lhs may leave some undesired entries in context.path - # which may not let us infer right value of rhs - lhs_context = context.clone() - rhs_context = context.clone() - - for lhs in left.infer(context=lhs_context): - if lhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - for rhs in right.infer(context=rhs_context): - if rhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - try: - for result in _infer_binary_operation(lhs, rhs, self, - context, _get_binop_flow): - yield result - except exceptions._NonDeducibleTypeHierarchy: - yield util.Uninferable - - -@decorators.yes_if_nothing_inferred -@decorators.path_wrapper -def infer_binop(self, context=None): - return _filter_operation_errors(self, _infer_binop, context, - util.BadBinaryOperationMessage) - -nodes.BinOp._infer_binop = _infer_binop -nodes.BinOp._infer = infer_binop - - -def _infer_augassign(self, context=None): - """Inference logic for augmented binary operations.""" - if context is None: - context = contextmod.InferenceContext() - - for lhs in self.target.infer_lhs(context=context): - if lhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - # TODO(cpopa): if we have A() * A(), trying to infer - # the rhs with the same context will result in an - # inference error, so we create another context for it. - # This is a bug which should be fixed in InferenceContext at some point. - rhs_context = context.clone() - rhs_context.path = set() - for rhs in self.value.infer(context=rhs_context): - if rhs is util.Uninferable: - # Don't know how to process this. - yield util.Uninferable - return - - try: - results = _infer_binary_operation(lhs, rhs, self, context, _get_aug_flow) - except exceptions._NonDeducibleTypeHierarchy: - yield util.Uninferable - else: - for result in results: - yield result - - -@decorators.path_wrapper -def infer_augassign(self, context=None): - return _filter_operation_errors(self, _infer_augassign, context, - util.BadBinaryOperationMessage) - -nodes.AugAssign._infer_augassign = _infer_augassign -nodes.AugAssign._infer = infer_augassign - -# End of binary operation inference. - - -def infer_arguments(self, context=None): - name = context.lookupname - if name is None: - raise exceptions.InferenceError(node=self, context=context) - return protocols._arguments_infer_argname(self, name, context) -nodes.Arguments._infer = infer_arguments - - -@decorators.path_wrapper -def infer_assign(self, context=None): - """infer a AssignName/AssignAttr: need to inspect the RHS part of the - assign node - """ - stmt = self.statement() - if isinstance(stmt, nodes.AugAssign): - return stmt.infer(context) - - stmts = list(self.assigned_stmts(context=context)) - return bases._infer_stmts(stmts, context) -nodes.AssignName._infer = infer_assign -nodes.AssignAttr._infer = infer_assign - - -# no infer method on DelName and DelAttr (expected InferenceError) - -@decorators.path_wrapper -def infer_empty_node(self, context=None): - if not self.has_underlying_object(): - yield util.Uninferable - else: - try: - for inferred in MANAGER.infer_ast_from_something(self.object, - context=context): - yield inferred - except exceptions.AstroidError: - yield util.Uninferable -nodes.EmptyNode._infer = infer_empty_node - - -def infer_index(self, context=None): - return self.value.infer(context) -nodes.Index._infer = infer_index - -# TODO: move directly into bases.Instance when the dependency hell -# will be solved. -def instance_getitem(self, index, context=None): - # Rewrap index to Const for this case - if context: - new_context = context.clone() - else: - context = new_context = contextmod.InferenceContext() - - # Create a new callcontext for providing index as an argument. - new_context.callcontext = contextmod.CallContext(args=[index]) - new_context.boundnode = self - - method = next(self.igetattr('__getitem__', context=context)) - if not isinstance(method, bases.BoundMethod): - raise exceptions.InferenceError( - 'Could not find __getitem__ for {node!r}.', - node=self, context=context) - - try: - return next(method.infer_call_result(self, new_context)) - except StopIteration: - util.reraise(exceptions.InferenceError( - message='Inference for {node!r}[{index!s}] failed.', - node=self, index=index, context=context)) - -bases.Instance.getitem = instance_getitem diff --git a/pymode/libs/astroid/interpreter/_import/spec.py b/pymode/libs/astroid/interpreter/_import/spec.py deleted file mode 100644 index 1edd23d6..00000000 --- a/pymode/libs/astroid/interpreter/_import/spec.py +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -import abc -import collections -import enum -import imp -import os -import sys -import zipimport -try: - import importlib.machinery - _HAS_MACHINERY = True -except ImportError: - _HAS_MACHINERY = False - -try: - from functools import lru_cache -except ImportError: - from backports.functools_lru_cache import lru_cache - -from . import util - -ModuleType = enum.Enum('ModuleType', 'C_BUILTIN C_EXTENSION PKG_DIRECTORY ' - 'PY_CODERESOURCE PY_COMPILED PY_FROZEN PY_RESOURCE ' - 'PY_SOURCE PY_ZIPMODULE PY_NAMESPACE') -_ImpTypes = {imp.C_BUILTIN: ModuleType.C_BUILTIN, - imp.C_EXTENSION: ModuleType.C_EXTENSION, - imp.PKG_DIRECTORY: ModuleType.PKG_DIRECTORY, - imp.PY_COMPILED: ModuleType.PY_COMPILED, - imp.PY_FROZEN: ModuleType.PY_FROZEN, - imp.PY_SOURCE: ModuleType.PY_SOURCE, - } -if hasattr(imp, 'PY_RESOURCE'): - _ImpTypes[imp.PY_RESOURCE] = ModuleType.PY_RESOURCE -if hasattr(imp, 'PY_CODERESOURCE'): - _ImpTypes[imp.PY_CODERESOURCE] = ModuleType.PY_CODERESOURCE - -def _imp_type_to_module_type(imp_type): - return _ImpTypes[imp_type] - -_ModuleSpec = collections.namedtuple('_ModuleSpec', 'name type location ' - 'origin submodule_search_locations') - -class ModuleSpec(_ModuleSpec): - """Defines a class similar to PEP 420's ModuleSpec - - A module spec defines a name of a module, its type, location - and where submodules can be found, if the module is a package. - """ - - def __new__(cls, name, module_type, location=None, origin=None, - submodule_search_locations=None): - return _ModuleSpec.__new__(cls, name=name, type=module_type, - location=location, origin=origin, - submodule_search_locations=submodule_search_locations) - - -class Finder(object): - """A finder is a class which knows how to find a particular module.""" - - def __init__(self, path=None): - self._path = path or sys.path - - @abc.abstractmethod - def find_module(self, modname, module_parts, processed, submodule_path): - """Find the given module - - Each finder is responsible for each protocol of finding, as long as - they all return a ModuleSpec. - - :param str modname: The module which needs to be searched. - :param list module_parts: It should be a list of strings, - where each part contributes to the module's - namespace. - :param list processed: What parts from the module parts were processed - so far. - :param list submodule_path: A list of paths where the module - can be looked into. - :returns: A ModuleSpec, describing how and where the module was found, - None, otherwise. - """ - - def contribute_to_path(self, spec, processed): - """Get a list of extra paths where this finder can search.""" - - -class ImpFinder(Finder): - """A finder based on the imp module.""" - - def find_module(self, modname, module_parts, processed, submodule_path): - if submodule_path is not None: - submodule_path = list(submodule_path) - try: - stream, mp_filename, mp_desc = imp.find_module(modname, submodule_path) - except ImportError: - return None - - # Close resources. - if stream: - stream.close() - - return ModuleSpec(name=modname, location=mp_filename, - module_type=_imp_type_to_module_type(mp_desc[2])) - - def contribute_to_path(self, spec, processed): - if spec.location is None: - # Builtin. - return None - - if _is_setuptools_namespace(spec.location): - # extend_path is called, search sys.path for module/packages - # of this name see pkgutil.extend_path documentation - path = [os.path.join(p, *processed) for p in sys.path - if os.path.isdir(os.path.join(p, *processed))] - else: - path = [spec.location] - return path - - -class ExplicitNamespacePackageFinder(ImpFinder): - """A finder for the explicit namespace packages, generated through pkg_resources.""" - - def find_module(self, modname, module_parts, processed, submodule_path): - if util.is_namespace(modname) and modname in sys.modules: - submodule_path = sys.modules[modname].__path__ - return ModuleSpec(name=modname, location='', - origin='namespace', - module_type=ModuleType.PY_NAMESPACE, - submodule_search_locations=submodule_path) - - - def contribute_to_path(self, spec, processed): - return spec.submodule_search_locations - - -class ZipFinder(Finder): - """Finder that knows how to find a module inside zip files.""" - - def __init__(self, path): - super(ZipFinder, self).__init__(path) - self._zipimporters = _precache_zipimporters(path) - - def find_module(self, modname, module_parts, processed, submodule_path): - try: - file_type, filename, path = _search_zip(module_parts, self._zipimporters) - except ImportError: - return None - - return ModuleSpec(name=modname, location=filename, - origin='egg', module_type=file_type, - submodule_search_locations=path) - - -class PathSpecFinder(Finder): - """Finder based on importlib.machinery.PathFinder.""" - - def find_module(self, modname, module_parts, processed, submodule_path): - spec = importlib.machinery.PathFinder.find_spec(modname, path=submodule_path) - if spec: - location = spec.origin if spec.origin != 'namespace' else None - module_type = ModuleType.PY_NAMESPACE if spec.origin == 'namespace' else None - spec = ModuleSpec(name=spec.name, location=location, - origin=spec.origin, module_type=module_type, - submodule_search_locations=list(spec.submodule_search_locations - or [])) - return spec - - def contribute_to_path(self, spec, processed): - if spec.type == ModuleType.PY_NAMESPACE: - return spec.submodule_search_locations - return None - - -_SPEC_FINDERS = ( - ImpFinder, - ZipFinder, -) -if _HAS_MACHINERY and sys.version_info[:2] > (3, 3): - _SPEC_FINDERS += (PathSpecFinder, ) -_SPEC_FINDERS += (ExplicitNamespacePackageFinder, ) - - -def _is_setuptools_namespace(location): - try: - with open(os.path.join(location, '__init__.py'), 'rb') as stream: - data = stream.read(4096) - except IOError: - pass - else: - extend_path = b'pkgutil' in data and b'extend_path' in data - declare_namespace = ( - b"pkg_resources" in data - and b"declare_namespace(__name__)" in data) - return extend_path or declare_namespace - - -@lru_cache() -def _cached_set_diff(left, right): - result = set(left) - result.difference_update(right) - return result - - -def _precache_zipimporters(path=None): - pic = sys.path_importer_cache - - # When measured, despite having the same complexity (O(n)), - # converting to tuples and then caching the conversion to sets - # and the set difference is faster than converting to sets - # and then only caching the set difference. - - req_paths = tuple(path or sys.path) - cached_paths = tuple(pic) - new_paths = _cached_set_diff(req_paths, cached_paths) - for entry_path in new_paths: - try: - pic[entry_path] = zipimport.zipimporter(entry_path) - except zipimport.ZipImportError: - continue - return pic - - -def _search_zip(modpath, pic): - for filepath, importer in list(pic.items()): - if importer is not None: - found = importer.find_module(modpath[0]) - if found: - if not importer.find_module(os.path.sep.join(modpath)): - raise ImportError('No module named %s in %s/%s' % ( - '.'.join(modpath[1:]), filepath, modpath)) - #import code; code.interact(local=locals()) - return (ModuleType.PY_ZIPMODULE, - os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), - filepath) - raise ImportError('No module named %s' % '.'.join(modpath)) - - -def _find_spec_with_path(search_path, modname, module_parts, processed, submodule_path): - finders = [finder(search_path) for finder in _SPEC_FINDERS] - for finder in finders: - spec = finder.find_module(modname, module_parts, processed, submodule_path) - if spec is None: - continue - return finder, spec - - raise ImportError('No module named %s' % '.'.join(module_parts)) - - -def find_spec(modpath, path=None): - """Find a spec for the given module. - - :type modpath: list or tuple - :param modpath: - split module's name (i.e name of a module or package split - on '.'), with leading empty strings for explicit relative import - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :rtype: ModuleSpec - :return: A module spec, which describes how the module was - found and where. - """ - _path = path or sys.path - - # Need a copy for not mutating the argument. - modpath = modpath[:] - - submodule_path = None - module_parts = modpath[:] - processed = [] - - while modpath: - modname = modpath.pop(0) - finder, spec = _find_spec_with_path(_path, modname, - module_parts, processed, - submodule_path or path) - processed.append(modname) - if modpath: - submodule_path = finder.contribute_to_path(spec, processed) - - if spec.type == ModuleType.PKG_DIRECTORY: - spec = spec._replace(submodule_search_locations=submodule_path) - - return spec diff --git a/pymode/libs/astroid/interpreter/_import/util.py b/pymode/libs/astroid/interpreter/_import/util.py deleted file mode 100644 index 79ff4e0a..00000000 --- a/pymode/libs/astroid/interpreter/_import/util.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa - -try: - import pkg_resources -except ImportError: - pkg_resources = None - - -def is_namespace(modname): - # pylint: disable=no-member; astroid issue #290, modifying globals at runtime. - return (pkg_resources is not None - and modname in pkg_resources._namespace_packages) diff --git a/pymode/libs/astroid/interpreter/dunder_lookup.py b/pymode/libs/astroid/interpreter/dunder_lookup.py deleted file mode 100644 index 424ed01f..00000000 --- a/pymode/libs/astroid/interpreter/dunder_lookup.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Contains logic for retrieving special methods. - -This implementation does not rely on the dot attribute access -logic, found in ``.getattr()``. The difference between these two -is that the dunder methods are looked with the type slots -(you can find more about these here -http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/) -As such, the lookup for the special methods is actually simpler than -the dot attribute access. -""" -import itertools - -import astroid -from astroid import exceptions - - -def _lookup_in_mro(node, name): - attrs = node.locals.get(name, []) - - nodes = itertools.chain.from_iterable( - ancestor.locals.get(name, []) - for ancestor in node.ancestors(recurs=True) - ) - values = list(itertools.chain(attrs, nodes)) - if not values: - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - return values - - -def lookup(node, name): - """Lookup the given special method name in the given *node* - - If the special method was found, then a list of attributes - will be returned. Otherwise, `astroid.AttributeInferenceError` - is going to be raised. - """ - if isinstance(node, (astroid.List, - astroid.Tuple, - astroid.Const, - astroid.Dict, - astroid.Set)): - return _builtin_lookup(node, name) - elif isinstance(node, astroid.Instance): - return _lookup_in_mro(node, name) - elif isinstance(node, astroid.ClassDef): - return _class_lookup(node, name) - - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - -def _class_lookup(node, name): - metaclass = node.metaclass() - if metaclass is None: - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - return _lookup_in_mro(metaclass, name) - - -def _builtin_lookup(node, name): - values = node.locals.get(name, []) - if not values: - raise exceptions.AttributeInferenceError( - attribute=name, - target=node - ) - - return values diff --git a/pymode/libs/astroid/interpreter/objectmodel.py b/pymode/libs/astroid/interpreter/objectmodel.py deleted file mode 100644 index 067095cc..00000000 --- a/pymode/libs/astroid/interpreter/objectmodel.py +++ /dev/null @@ -1,632 +0,0 @@ -# Copyright (c) 2016 Claudiu Popa -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER -""" -Data object model, as per https://docs.python.org/3/reference/datamodel.html. - -This module describes, at least partially, a data object model for some -of astroid's nodes. The model contains special attributes that nodes such -as functions, classes, modules etc have, such as __doc__, __class__, -__module__ etc, being used when doing attribute lookups over nodes. - -For instance, inferring `obj.__class__` will first trigger an inference -of the `obj` variable. If it was succesfully inferred, then an attribute -`__class__ will be looked for in the inferred object. This is the part -where the data model occurs. The model is attached to those nodes -and the lookup mechanism will try to see if attributes such as -`__class__` are defined by the model or not. If they are defined, -the model will be requested to return the corresponding value of that -attribute. Thus the model can be viewed as a special part of the lookup -mechanism. -""" - -try: - from functools import lru_cache -except ImportError: - from backports.functools_lru_cache import lru_cache - -import itertools -import pprint -import os -import types - -import six - -import astroid -from astroid import context as contextmod -from astroid import exceptions -from astroid import node_classes - - -def _dunder_dict(instance, attributes): - obj = node_classes.Dict(parent=instance) - - # Convert the keys to node strings - keys = [node_classes.Const(value=value, parent=obj) - for value in list(attributes.keys())] - - # The original attribute has a list of elements for each key, - # but that is not useful for retrieving the special attribute's value. - # In this case, we're picking the last value from each list. - values = [elem[-1] for elem in attributes.values()] - - obj.postinit(list(zip(keys, values))) - return obj - - -class ObjectModel(object): - - def __init__(self): - self._instance = None - - def __repr__(self): - result = [] - cname = type(self).__name__ - string = '%(cname)s(%(fields)s)' - alignment = len(cname) + 1 - for field in sorted(self.attributes()): - width = 80 - len(field) - alignment - lines = pprint.pformat(field, indent=2, - width=width).splitlines(True) - - inner = [lines[0]] - for line in lines[1:]: - inner.append(' ' * alignment + line) - result.append(field) - - return string % {'cname': cname, - 'fields': (',\n' + ' ' * alignment).join(result)} - - def __call__(self, instance): - self._instance = instance - return self - - def __get__(self, instance, cls=None): - # ObjectModel needs to be a descriptor so that just doing - # `special_attributes = SomeObjectModel` should be enough in the body of a node. - # But at the same time, node.special_attributes should return an object - # which can be used for manipulating the special attributes. That's the reason - # we pass the instance through which it got accessed to ObjectModel.__call__, - # returning itself afterwards, so we can still have access to the - # underlying data model and to the instance for which it got accessed. - return self(instance) - - def __contains__(self, name): - return name in self.attributes() - - @lru_cache(maxsize=None) - def attributes(self): - """Get the attributes which are exported by this object model.""" - return [obj[2:] for obj in dir(self) if obj.startswith('py')] - - def lookup(self, name): - """Look up the given *name* in the current model - - It should return an AST or an interpreter object, - but if the name is not found, then an AttributeInferenceError will be raised. - """ - - if name in self.attributes(): - return getattr(self, "py" + name) - raise exceptions.AttributeInferenceError(target=self._instance, attribute=name) - - -class ModuleModel(ObjectModel): - - def _builtins(self): - builtins = astroid.MANAGER.astroid_cache[six.moves.builtins.__name__] - return builtins.special_attributes.lookup('__dict__') - - if six.PY3: - @property - def pybuiltins(self): - return self._builtins() - - else: - @property - def py__builtin__(self): - return self._builtins() - - # __path__ is a standard attribute on *packages* not - # non-package modules. The only mention of it in the - # official 2.7 documentation I can find is in the - # tutorial. - - @property - def py__path__(self): - if not self._instance.package: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='__path__') - - path = os.path.dirname(self._instance.file) - path_obj = node_classes.Const(value=path, parent=self._instance) - - container = node_classes.List(parent=self._instance) - container.postinit([path_obj]) - - return container - - @property - def py__name__(self): - return node_classes.Const(value=self._instance.name, - parent=self._instance) - - @property - def py__doc__(self): - return node_classes.Const(value=self._instance.doc, - parent=self._instance) - - @property - def py__file__(self): - return node_classes.Const(value=self._instance.file, - parent=self._instance) - - @property - def py__dict__(self): - return _dunder_dict(self._instance, self._instance.globals) - - # __package__ isn't mentioned anywhere outside a PEP: - # https://www.python.org/dev/peps/pep-0366/ - @property - def py__package__(self): - if not self._instance.package: - value = '' - else: - value = self._instance.name - - return node_classes.Const(value=value, parent=self._instance) - - # These are related to the Python 3 implementation of the - # import system, - # https://docs.python.org/3/reference/import.html#import-related-module-attributes - - @property - def py__spec__(self): - # No handling for now. - return node_classes.Unknown() - - @property - def py__loader__(self): - # No handling for now. - return node_classes.Unknown() - - @property - def py__cached__(self): - # No handling for now. - return node_classes.Unknown() - - -class FunctionModel(ObjectModel): - - @property - def py__name__(self): - return node_classes.Const(value=self._instance.name, - parent=self._instance) - - @property - def py__doc__(self): - return node_classes.Const(value=self._instance.doc, - parent=self._instance) - - @property - def py__qualname__(self): - return node_classes.Const(value=self._instance.qname(), - parent=self._instance) - - @property - def py__defaults__(self): - func = self._instance - if not func.args.defaults: - return node_classes.Const(value=None, parent=func) - - defaults_obj = node_classes.Tuple(parent=func) - defaults_obj.postinit(func.args.defaults) - return defaults_obj - - @property - def py__annotations__(self): - obj = node_classes.Dict(parent=self._instance) - - if not self._instance.returns: - returns = None - else: - returns = self._instance.returns - - args = self._instance.args - pair_annotations = itertools.chain( - six.moves.zip(args.args, args.annotations), - six.moves.zip(args.kwonlyargs, args.kwonlyargs_annotations) - ) - - annotations = { - arg.name: annotation - for (arg, annotation) in pair_annotations - if annotation - } - if args.varargannotation: - annotations[args.vararg] = args.varargannotation - if args.kwargannotation: - annotations[args.kwarg] = args.kwargannotation - if returns: - annotations['return'] = returns - - items = [(node_classes.Const(key, parent=obj), value) - for (key, value) in annotations.items()] - - obj.postinit(items) - return obj - - @property - def py__dict__(self): - return node_classes.Dict(parent=self._instance) - - py__globals__ = py__dict__ - - @property - def py__kwdefaults__(self): - - def _default_args(args, parent): - for arg in args.kwonlyargs: - try: - default = args.default_value(arg.name) - except exceptions.NoDefault: - continue - - name = node_classes.Const(arg.name, parent=parent) - yield name, default - - args = self._instance.args - obj = node_classes.Dict(parent=self._instance) - defaults = dict(_default_args(args, obj)) - - obj.postinit(list(defaults.items())) - return obj - - @property - def py__module__(self): - return node_classes.Const(self._instance.root().qname()) - - @property - def py__get__(self): - from astroid import bases - - func = self._instance - - class DescriptorBoundMethod(bases.BoundMethod): - """Bound method which knows how to understand calling descriptor binding.""" - def infer_call_result(self, caller, context=None): - if len(caller.args) != 2: - raise exceptions.InferenceError( - "Invalid arguments for descriptor binding", - target=self, context=context) - - context = contextmod.copy_context(context) - cls = next(caller.args[0].infer(context=context)) - - # Rebuild the original value, but with the parent set as the - # class where it will be bound. - new_func = func.__class__(name=func.name, doc=func.doc, - lineno=func.lineno, col_offset=func.col_offset, - parent=cls) - # pylint: disable=no-member - new_func.postinit(func.args, func.body, - func.decorators, func.returns) - - # Build a proper bound method that points to our newly built function. - proxy = bases.UnboundMethod(new_func) - yield bases.BoundMethod(proxy=proxy, bound=cls) - - return DescriptorBoundMethod(proxy=self._instance, bound=self._instance) - - # These are here just for completion. - @property - def py__ne__(self): - return node_classes.Unknown() - - py__subclasshook__ = py__ne__ - py__str__ = py__ne__ - py__sizeof__ = py__ne__ - py__setattr__ = py__ne__ - py__repr__ = py__ne__ - py__reduce__ = py__ne__ - py__reduce_ex__ = py__ne__ - py__new__ = py__ne__ - py__lt__ = py__ne__ - py__eq__ = py__ne__ - py__gt__ = py__ne__ - py__format__ = py__ne__ - py__delattr__ = py__ne__ - py__getattribute__ = py__ne__ - py__hash__ = py__ne__ - py__init__ = py__ne__ - py__dir__ = py__ne__ - py__call__ = py__ne__ - py__class__ = py__ne__ - py__closure__ = py__ne__ - py__code__ = py__ne__ - - if six.PY2: - pyfunc_name = py__name__ - pyfunc_doc = py__doc__ - pyfunc_globals = py__globals__ - pyfunc_dict = py__dict__ - pyfunc_defaults = py__defaults__ - pyfunc_code = py__code__ - pyfunc_closure = py__closure__ - - -class ClassModel(ObjectModel): - - @property - def py__module__(self): - return node_classes.Const(self._instance.root().qname()) - - @property - def py__name__(self): - return node_classes.Const(self._instance.name) - - @property - def py__qualname__(self): - return node_classes.Const(self._instance.qname()) - - @property - def py__doc__(self): - return node_classes.Const(self._instance.doc) - - @property - def py__mro__(self): - if not self._instance.newstyle: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='__mro__') - - mro = self._instance.mro() - obj = node_classes.Tuple(parent=self._instance) - obj.postinit(mro) - return obj - - @property - def pymro(self): - if not self._instance.newstyle: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='mro') - - from astroid import bases - - other_self = self - - # Cls.mro is a method and we need to return one in order to have a proper inference. - # The method we're returning is capable of inferring the underlying MRO though. - class MroBoundMethod(bases.BoundMethod): - def infer_call_result(self, caller, context=None): - yield other_self.py__mro__ - - implicit_metaclass = self._instance.implicit_metaclass() - mro_method = implicit_metaclass.locals['mro'][0] - return MroBoundMethod(proxy=mro_method, bound=implicit_metaclass) - - @property - def py__bases__(self): - obj = node_classes.Tuple() - context = contextmod.InferenceContext() - elts = list(self._instance._inferred_bases(context)) - obj.postinit(elts=elts) - return obj - - @property - def py__class__(self): - from astroid import helpers - return helpers.object_type(self._instance) - - @property - def py__subclasses__(self): - """Get the subclasses of the underlying class - - This looks only in the current module for retrieving the subclasses, - thus it might miss a couple of them. - """ - from astroid import bases - from astroid import scoped_nodes - - if not self._instance.newstyle: - raise exceptions.AttributeInferenceError(target=self._instance, - attribute='__subclasses__') - - qname = self._instance.qname() - root = self._instance.root() - classes = [cls for cls in root.nodes_of_class(scoped_nodes.ClassDef) - if cls != self._instance and cls.is_subtype_of(qname)] - - obj = node_classes.List(parent=self._instance) - obj.postinit(classes) - - class SubclassesBoundMethod(bases.BoundMethod): - def infer_call_result(self, caller, context=None): - yield obj - - implicit_metaclass = self._instance.implicit_metaclass() - subclasses_method = implicit_metaclass.locals['__subclasses__'][0] - return SubclassesBoundMethod(proxy=subclasses_method, - bound=implicit_metaclass) - - @property - def py__dict__(self): - return node_classes.Dict(parent=self._instance) - - -class SuperModel(ObjectModel): - - @property - def py__thisclass__(self): - return self._instance.mro_pointer - - @property - def py__self_class__(self): - return self._instance._self_class - - @property - def py__self__(self): - return self._instance.type - - @property - def py__class__(self): - return self._instance._proxied - - -class UnboundMethodModel(ObjectModel): - - @property - def py__class__(self): - from astroid import helpers - return helpers.object_type(self._instance) - - @property - def py__func__(self): - return self._instance._proxied - - @property - def py__self__(self): - return node_classes.Const(value=None, parent=self._instance) - - pyim_func = py__func__ - pyim_class = py__class__ - pyim_self = py__self__ - - -class BoundMethodModel(FunctionModel): - - @property - def py__func__(self): - return self._instance._proxied._proxied - - @property - def py__self__(self): - return self._instance.bound - - -class GeneratorModel(FunctionModel): - - def __new__(cls, *args, **kwargs): - # Append the values from the GeneratorType unto this object. - ret = super(GeneratorModel, cls).__new__(cls, *args, **kwargs) - generator = astroid.MANAGER.astroid_cache[six.moves.builtins.__name__]['generator'] - for name, values in generator.locals.items(): - print(name, values) - method = values[0] - patched = lambda cls, meth=method: meth - - setattr(type(ret), 'py' + name, property(patched)) - - return ret - - @property - def py__name__(self): - return node_classes.Const(value=self._instance.parent.name, - parent=self._instance) - - @property - def py__doc__(self): - return node_classes.Const(value=self._instance.parent.doc, - parent=self._instance) - - -class InstanceModel(ObjectModel): - - @property - def py__class__(self): - return self._instance._proxied - - @property - def py__module__(self): - return node_classes.Const(self._instance.root().qname()) - - @property - def py__doc__(self): - return node_classes.Const(self._instance.doc) - - @property - def py__dict__(self): - return _dunder_dict(self._instance, self._instance.instance_attrs) - - -class ExceptionInstanceModel(InstanceModel): - - @property - def pyargs(self): - message = node_classes.Const('') - args = node_classes.Tuple(parent=self._instance) - args.postinit((message, )) - return args - - if six.PY3: - # It's available only on Python 3. - - @property - def py__traceback__(self): - builtins = astroid.MANAGER.astroid_cache[six.moves.builtins.__name__] - traceback_type = builtins[types.TracebackType.__name__] - return traceback_type.instantiate_class() - - if six.PY2: - # It's available only on Python 2. - - @property - def pymessage(self): - return node_classes.Const('') - - -class DictModel(ObjectModel): - - @property - def py__class__(self): - return self._instance._proxied - - def _generic_dict_attribute(self, obj, name): - """Generate a bound method that can infer the given *obj*.""" - - class DictMethodBoundMethod(astroid.BoundMethod): - def infer_call_result(self, caller, context=None): - yield obj - - meth = next(self._instance._proxied.igetattr(name)) - return DictMethodBoundMethod(proxy=meth, bound=self._instance) - - @property - def pyitems(self): - elems = [] - obj = node_classes.List(parent=self._instance) - for key, value in self._instance.items: - elem = node_classes.Tuple(parent=obj) - elem.postinit((key, value)) - elems.append(elem) - obj.postinit(elts=elems) - - if six.PY3: - from astroid import objects - obj = objects.DictItems(obj) - - return self._generic_dict_attribute(obj, 'items') - - @property - def pykeys(self): - keys = [key for (key, _) in self._instance.items] - obj = node_classes.List(parent=self._instance) - obj.postinit(elts=keys) - - if six.PY3: - from astroid import objects - obj = objects.DictKeys(obj) - - return self._generic_dict_attribute(obj, 'keys') - - @property - def pyvalues(self): - - values = [value for (_, value) in self._instance.items] - obj = node_classes.List(parent=self._instance) - obj.postinit(values) - - if six.PY3: - from astroid import objects - obj = objects.DictValues(obj) - - return self._generic_dict_attribute(obj, 'values') diff --git a/pymode/libs/astroid/manager.py b/pymode/libs/astroid/manager.py deleted file mode 100644 index 4e7e35d0..00000000 --- a/pymode/libs/astroid/manager.py +++ /dev/null @@ -1,292 +0,0 @@ -# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""astroid manager: avoid multiple astroid build of a same module when -possible by providing a class responsible to get astroid representation -from various source and using a cache of built modules) -""" - -import os -import sys -import zipimport - -import six - -from astroid import exceptions -from astroid.interpreter._import import spec -from astroid import modutils -from astroid import transforms -from astroid import util - - -def safe_repr(obj): - try: - return repr(obj) - except Exception: # pylint: disable=broad-except - return '???' - - -class AstroidManager(object): - """the astroid manager, responsible to build astroid from files - or modules. - - Use the Borg pattern. - """ - - name = 'astroid loader' - brain = {} - - def __init__(self): - self.__dict__ = AstroidManager.brain - if not self.__dict__: - # NOTE: cache entries are added by the [re]builder - self.astroid_cache = {} - self._mod_file_cache = {} - self._failed_import_hooks = [] - self.always_load_extensions = False - self.optimize_ast = False - self.extension_package_whitelist = set() - self._transform = transforms.TransformVisitor() - - # Export these APIs for convenience - self.register_transform = self._transform.register_transform - self.unregister_transform = self._transform.unregister_transform - - def visit_transforms(self, node): - """Visit the transforms and apply them to the given *node*.""" - return self._transform.visit(node) - - def ast_from_file(self, filepath, modname=None, fallback=True, source=False): - """given a module name, return the astroid object""" - try: - filepath = modutils.get_source_file(filepath, include_no_ext=True) - source = True - except modutils.NoSourceFile: - pass - if modname is None: - try: - modname = '.'.join(modutils.modpath_from_file(filepath)) - except ImportError: - modname = filepath - if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: - return self.astroid_cache[modname] - if source: - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).file_build(filepath, modname) - elif fallback and modname: - return self.ast_from_module_name(modname) - raise exceptions.AstroidBuildingError( - 'Unable to build an AST for {path}.', path=filepath) - - def _build_stub_module(self, modname): - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).string_build('', modname) - - def _build_namespace_module(self, modname, path): - from astroid.builder import build_namespace_package_module - return build_namespace_package_module(modname, path) - - def _can_load_extension(self, modname): - if self.always_load_extensions: - return True - if modutils.is_standard_module(modname): - return True - parts = modname.split('.') - return any( - '.'.join(parts[:x]) in self.extension_package_whitelist - for x in range(1, len(parts) + 1)) - - def ast_from_module_name(self, modname, context_file=None): - """given a module name, return the astroid object""" - if modname in self.astroid_cache: - return self.astroid_cache[modname] - if modname == '__main__': - return self._build_stub_module(modname) - old_cwd = os.getcwd() - if context_file: - os.chdir(os.path.dirname(context_file)) - try: - found_spec = self.file_from_module_name(modname, context_file) - # pylint: disable=no-member - if found_spec.type == spec.ModuleType.PY_ZIPMODULE: - # pylint: disable=no-member - module = self.zip_import_data(found_spec.location) - if module is not None: - return module - - elif found_spec.type in (spec.ModuleType.C_BUILTIN, - spec.ModuleType.C_EXTENSION): - # pylint: disable=no-member - if (found_spec.type == spec.ModuleType.C_EXTENSION - and not self._can_load_extension(modname)): - return self._build_stub_module(modname) - try: - module = modutils.load_module_from_name(modname) - except Exception as ex: # pylint: disable=broad-except - util.reraise(exceptions.AstroidImportError( - 'Loading {modname} failed with:\n{error}', - modname=modname, path=found_spec.location, error=ex)) - return self.ast_from_module(module, modname) - - elif found_spec.type == spec.ModuleType.PY_COMPILED: - raise exceptions.AstroidImportError( - "Unable to load compiled module {modname}.", - # pylint: disable=no-member - modname=modname, path=found_spec.location) - - elif found_spec.type == spec.ModuleType.PY_NAMESPACE: - return self._build_namespace_module(modname, - # pylint: disable=no-member - found_spec.submodule_search_locations) - - # pylint: disable=no-member - if found_spec.location is None: - raise exceptions.AstroidImportError( - "Can't find a file for module {modname}.", - modname=modname) - - # pylint: disable=no-member - return self.ast_from_file(found_spec.location, modname, fallback=False) - except exceptions.AstroidBuildingError as e: - for hook in self._failed_import_hooks: - try: - return hook(modname) - except exceptions.AstroidBuildingError: - pass - raise e - finally: - os.chdir(old_cwd) - - def zip_import_data(self, filepath): - if zipimport is None: - return None - from astroid.builder import AstroidBuilder - builder = AstroidBuilder(self) - for ext in ('.zip', '.egg'): - try: - eggpath, resource = filepath.rsplit(ext + os.path.sep, 1) - except ValueError: - continue - try: - importer = zipimport.zipimporter(eggpath + ext) - zmodname = resource.replace(os.path.sep, '.') - if importer.is_package(resource): - zmodname = zmodname + '.__init__' - module = builder.string_build(importer.get_source(resource), - zmodname, filepath) - return module - except Exception: # pylint: disable=broad-except - continue - return None - - def file_from_module_name(self, modname, contextfile): - try: - value = self._mod_file_cache[(modname, contextfile)] - traceback = sys.exc_info()[2] - except KeyError: - try: - value = modutils.file_info_from_modpath( - modname.split('.'), context_file=contextfile) - traceback = sys.exc_info()[2] - except ImportError as ex: - value = exceptions.AstroidImportError( - 'Failed to import module {modname} with error:\n{error}.', - modname=modname, error=ex) - traceback = sys.exc_info()[2] - self._mod_file_cache[(modname, contextfile)] = value - if isinstance(value, exceptions.AstroidBuildingError): - six.reraise(exceptions.AstroidBuildingError, - value, traceback) - return value - - def ast_from_module(self, module, modname=None): - """given an imported module, return the astroid object""" - modname = modname or module.__name__ - if modname in self.astroid_cache: - return self.astroid_cache[modname] - try: - # some builtin modules don't have __file__ attribute - filepath = module.__file__ - if modutils.is_python_source(filepath): - return self.ast_from_file(filepath, modname) - except AttributeError: - pass - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).module_build(module, modname) - - def ast_from_class(self, klass, modname=None): - """get astroid for the given class""" - if modname is None: - try: - modname = klass.__module__ - except AttributeError: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to get module for class {class_name}.', - cls=klass, class_repr=safe_repr(klass), modname=modname)) - modastroid = self.ast_from_module_name(modname) - return modastroid.getattr(klass.__name__)[0] # XXX - - def infer_ast_from_something(self, obj, context=None): - """infer astroid for the given class""" - if hasattr(obj, '__class__') and not isinstance(obj, type): - klass = obj.__class__ - else: - klass = obj - try: - modname = klass.__module__ - except AttributeError: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to get module for {class_repr}.', - cls=klass, class_repr=safe_repr(klass))) - except Exception as ex: # pylint: disable=broad-except - util.reraise(exceptions.AstroidImportError( - 'Unexpected error while retrieving module for {class_repr}:\n' - '{error}', cls=klass, class_repr=safe_repr(klass), error=ex)) - try: - name = klass.__name__ - except AttributeError: - util.reraise(exceptions.AstroidBuildingError( - 'Unable to get name for {class_repr}:\n', - cls=klass, class_repr=safe_repr(klass))) - except Exception as ex: # pylint: disable=broad-except - util.reraise(exceptions.AstroidImportError( - 'Unexpected error while retrieving name for {class_repr}:\n' - '{error}', cls=klass, class_repr=safe_repr(klass), error=ex)) - # take care, on living object __module__ is regularly wrong :( - modastroid = self.ast_from_module_name(modname) - if klass is obj: - for inferred in modastroid.igetattr(name, context): - yield inferred - else: - for inferred in modastroid.igetattr(name, context): - yield inferred.instantiate_class() - - def register_failed_import_hook(self, hook): - """Registers a hook to resolve imports that cannot be found otherwise. - - `hook` must be a function that accepts a single argument `modname` which - contains the name of the module or package that could not be imported. - If `hook` can resolve the import, must return a node of type `astroid.Module`, - otherwise, it must raise `AstroidBuildingError`. - """ - self._failed_import_hooks.append(hook) - - def cache_module(self, module): - """Cache a module if no module with the same name is known yet.""" - self.astroid_cache.setdefault(module.name, module) - - def clear_cache(self, astroid_builtin=None): - # XXX clear transforms - self.astroid_cache.clear() - # force bootstrap again, else we may ends up with cache inconsistency - # between the manager and CONST_PROXY, making - # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the - # test order - import astroid.raw_building - astroid.raw_building._astroid_bootstrapping( - astroid_builtin=astroid_builtin) diff --git a/pymode/libs/astroid/mixins.py b/pymode/libs/astroid/mixins.py deleted file mode 100644 index 739828d5..00000000 --- a/pymode/libs/astroid/mixins.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright (c) 2010-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""This module contains some mixins for the different nodes. -""" - -import warnings - -from astroid import decorators -from astroid import exceptions - - -class BlockRangeMixIn(object): - """override block range """ - - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.lineno - - def _elsed_block_range(self, lineno, orelse, last=None): - """handle block line numbers range for try/finally, for, if and while - statements - """ - if lineno == self.fromlineno: - return lineno, lineno - if orelse: - if lineno >= orelse[0].fromlineno: - return lineno, orelse[-1].tolineno - return lineno, orelse[0].fromlineno - 1 - return lineno, last or self.tolineno - - -class FilterStmtsMixin(object): - """Mixin for statement filtering and assignment type""" - - def _get_filtered_stmts(self, _, node, _stmts, mystmt): - """method used in _filter_stmts to get statements and trigger break""" - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - def assign_type(self): - return self - - def ass_type(self): - warnings.warn('%s.ass_type() is deprecated and slated for removal ' - 'in astroid 2.0, use %s.assign_type() instead.' - % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.assign_type() - - -class AssignTypeMixin(object): - - def assign_type(self): - return self - - def ass_type(self): - warnings.warn('%s.ass_type() is deprecated and slated for removal ' - 'in astroid 2.0, use %s.assign_type() instead.' - % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.assign_type() - - def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - return _stmts, True - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - -class ParentAssignTypeMixin(AssignTypeMixin): - - def assign_type(self): - return self.parent.assign_type() - - def ass_type(self): - warnings.warn('%s.ass_type() is deprecated and slated for removal ' - 'in astroid 2.0, use %s.assign_type() instead.' - % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.assign_type() - - -class ImportFromMixin(FilterStmtsMixin): - """MixIn for From and Import Nodes""" - - def _infer_name(self, frame, name): - return name - - def do_import_module(self, modname=None): - """return the ast for a module whose name is imported by - """ - # handle special case where we are on a package node importing a module - # using the same name as the package, which may end in an infinite loop - # on relative imports - # XXX: no more needed ? - mymodule = self.root() - level = getattr(self, 'level', None) # Import as no level - if modname is None: - modname = self.modname - # XXX we should investigate deeper if we really want to check - # importing itself: modname and mymodule.name be relative or absolute - if mymodule.relative_to_absolute_name(modname, level) == mymodule.name: - # FIXME: we used to raise InferenceError here, but why ? - return mymodule - - return mymodule.import_module(modname, level=level, - relative_only=level and level >= 1) - - def real_name(self, asname): - """get name from 'as' name""" - for name, _asname in self.names: - if name == '*': - return asname - if not _asname: - name = name.split('.', 1)[0] - _asname = name - if asname == _asname: - return name - raise exceptions.AttributeInferenceError( - 'Could not find original name for {attribute} in {target!r}', - target=self, attribute=asname) diff --git a/pymode/libs/astroid/modutils.py b/pymode/libs/astroid/modutils.py deleted file mode 100644 index f17bfdab..00000000 --- a/pymode/libs/astroid/modutils.py +++ /dev/null @@ -1,645 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015 Florian Bruhin -# Copyright (c) 2015 Radosław Ganczarek -# Copyright (c) 2016 Jakub Wilk - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Python modules manipulation utility functions. - -:type PY_SOURCE_EXTS: tuple(str) -:var PY_SOURCE_EXTS: list of possible python source file extension - -:type STD_LIB_DIRS: set of str -:var STD_LIB_DIRS: directories where standard modules are located - -:type BUILTIN_MODULES: dict -:var BUILTIN_MODULES: dictionary with builtin module names has key -""" -import imp -import os -import platform -import sys -from distutils.sysconfig import get_python_lib # pylint: disable=import-error -# pylint: disable=import-error, no-name-in-module -from distutils.errors import DistutilsPlatformError -# distutils is replaced by virtualenv with a module that does -# weird path manipulations in order to get to the -# real distutils module. - -import six - -from .interpreter._import import spec -from .interpreter._import import util - -if sys.platform.startswith('win'): - PY_SOURCE_EXTS = ('py', 'pyw') - PY_COMPILED_EXTS = ('dll', 'pyd') -else: - PY_SOURCE_EXTS = ('py',) - PY_COMPILED_EXTS = ('so',) - - -try: - # The explicit sys.prefix is to work around a patch in virtualenv that - # replaces the 'real' sys.prefix (i.e. the location of the binary) - # with the prefix from which the virtualenv was created. This throws - # off the detection logic for standard library modules, thus the - # workaround. - STD_LIB_DIRS = set([ - get_python_lib(standard_lib=True, prefix=sys.prefix), - # Take care of installations where exec_prefix != prefix. - get_python_lib(standard_lib=True, prefix=sys.exec_prefix), - get_python_lib(standard_lib=True)]) -# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to -# non-valid path, see https://bugs.pypy.org/issue1164 -except DistutilsPlatformError: - STD_LIB_DIRS = set() - -if os.name == 'nt': - STD_LIB_DIRS.add(os.path.join(sys.prefix, 'dlls')) - try: - # real_prefix is defined when running inside virtual environments, - # created with the **virtualenv** library. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'dlls')) - except AttributeError: - # sys.base_exec_prefix is always defined, but in a virtual environment - # created with the stdlib **venv** module, it points to the original - # installation, if the virtual env is activated. - try: - STD_LIB_DIRS.add(os.path.join(sys.base_exec_prefix, 'dlls')) - except AttributeError: - pass - -if platform.python_implementation() == 'PyPy': - _root = os.path.join(sys.prefix, 'lib_pypy') - STD_LIB_DIRS.add(_root) - try: - # real_prefix is defined when running inside virtualenv. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'lib_pypy')) - except AttributeError: - pass - del _root -if os.name == 'posix': - # Need the real prefix is we're under a virtualenv, otherwise - # the usual one will do. - try: - prefix = sys.real_prefix - except AttributeError: - prefix = sys.prefix - - def _posix_path(path): - base_python = 'python%d.%d' % sys.version_info[:2] - return os.path.join(prefix, path, base_python) - - STD_LIB_DIRS.add(_posix_path('lib')) - if sys.maxsize > 2**32: - # This tries to fix a problem with /usr/lib64 builds, - # where systems are running both 32-bit and 64-bit code - # on the same machine, which reflects into the places where - # standard library could be found. More details can be found - # here http://bugs.python.org/issue1294959. - # An easy reproducing case would be - # https://github.com/PyCQA/pylint/issues/712#issuecomment-163178753 - STD_LIB_DIRS.add(_posix_path('lib64')) - -EXT_LIB_DIR = get_python_lib() -IS_JYTHON = platform.python_implementation() == 'Jython' -BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - -def _normalize_path(path): - return os.path.normcase(os.path.abspath(path)) - - -def _canonicalize_path(path): - return os.path.realpath(os.path.expanduser(path)) - - -def _path_from_filename(filename, is_jython=IS_JYTHON): - if not is_jython: - if sys.version_info > (3, 0): - return filename - else: - if filename.endswith(".pyc"): - return filename[:-1] - return filename - head, has_pyclass, _ = filename.partition("$py.class") - if has_pyclass: - return head + ".py" - return filename - - -def _handle_blacklist(blacklist, dirnames, filenames): - """remove files/directories in the black list - - dirnames/filenames are usually from os.walk - """ - for norecurs in blacklist: - if norecurs in dirnames: - dirnames.remove(norecurs) - elif norecurs in filenames: - filenames.remove(norecurs) - - -_NORM_PATH_CACHE = {} - -def _cache_normalize_path(path): - """abspath with caching""" - # _module_file calls abspath on every path in sys.path every time it's - # called; on a larger codebase this easily adds up to half a second just - # assembling path components. This cache alleviates that. - try: - return _NORM_PATH_CACHE[path] - except KeyError: - if not path: # don't cache result for '' - return _normalize_path(path) - result = _NORM_PATH_CACHE[path] = _normalize_path(path) - return result - -def load_module_from_name(dotted_name, path=None, use_sys=True): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=1): - """Load a python module from its split name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package split on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = imp.find_module(part, path) - module = imp.load_module(curname, mp_file, mp_filename, mp_desc) - # mp_file still needs to be closed. - if mp_file: - mp_file.close() - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, '__file__', '') - prevmodule = module - if not _file and util.is_namespace(curname): - continue - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):])) - path = [os.path.dirname(_file)] - return module - - -def load_module_from_file(filepath, path=None, use_sys=True, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def check_modpath_has_init(path, mod_path): - """check there are some __init__.py all along the way""" - modpath = [] - for part in mod_path: - modpath.append(part) - path = os.path.join(path, part) - if not _has_init(path): - old_namespace = util.is_namespace('.'.join(modpath)) - if not old_namespace: - return False - return True - - -def modpath_from_file_with_callback(filename, extrapath=None, is_package_cb=None): - filename = _path_from_filename(filename) - filename = os.path.realpath(os.path.expanduser(filename)) - base = os.path.splitext(filename)[0] - - if extrapath is not None: - for path_ in six.moves.map(_canonicalize_path, extrapath): - path = os.path.abspath(path_) - if path and os.path.normcase(base[:len(path)]) == os.path.normcase(path): - submodpath = [pkg for pkg in base[len(path):].split(os.sep) - if pkg] - if is_package_cb(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - - for path in six.moves.map(_canonicalize_path, sys.path): - path = _cache_normalize_path(path) - if path and os.path.normcase(base).startswith(path): - modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] - if is_package_cb(path, modpath[:-1]): - return modpath - - raise ImportError('Unable to find module for %s in %s' % ( - filename, ', \n'.join(sys.path))) - - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding split module's name - (i.e name of a module or package split on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package split in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding split module's name - """ - return modpath_from_file_with_callback(filename, extrapath, check_modpath_has_init) - - -def file_from_modpath(modpath, path=None, context_file=None): - return file_info_from_modpath(modpath, path, context_file).location - -def file_info_from_modpath(modpath, path=None, context_file=None): - """given a mod path (i.e. split module / package name), return the - corresponding file, giving priority to source file over precompiled - file if it exists - - :type modpath: list or tuple - :param modpath: - split module's name (i.e name of a module or package split - on '.') - (this means explicit relative imports that start with dots have - empty strings in this list!) - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - :raise ImportError: if there is no such module in the directory - - :rtype: (str or None, import type) - :return: - the path to the module's file or None if it's an integrated - builtin module such as 'sys' - """ - if context_file is not None: - context = os.path.dirname(context_file) - else: - context = context_file - if modpath[0] == 'xml': - # handle _xmlplus - try: - return _spec_from_modpath(['_xmlplus'] + modpath[1:], path, context) - except ImportError: - return _spec_from_modpath(modpath, path, context) - elif modpath == ['os', 'path']: - # FIXME: currently ignoring search_path... - return spec.ModuleSpec(name='os.path', location=os.path.__file__, module_type=imp.PY_SOURCE) - return _spec_from_modpath(modpath, path, context) - - -def get_module_part(dotted_name, context_file=None): - """given a dotted name return the module part of the name : - - >>> get_module_part('astroid.as_string.dump') - 'astroid.as_string' - - :type dotted_name: str - :param dotted_name: full name of the identifier we are interested in - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the module part of the name or None if we have not been able at - all to import the given name - - XXX: deprecated, since it doesn't handle package precedence over module - (see #10066) - """ - # os.path trick - if dotted_name.startswith('os.path'): - return 'os.path' - parts = dotted_name.split('.') - if context_file is not None: - # first check for builtin module which won't be considered latter - # in that case (path != None) - if parts[0] in BUILTIN_MODULES: - if len(parts) > 2: - raise ImportError(dotted_name) - return parts[0] - # don't use += or insert, we want a new list to be created ! - path = None - starti = 0 - if parts[0] == '': - assert context_file is not None, \ - 'explicit relative import, but no context_file?' - path = [] # prevent resolving the import non-relatively - starti = 1 - while parts[starti] == '': # for all further dots: change context - starti += 1 - context_file = os.path.dirname(context_file) - for i in range(starti, len(parts)): - try: - file_from_modpath(parts[starti:i+1], path=path, - context_file=context_file) - except ImportError: - if i < max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_module_files(src_directory, blacklist, list_all=False): - """given a package directory return a list of all available python - module's files in the package and its subpackages - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: iterable - list of files or directories to ignore. - - :type list_all: bool - :param list_all: - get files from all paths, including ones without __init__.py - - :rtype: list - :return: - the list of all available python module's files in the package and - its subpackages - """ - files = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not list_all and '__init__.py' not in filenames: - dirnames[:] = () - continue - for filename in filenames: - if _is_python_file(filename): - src = os.path.join(directory, filename) - files.append(src) - return files - - -def get_source_file(filename, include_no_ext=False): - """given a python module's file name return the matching source file - name (the filename will be returned identically if it's a already an - absolute path to a python source file...) - - :type filename: str - :param filename: python module's file name - - - :raise NoSourceFile: if no source file exists on the file system - - :rtype: str - :return: the absolute path of the source file if it exists - """ - filename = os.path.abspath(_path_from_filename(filename)) - base, orig_ext = os.path.splitext(filename) - for ext in PY_SOURCE_EXTS: - source_path = '%s.%s' % (base, ext) - if os.path.exists(source_path): - return source_path - if include_no_ext and not orig_ext and os.path.exists(base): - return base - raise NoSourceFile(filename) - - -def is_python_source(filename): - """ - rtype: bool - return: True if the filename is a python source file - """ - return os.path.splitext(filename)[1][1:] in PY_SOURCE_EXTS - - -def is_standard_module(modname, std_path=None): - """try to guess if a module is a standard python module (by default, - see `std_path` parameter's description) - - :type modname: str - :param modname: name of the module we are interested in - - :type std_path: list(str) or tuple(str) - :param std_path: list of path considered has standard - - - :rtype: bool - :return: - true if the module: - - is located on the path listed in one of the directory in `std_path` - - is a built-in module - """ - modname = modname.split('.')[0] - try: - filename = file_from_modpath([modname]) - except ImportError: - # import failed, i'm probably not so wrong by supposing it's - # not standard... - return False - # modules which are not living in a file are considered standard - # (sys and __builtin__ for instance) - if filename is None: - # we assume there are no namespaces in stdlib - return not util.is_namespace(modname) - filename = _normalize_path(filename) - if filename.startswith(_cache_normalize_path(EXT_LIB_DIR)): - return False - if std_path is None: - std_path = STD_LIB_DIRS - for path in std_path: - if filename.startswith(_cache_normalize_path(path)): - return True - return False - - - -def is_relative(modname, from_file): - """return true if the given module name is relative to the given - file name - - :type modname: str - :param modname: name of the module we are interested in - - :type from_file: str - :param from_file: - path of the module from which modname has been imported - - :rtype: bool - :return: - true if the module has been imported relatively to `from_file` - """ - if not os.path.isdir(from_file): - from_file = os.path.dirname(from_file) - if from_file in sys.path: - return False - try: - stream, _, _ = imp.find_module(modname.split('.')[0], [from_file]) - - # Close the stream to avoid ResourceWarnings. - if stream: - stream.close() - return True - except ImportError: - return False - - -# internal only functions ##################################################### - -def _spec_from_modpath(modpath, path=None, context=None): - """given a mod path (i.e. split module / package name), return the - corresponding spec - - this function is used internally, see `file_from_modpath`'s - documentation for more information - """ - assert modpath - location = None - if context is not None: - try: - found_spec = spec.find_spec(modpath, [context]) - location = found_spec.location - except ImportError: - found_spec = spec.find_spec(modpath, path) - location = found_spec.location - else: - found_spec = spec.find_spec(modpath, path) - if found_spec.type == spec.ModuleType.PY_COMPILED: - try: - location = get_source_file(found_spec.location) - return found_spec._replace(location=location, type=spec.ModuleType.PY_SOURCE) - except NoSourceFile: - return found_spec._replace(location=location) - elif found_spec.type == spec.ModuleType.C_BUILTIN: - # integrated builtin module - return found_spec._replace(location=None) - elif found_spec.type == spec.ModuleType.PKG_DIRECTORY: - location = _has_init(found_spec.location) - return found_spec._replace(location=location, type=spec.ModuleType.PY_SOURCE) - return found_spec - - -def _is_python_file(filename): - """return true if the given filename should be considered as a python file - - .pyc and .pyo are ignored - """ - for ext in ('.py', '.so', '.pyd', '.pyw'): - if filename.endswith(ext): - return True - return False - - -def _has_init(directory): - """if the given directory has a valid __init__ file, return its path, - else return None - """ - mod_or_pack = os.path.join(directory, '__init__') - for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'): - if os.path.exists(mod_or_pack + '.' + ext): - return mod_or_pack + '.' + ext - return None - - -def is_namespace(specobj): - return specobj.type == spec.ModuleType.PY_NAMESPACE - -def is_directory(specobj): - return specobj.type == spec.ModuleType.PKG_DIRECTORY diff --git a/pymode/libs/astroid/node_classes.py b/pymode/libs/astroid/node_classes.py deleted file mode 100644 index b4e71874..00000000 --- a/pymode/libs/astroid/node_classes.py +++ /dev/null @@ -1,2082 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2014, 2016 Google, Inc. -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2016 Jakub Wilk - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Module for some node classes. More nodes in scoped_nodes.py -""" - -import abc -import pprint -import warnings -try: - from functools import singledispatch as _singledispatch -except ImportError: - from singledispatch import singledispatch as _singledispatch - -import six - -from astroid import as_string -from astroid import bases -from astroid import context as contextmod -from astroid import decorators -from astroid import exceptions -from astroid import manager -from astroid import mixins -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ -MANAGER = manager.AstroidManager() - - -@decorators.raise_if_nothing_inferred -def unpack_infer(stmt, context=None): - """recursively generate nodes inferred by the given statement. - If the inferred value is a list or a tuple, recurse on the elements - """ - if isinstance(stmt, (List, Tuple)): - for elt in stmt.elts: - if elt is util.Uninferable: - yield elt - continue - for inferred_elt in unpack_infer(elt, context): - yield inferred_elt - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=stmt, context=context)) - # if inferred is a final node, return it and stop - inferred = next(stmt.infer(context)) - if inferred is stmt: - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=stmt, context=context)) - # else, infer recursively, except Uninferable object that should be returned as is - for inferred in stmt.infer(context): - if inferred is util.Uninferable: - yield inferred - else: - for inf_inf in unpack_infer(inferred, context): - yield inf_inf - raise StopIteration(dict(node=stmt, context=context)) - - -def are_exclusive(stmt1, stmt2, exceptions=None): # pylint: disable=redefined-outer-name - """return true if the two given statements are mutually exclusive - - `exceptions` may be a list of exception names. If specified, discard If - branches and check one of the statement is in an exception handler catching - one of the given exceptions. - - algorithm : - 1) index stmt1's parents - 2) climb among stmt2's parents until we find a common parent - 3) if the common parent is a If or TryExcept statement, look if nodes are - in exclusive branches - """ - # index stmt1's parents - stmt1_parents = {} - children = {} - node = stmt1.parent - previous = stmt1 - while node: - stmt1_parents[node] = 1 - children[node] = previous - previous = node - node = node.parent - # climb among stmt2's parents until we find a common parent - node = stmt2.parent - previous = stmt2 - while node: - if node in stmt1_parents: - # if the common parent is a If or TryExcept statement, look if - # nodes are in exclusive branches - if isinstance(node, If) and exceptions is None: - if (node.locate_child(previous)[1] - is not node.locate_child(children[node])[1]): - return True - elif isinstance(node, TryExcept): - c2attr, c2node = node.locate_child(previous) - c1attr, c1node = node.locate_child(children[node]) - if c1node is not c2node: - first_in_body_caught_by_handlers = ( - c2attr == 'handlers' - and c1attr == 'body' - and previous.catch(exceptions)) - second_in_body_caught_by_handlers = ( - c2attr == 'body' - and c1attr == 'handlers' - and children[node].catch(exceptions)) - first_in_else_other_in_handlers = ( - c2attr == 'handlers' and c1attr == 'orelse') - second_in_else_other_in_handlers = ( - c2attr == 'orelse' and c1attr == 'handlers') - if any((first_in_body_caught_by_handlers, - second_in_body_caught_by_handlers, - first_in_else_other_in_handlers, - second_in_else_other_in_handlers)): - return True - elif c2attr == 'handlers' and c1attr == 'handlers': - return previous is not children[node] - return False - previous = node - node = node.parent - return False - - -# getitem() helpers. - -_SLICE_SENTINEL = object() - - -def _slice_value(index, context=None): - """Get the value of the given slice index.""" - - if isinstance(index, Const): - if isinstance(index.value, (int, type(None))): - return index.value - elif index is None: - return None - else: - # Try to infer what the index actually is. - # Since we can't return all the possible values, - # we'll stop at the first possible value. - try: - inferred = next(index.infer(context=context)) - except exceptions.InferenceError: - pass - else: - if isinstance(inferred, Const): - if isinstance(inferred.value, (int, type(None))): - return inferred.value - - # Use a sentinel, because None can be a valid - # value that this function can return, - # as it is the case for unspecified bounds. - return _SLICE_SENTINEL - - -def _infer_slice(node, context=None): - lower = _slice_value(node.lower, context) - upper = _slice_value(node.upper, context) - step = _slice_value(node.step, context) - if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)): - return slice(lower, upper, step) - - raise exceptions.AstroidTypeError( - message='Could not infer slice used in subscript', - node=node, index=node.parent, context=context) - - -def _container_getitem(instance, elts, index, context=None): - """Get a slice or an item, using the given *index*, for the given sequence.""" - try: - if isinstance(index, Slice): - index_slice = _infer_slice(index, context=context) - new_cls = instance.__class__() - new_cls.elts = elts[index_slice] - new_cls.parent = instance.parent - return new_cls - elif isinstance(index, Const): - return elts[index.value] - except IndexError: - util.reraise(exceptions.AstroidIndexError( - message='Index {index!s} out of range', - node=instance, index=index, context=context)) - except TypeError as exc: - util.reraise(exceptions.AstroidTypeError( - message='Type error {error!r}', error=exc, - node=instance, index=index, context=context)) - - raise exceptions.AstroidTypeError( - 'Could not use %s as subscript index' % index - ) - - -class NodeNG(object): - """Base Class for all Astroid node classes. - - It represents a node of the new abstract syntax tree. - """ - is_statement = False - optional_assign = False # True for For (and for Comprehension if py <3.0) - is_function = False # True for FunctionDef nodes - # attributes below are set by the builder module or by raw factories - lineno = None - col_offset = None - # parent node in the tree - parent = None - # attributes containing child node(s) redefined in most concrete classes: - _astroid_fields = () - # attributes containing non-nodes: - _other_fields = () - # attributes containing AST-dependent fields: - _other_other_fields = () - # instance specific inference function infer(node, context) - _explicit_inference = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.lineno = lineno - self.col_offset = col_offset - self.parent = parent - - def infer(self, context=None, **kwargs): - """main interface to the interface system, return a generator on inferred - values. - - If the instance has some explicit inference function set, it will be - called instead of the default interface. - """ - if self._explicit_inference is not None: - # explicit_inference is not bound, give it self explicitly - try: - # pylint: disable=not-callable - return self._explicit_inference(self, context, **kwargs) - except exceptions.UseInferenceDefault: - pass - - if not context: - return self._infer(context, **kwargs) - - key = (self, context.lookupname, - context.callcontext, context.boundnode) - if key in context.inferred: - return iter(context.inferred[key]) - - return context.cache_generator(key, self._infer(context, **kwargs)) - - def _repr_name(self): - """return self.name or self.attrname or '' for nice representation""" - return getattr(self, 'name', getattr(self, 'attrname', '')) - - def __str__(self): - rname = self._repr_name() - cname = type(self).__name__ - if rname: - string = '%(cname)s.%(rname)s(%(fields)s)' - alignment = len(cname) + len(rname) + 2 - else: - string = '%(cname)s(%(fields)s)' - alignment = len(cname) + 1 - result = [] - for field in self._other_fields + self._astroid_fields: - value = getattr(self, field) - width = 80 - len(field) - alignment - lines = pprint.pformat(value, indent=2, - width=width).splitlines(True) - - inner = [lines[0]] - for line in lines[1:]: - inner.append(' ' * alignment + line) - result.append('%s=%s' % (field, ''.join(inner))) - - return string % {'cname': cname, - 'rname': rname, - 'fields': (',\n' + ' ' * alignment).join(result)} - - def __repr__(self): - rname = self._repr_name() - if rname: - string = '<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>' - else: - string = '<%(cname)s l.%(lineno)s at 0x%(id)x>' - return string % {'cname': type(self).__name__, - 'rname': rname, - 'lineno': self.fromlineno, - 'id': id(self)} - - def accept(self, visitor): - func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) - return func(self) - - def get_children(self): - for field in self._astroid_fields: - attr = getattr(self, field) - if attr is None: - continue - if isinstance(attr, (list, tuple)): - for elt in attr: - yield elt - else: - yield attr - - def last_child(self): - """an optimized version of list(get_children())[-1]""" - for field in self._astroid_fields[::-1]: - attr = getattr(self, field) - if not attr: # None or empty listy / tuple - continue - if isinstance(attr, (list, tuple)): - return attr[-1] - - return attr - return None - - def parent_of(self, node): - """return true if i'm a parent of the given node""" - parent = node.parent - while parent is not None: - if self is parent: - return True - parent = parent.parent - return False - - def statement(self): - """return the first parent node marked as statement node""" - if self.is_statement: - return self - return self.parent.statement() - - def frame(self): - """return the first parent frame node (i.e. Module, FunctionDef or - ClassDef) - - """ - return self.parent.frame() - - def scope(self): - """return the first node defining a new scope (i.e. Module, - FunctionDef, ClassDef, Lambda but also GenExpr) - - """ - return self.parent.scope() - - def root(self): - """return the root node of the tree, (i.e. a Module)""" - if self.parent: - return self.parent.root() - return self - - def child_sequence(self, child): - """search for the right sequence where the child lies in""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - if node_or_sequence is child: - return [node_or_sequence] - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if (isinstance(node_or_sequence, (tuple, list)) - and child in node_or_sequence): - return node_or_sequence - - msg = 'Could not find %s in %s\'s children' - raise exceptions.AstroidError(msg % (repr(child), repr(self))) - - def locate_child(self, child): - """return a 2-uple (child attribute name, sequence or node)""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if child is node_or_sequence: - return field, child - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: - return field, node_or_sequence - msg = 'Could not find %s in %s\'s children' - raise exceptions.AstroidError(msg % (repr(child), repr(self))) - # FIXME : should we merge child_sequence and locate_child ? locate_child - # is only used in are_exclusive, child_sequence one time in pylint. - - def next_sibling(self): - """return the next sibling statement""" - return self.parent.next_sibling() - - def previous_sibling(self): - """return the previous sibling statement""" - return self.parent.previous_sibling() - - def nearest(self, nodes): - """return the node which is the nearest before this one in the - given list of nodes - """ - myroot = self.root() - mylineno = self.fromlineno - nearest = None, 0 - for node in nodes: - assert node.root() is myroot, \ - 'nodes %s and %s are not from the same module' % (self, node) - lineno = node.fromlineno - if node.fromlineno > mylineno: - break - if lineno > nearest[1]: - nearest = node, lineno - # FIXME: raise an exception if nearest is None ? - return nearest[0] - - # these are lazy because they're relatively expensive to compute for every - # single node, and they rarely get looked at - - @decorators.cachedproperty - def fromlineno(self): - if self.lineno is None: - return self._fixed_source_line() - - return self.lineno - - @decorators.cachedproperty - def tolineno(self): - if not self._astroid_fields: - # can't have children - lastchild = None - else: - lastchild = self.last_child() - if lastchild is None: - return self.fromlineno - - return lastchild.tolineno - - def _fixed_source_line(self): - """return the line number where the given node appears - - we need this method since not all nodes have the lineno attribute - correctly set... - """ - line = self.lineno - _node = self - try: - while line is None: - _node = next(_node.get_children()) - line = _node.lineno - except StopIteration: - _node = self.parent - while _node and line is None: - line = _node.lineno - _node = _node.parent - return line - - def block_range(self, lineno): - """handle block line numbers range for non block opening statements - """ - return lineno, self.tolineno - - def set_local(self, name, stmt): - """delegate to a scoped parent handling a locals dictionary""" - self.parent.set_local(name, stmt) - - def nodes_of_class(self, klass, skip_klass=None): - """return an iterator on nodes which are instance of the given class(es) - - klass may be a class object or a tuple of class objects - """ - if isinstance(self, klass): - yield self - for child_node in self.get_children(): - if skip_klass is not None and isinstance(child_node, skip_klass): - continue - for matching in child_node.nodes_of_class(klass, skip_klass): - yield matching - - def _infer_name(self, frame, name): - # overridden for ImportFrom, Import, Global, TryExcept and Arguments - return None - - def _infer(self, context=None): - """we don't know how to resolve a statement by default""" - # this method is overridden by most concrete classes - raise exceptions.InferenceError('No inference function for {node!r}.', - node=self, context=context) - - def inferred(self): - '''return list of inferred values for a more simple inference usage''' - return list(self.infer()) - - def infered(self): - warnings.warn('%s.infered() is deprecated and slated for removal ' - 'in astroid 2.0, use %s.inferred() instead.' - % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.inferred() - - def instantiate_class(self): - """instantiate a node if it is a ClassDef node, else return self""" - return self - - def has_base(self, node): - return False - - def callable(self): - return False - - def eq(self, value): - return False - - def as_string(self): - return as_string.to_code(self) - - def repr_tree(self, ids=False, include_linenos=False, - ast_state=False, indent=' ', max_depth=0, max_width=80): - """Returns a string representation of the AST from this node. - - :param ids: If true, includes the ids with the node type names. - - :param include_linenos: If true, includes the line numbers and - column offsets. - - :param ast_state: If true, includes information derived from - the whole AST like local and global variables. - - :param indent: A string to use to indent the output string. - - :param max_depth: If set to a positive integer, won't return - nodes deeper than max_depth in the string. - - :param max_width: Only positive integer values are valid, the - default is 80. Attempts to format the output string to stay - within max_width characters, but can exceed it under some - circumstances. - """ - @_singledispatch - def _repr_tree(node, result, done, cur_indent='', depth=1): - """Outputs a representation of a non-tuple/list, non-node that's - contained within an AST, including strings. - """ - lines = pprint.pformat(node, - width=max(max_width - len(cur_indent), - 1)).splitlines(True) - result.append(lines[0]) - result.extend([cur_indent + line for line in lines[1:]]) - return len(lines) != 1 - - # pylint: disable=unused-variable; doesn't understand singledispatch - @_repr_tree.register(tuple) - @_repr_tree.register(list) - def _repr_seq(node, result, done, cur_indent='', depth=1): - """Outputs a representation of a sequence that's contained within an AST.""" - cur_indent += indent - result.append('[') - if not node: - broken = False - elif len(node) == 1: - broken = _repr_tree(node[0], result, done, cur_indent, depth) - elif len(node) == 2: - broken = _repr_tree(node[0], result, done, cur_indent, depth) - if not broken: - result.append(', ') - else: - result.append(',\n') - result.append(cur_indent) - broken = (_repr_tree(node[1], result, done, cur_indent, depth) - or broken) - else: - result.append('\n') - result.append(cur_indent) - for child in node[:-1]: - _repr_tree(child, result, done, cur_indent, depth) - result.append(',\n') - result.append(cur_indent) - _repr_tree(node[-1], result, done, cur_indent, depth) - broken = True - result.append(']') - return broken - - # pylint: disable=unused-variable; doesn't understand singledispatch - @_repr_tree.register(NodeNG) - def _repr_node(node, result, done, cur_indent='', depth=1): - """Outputs a strings representation of an astroid node.""" - if node in done: - result.append(indent + ' max_depth: - result.append('...') - return False - depth += 1 - cur_indent += indent - if ids: - result.append('%s<0x%x>(\n' % (type(node).__name__, id(node))) - else: - result.append('%s(' % type(node).__name__) - fields = [] - if include_linenos: - fields.extend(('lineno', 'col_offset')) - fields.extend(node._other_fields) - fields.extend(node._astroid_fields) - if ast_state: - fields.extend(node._other_other_fields) - if not fields: - broken = False - elif len(fields) == 1: - result.append('%s=' % fields[0]) - broken = _repr_tree(getattr(node, fields[0]), result, done, - cur_indent, depth) - else: - result.append('\n') - result.append(cur_indent) - for field in fields[:-1]: - result.append('%s=' % field) - _repr_tree(getattr(node, field), result, done, cur_indent, - depth) - result.append(',\n') - result.append(cur_indent) - result.append('%s=' % fields[-1]) - _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, - depth) - broken = True - result.append(')') - return broken - - result = [] - _repr_tree(self, result, set()) - return ''.join(result) - - def bool_value(self): - """Determine the bool value of this node - - The boolean value of a node can have three - possible values: - - * False. For instance, empty data structures, - False, empty strings, instances which return - explicitly False from the __nonzero__ / __bool__ - method. - * True. Most of constructs are True by default: - classes, functions, modules etc - * Uninferable: the inference engine is uncertain of the - node's value. - """ - return util.Uninferable - - -class Statement(NodeNG): - """Statement node adding a few attributes""" - is_statement = True - - def next_sibling(self): - """return the next sibling statement""" - stmts = self.parent.child_sequence(self) - index = stmts.index(self) - try: - return stmts[index +1] - except IndexError: - pass - - def previous_sibling(self): - """return the previous sibling statement""" - stmts = self.parent.child_sequence(self) - index = stmts.index(self) - if index >= 1: - return stmts[index -1] - - - -@six.add_metaclass(abc.ABCMeta) -class _BaseContainer(mixins.ParentAssignTypeMixin, - NodeNG, bases.Instance): - """Base class for Set, FrozenSet, Tuple and List.""" - - _astroid_fields = ('elts',) - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.elts = [] - super(_BaseContainer, self).__init__(lineno, col_offset, parent) - - def postinit(self, elts): - self.elts = elts - - @classmethod - def from_constants(cls, elts=None): - node = cls() - if elts is None: - node.elts = [] - else: - node.elts = [const_factory(e) for e in elts] - return node - - def itered(self): - return self.elts - - def bool_value(self): - return bool(self.elts) - - @abc.abstractmethod - def pytype(self): - pass - - -class LookupMixIn(object): - """Mixin looking up a name in the right scope - """ - - def lookup(self, name): - """lookup a variable name - - return the scope node and the list of assignments associated to the - given name according to the scope where it has been found (locals, - globals or builtin) - - The lookup is starting from self's scope. If self is not a frame itself - and the name is found in the inner frame locals, statements will be - filtered to remove ignorable statements according to self's location - """ - return self.scope().scope_lookup(self, name) - - def ilookup(self, name): - """inferred lookup - - return an iterator on inferred values of the statements returned by - the lookup method - """ - frame, stmts = self.lookup(name) - context = contextmod.InferenceContext() - return bases._infer_stmts(stmts, context, frame) - - def _filter_stmts(self, stmts, frame, offset): - """filter statements to remove ignorable statements. - - If self is not a frame itself and the name is found in the inner - frame locals, statements will be filtered to remove ignorable - statements according to self's location - """ - # if offset == -1, my actual frame is not the inner frame but its parent - # - # class A(B): pass - # - # we need this to resolve B correctly - if offset == -1: - myframe = self.frame().parent.frame() - else: - myframe = self.frame() - # If the frame of this node is the same as the statement - # of this node, then the node is part of a class or - # a function definition and the frame of this node should be the - # the upper frame, not the frame of the definition. - # For more information why this is important, - # see Pylint issue #295. - # For example, for 'b', the statement is the same - # as the frame / scope: - # - # def test(b=1): - # ... - - if self.statement() is myframe and myframe.parent: - myframe = myframe.parent.frame() - mystmt = self.statement() - # line filtering if we are in the same frame - # - # take care node may be missing lineno information (this is the case for - # nodes inserted for living objects) - if myframe is frame and mystmt.fromlineno is not None: - assert mystmt.fromlineno is not None, mystmt - mylineno = mystmt.fromlineno + offset - else: - # disabling lineno filtering - mylineno = 0 - _stmts = [] - _stmt_parents = [] - for node in stmts: - stmt = node.statement() - # line filtering is on and we have reached our location, break - if mylineno > 0 and stmt.fromlineno > mylineno: - break - assert hasattr(node, 'assign_type'), (node, node.scope(), - node.scope().locals) - assign_type = node.assign_type() - if node.has_base(self): - break - - _stmts, done = assign_type._get_filtered_stmts(self, node, _stmts, mystmt) - if done: - break - - optional_assign = assign_type.optional_assign - if optional_assign and assign_type.parent_of(self): - # we are inside a loop, loop var assignment is hiding previous - # assignment - _stmts = [node] - _stmt_parents = [stmt.parent] - continue - - # XXX comment various branches below!!! - try: - pindex = _stmt_parents.index(stmt.parent) - except ValueError: - pass - else: - # we got a parent index, this means the currently visited node - # is at the same block level as a previously visited node - if _stmts[pindex].assign_type().parent_of(assign_type): - # both statements are not at the same block level - continue - # if currently visited node is following previously considered - # assignment and both are not exclusive, we can drop the - # previous one. For instance in the following code :: - # - # if a: - # x = 1 - # else: - # x = 2 - # print x - # - # we can't remove neither x = 1 nor x = 2 when looking for 'x' - # of 'print x'; while in the following :: - # - # x = 1 - # x = 2 - # print x - # - # we can remove x = 1 when we see x = 2 - # - # moreover, on loop assignment types, assignment won't - # necessarily be done if the loop has no iteration, so we don't - # want to clear previous assignments if any (hence the test on - # optional_assign) - if not (optional_assign or are_exclusive(_stmts[pindex], node)): - del _stmt_parents[pindex] - del _stmts[pindex] - if isinstance(node, AssignName): - if not optional_assign and stmt.parent is mystmt.parent: - _stmts = [] - _stmt_parents = [] - elif isinstance(node, DelName): - _stmts = [] - _stmt_parents = [] - continue - if not are_exclusive(self, node): - _stmts.append(node) - _stmt_parents.append(stmt.parent) - return _stmts - - -# Name classes - -class AssignName(LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG): - """class representing an AssignName node""" - _other_fields = ('name',) - - def __init__(self, name=None, lineno=None, col_offset=None, parent=None): - self.name = name - super(AssignName, self).__init__(lineno, col_offset, parent) - - -class DelName(LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG): - """class representing a DelName node""" - _other_fields = ('name',) - - def __init__(self, name=None, lineno=None, col_offset=None, parent=None): - self.name = name - super(DelName, self).__init__(lineno, col_offset, parent) - - -class Name(LookupMixIn, NodeNG): - """class representing a Name node""" - _other_fields = ('name',) - - def __init__(self, name=None, lineno=None, col_offset=None, parent=None): - self.name = name - super(Name, self).__init__(lineno, col_offset, parent) - - -class Arguments(mixins.AssignTypeMixin, NodeNG): - """class representing an Arguments node""" - if six.PY3: - # Python 3.4+ uses a different approach regarding annotations, - # each argument is a new class, _ast.arg, which exposes an - # 'annotation' attribute. In astroid though, arguments are exposed - # as is in the Arguments node and the only way to expose annotations - # is by using something similar with Python 3.3: - # - we expose 'varargannotation' and 'kwargannotation' of annotations - # of varargs and kwargs. - # - we expose 'annotation', a list with annotations for - # for each normal argument. If an argument doesn't have an - # annotation, its value will be None. - - _astroid_fields = ('args', 'defaults', 'kwonlyargs', - 'kw_defaults', 'annotations', 'varargannotation', - 'kwargannotation', 'kwonlyargs_annotations') - varargannotation = None - kwargannotation = None - else: - _astroid_fields = ('args', 'defaults', 'kwonlyargs', 'kw_defaults') - _other_fields = ('vararg', 'kwarg') - - def __init__(self, vararg=None, kwarg=None, parent=None): - super(Arguments, self).__init__(parent=parent) - self.vararg = vararg - self.kwarg = kwarg - self.args = [] - self.defaults = [] - self.kwonlyargs = [] - self.kw_defaults = [] - self.annotations = [] - self.kwonlyargs_annotations = [] - - def postinit(self, args, defaults, kwonlyargs, kw_defaults, - annotations, - kwonlyargs_annotations=None, - varargannotation=None, - kwargannotation=None): - self.args = args - self.defaults = defaults - self.kwonlyargs = kwonlyargs - self.kw_defaults = kw_defaults - self.annotations = annotations - self.kwonlyargs_annotations = kwonlyargs_annotations - self.varargannotation = varargannotation - self.kwargannotation = kwargannotation - - def _infer_name(self, frame, name): - if self.parent is frame: - return name - return None - - @decorators.cachedproperty - def fromlineno(self): - lineno = super(Arguments, self).fromlineno - return max(lineno, self.parent.fromlineno or 0) - - def format_args(self): - """return arguments formatted as string""" - result = [] - if self.args: - result.append( - _format_args(self.args, self.defaults, - getattr(self, 'annotations', None)) - ) - if self.vararg: - result.append('*%s' % self.vararg) - if self.kwonlyargs: - if not self.vararg: - result.append('*') - result.append(_format_args( - self.kwonlyargs, - self.kw_defaults, - self.kwonlyargs_annotations - )) - if self.kwarg: - result.append('**%s' % self.kwarg) - return ', '.join(result) - - def default_value(self, argname): - """return the default value for an argument - - :raise `NoDefault`: if there is no default value defined - """ - i = _find_arg(argname, self.args)[0] - if i is not None: - idx = i - (len(self.args) - len(self.defaults)) - if idx >= 0: - return self.defaults[idx] - i = _find_arg(argname, self.kwonlyargs)[0] - if i is not None and self.kw_defaults[i] is not None: - return self.kw_defaults[i] - raise exceptions.NoDefault(func=self.parent, name=argname) - - def is_argument(self, name): - """return True if the name is defined in arguments""" - if name == self.vararg: - return True - if name == self.kwarg: - return True - return (self.find_argname(name, True)[1] is not None or - self.kwonlyargs and _find_arg(name, self.kwonlyargs, True)[1] is not None) - - def find_argname(self, argname, rec=False): - """return index and Name node with given name""" - if self.args: # self.args may be None in some cases (builtin function) - return _find_arg(argname, self.args, rec) - return None, None - - def get_children(self): - """override get_children to skip over None elements in kw_defaults""" - for child in super(Arguments, self).get_children(): - if child is not None: - yield child - - -def _find_arg(argname, args, rec=False): - for i, arg in enumerate(args): - if isinstance(arg, Tuple): - if rec: - found = _find_arg(argname, arg.elts) - if found[0] is not None: - return found - elif arg.name == argname: - return i, arg - return None, None - - -def _format_args(args, defaults=None, annotations=None): - values = [] - if args is None: - return '' - if annotations is None: - annotations = [] - if defaults is not None: - default_offset = len(args) - len(defaults) - packed = six.moves.zip_longest(args, annotations) - for i, (arg, annotation) in enumerate(packed): - if isinstance(arg, Tuple): - values.append('(%s)' % _format_args(arg.elts)) - else: - argname = arg.name - if annotation is not None: - argname += ':' + annotation.as_string() - values.append(argname) - - if defaults is not None and i >= default_offset: - if defaults[i-default_offset] is not None: - values[-1] += '=' + defaults[i-default_offset].as_string() - return ', '.join(values) - - -class AssignAttr(mixins.ParentAssignTypeMixin, NodeNG): - """class representing an AssignAttr node""" - _astroid_fields = ('expr',) - _other_fields = ('attrname',) - expr = None - - def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): - self.attrname = attrname - super(AssignAttr, self).__init__(lineno, col_offset, parent) - - def postinit(self, expr=None): - self.expr = expr - - -class Assert(Statement): - """class representing an Assert node""" - _astroid_fields = ('test', 'fail',) - test = None - fail = None - - def postinit(self, test=None, fail=None): - self.fail = fail - self.test = test - - -class Assign(mixins.AssignTypeMixin, Statement): - """class representing an Assign node""" - _astroid_fields = ('targets', 'value',) - targets = None - value = None - - def postinit(self, targets=None, value=None): - self.targets = targets - self.value = value - - -class AnnAssign(mixins.AssignTypeMixin, Statement): - """Class representing an AnnAssign node""" - - _astroid_fields = ('target', 'annotation', 'value',) - _other_fields = ('simple',) - target = None - annotation = None - value = None - simple = None - - def postinit(self, target, annotation, simple, value=None): - self.target = target - self.annotation = annotation - self.value = value - self.simple = simple - - -class AugAssign(mixins.AssignTypeMixin, Statement): - """class representing an AugAssign node""" - _astroid_fields = ('target', 'value') - _other_fields = ('op',) - target = None - value = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(AugAssign, self).__init__(lineno, col_offset, parent) - - def postinit(self, target=None, value=None): - self.target = target - self.value = value - - # This is set by inference.py - def _infer_augassign(self, context=None): - raise NotImplementedError - - def type_errors(self, context=None): - """Return a list of TypeErrors which can occur during inference. - - Each TypeError is represented by a :class:`BadBinaryOperationMessage`, - which holds the original exception. - """ - try: - results = self._infer_augassign(context=context) - return [result for result in results - if isinstance(result, util.BadBinaryOperationMessage)] - except exceptions.InferenceError: - return [] - - -class Repr(NodeNG): - """class representing a Repr node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class BinOp(NodeNG): - """class representing a BinOp node""" - _astroid_fields = ('left', 'right') - _other_fields = ('op',) - left = None - right = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(BinOp, self).__init__(lineno, col_offset, parent) - - def postinit(self, left=None, right=None): - self.left = left - self.right = right - - # This is set by inference.py - def _infer_binop(self, context=None): - raise NotImplementedError - - def type_errors(self, context=None): - """Return a list of TypeErrors which can occur during inference. - - Each TypeError is represented by a :class:`BadBinaryOperationMessage`, - which holds the original exception. - """ - try: - results = self._infer_binop(context=context) - return [result for result in results - if isinstance(result, util.BadBinaryOperationMessage)] - except exceptions.InferenceError: - return [] - - -class BoolOp(NodeNG): - """class representing a BoolOp node""" - _astroid_fields = ('values',) - _other_fields = ('op',) - values = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(BoolOp, self).__init__(lineno, col_offset, parent) - - def postinit(self, values=None): - self.values = values - - -class Break(Statement): - """class representing a Break node""" - - -class Call(NodeNG): - """class representing a Call node""" - _astroid_fields = ('func', 'args', 'keywords') - func = None - args = None - keywords = None - - def postinit(self, func=None, args=None, keywords=None): - self.func = func - self.args = args - self.keywords = keywords - - @property - def starargs(self): - args = self.args or [] - return [arg for arg in args if isinstance(arg, Starred)] - - @property - def kwargs(self): - keywords = self.keywords or [] - return [keyword for keyword in keywords if keyword.arg is None] - - -class Compare(NodeNG): - """class representing a Compare node""" - _astroid_fields = ('left', 'ops',) - left = None - ops = None - - def postinit(self, left=None, ops=None): - self.left = left - self.ops = ops - - def get_children(self): - """override get_children for tuple fields""" - yield self.left - for _, comparator in self.ops: - yield comparator # we don't want the 'op' - - def last_child(self): - """override last_child""" - # XXX maybe if self.ops: - return self.ops[-1][1] - #return self.left - - -class Comprehension(NodeNG): - """class representing a Comprehension node""" - _astroid_fields = ('target', 'iter', 'ifs') - _other_fields = ('is_async',) - target = None - iter = None - ifs = None - is_async = None - - def __init__(self, parent=None): - super(Comprehension, self).__init__() - self.parent = parent - - # pylint: disable=redefined-builtin; same name as builtin ast module. - def postinit(self, target=None, iter=None, ifs=None, is_async=None): - self.target = target - self.iter = iter - self.ifs = ifs - self.is_async = is_async - - optional_assign = True - def assign_type(self): - return self - - def ass_type(self): - warnings.warn('%s.ass_type() is deprecated and slated for removal' - 'in astroid 2.0, use %s.assign_type() instead.' - % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.assign_type() - - def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - if isinstance(lookup_node, (Const, Name)): - return [lookup_node], True - - elif self.statement() is mystmt: - # original node's statement is the assignment, only keeps - # current node (gen exp, list comp) - - return [node], True - - return stmts, False - - -class Const(NodeNG, bases.Instance): - """represent a constant node like num, str, bool, None, bytes""" - _other_fields = ('value',) - - def __init__(self, value, lineno=None, col_offset=None, parent=None): - self.value = value - super(Const, self).__init__(lineno, col_offset, parent) - - def getitem(self, index, context=None): - if isinstance(index, Const): - index_value = index.value - elif isinstance(index, Slice): - index_value = _infer_slice(index, context=context) - - else: - raise exceptions.AstroidTypeError( - 'Could not use type {} as subscript index'.format(type(index)) - ) - - try: - if isinstance(self.value, six.string_types): - return Const(self.value[index_value]) - if isinstance(self.value, bytes) and six.PY3: - # Bytes aren't instances of six.string_types - # on Python 3. Also, indexing them should return - # integers. - return Const(self.value[index_value]) - except IndexError as exc: - util.reraise(exceptions.AstroidIndexError( - message='Index {index!r} out of range', error=exc, - node=self, index=index, context=context)) - except TypeError as exc: - util.reraise(exceptions.AstroidTypeError( - message='Type error {error!r}', error=exc, - node=self, index=index, context=context)) - - raise exceptions.AstroidTypeError( - '%r (value=%s)' % (self, self.value) - ) - - def has_dynamic_getattr(self): - return False - - def itered(self): - if isinstance(self.value, six.string_types): - return self.value - raise TypeError() - - def pytype(self): - return self._proxied.qname() - - def bool_value(self): - return bool(self.value) - - -class Continue(Statement): - """class representing a Continue node""" - - -class Decorators(NodeNG): - """class representing a Decorators node""" - _astroid_fields = ('nodes',) - nodes = None - - def postinit(self, nodes): - self.nodes = nodes - - def scope(self): - # skip the function node to go directly to the upper level scope - return self.parent.parent.scope() - - -class DelAttr(mixins.ParentAssignTypeMixin, NodeNG): - """class representing a DelAttr node""" - _astroid_fields = ('expr',) - _other_fields = ('attrname',) - expr = None - - def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): - self.attrname = attrname - super(DelAttr, self).__init__(lineno, col_offset, parent) - - def postinit(self, expr=None): - self.expr = expr - - -class Delete(mixins.AssignTypeMixin, Statement): - """class representing a Delete node""" - _astroid_fields = ('targets',) - targets = None - - def postinit(self, targets=None): - self.targets = targets - - -class Dict(NodeNG, bases.Instance): - """class representing a Dict node""" - _astroid_fields = ('items',) - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.items = [] - super(Dict, self).__init__(lineno, col_offset, parent) - - def postinit(self, items): - self.items = items - - @classmethod - def from_constants(cls, items=None): - node = cls() - if items is None: - node.items = [] - else: - node.items = [(const_factory(k), const_factory(v)) - for k, v in items.items()] - return node - - def pytype(self): - return '%s.dict' % BUILTINS - - def get_children(self): - """get children of a Dict node""" - # overrides get_children - for key, value in self.items: - yield key - yield value - - def last_child(self): - """override last_child""" - if self.items: - return self.items[-1][1] - return None - - def itered(self): - return self.items[::2] - - def getitem(self, index, context=None): - for key, value in self.items: - # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}. - if isinstance(key, DictUnpack): - try: - return value.getitem(index, context) - except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): - continue - for inferredkey in key.infer(context): - if inferredkey is util.Uninferable: - continue - if isinstance(inferredkey, Const) and isinstance(index, Const): - if inferredkey.value == index.value: - return value - - raise exceptions.AstroidIndexError(index) - - def bool_value(self): - return bool(self.items) - - -class Expr(Statement): - """class representing a Expr node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class Ellipsis(NodeNG): # pylint: disable=redefined-builtin - """class representing an Ellipsis node""" - - def bool_value(self): - return True - - -class EmptyNode(NodeNG): - """class representing an EmptyNode node""" - - object = None - - -class ExceptHandler(mixins.AssignTypeMixin, Statement): - """class representing an ExceptHandler node""" - _astroid_fields = ('type', 'name', 'body',) - type = None - name = None - body = None - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, type=None, name=None, body=None): - self.type = type - self.name = name - self.body = body - - @decorators.cachedproperty - def blockstart_tolineno(self): - if self.name: - return self.name.tolineno - elif self.type: - return self.type.tolineno - - return self.lineno - - def catch(self, exceptions): # pylint: disable=redefined-outer-name - if self.type is None or exceptions is None: - return True - for node in self.type.nodes_of_class(Name): - if node.name in exceptions: - return True - - -class Exec(Statement): - """class representing an Exec node""" - _astroid_fields = ('expr', 'globals', 'locals',) - expr = None - globals = None - locals = None - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, expr=None, globals=None, locals=None): - self.expr = expr - self.globals = globals - self.locals = locals - - -class ExtSlice(NodeNG): - """class representing an ExtSlice node""" - _astroid_fields = ('dims',) - dims = None - - def postinit(self, dims=None): - self.dims = dims - - -class For(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, Statement): - """class representing a For node""" - _astroid_fields = ('target', 'iter', 'body', 'orelse',) - target = None - iter = None - body = None - orelse = None - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, target=None, iter=None, body=None, orelse=None): - self.target = target - self.iter = iter - self.body = body - self.orelse = orelse - - optional_assign = True - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.iter.tolineno - - -class AsyncFor(For): - """Asynchronous For built with `async` keyword.""" - - -class Await(NodeNG): - """Await node for the `await` keyword.""" - - _astroid_fields = ('value', ) - value = None - - def postinit(self, value=None): - self.value = value - - -class ImportFrom(mixins.ImportFromMixin, Statement): - """class representing a ImportFrom node""" - _other_fields = ('modname', 'names', 'level') - - def __init__(self, fromname, names, level=0, lineno=None, - col_offset=None, parent=None): - self.modname = fromname - self.names = names - self.level = level - super(ImportFrom, self).__init__(lineno, col_offset, parent) - - -class Attribute(NodeNG): - """class representing a Attribute node""" - _astroid_fields = ('expr',) - _other_fields = ('attrname',) - expr = None - - def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None): - self.attrname = attrname - super(Attribute, self).__init__(lineno, col_offset, parent) - - def postinit(self, expr=None): - self.expr = expr - - -class Global(Statement): - """class representing a Global node""" - _other_fields = ('names',) - - def __init__(self, names, lineno=None, col_offset=None, parent=None): - self.names = names - super(Global, self).__init__(lineno, col_offset, parent) - - def _infer_name(self, frame, name): - return name - - -class If(mixins.BlockRangeMixIn, Statement): - """class representing an If node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - def postinit(self, test=None, body=None, orelse=None): - self.test = test - self.body = body - self.orelse = orelse - - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for if statements""" - if lineno == self.body[0].fromlineno: - return lineno, lineno - if lineno <= self.body[-1].tolineno: - return lineno, self.body[-1].tolineno - return self._elsed_block_range(lineno, self.orelse, - self.body[0].fromlineno - 1) - - -class IfExp(NodeNG): - """class representing an IfExp node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - def postinit(self, test=None, body=None, orelse=None): - self.test = test - self.body = body - self.orelse = orelse - - -class Import(mixins.ImportFromMixin, Statement): - """class representing an Import node""" - _other_fields = ('names',) - - def __init__(self, names=None, lineno=None, col_offset=None, parent=None): - self.names = names - super(Import, self).__init__(lineno, col_offset, parent) - - -class Index(NodeNG): - """class representing an Index node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class Keyword(NodeNG): - """class representing a Keyword node""" - _astroid_fields = ('value',) - _other_fields = ('arg',) - value = None - - def __init__(self, arg=None, lineno=None, col_offset=None, parent=None): - self.arg = arg - super(Keyword, self).__init__(lineno, col_offset, parent) - - def postinit(self, value=None): - self.value = value - - -class List(_BaseContainer): - """class representing a List node""" - _other_fields = ('ctx',) - - def __init__(self, ctx=None, lineno=None, - col_offset=None, parent=None): - self.ctx = ctx - super(List, self).__init__(lineno, col_offset, parent) - - def pytype(self): - return '%s.list' % BUILTINS - - def getitem(self, index, context=None): - return _container_getitem(self, self.elts, index, context=context) - - -class Nonlocal(Statement): - """class representing a Nonlocal node""" - _other_fields = ('names',) - - def __init__(self, names, lineno=None, col_offset=None, parent=None): - self.names = names - super(Nonlocal, self).__init__(lineno, col_offset, parent) - - def _infer_name(self, frame, name): - return name - - -class Pass(Statement): - """class representing a Pass node""" - - -class Print(Statement): - """class representing a Print node""" - _astroid_fields = ('dest', 'values',) - dest = None - values = None - - def __init__(self, nl=None, lineno=None, col_offset=None, parent=None): - self.nl = nl - super(Print, self).__init__(lineno, col_offset, parent) - - def postinit(self, dest=None, values=None): - self.dest = dest - self.values = values - - -class Raise(Statement): - """class representing a Raise node""" - exc = None - if six.PY2: - _astroid_fields = ('exc', 'inst', 'tback') - inst = None - tback = None - - def postinit(self, exc=None, inst=None, tback=None): - self.exc = exc - self.inst = inst - self.tback = tback - else: - _astroid_fields = ('exc', 'cause') - exc = None - cause = None - - def postinit(self, exc=None, cause=None): - self.exc = exc - self.cause = cause - - def raises_not_implemented(self): - if not self.exc: - return - for name in self.exc.nodes_of_class(Name): - if name.name == 'NotImplementedError': - return True - - -class Return(Statement): - """class representing a Return node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class Set(_BaseContainer): - """class representing a Set node""" - - def pytype(self): - return '%s.set' % BUILTINS - - -class Slice(NodeNG): - """class representing a Slice node""" - _astroid_fields = ('lower', 'upper', 'step') - lower = None - upper = None - step = None - - def postinit(self, lower=None, upper=None, step=None): - self.lower = lower - self.upper = upper - self.step = step - - def _wrap_attribute(self, attr): - """Wrap the empty attributes of the Slice in a Const node.""" - if not attr: - const = const_factory(attr) - const.parent = self - return const - return attr - - @decorators.cachedproperty - def _proxied(self): - builtins = MANAGER.astroid_cache[BUILTINS] - return builtins.getattr('slice')[0] - - def pytype(self): - return '%s.slice' % BUILTINS - - def igetattr(self, attrname, context=None): - if attrname == 'start': - yield self._wrap_attribute(self.lower) - elif attrname == 'stop': - yield self._wrap_attribute(self.upper) - elif attrname == 'step': - yield self._wrap_attribute(self.step) - else: - for value in self.getattr(attrname, context=context): - yield value - - def getattr(self, attrname, context=None): - return self._proxied.getattr(attrname, context) - - -class Starred(mixins.ParentAssignTypeMixin, NodeNG): - """class representing a Starred node""" - _astroid_fields = ('value',) - _other_fields = ('ctx', ) - value = None - - def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): - self.ctx = ctx - super(Starred, self).__init__(lineno=lineno, - col_offset=col_offset, parent=parent) - - def postinit(self, value=None): - self.value = value - - -class Subscript(NodeNG): - """class representing a Subscript node""" - _astroid_fields = ('value', 'slice') - _other_fields = ('ctx', ) - value = None - slice = None - - def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None): - self.ctx = ctx - super(Subscript, self).__init__(lineno=lineno, - col_offset=col_offset, parent=parent) - - # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. - def postinit(self, value=None, slice=None): - self.value = value - self.slice = slice - - -class TryExcept(mixins.BlockRangeMixIn, Statement): - """class representing a TryExcept node""" - _astroid_fields = ('body', 'handlers', 'orelse',) - body = None - handlers = None - orelse = None - - def postinit(self, body=None, handlers=None, orelse=None): - self.body = body - self.handlers = handlers - self.orelse = orelse - - def _infer_name(self, frame, name): - return name - - def block_range(self, lineno): - """handle block line numbers range for try/except statements""" - last = None - for exhandler in self.handlers: - if exhandler.type and lineno == exhandler.type.fromlineno: - return lineno, lineno - if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: - return lineno, exhandler.body[-1].tolineno - if last is None: - last = exhandler.body[0].fromlineno - 1 - return self._elsed_block_range(lineno, self.orelse, last) - - -class TryFinally(mixins.BlockRangeMixIn, Statement): - """class representing a TryFinally node""" - _astroid_fields = ('body', 'finalbody',) - body = None - finalbody = None - - def postinit(self, body=None, finalbody=None): - self.body = body - self.finalbody = finalbody - - def block_range(self, lineno): - """handle block line numbers range for try/finally statements""" - child = self.body[0] - # py2.5 try: except: finally: - if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno - and lineno > self.fromlineno and lineno <= child.tolineno): - return child.block_range(lineno) - return self._elsed_block_range(lineno, self.finalbody) - - -class Tuple(_BaseContainer): - """class representing a Tuple node""" - - _other_fields = ('ctx',) - - def __init__(self, ctx=None, lineno=None, - col_offset=None, parent=None): - self.ctx = ctx - super(Tuple, self).__init__(lineno, col_offset, parent) - - def pytype(self): - return '%s.tuple' % BUILTINS - - def getitem(self, index, context=None): - return _container_getitem(self, self.elts, index, context=context) - - -class UnaryOp(NodeNG): - """class representing an UnaryOp node""" - _astroid_fields = ('operand',) - _other_fields = ('op',) - operand = None - - def __init__(self, op=None, lineno=None, col_offset=None, parent=None): - self.op = op - super(UnaryOp, self).__init__(lineno, col_offset, parent) - - def postinit(self, operand=None): - self.operand = operand - - # This is set by inference.py - def _infer_unaryop(self, context=None): - raise NotImplementedError - - def type_errors(self, context=None): - """Return a list of TypeErrors which can occur during inference. - - Each TypeError is represented by a :class:`BadUnaryOperationMessage`, - which holds the original exception. - """ - try: - results = self._infer_unaryop(context=context) - return [result for result in results - if isinstance(result, util.BadUnaryOperationMessage)] - except exceptions.InferenceError: - return [] - - -class While(mixins.BlockRangeMixIn, Statement): - """class representing a While node""" - _astroid_fields = ('test', 'body', 'orelse',) - test = None - body = None - orelse = None - - def postinit(self, test=None, body=None, orelse=None): - self.test = test - self.body = body - self.orelse = orelse - - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for and while statements""" - return self. _elsed_block_range(lineno, self.orelse) - - -class With(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, Statement): - """class representing a With node""" - _astroid_fields = ('items', 'body') - items = None - body = None - - def postinit(self, items=None, body=None): - self.items = items - self.body = body - - @decorators.cachedproperty - def blockstart_tolineno(self): - return self.items[-1][0].tolineno - - def get_children(self): - for expr, var in self.items: - yield expr - if var: - yield var - for elt in self.body: - yield elt - - -class AsyncWith(With): - """Asynchronous `with` built with the `async` keyword.""" - - -class Yield(NodeNG): - """class representing a Yield node""" - _astroid_fields = ('value',) - value = None - - def postinit(self, value=None): - self.value = value - - -class YieldFrom(Yield): - """ Class representing a YieldFrom node. """ - - -class DictUnpack(NodeNG): - """Represents the unpacking of dicts into dicts using PEP 448.""" - - -class FormattedValue(NodeNG): - """Represents a PEP 498 format string.""" - _astroid_fields = ('value', 'format_spec') - value = None - conversion = None - format_spec = None - - def postinit(self, value, conversion=None, format_spec=None): - self.value = value - self.conversion = conversion - self.format_spec = format_spec - - -class JoinedStr(NodeNG): - """Represents a list of string expressions to be joined.""" - _astroid_fields = ('values',) - values = None - - def postinit(self, values=None): - self.values = values - - -class Unknown(NodeNG): - '''This node represents a node in a constructed AST where - introspection is not possible. At the moment, it's only used in - the args attribute of FunctionDef nodes where function signature - introspection failed. - ''' - def infer(self, context=None, **kwargs): - '''Inference on an Unknown node immediately terminates.''' - yield util.Uninferable - - -# constants ############################################################## - -CONST_CLS = { - list: List, - tuple: Tuple, - dict: Dict, - set: Set, - type(None): Const, - type(NotImplemented): Const, - } - -def _update_const_classes(): - """update constant classes, so the keys of CONST_CLS can be reused""" - klasses = (bool, int, float, complex, str) - if six.PY2: - # pylint: disable=undefined-variable - klasses += (unicode, long) - klasses += (bytes,) - for kls in klasses: - CONST_CLS[kls] = Const -_update_const_classes() - - -def _two_step_initialization(cls, value): - instance = cls() - instance.postinit(value) - return instance - - -def _dict_initialization(cls, value): - if isinstance(value, dict): - value = tuple(value.items()) - return _two_step_initialization(cls, value) - - -_CONST_CLS_CONSTRUCTORS = { - List: _two_step_initialization, - Tuple: _two_step_initialization, - Dict: _dict_initialization, - Set: _two_step_initialization, - Const: lambda cls, value: cls(value) -} - - -def const_factory(value): - """return an astroid node for a python value""" - # XXX we should probably be stricter here and only consider stuff in - # CONST_CLS or do better treatment: in case where value is not in CONST_CLS, - # we should rather recall the builder on this value than returning an empty - # node (another option being that const_factory shouldn't be called with something - # not in CONST_CLS) - assert not isinstance(value, NodeNG) - - # Hack for ignoring elements of a sequence - # or a mapping, in order to avoid transforming - # each element to an AST. This is fixed in 2.0 - # and this approach is a temporary hack. - if isinstance(value, (list, set, tuple, dict)): - elts = [] - else: - elts = value - - try: - initializer_cls = CONST_CLS[value.__class__] - initializer = _CONST_CLS_CONSTRUCTORS[initializer_cls] - return initializer(initializer_cls, elts) - except (KeyError, AttributeError): - node = EmptyNode() - node.object = value - return node - - -# Backward-compatibility aliases - -Backquote = util.proxy_alias('Backquote', Repr) -Discard = util.proxy_alias('Discard', Expr) -AssName = util.proxy_alias('AssName', AssignName) -AssAttr = util.proxy_alias('AssAttr', AssignAttr) -Getattr = util.proxy_alias('Getattr', Attribute) -CallFunc = util.proxy_alias('CallFunc', Call) -From = util.proxy_alias('From', ImportFrom) diff --git a/pymode/libs/astroid/nodes.py b/pymode/libs/astroid/nodes.py deleted file mode 100644 index 2b207d02..00000000 --- a/pymode/libs/astroid/nodes.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2006-2011, 2013 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -""" -on all nodes : - .is_statement, returning true if the node should be considered as a - statement node - .root(), returning the root node of the tree (i.e. a Module) - .previous_sibling(), returning previous sibling statement node - .next_sibling(), returning next sibling statement node - .statement(), returning the first parent node marked as statement node - .frame(), returning the first node defining a new local scope (i.e. - Module, FunctionDef or ClassDef) - .set_local(name, node), define an identifier on the first parent frame, - with the node defining it. This is used by the astroid builder and should not - be used from out there. - -on ImportFrom and Import : - .real_name(name), - - -""" -# pylint: disable=unused-import,redefined-builtin - -from astroid.node_classes import ( - Arguments, AssignAttr, Assert, Assign, AnnAssign, - AssignName, AugAssign, Repr, BinOp, BoolOp, Break, Call, Compare, - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, - Dict, Expr, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, - ImportFrom, Attribute, Global, If, IfExp, Import, Index, Keyword, - List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, - const_factory, - AsyncFor, Await, AsyncWith, - FormattedValue, JoinedStr, - # Backwards-compatibility aliases - Backquote, Discard, AssName, AssAttr, Getattr, CallFunc, From, - # Node not present in the builtin ast module. - DictUnpack, - Unknown, -) -from astroid.scoped_nodes import ( - Module, GeneratorExp, Lambda, DictComp, - ListComp, SetComp, FunctionDef, ClassDef, - AsyncFunctionDef, - # Backwards-compatibility aliases - Class, Function, GenExpr, -) - - - -ALL_NODE_CLASSES = ( - AsyncFunctionDef, AsyncFor, AsyncWith, Await, - - Arguments, AssignAttr, Assert, Assign, AnnAssign, AssignName, AugAssign, - Repr, BinOp, BoolOp, Break, - Call, ClassDef, Compare, Comprehension, Const, Continue, - Decorators, DelAttr, DelName, Delete, - Dict, DictComp, DictUnpack, Expr, - Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, - For, ImportFrom, FunctionDef, - Attribute, GeneratorExp, Global, - If, IfExp, Import, Index, - Keyword, - Lambda, List, ListComp, - Name, Nonlocal, - Module, - Pass, Print, - Raise, Return, - Set, SetComp, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, - UnaryOp, - While, With, - Yield, YieldFrom, - FormattedValue, JoinedStr, - ) diff --git a/pymode/libs/astroid/objects.py b/pymode/libs/astroid/objects.py deleted file mode 100644 index 7a873449..00000000 --- a/pymode/libs/astroid/objects.py +++ /dev/null @@ -1,220 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -""" -Inference objects are a way to represent composite AST nodes, -which are used only as inference results, so they can't be found in the -original AST tree. For instance, inferring the following frozenset use, -leads to an inferred FrozenSet: - - Call(func=Name('frozenset'), args=Tuple(...)) -""" - -import six - -from astroid import bases -from astroid import decorators -from astroid import exceptions -from astroid import MANAGER -from astroid import node_classes -from astroid import scoped_nodes -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ -objectmodel = util.lazy_import('interpreter.objectmodel') - - -class FrozenSet(node_classes._BaseContainer): - """class representing a FrozenSet composite node""" - - def pytype(self): - return '%s.frozenset' % BUILTINS - - def _infer(self, context=None): - yield self - - @decorators.cachedproperty - def _proxied(self): # pylint: disable=method-hidden - builtins = MANAGER.astroid_cache[BUILTINS] - return builtins.getattr('frozenset')[0] - - -class Super(node_classes.NodeNG): - """Proxy class over a super call. - - This class offers almost the same behaviour as Python's super, - which is MRO lookups for retrieving attributes from the parents. - - The *mro_pointer* is the place in the MRO from where we should - start looking, not counting it. *mro_type* is the object which - provides the MRO, it can be both a type or an instance. - *self_class* is the class where the super call is, while - *scope* is the function where the super call is. - """ - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.SuperModel()) - - # pylint: disable=super-init-not-called - def __init__(self, mro_pointer, mro_type, self_class, scope): - self.type = mro_type - self.mro_pointer = mro_pointer - self._class_based = False - self._self_class = self_class - self._scope = scope - - def _infer(self, context=None): - yield self - - def super_mro(self): - """Get the MRO which will be used to lookup attributes in this super.""" - if not isinstance(self.mro_pointer, scoped_nodes.ClassDef): - raise exceptions.SuperError( - "The first argument to super must be a subtype of " - "type, not {mro_pointer}.", super_=self) - - if isinstance(self.type, scoped_nodes.ClassDef): - # `super(type, type)`, most likely in a class method. - self._class_based = True - mro_type = self.type - else: - mro_type = getattr(self.type, '_proxied', None) - if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)): - raise exceptions.SuperError( - "The second argument to super must be an " - "instance or subtype of type, not {type}.", - super_=self) - - if not mro_type.newstyle: - raise exceptions.SuperError("Unable to call super on old-style classes.", super_=self) - - mro = mro_type.mro() - if self.mro_pointer not in mro: - raise exceptions.SuperError( - "The second argument to super must be an " - "instance or subtype of type, not {type}.", - super_=self) - - index = mro.index(self.mro_pointer) - return mro[index + 1:] - - @decorators.cachedproperty - def _proxied(self): - builtins = MANAGER.astroid_cache[BUILTINS] - return builtins.getattr('super')[0] - - def pytype(self): - return '%s.super' % BUILTINS - - def display_type(self): - return 'Super of' - - @property - def name(self): - """Get the name of the MRO pointer.""" - return self.mro_pointer.name - - def igetattr(self, name, context=None): - """Retrieve the inferred values of the given attribute name.""" - - if name in self.special_attributes: - yield self.special_attributes.lookup(name) - return - - try: - mro = self.super_mro() - # Don't let invalid MROs or invalid super calls - # leak out as is from this function. - except exceptions.SuperError as exc: - util.reraise(exceptions.AttributeInferenceError( - ('Lookup for {name} on {target!r} because super call {super!r} ' - 'is invalid.'), - target=self, attribute=name, context=context, super_=exc.super_)) - except exceptions.MroError as exc: - util.reraise(exceptions.AttributeInferenceError( - ('Lookup for {name} on {target!r} failed because {cls!r} has an ' - 'invalid MRO.'), - target=self, attribute=name, context=context, mros=exc.mros, - cls=exc.cls)) - found = False - for cls in mro: - if name not in cls.locals: - continue - - found = True - for inferred in bases._infer_stmts([cls[name]], context, frame=self): - if not isinstance(inferred, scoped_nodes.FunctionDef): - yield inferred - continue - - # We can obtain different descriptors from a super depending - # on what we are accessing and where the super call is. - if inferred.type == 'classmethod': - yield bases.BoundMethod(inferred, cls) - elif self._scope.type == 'classmethod' and inferred.type == 'method': - yield inferred - elif self._class_based or inferred.type == 'staticmethod': - yield inferred - elif bases._is_property(inferred): - # TODO: support other descriptors as well. - for value in inferred.infer_call_result(self, context): - yield value - else: - yield bases.BoundMethod(inferred, cls) - - if not found: - raise exceptions.AttributeInferenceError(target=self, - attribute=name, - context=context) - - def getattr(self, name, context=None): - return list(self.igetattr(name, context=context)) - - -class ExceptionInstance(bases.Instance): - """Class for instances of exceptions - - It has special treatment for some of the exceptions's attributes, - which are transformed at runtime into certain concrete objects, such as - the case of .args. - """ - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.ExceptionInstanceModel()) - - -class DictInstance(bases.Instance): - """Special kind of instances for dictionaries - - This instance knows the underlying object model of the dictionaries, which means - that methods such as .values or .items can be properly inferred. - """ - - # pylint: disable=unnecessary-lambda - special_attributes = util.lazy_descriptor(lambda: objectmodel.DictModel()) - - -# Custom objects tailored for dictionaries, which are used to -# disambiguate between the types of Python 2 dict's method returns -# and Python 3 (where they return set like objects). -class DictItems(bases.Proxy): - __str__ = node_classes.NodeNG.__str__ - __repr__ = node_classes.NodeNG.__repr__ - - -class DictKeys(bases.Proxy): - __str__ = node_classes.NodeNG.__str__ - __repr__ = node_classes.NodeNG.__repr__ - - -class DictValues(bases.Proxy): - __str__ = node_classes.NodeNG.__str__ - __repr__ = node_classes.NodeNG.__repr__ - -# TODO: Hack to solve the circular import problem between node_classes and objects -# This is not needed in 2.0, which has a cleaner design overall -node_classes.Dict.__bases__ = (node_classes.NodeNG, DictInstance) diff --git a/pymode/libs/astroid/protocols.py b/pymode/libs/astroid/protocols.py deleted file mode 100644 index f19a9f41..00000000 --- a/pymode/libs/astroid/protocols.py +++ /dev/null @@ -1,600 +0,0 @@ -# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""this module contains a set of functions to handle python protocols for nodes -where it makes sense. -""" - -import collections -import operator as operator_mod -import sys - -import six - -from astroid import arguments -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import decorators -from astroid import node_classes -from astroid import helpers -from astroid import nodes -from astroid import util - -raw_building = util.lazy_import('raw_building') -objects = util.lazy_import('objects') - -def _reflected_name(name): - return "__r" + name[2:] - -def _augmented_name(name): - return "__i" + name[2:] - - -_CONTEXTLIB_MGR = 'contextlib.contextmanager' -BIN_OP_METHOD = {'+': '__add__', - '-': '__sub__', - '/': '__div__' if six.PY2 else '__truediv__', - '//': '__floordiv__', - '*': '__mul__', - '**': '__pow__', - '%': '__mod__', - '&': '__and__', - '|': '__or__', - '^': '__xor__', - '<<': '__lshift__', - '>>': '__rshift__', - '@': '__matmul__' - } - -REFLECTED_BIN_OP_METHOD = { - key: _reflected_name(value) - for (key, value) in BIN_OP_METHOD.items() -} -AUGMENTED_OP_METHOD = { - key + "=": _augmented_name(value) - for (key, value) in BIN_OP_METHOD.items() -} - -UNARY_OP_METHOD = {'+': '__pos__', - '-': '__neg__', - '~': '__invert__', - 'not': None, # XXX not '__nonzero__' - } -_UNARY_OPERATORS = { - '+': operator_mod.pos, - '-': operator_mod.neg, - '~': operator_mod.invert, - 'not': operator_mod.not_, -} - - -def _infer_unary_op(obj, op): - func = _UNARY_OPERATORS[op] - value = func(obj) - return nodes.const_factory(value) - -nodes.Tuple.infer_unary_op = lambda self, op: _infer_unary_op(tuple(self.elts), op) -nodes.List.infer_unary_op = lambda self, op: _infer_unary_op(self.elts, op) -nodes.Set.infer_unary_op = lambda self, op: _infer_unary_op(set(self.elts), op) -nodes.Const.infer_unary_op = lambda self, op: _infer_unary_op(self.value, op) -nodes.Dict.infer_unary_op = lambda self, op: _infer_unary_op(dict(self.items), op) - -# Binary operations - -BIN_OP_IMPL = {'+': lambda a, b: a + b, - '-': lambda a, b: a - b, - '/': lambda a, b: a / b, - '//': lambda a, b: a // b, - '*': lambda a, b: a * b, - '**': lambda a, b: a ** b, - '%': lambda a, b: a % b, - '&': lambda a, b: a & b, - '|': lambda a, b: a | b, - '^': lambda a, b: a ^ b, - '<<': lambda a, b: a << b, - '>>': lambda a, b: a >> b, - } -if sys.version_info >= (3, 5): - # MatMult is available since Python 3.5+. - BIN_OP_IMPL['@'] = operator_mod.matmul - -for _KEY, _IMPL in list(BIN_OP_IMPL.items()): - BIN_OP_IMPL[_KEY + '='] = _IMPL - - -@decorators.yes_if_nothing_inferred -def const_infer_binary_op(self, opnode, operator, other, context, _): - not_implemented = nodes.Const(NotImplemented) - if isinstance(other, nodes.Const): - try: - impl = BIN_OP_IMPL[operator] - try: - yield nodes.const_factory(impl(self.value, other.value)) - except TypeError: - # ArithmeticError is not enough: float >> float is a TypeError - yield not_implemented - except Exception: # pylint: disable=broad-except - yield util.Uninferable - except TypeError: - yield not_implemented - elif isinstance(self.value, six.string_types) and operator == '%': - # TODO(cpopa): implement string interpolation later on. - yield util.Uninferable - else: - yield not_implemented - -nodes.Const.infer_binary_op = const_infer_binary_op - - -def _multiply_seq_by_int(self, opnode, other, context): - node = self.__class__(parent=opnode) - elts = [] - for elt in self.elts: - infered = helpers.safe_infer(elt, context) - if infered is None: - infered = util.Uninferable - elts.append(infered) - node.elts = elts * other.value - return node - - -def _filter_uninferable_nodes(elts, context): - for elt in elts: - if elt is util.Uninferable: - yield nodes.Unknown() - else: - for inferred in elt.infer(context): - if inferred is not util.Uninferable: - yield inferred - else: - yield nodes.Unknown() - - -@decorators.yes_if_nothing_inferred -def tl_infer_binary_op(self, opnode, operator, other, context, method): - not_implemented = nodes.Const(NotImplemented) - if isinstance(other, self.__class__) and operator == '+': - node = self.__class__(parent=opnode) - elts = list(_filter_uninferable_nodes(self.elts, context)) - elts += list(_filter_uninferable_nodes(other.elts, context)) - node.elts = elts - yield node - elif isinstance(other, nodes.Const) and operator == '*': - if not isinstance(other.value, int): - yield not_implemented - return - yield _multiply_seq_by_int(self, opnode, other, context) - elif isinstance(other, bases.Instance) and operator == '*': - # Verify if the instance supports __index__. - as_index = helpers.class_instance_as_index(other) - if not as_index: - yield util.Uninferable - else: - yield _multiply_seq_by_int(self, opnode, as_index, context) - else: - yield not_implemented - -nodes.Tuple.infer_binary_op = tl_infer_binary_op -nodes.List.infer_binary_op = tl_infer_binary_op - - -@decorators.yes_if_nothing_inferred -def instance_class_infer_binary_op(self, opnode, operator, other, context, method): - return method.infer_call_result(self, context) - -bases.Instance.infer_binary_op = instance_class_infer_binary_op -nodes.ClassDef.infer_binary_op = instance_class_infer_binary_op - - -# assignment ################################################################## - -"""the assigned_stmts method is responsible to return the assigned statement -(e.g. not inferred) according to the assignment type. - -The `asspath` argument is used to record the lhs path of the original node. -For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath -will be [1, 1] once arrived to the Assign node. - -The `context` argument is the current inference context which should be given -to any intermediary inference necessary. -""" - -def _resolve_looppart(parts, asspath, context): - """recursive function to resolve multiple assignments on loops""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if part is util.Uninferable: - continue - # XXX handle __iter__ and log potentially detected errors - if not hasattr(part, 'itered'): - continue - try: - itered = part.itered() - except TypeError: - continue # XXX log error - for stmt in itered: - index_node = nodes.Const(index) - try: - assigned = stmt.getitem(index_node, context) - except (AttributeError, - exceptions.AstroidTypeError, - exceptions.AstroidIndexError): - continue - if not asspath: - # we achieved to resolved the assignment path, - # don't infer the last part - yield assigned - elif assigned is util.Uninferable: - break - else: - # we are not yet on the last part of the path - # search on each possibly inferred value - try: - for inferred in _resolve_looppart(assigned.infer(context), - asspath, context): - yield inferred - except exceptions.InferenceError: - break - -@decorators.raise_if_nothing_inferred -def for_assigned_stmts(self, node=None, context=None, asspath=None): - if isinstance(self, nodes.AsyncFor) or getattr(self, 'is_async', False): - # Skip inferring of async code for now - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - if asspath is None: - for lst in self.iter.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.elts: - yield item - else: - for inferred in _resolve_looppart(self.iter.infer(context), - asspath, context): - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - -nodes.For.assigned_stmts = for_assigned_stmts -nodes.Comprehension.assigned_stmts = for_assigned_stmts - - -def sequence_assigned_stmts(self, node=None, context=None, asspath=None): - if asspath is None: - asspath = [] - try: - index = self.elts.index(node) - except ValueError: - util.reraise(exceptions.InferenceError( - 'Tried to retrieve a node {node!r} which does not exist', - node=self, assign_path=asspath, context=context)) - - asspath.insert(0, index) - return self.parent.assigned_stmts(node=self, context=context, asspath=asspath) - -nodes.Tuple.assigned_stmts = sequence_assigned_stmts -nodes.List.assigned_stmts = sequence_assigned_stmts - - -def assend_assigned_stmts(self, node=None, context=None, asspath=None): - return self.parent.assigned_stmts(node=self, context=context) -nodes.AssignName.assigned_stmts = assend_assigned_stmts -nodes.AssignAttr.assigned_stmts = assend_assigned_stmts - - -def _arguments_infer_argname(self, name, context): - # arguments information may be missing, in which case we can't do anything - # more - if not (self.args or self.vararg or self.kwarg): - yield util.Uninferable - return - # first argument of instance/class method - if self.args and getattr(self.args[0], 'name', None) == name: - functype = self.parent.type - cls = self.parent.parent.scope() - is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == 'metaclass' - # If this is a metaclass, then the first argument will always - # be the class, not an instance. - if is_metaclass or functype == 'classmethod': - yield cls - return - if functype == 'method': - yield bases.Instance(self.parent.parent.frame()) - return - - if context and context.callcontext: - call_site = arguments.CallSite(context.callcontext) - for value in call_site.infer_argument(self.parent, name, context): - yield value - return - - # TODO: just provide the type here, no need to have an empty Dict. - if name == self.vararg: - vararg = nodes.const_factory(()) - vararg.parent = self - yield vararg - return - if name == self.kwarg: - kwarg = nodes.const_factory({}) - kwarg.parent = self - yield kwarg - return - # if there is a default value, yield it. And then yield Uninferable to reflect - # we can't guess given argument value - try: - context = contextmod.copy_context(context) - for inferred in self.default_value(name).infer(context): - yield inferred - yield util.Uninferable - except exceptions.NoDefault: - yield util.Uninferable - - -def arguments_assigned_stmts(self, node=None, context=None, asspath=None): - if context.callcontext: - # reset call context/name - callcontext = context.callcontext - context = contextmod.copy_context(context) - context.callcontext = None - args = arguments.CallSite(callcontext) - return args.infer_argument(self.parent, node.name, context) - return _arguments_infer_argname(self, node.name, context) - -nodes.Arguments.assigned_stmts = arguments_assigned_stmts - - -@decorators.raise_if_nothing_inferred -def assign_assigned_stmts(self, node=None, context=None, asspath=None): - if not asspath: - yield self.value - return - for inferred in _resolve_asspart(self.value.infer(context), asspath, context): - yield inferred - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - - -def assign_annassigned_stmts(self, node=None, context=None, asspath=None): - for inferred in assign_assigned_stmts(self, node, context, asspath): - if inferred is None: - yield util.Uninferable - else: - yield inferred - -nodes.Assign.assigned_stmts = assign_assigned_stmts -nodes.AnnAssign.assigned_stmts = assign_annassigned_stmts -nodes.AugAssign.assigned_stmts = assign_assigned_stmts - - -def _resolve_asspart(parts, asspath, context): - """recursive function to resolve multiple assignments""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if hasattr(part, 'getitem'): - index_node = nodes.Const(index) - try: - assigned = part.getitem(index_node, context) - # XXX raise a specific exception to avoid potential hiding of - # unexpected exception ? - except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): - return - if not asspath: - # we achieved to resolved the assignment path, don't infer the - # last part - yield assigned - elif assigned is util.Uninferable: - return - else: - # we are not yet on the last part of the path search on each - # possibly inferred value - try: - for inferred in _resolve_asspart(assigned.infer(context), - asspath, context): - yield inferred - except exceptions.InferenceError: - return - - -@decorators.raise_if_nothing_inferred -def excepthandler_assigned_stmts(self, node=None, context=None, asspath=None): - for assigned in node_classes.unpack_infer(self.type): - if isinstance(assigned, nodes.ClassDef): - assigned = objects.ExceptionInstance(assigned) - - yield assigned - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - - -nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts - - -def _infer_context_manager(self, mgr, context): - try: - inferred = next(mgr.infer(context=context)) - except exceptions.InferenceError: - return - if isinstance(inferred, bases.Generator): - # Check if it is decorated with contextlib.contextmanager. - func = inferred.parent - if not func.decorators: - return - for decorator_node in func.decorators.nodes: - decorator = next(decorator_node.infer(context)) - if isinstance(decorator, nodes.FunctionDef): - if decorator.qname() == _CONTEXTLIB_MGR: - break - else: - # It doesn't interest us. - return - - # Get the first yield point. If it has multiple yields, - # then a RuntimeError will be raised. - # TODO(cpopa): Handle flows. - yield_point = next(func.nodes_of_class(nodes.Yield), None) - if yield_point: - if not yield_point.value: - # TODO(cpopa): an empty yield. Should be wrapped to Const. - const = nodes.Const(None) - const.parent = yield_point - const.lineno = yield_point.lineno - yield const - else: - for inferred in yield_point.value.infer(context=context): - yield inferred - elif isinstance(inferred, bases.Instance): - try: - enter = next(inferred.igetattr('__enter__', context=context)) - except (exceptions.InferenceError, exceptions.AttributeInferenceError): - return - if not isinstance(enter, bases.BoundMethod): - return - if not context.callcontext: - context.callcontext = contextmod.CallContext(args=[inferred]) - for result in enter.infer_call_result(self, context): - yield result - - -@decorators.raise_if_nothing_inferred -def with_assigned_stmts(self, node=None, context=None, asspath=None): - """Infer names and other nodes from a *with* statement. - - This enables only inference for name binding in a *with* statement. - For instance, in the following code, inferring `func` will return - the `ContextManager` class, not whatever ``__enter__`` returns. - We are doing this intentionally, because we consider that the context - manager result is whatever __enter__ returns and what it is binded - using the ``as`` keyword. - - class ContextManager(object): - def __enter__(self): - return 42 - with ContextManager() as f: - pass - # ContextManager().infer() will return ContextManager - # f.infer() will return 42. - - Arguments: - self: nodes.With - node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`. - context: TODO - asspath: TODO - """ - mgr = next(mgr for (mgr, vars) in self.items if vars == node) - if asspath is None: - for result in _infer_context_manager(self, mgr, context): - yield result - else: - for result in _infer_context_manager(self, mgr, context): - # Walk the asspath and get the item at the final index. - obj = result - for index in asspath: - if not hasattr(obj, 'elts'): - raise exceptions.InferenceError( - 'Wrong type ({targets!r}) for {node!r} assignment', - node=self, targets=node, assign_path=asspath, - context=context) - try: - obj = obj.elts[index] - except IndexError: - util.reraise(exceptions.InferenceError( - 'Tried to infer a nonexistent target with index {index} ' - 'in {node!r}.', node=self, targets=node, - assign_path=asspath, context=context)) - except TypeError: - util.reraise(exceptions.InferenceError( - 'Tried to unpack an non-iterable value ' - 'in {node!r}.', node=self, targets=node, - assign_path=asspath, context=context)) - yield obj - # Explicit StopIteration to return error information, see comment - # in raise_if_nothing_inferred. - raise StopIteration(dict(node=self, unknown=node, - assign_path=asspath, context=context)) - -nodes.With.assigned_stmts = with_assigned_stmts - - -@decorators.yes_if_nothing_inferred -def starred_assigned_stmts(self, node=None, context=None, asspath=None): - """ - Arguments: - self: nodes.Starred - node: TODO - context: TODO - asspath: TODO - """ - stmt = self.statement() - if not isinstance(stmt, (nodes.Assign, nodes.For)): - raise exceptions.InferenceError('Statement {stmt!r} enclosing {node!r} ' - 'must be an Assign or For node.', - node=self, stmt=stmt, unknown=node, - context=context) - - if isinstance(stmt, nodes.Assign): - value = stmt.value - lhs = stmt.targets[0] - - if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1: - raise exceptions.InferenceError('Too many starred arguments in the ' - ' assignment targets {lhs!r}.', - node=self, targets=lhs, - unknown=node, context=context) - - if context is None: - context = contextmod.InferenceContext() - try: - rhs = next(value.infer(context)) - except exceptions.InferenceError: - yield util.Uninferable - return - if rhs is util.Uninferable or not hasattr(rhs, 'elts'): - # Not interested in inferred values without elts. - yield util.Uninferable - return - - elts = collections.deque(rhs.elts[:]) - if len(lhs.elts) > len(rhs.elts): - raise exceptions.InferenceError('More targets, {targets!r}, than ' - 'values to unpack, {values!r}.', - node=self, targets=lhs, - values=rhs, unknown=node, - context=context) - - # Unpack iteratively the values from the rhs of the assignment, - # until the find the starred node. What will remain will - # be the list of values which the Starred node will represent - # This is done in two steps, from left to right to remove - # anything before the starred node and from right to left - # to remove anything after the starred node. - - for index, left_node in enumerate(lhs.elts): - if not isinstance(left_node, nodes.Starred): - elts.popleft() - continue - lhs_elts = collections.deque(reversed(lhs.elts[index:])) - for right_node in lhs_elts: - if not isinstance(right_node, nodes.Starred): - elts.pop() - continue - # We're done - packed = nodes.List() - packed.elts = elts - packed.parent = self - yield packed - break - -nodes.Starred.assigned_stmts = starred_assigned_stmts diff --git a/pymode/libs/astroid/raw_building.py b/pymode/libs/astroid/raw_building.py deleted file mode 100644 index 21e60d11..00000000 --- a/pymode/libs/astroid/raw_building.py +++ /dev/null @@ -1,416 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2014-2016 Claudiu Popa -# Copyright (c) 2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""this module contains a set of functions to create astroid trees from scratch -(build_* functions) or from living object (object_build_* functions) -""" - -import inspect -import logging -import os -import sys -import types - -import six - -from astroid import bases -from astroid import manager -from astroid import node_classes -from astroid import nodes - - -MANAGER = manager.AstroidManager() -# the keys of CONST_CLS eg python builtin types -_CONSTANTS = tuple(node_classes.CONST_CLS) -_JYTHON = os.name == 'java' -_BUILTINS = vars(six.moves.builtins) -_LOG = logging.getLogger(__name__) - - -def _io_discrepancy(member): - # _io module names itself `io`: http://bugs.python.org/issue18602 - member_self = getattr(member, '__self__', None) - return (member_self and - inspect.ismodule(member_self) and - member_self.__name__ == '_io' and - member.__module__ == 'io') - -def _attach_local_node(parent, node, name): - node.name = name # needed by add_local_node - parent.add_local_node(node) - - -def _add_dunder_class(func, member): - """Add a __class__ member to the given func node, if we can determine it.""" - python_cls = member.__class__ - cls_name = getattr(python_cls, '__name__', None) - if not cls_name: - return - cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__] - ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__) - func.instance_attrs['__class__'] = [ast_klass] - - -_marker = object() - - -def attach_dummy_node(node, name, runtime_object=_marker): - """create a dummy node and register it in the locals of the given - node with the specified name - """ - enode = nodes.EmptyNode() - enode.object = runtime_object - _attach_local_node(node, enode, name) - -def _has_underlying_object(self): - return self.object is not None and self.object is not _marker - -nodes.EmptyNode.has_underlying_object = _has_underlying_object - -def attach_const_node(node, name, value): - """create a Const node and register it in the locals of the given - node with the specified name - """ - if name not in node.special_attributes: - _attach_local_node(node, nodes.const_factory(value), name) - -def attach_import_node(node, modname, membername): - """create a ImportFrom node and register it in the locals of the given - node with the specified name - """ - from_node = nodes.ImportFrom(modname, [(membername, None)]) - _attach_local_node(node, from_node, membername) - - -def build_module(name, doc=None): - """create and initialize a astroid Module node""" - node = nodes.Module(name, doc, pure_python=False) - node.package = False - node.parent = None - return node - - -def build_class(name, basenames=(), doc=None): - """create and initialize a astroid ClassDef node""" - node = nodes.ClassDef(name, doc) - for base in basenames: - basenode = nodes.Name() - basenode.name = base - node.bases.append(basenode) - basenode.parent = node - return node - - -def build_function(name, args=None, defaults=None, doc=None): - """create and initialize a astroid FunctionDef node""" - args, defaults = args or [], defaults or [] - # first argument is now a list of decorators - func = nodes.FunctionDef(name, doc) - func.args = argsnode = nodes.Arguments() - argsnode.args = [] - for arg in args: - argsnode.args.append(nodes.Name()) - argsnode.args[-1].name = arg - argsnode.args[-1].parent = argsnode - argsnode.defaults = [] - for default in defaults: - argsnode.defaults.append(nodes.const_factory(default)) - argsnode.defaults[-1].parent = argsnode - argsnode.kwarg = None - argsnode.vararg = None - argsnode.parent = func - if args: - register_arguments(func) - return func - - -def build_from_import(fromname, names): - """create and initialize an astroid ImportFrom import statement""" - return nodes.ImportFrom(fromname, [(name, None) for name in names]) - -def register_arguments(func, args=None): - """add given arguments to local - - args is a list that may contains nested lists - (i.e. def func(a, (b, c, d)): ...) - """ - if args is None: - args = func.args.args - if func.args.vararg: - func.set_local(func.args.vararg, func.args) - if func.args.kwarg: - func.set_local(func.args.kwarg, func.args) - for arg in args: - if isinstance(arg, nodes.Name): - func.set_local(arg.name, arg) - else: - register_arguments(func, arg.elts) - - -def object_build_class(node, member, localname): - """create astroid for a living class object""" - basenames = [base.__name__ for base in member.__bases__] - return _base_class_object_build(node, member, basenames, - localname=localname) - - -def object_build_function(node, member, localname): - """create astroid for a living function object""" - # pylint: disable=deprecated-method; completely removed in 2.0 - args, varargs, varkw, defaults = inspect.getargspec(member) - if varargs is not None: - args.append(varargs) - if varkw is not None: - args.append(varkw) - func = build_function(getattr(member, '__name__', None) or localname, args, - defaults, member.__doc__) - node.add_local_node(func, localname) - - -def object_build_datadescriptor(node, member, name): - """create astroid for a living data descriptor object""" - return _base_class_object_build(node, member, [], name) - - -def object_build_methoddescriptor(node, member, localname): - """create astroid for a living method descriptor object""" - # FIXME get arguments ? - func = build_function(getattr(member, '__name__', None) or localname, - doc=member.__doc__) - # set node's arguments to None to notice that we have no information, not - # and empty argument list - func.args.args = None - node.add_local_node(func, localname) - _add_dunder_class(func, member) - - -def _base_class_object_build(node, member, basenames, name=None, localname=None): - """create astroid for a living class object, with a given set of base names - (e.g. ancestors) - """ - klass = build_class(name or getattr(member, '__name__', None) or localname, - basenames, member.__doc__) - klass._newstyle = isinstance(member, type) - node.add_local_node(klass, localname) - try: - # limit the instantiation trick since it's too dangerous - # (such as infinite test execution...) - # this at least resolves common case such as Exception.args, - # OSError.errno - if issubclass(member, Exception): - instdict = member().__dict__ - else: - raise TypeError - except: # pylint: disable=bare-except - pass - else: - for item_name, obj in instdict.items(): - valnode = nodes.EmptyNode() - valnode.object = obj - valnode.parent = klass - valnode.lineno = 1 - klass.instance_attrs[item_name] = [valnode] - return klass - - -def _build_from_function(node, name, member, module): - # verify this is not an imported function - try: - code = six.get_function_code(member) - except AttributeError: - # Some implementations don't provide the code object, - # such as Jython. - code = None - filename = getattr(code, 'co_filename', None) - if filename is None: - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif filename != getattr(module, '__file__', None): - attach_dummy_node(node, name, member) - else: - object_build_function(node, member, name) - - -class InspectBuilder(object): - """class for building nodes from living object - - this is actually a really minimal representation, including only Module, - FunctionDef and ClassDef nodes and some others as guessed. - """ - - # astroid from living objects ############################################### - - def __init__(self): - self._done = {} - self._module = None - - def inspect_build(self, module, modname=None, path=None): - """build astroid from a living module (i.e. using inspect) - this is used when there is no python source code available (either - because it's a built-in module or because the .py is not available) - """ - self._module = module - if modname is None: - modname = module.__name__ - try: - node = build_module(modname, module.__doc__) - except AttributeError: - # in jython, java modules have no __doc__ (see #109562) - node = build_module(modname) - node.file = node.path = os.path.abspath(path) if path else path - node.name = modname - MANAGER.cache_module(node) - node.package = hasattr(module, '__path__') - self._done = {} - self.object_build(node, module) - return node - - def object_build(self, node, obj): - """recursive method which create a partial ast from real objects - (only function, class, and method are handled) - """ - if obj in self._done: - return self._done[obj] - self._done[obj] = node - for name in dir(obj): - try: - member = getattr(obj, name) - except AttributeError: - # damned ExtensionClass.Base, I know you're there ! - attach_dummy_node(node, name) - continue - if inspect.ismethod(member): - member = six.get_method_function(member) - if inspect.isfunction(member): - _build_from_function(node, name, member, self._module) - elif inspect.isbuiltin(member): - if (not _io_discrepancy(member) and - self.imported_member(node, member, name)): - continue - object_build_methoddescriptor(node, member, name) - elif inspect.isclass(member): - if self.imported_member(node, member, name): - continue - if member in self._done: - class_node = self._done[member] - if class_node not in node.locals.get(name, ()): - node.add_local_node(class_node, name) - else: - class_node = object_build_class(node, member, name) - # recursion - self.object_build(class_node, member) - if name == '__class__' and class_node.parent is None: - class_node.parent = self._done[self._module] - elif inspect.ismethoddescriptor(member): - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif inspect.isdatadescriptor(member): - assert isinstance(member, object) - object_build_datadescriptor(node, member, name) - elif isinstance(member, _CONSTANTS): - attach_const_node(node, name, member) - elif inspect.isroutine(member): - # This should be called for Jython, where some builtin - # methods aren't caught by isbuiltin branch. - _build_from_function(node, name, member, self._module) - else: - # create an empty node so that the name is actually defined - attach_dummy_node(node, name, member) - - def imported_member(self, node, member, name): - """verify this is not an imported class or handle it""" - # /!\ some classes like ExtensionClass doesn't have a __module__ - # attribute ! Also, this may trigger an exception on badly built module - # (see http://www.logilab.org/ticket/57299 for instance) - try: - modname = getattr(member, '__module__', None) - except: # pylint: disable=bare-except - _LOG.exception('unexpected error while building ' - 'astroid from living object') - modname = None - if modname is None: - if (name in ('__new__', '__subclasshook__') - or (name in _BUILTINS and _JYTHON)): - # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14) - # >>> print object.__new__.__module__ - # None - modname = six.moves.builtins.__name__ - else: - attach_dummy_node(node, name, member) - return True - - real_name = { - 'gtk': 'gtk_gtk', - '_io': 'io', - }.get(modname, modname) - - if real_name != self._module.__name__: - # check if it sounds valid and then add an import node, else use a - # dummy node - try: - getattr(sys.modules[modname], name) - except (KeyError, AttributeError): - attach_dummy_node(node, name, member) - else: - attach_import_node(node, modname, name) - return True - return False - - -### astroid bootstrapping ###################################################### -Astroid_BUILDER = InspectBuilder() - -_CONST_PROXY = {} -def _astroid_bootstrapping(astroid_builtin=None): - """astroid boot strapping the builtins module""" - # this boot strapping is necessary since we need the Const nodes to - # inspect_build builtins, and then we can proxy Const - if astroid_builtin is None: - from six.moves import builtins - astroid_builtin = Astroid_BUILDER.inspect_build(builtins) - - # pylint: disable=redefined-outer-name - for cls, node_cls in node_classes.CONST_CLS.items(): - if cls is type(None): - proxy = build_class('NoneType') - proxy.parent = astroid_builtin - elif cls is type(NotImplemented): - proxy = build_class('NotImplementedType') - proxy.parent = astroid_builtin - else: - proxy = astroid_builtin.getattr(cls.__name__)[0] - if cls in (dict, list, set, tuple): - node_cls._proxied = proxy - else: - _CONST_PROXY[cls] = proxy - -_astroid_bootstrapping() - -# TODO : find a nicer way to handle this situation; -# However __proxied introduced an -# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870) -def _set_proxied(const): - return _CONST_PROXY[const.value.__class__] -nodes.Const._proxied = property(_set_proxied) - -_GeneratorType = nodes.ClassDef(types.GeneratorType.__name__, types.GeneratorType.__doc__) -_GeneratorType.parent = MANAGER.astroid_cache[six.moves.builtins.__name__] -bases.Generator._proxied = _GeneratorType -Astroid_BUILDER.object_build(bases.Generator._proxied, types.GeneratorType) - -_builtins = MANAGER.astroid_cache[six.moves.builtins.__name__] -BUILTIN_TYPES = (types.GetSetDescriptorType, types.GeneratorType, - types.MemberDescriptorType, type(None), type(NotImplemented), - types.FunctionType, types.MethodType, - types.BuiltinFunctionType, types.ModuleType, types.TracebackType) -for _type in BUILTIN_TYPES: - if _type.__name__ not in _builtins: - cls = nodes.ClassDef(_type.__name__, _type.__doc__) - cls.parent = MANAGER.astroid_cache[six.moves.builtins.__name__] - Astroid_BUILDER.object_build(cls, _type) - _builtins[_type.__name__] = cls diff --git a/pymode/libs/astroid/rebuilder.py b/pymode/libs/astroid/rebuilder.py deleted file mode 100644 index 60a1ad77..00000000 --- a/pymode/libs/astroid/rebuilder.py +++ /dev/null @@ -1,914 +0,0 @@ -# Copyright (c) 2009-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""this module contains utilities for rebuilding a _ast tree in -order to get a single Astroid representation -""" - -import sys -import _ast - -import astroid -from astroid import astpeephole -from astroid import nodes - - - -_BIN_OP_CLASSES = {_ast.Add: '+', - _ast.BitAnd: '&', - _ast.BitOr: '|', - _ast.BitXor: '^', - _ast.Div: '/', - _ast.FloorDiv: '//', - _ast.Mod: '%', - _ast.Mult: '*', - _ast.Pow: '**', - _ast.Sub: '-', - _ast.LShift: '<<', - _ast.RShift: '>>', - } -if sys.version_info >= (3, 5): - _BIN_OP_CLASSES[_ast.MatMult] = '@' - -_BOOL_OP_CLASSES = {_ast.And: 'and', - _ast.Or: 'or', - } - -_UNARY_OP_CLASSES = {_ast.UAdd: '+', - _ast.USub: '-', - _ast.Not: 'not', - _ast.Invert: '~', - } - -_CMP_OP_CLASSES = {_ast.Eq: '==', - _ast.Gt: '>', - _ast.GtE: '>=', - _ast.In: 'in', - _ast.Is: 'is', - _ast.IsNot: 'is not', - _ast.Lt: '<', - _ast.LtE: '<=', - _ast.NotEq: '!=', - _ast.NotIn: 'not in', - } - -CONST_NAME_TRANSFORMS = {'None': None, - 'True': True, - 'False': False, - } - -REDIRECT = {'arguments': 'Arguments', - 'comprehension': 'Comprehension', - "ListCompFor": 'Comprehension', - "GenExprFor": 'Comprehension', - 'excepthandler': 'ExceptHandler', - 'keyword': 'Keyword', - } -PY3 = sys.version_info >= (3, 0) -PY34 = sys.version_info >= (3, 4) -CONTEXTS = {_ast.Load: astroid.Load, - _ast.Store: astroid.Store, - _ast.Del: astroid.Del, - _ast.Param: astroid.Store} - - -def _get_doc(node): - try: - if isinstance(node.body[0], _ast.Expr) and isinstance(node.body[0].value, _ast.Str): - doc = node.body[0].value.s - node.body = node.body[1:] - return node, doc - except IndexError: - pass # ast built from scratch - return node, None - -def _visit_or_none(node, attr, visitor, parent, visit='visit', - **kws): - """If the given node has an attribute, visits the attribute, and - otherwise returns None. - - """ - value = getattr(node, attr, None) - if value: - return getattr(visitor, visit)(value, parent, **kws) - - return None - - -def _get_context(node): - return CONTEXTS.get(type(node.ctx), astroid.Load) - - -class TreeRebuilder(object): - """Rebuilds the _ast tree to become an Astroid tree""" - - def __init__(self, manager): - self._manager = manager - self._global_names = [] - self._import_from_nodes = [] - self._delayed_assattr = [] - self._visit_meths = {} - self._peepholer = astpeephole.ASTPeepholeOptimizer() - - def visit_module(self, node, modname, modpath, package): - """visit a Module node by returning a fresh instance of it""" - node, doc = _get_doc(node) - newnode = nodes.Module(name=modname, doc=doc, file=modpath, path=modpath, - package=package, parent=None) - newnode.postinit([self.visit(child, newnode) for child in node.body]) - return newnode - - def visit(self, node, parent): - cls = node.__class__ - if cls in self._visit_meths: - visit_method = self._visit_meths[cls] - else: - cls_name = cls.__name__ - visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower() - visit_method = getattr(self, visit_name) - self._visit_meths[cls] = visit_method - return visit_method(node, parent) - - def _save_assignment(self, node, name=None): - """save assignement situation since node.parent is not available yet""" - if self._global_names and node.name in self._global_names[-1]: - node.root().set_local(node.name, node) - else: - node.parent.set_local(node.name, node) - - def visit_arguments(self, node, parent): - """visit a Arguments node by returning a fresh instance of it""" - vararg, kwarg = node.vararg, node.kwarg - if PY34: - newnode = nodes.Arguments(vararg.arg if vararg else None, - kwarg.arg if kwarg else None, - parent) - else: - newnode = nodes.Arguments(vararg, kwarg, parent) - args = [self.visit(child, newnode) for child in node.args] - defaults = [self.visit(child, newnode) - for child in node.defaults] - varargannotation = None - kwargannotation = None - # change added in 82732 (7c5c678e4164), vararg and kwarg - # are instances of `_ast.arg`, not strings - if vararg: - if PY34: - if node.vararg.annotation: - varargannotation = self.visit(node.vararg.annotation, - newnode) - vararg = vararg.arg - elif PY3 and node.varargannotation: - varargannotation = self.visit(node.varargannotation, - newnode) - if kwarg: - if PY34: - if node.kwarg.annotation: - kwargannotation = self.visit(node.kwarg.annotation, - newnode) - kwarg = kwarg.arg - elif PY3: - if node.kwargannotation: - kwargannotation = self.visit(node.kwargannotation, - newnode) - if PY3: - kwonlyargs = [self.visit(child, newnode) for child - in node.kwonlyargs] - kw_defaults = [self.visit(child, newnode) if child else - None for child in node.kw_defaults] - annotations = [self.visit(arg.annotation, newnode) if - arg.annotation else None for arg in node.args] - kwonlyargs_annotations = [ - self.visit(arg.annotation, newnode) if arg.annotation else None - for arg in node.kwonlyargs - ] - else: - kwonlyargs = [] - kw_defaults = [] - annotations = [] - kwonlyargs_annotations = [] - - newnode.postinit( - args=args, - defaults=defaults, - kwonlyargs=kwonlyargs, - kw_defaults=kw_defaults, - annotations=annotations, - kwonlyargs_annotations=kwonlyargs_annotations, - varargannotation=varargannotation, - kwargannotation=kwargannotation - ) - # save argument names in locals: - if vararg: - newnode.parent.set_local(vararg, newnode) - if kwarg: - newnode.parent.set_local(kwarg, newnode) - return newnode - - def visit_assert(self, node, parent): - """visit a Assert node by returning a fresh instance of it""" - newnode = nodes.Assert(node.lineno, node.col_offset, parent) - if node.msg: - msg = self.visit(node.msg, newnode) - else: - msg = None - newnode.postinit(self.visit(node.test, newnode), msg) - return newnode - - def visit_assign(self, node, parent): - """visit a Assign node by returning a fresh instance of it""" - newnode = nodes.Assign(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.targets], - self.visit(node.value, newnode)) - return newnode - - def visit_assignname(self, node, parent, node_name=None): - '''visit a node and return a AssignName node''' - newnode = nodes.AssignName(node_name, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - self._save_assignment(newnode) - return newnode - - def visit_augassign(self, node, parent): - """visit a AugAssign node by returning a fresh instance of it""" - newnode = nodes.AugAssign(_BIN_OP_CLASSES[type(node.op)] + "=", - node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.target, newnode), - self.visit(node.value, newnode)) - return newnode - - def visit_repr(self, node, parent): - """visit a Backquote node by returning a fresh instance of it""" - newnode = nodes.Repr(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_binop(self, node, parent): - """visit a BinOp node by returning a fresh instance of it""" - if isinstance(node.left, _ast.BinOp) and self._manager.optimize_ast: - # Optimize BinOp operations in order to remove - # redundant recursion. For instance, if the - # following code is parsed in order to obtain - # its ast, then the rebuilder will fail with an - # infinite recursion, the same will happen with the - # inference engine as well. There's no need to hold - # so many objects for the BinOp if they can be reduced - # to something else (also, the optimization - # might handle only Const binops, which isn't a big - # problem for the correctness of the program). - # - # ("a" + "b" + # one thousand more + "c") - optimized = self._peepholer.optimize_binop(node, parent) - if optimized: - return optimized - - newnode = nodes.BinOp(_BIN_OP_CLASSES[type(node.op)], - node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.left, newnode), - self.visit(node.right, newnode)) - return newnode - - def visit_boolop(self, node, parent): - """visit a BoolOp node by returning a fresh instance of it""" - newnode = nodes.BoolOp(_BOOL_OP_CLASSES[type(node.op)], - node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.values]) - return newnode - - def visit_break(self, node, parent): - """visit a Break node by returning a fresh instance of it""" - return nodes.Break(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), - parent) - - def visit_call(self, node, parent): - """visit a CallFunc node by returning a fresh instance of it""" - newnode = nodes.Call(node.lineno, node.col_offset, parent) - starargs = _visit_or_none(node, 'starargs', self, newnode) - kwargs = _visit_or_none(node, 'kwargs', self, newnode) - args = [self.visit(child, newnode) - for child in node.args] - - if node.keywords: - keywords = [self.visit(child, newnode) - for child in node.keywords] - else: - keywords = None - if starargs: - new_starargs = nodes.Starred(col_offset=starargs.col_offset, - lineno=starargs.lineno, - parent=starargs.parent) - new_starargs.postinit(value=starargs) - args.append(new_starargs) - if kwargs: - new_kwargs = nodes.Keyword(arg=None, col_offset=kwargs.col_offset, - lineno=kwargs.lineno, - parent=kwargs.parent) - new_kwargs.postinit(value=kwargs) - if keywords: - keywords.append(new_kwargs) - else: - keywords = [new_kwargs] - - newnode.postinit(self.visit(node.func, newnode), - args, keywords) - return newnode - - def visit_classdef(self, node, parent, newstyle=None): - """visit a ClassDef node to become astroid""" - node, doc = _get_doc(node) - newnode = nodes.ClassDef(node.name, doc, node.lineno, - node.col_offset, parent) - metaclass = None - if PY3: - for keyword in node.keywords: - if keyword.arg == 'metaclass': - metaclass = self.visit(keyword, newnode).value - break - if node.decorator_list: - decorators = self.visit_decorators(node, newnode) - else: - decorators = None - newnode.postinit([self.visit(child, newnode) - for child in node.bases], - [self.visit(child, newnode) - for child in node.body], - decorators, newstyle, metaclass, - [self.visit(kwd, newnode) for kwd in node.keywords - if kwd.arg != 'metaclass'] if PY3 else []) - return newnode - - def visit_const(self, node, parent): - """visit a Const node by returning a fresh instance of it""" - return nodes.Const(node.value, - getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - def visit_continue(self, node, parent): - """visit a Continue node by returning a fresh instance of it""" - return nodes.Continue(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), - parent) - - def visit_compare(self, node, parent): - """visit a Compare node by returning a fresh instance of it""" - newnode = nodes.Compare(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.left, newnode), - [(_CMP_OP_CLASSES[op.__class__], - self.visit(expr, newnode)) - for (op, expr) in zip(node.ops, node.comparators)]) - return newnode - - def visit_comprehension(self, node, parent): - """visit a Comprehension node by returning a fresh instance of it""" - newnode = nodes.Comprehension(parent) - newnode.postinit(self.visit(node.target, newnode), - self.visit(node.iter, newnode), - [self.visit(child, newnode) - for child in node.ifs], - getattr(node, 'is_async', None)) - return newnode - - def visit_decorators(self, node, parent): - """visit a Decorators node by returning a fresh instance of it""" - # /!\ node is actually a _ast.FunctionDef node while - # parent is a astroid.nodes.FunctionDef node - newnode = nodes.Decorators(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.decorator_list]) - return newnode - - def visit_delete(self, node, parent): - """visit a Delete node by returning a fresh instance of it""" - newnode = nodes.Delete(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.targets]) - return newnode - - def _visit_dict_items(self, node, parent, newnode): - for key, value in zip(node.keys, node.values): - rebuilt_value = self.visit(value, newnode) - if not key: - # Python 3.5 and extended unpacking - rebuilt_key = nodes.DictUnpack(rebuilt_value.lineno, - rebuilt_value.col_offset, - parent) - else: - rebuilt_key = self.visit(key, newnode) - yield rebuilt_key, rebuilt_value - - def visit_dict(self, node, parent): - """visit a Dict node by returning a fresh instance of it""" - newnode = nodes.Dict(node.lineno, node.col_offset, parent) - items = list(self._visit_dict_items(node, parent, newnode)) - newnode.postinit(items) - return newnode - - def visit_dictcomp(self, node, parent): - """visit a DictComp node by returning a fresh instance of it""" - newnode = nodes.DictComp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.key, newnode), - self.visit(node.value, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_expr(self, node, parent): - """visit a Expr node by returning a fresh instance of it""" - newnode = nodes.Expr(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_ellipsis(self, node, parent): - """visit an Ellipsis node by returning a fresh instance of it""" - return nodes.Ellipsis(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - - def visit_emptynode(self, node, parent): - """visit an EmptyNode node by returning a fresh instance of it""" - return nodes.EmptyNode(getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent) - # /!\ node.name can be a tuple - newnode.postinit(_visit_or_none(node, 'type', self, newnode), - _visit_or_none(node, 'name', self, newnode), - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_exec(self, node, parent): - """visit an Exec node by returning a fresh instance of it""" - newnode = nodes.Exec(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.body, newnode), - _visit_or_none(node, 'globals', self, newnode), - _visit_or_none(node, 'locals', self, newnode)) - return newnode - - def visit_extslice(self, node, parent): - """visit an ExtSlice node by returning a fresh instance of it""" - newnode = nodes.ExtSlice(parent=parent) - newnode.postinit([self.visit(dim, newnode) - for dim in node.dims]) - return newnode - - def _visit_for(self, cls, node, parent): - """visit a For node by returning a fresh instance of it""" - newnode = cls(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.target, newnode), - self.visit(node.iter, newnode), - [self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_for(self, node, parent): - return self._visit_for(nodes.For, node, parent) - - def visit_importfrom(self, node, parent): - """visit an ImportFrom node by returning a fresh instance of it""" - names = [(alias.name, alias.asname) for alias in node.names] - newnode = nodes.ImportFrom(node.module or '', names, node.level or None, - getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - # store From names to add them to locals after building - self._import_from_nodes.append(newnode) - return newnode - - def _visit_functiondef(self, cls, node, parent): - """visit an FunctionDef node to become astroid""" - self._global_names.append({}) - node, doc = _get_doc(node) - newnode = cls(node.name, doc, node.lineno, - node.col_offset, parent) - if node.decorator_list: - decorators = self.visit_decorators(node, newnode) - else: - decorators = None - if PY3 and node.returns: - returns = self.visit(node.returns, newnode) - else: - returns = None - newnode.postinit(self.visit(node.args, newnode), - [self.visit(child, newnode) - for child in node.body], - decorators, returns) - self._global_names.pop() - return newnode - - def visit_functiondef(self, node, parent): - return self._visit_functiondef(nodes.FunctionDef, node, parent) - - def visit_generatorexp(self, node, parent): - """visit a GeneratorExp node by returning a fresh instance of it""" - newnode = nodes.GeneratorExp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.elt, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_attribute(self, node, parent): - """visit an Attribute node by returning a fresh instance of it""" - context = _get_context(node) - if context == astroid.Del: - # FIXME : maybe we should reintroduce and visit_delattr ? - # for instance, deactivating assign_ctx - newnode = nodes.DelAttr(node.attr, node.lineno, node.col_offset, - parent) - elif context == astroid.Store: - newnode = nodes.AssignAttr(node.attr, node.lineno, node.col_offset, - parent) - # Prohibit a local save if we are in an ExceptHandler. - if not isinstance(parent, astroid.ExceptHandler): - self._delayed_assattr.append(newnode) - else: - newnode = nodes.Attribute(node.attr, node.lineno, node.col_offset, - parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_global(self, node, parent): - """visit a Global node to become astroid""" - newnode = nodes.Global(node.names, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - if self._global_names: # global at the module level, no effect - for name in node.names: - self._global_names[-1].setdefault(name, []).append(newnode) - return newnode - - def visit_if(self, node, parent): - """visit an If node by returning a fresh instance of it""" - newnode = nodes.If(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.test, newnode), - [self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_ifexp(self, node, parent): - """visit a IfExp node by returning a fresh instance of it""" - newnode = nodes.IfExp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.test, newnode), - self.visit(node.body, newnode), - self.visit(node.orelse, newnode)) - return newnode - - def visit_import(self, node, parent): - """visit a Import node by returning a fresh instance of it""" - names = [(alias.name, alias.asname) for alias in node.names] - newnode = nodes.Import(names, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - # save import names in parent's locals: - for (name, asname) in newnode.names: - name = asname or name - parent.set_local(name.split('.')[0], newnode) - return newnode - - def visit_index(self, node, parent): - """visit a Index node by returning a fresh instance of it""" - newnode = nodes.Index(parent=parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_keyword(self, node, parent): - """visit a Keyword node by returning a fresh instance of it""" - newnode = nodes.Keyword(node.arg, parent=parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_lambda(self, node, parent): - """visit a Lambda node by returning a fresh instance of it""" - newnode = nodes.Lambda(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.args, newnode), - self.visit(node.body, newnode)) - return newnode - - def visit_list(self, node, parent): - """visit a List node by returning a fresh instance of it""" - context = _get_context(node) - newnode = nodes.List(ctx=context, - lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit([self.visit(child, newnode) - for child in node.elts]) - return newnode - - def visit_listcomp(self, node, parent): - """visit a ListComp node by returning a fresh instance of it""" - newnode = nodes.ListComp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.elt, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_name(self, node, parent): - """visit a Name node by returning a fresh instance of it""" - context = _get_context(node) - # True and False can be assigned to something in py2x, so we have to - # check first the context. - if context == astroid.Del: - newnode = nodes.DelName(node.id, node.lineno, node.col_offset, - parent) - elif context == astroid.Store: - newnode = nodes.AssignName(node.id, node.lineno, node.col_offset, - parent) - elif node.id in CONST_NAME_TRANSFORMS: - newnode = nodes.Const(CONST_NAME_TRANSFORMS[node.id], - getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - return newnode - else: - newnode = nodes.Name(node.id, node.lineno, node.col_offset, parent) - # XXX REMOVE me : - if context in (astroid.Del, astroid.Store): # 'Aug' ?? - self._save_assignment(newnode) - return newnode - - def visit_str(self, node, parent): - """visit a String/Bytes node by returning a fresh instance of Const""" - return nodes.Const(node.s, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - visit_bytes = visit_str - - def visit_num(self, node, parent): - """visit a Num node by returning a fresh instance of Const""" - return nodes.Const(node.n, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - def visit_pass(self, node, parent): - """visit a Pass node by returning a fresh instance of it""" - return nodes.Pass(node.lineno, node.col_offset, parent) - - def visit_print(self, node, parent): - """visit a Print node by returning a fresh instance of it""" - newnode = nodes.Print(node.nl, node.lineno, node.col_offset, parent) - newnode.postinit(_visit_or_none(node, 'dest', self, newnode), - [self.visit(child, newnode) - for child in node.values]) - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = nodes.Raise(node.lineno, node.col_offset, parent) - newnode.postinit(_visit_or_none(node, 'type', self, newnode), - _visit_or_none(node, 'inst', self, newnode), - _visit_or_none(node, 'tback', self, newnode)) - return newnode - - def visit_return(self, node, parent): - """visit a Return node by returning a fresh instance of it""" - newnode = nodes.Return(node.lineno, node.col_offset, parent) - if node.value is not None: - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_set(self, node, parent): - """visit a Set node by returning a fresh instance of it""" - newnode = nodes.Set(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.elts]) - return newnode - - def visit_setcomp(self, node, parent): - """visit a SetComp node by returning a fresh instance of it""" - newnode = nodes.SetComp(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.elt, newnode), - [self.visit(child, newnode) - for child in node.generators]) - return newnode - - def visit_slice(self, node, parent): - """visit a Slice node by returning a fresh instance of it""" - newnode = nodes.Slice(parent=parent) - newnode.postinit(_visit_or_none(node, 'lower', self, newnode), - _visit_or_none(node, 'upper', self, newnode), - _visit_or_none(node, 'step', self, newnode)) - return newnode - - def visit_subscript(self, node, parent): - """visit a Subscript node by returning a fresh instance of it""" - context = _get_context(node) - newnode = nodes.Subscript(ctx=context, - lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit(self.visit(node.value, newnode), - self.visit(node.slice, newnode)) - return newnode - - def visit_tryexcept(self, node, parent): - """visit a TryExcept node by returning a fresh instance of it""" - newnode = nodes.TryExcept(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.handlers], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_tryfinally(self, node, parent): - """visit a TryFinally node by returning a fresh instance of it""" - newnode = nodes.TryFinally(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.body], - [self.visit(n, newnode) - for n in node.finalbody]) - return newnode - - def visit_tuple(self, node, parent): - """visit a Tuple node by returning a fresh instance of it""" - context = _get_context(node) - newnode = nodes.Tuple(ctx=context, - lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit([self.visit(child, newnode) - for child in node.elts]) - return newnode - - def visit_unaryop(self, node, parent): - """visit a UnaryOp node by returning a fresh instance of it""" - newnode = nodes.UnaryOp(_UNARY_OP_CLASSES[node.op.__class__], - node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.operand, newnode)) - return newnode - - def visit_while(self, node, parent): - """visit a While node by returning a fresh instance of it""" - newnode = nodes.While(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.test, newnode), - [self.visit(child, newnode) - for child in node.body], - [self.visit(child, newnode) - for child in node.orelse]) - return newnode - - def visit_with(self, node, parent): - newnode = nodes.With(node.lineno, node.col_offset, parent) - expr = self.visit(node.context_expr, newnode) - if node.optional_vars is not None: - optional_vars = self.visit(node.optional_vars, newnode) - else: - optional_vars = None - newnode.postinit([(expr, optional_vars)], - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_yield(self, node, parent): - """visit a Yield node by returning a fresh instance of it""" - newnode = nodes.Yield(node.lineno, node.col_offset, parent) - if node.value is not None: - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - -class TreeRebuilder3(TreeRebuilder): - """extend and overwrite TreeRebuilder for python3k""" - - def visit_arg(self, node, parent): - """visit a arg node by returning a fresh AssName instance""" - # TODO(cpopa): introduce an Arg node instead of using AssignName. - return self.visit_assignname(node, parent, node.arg) - - def visit_nameconstant(self, node, parent): - # in Python 3.4 we have NameConstant for True / False / None - return nodes.Const(node.value, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent) - if node.name: - name = self.visit_assignname(node, newnode, node.name) - else: - name = None - newnode.postinit(_visit_or_none(node, 'type', self, newnode), - name, - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_nonlocal(self, node, parent): - """visit a Nonlocal node and return a new instance of it""" - return nodes.Nonlocal(node.names, getattr(node, 'lineno', None), - getattr(node, 'col_offset', None), parent) - - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = nodes.Raise(node.lineno, node.col_offset, parent) - # no traceback; anyway it is not used in Pylint - newnode.postinit(_visit_or_none(node, 'exc', self, newnode), - _visit_or_none(node, 'cause', self, newnode)) - return newnode - - def visit_starred(self, node, parent): - """visit a Starred node and return a new instance of it""" - context = _get_context(node) - newnode = nodes.Starred(ctx=context, lineno=node.lineno, - col_offset=node.col_offset, - parent=parent) - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_try(self, node, parent): - # python 3.3 introduce a new Try node replacing - # TryFinally/TryExcept nodes - if node.finalbody: - newnode = nodes.TryFinally(node.lineno, node.col_offset, parent) - if node.handlers: - body = [self.visit_tryexcept(node, newnode)] - else: - body = [self.visit(child, newnode) - for child in node.body] - newnode.postinit(body, - [self.visit(n, newnode) - for n in node.finalbody]) - return newnode - elif node.handlers: - return self.visit_tryexcept(node, parent) - - def visit_annassign(self, node, parent): - """visit an AnnAssign node by returning a fresh instance of it""" - newnode = nodes.AnnAssign(node.lineno, node.col_offset, parent) - annotation = _visit_or_none(node, 'annotation', self, newnode) - newnode.postinit(target=self.visit(node.target, newnode), - annotation=annotation, - simple=node.simple, - value=_visit_or_none(node, 'value', self, newnode)) - return newnode - - def _visit_with(self, cls, node, parent): - if 'items' not in node._fields: - # python < 3.3 - return super(TreeRebuilder3, self).visit_with(node, parent) - - newnode = cls(node.lineno, node.col_offset, parent) - def visit_child(child): - expr = self.visit(child.context_expr, newnode) - var = _visit_or_none(child, 'optional_vars', self, newnode) - return expr, var - newnode.postinit([visit_child(child) for child in node.items], - [self.visit(child, newnode) - for child in node.body]) - return newnode - - def visit_with(self, node, parent): - return self._visit_with(nodes.With, node, parent) - - def visit_yieldfrom(self, node, parent): - newnode = nodes.YieldFrom(node.lineno, node.col_offset, parent) - if node.value is not None: - newnode.postinit(self.visit(node.value, newnode)) - return newnode - - def visit_classdef(self, node, parent, newstyle=True): - return super(TreeRebuilder3, self).visit_classdef(node, parent, - newstyle=newstyle) - - # Async structs added in Python 3.5 - def visit_asyncfunctiondef(self, node, parent): - return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent) - - def visit_asyncfor(self, node, parent): - return self._visit_for(nodes.AsyncFor, node, parent) - - def visit_await(self, node, parent): - newnode = nodes.Await(node.lineno, node.col_offset, parent) - newnode.postinit(value=self.visit(node.value, newnode)) - return newnode - - def visit_asyncwith(self, node, parent): - return self._visit_with(nodes.AsyncWith, node, parent) - - def visit_joinedstr(self, node, parent): - newnode = nodes.JoinedStr(node.lineno, node.col_offset, parent) - newnode.postinit([self.visit(child, newnode) - for child in node.values]) - return newnode - - def visit_formattedvalue(self, node, parent): - newnode = nodes.FormattedValue(node.lineno, node.col_offset, parent) - newnode.postinit(self.visit(node.value, newnode), - node.conversion, - _visit_or_none(node, 'format_spec', self, newnode)) - return newnode - -if sys.version_info >= (3, 0): - TreeRebuilder = TreeRebuilder3 diff --git a/pymode/libs/astroid/scoped_nodes.py b/pymode/libs/astroid/scoped_nodes.py deleted file mode 100644 index 052ec688..00000000 --- a/pymode/libs/astroid/scoped_nodes.py +++ /dev/null @@ -1,1832 +0,0 @@ -# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) -# Copyright (c) 2011, 2013-2015 Google, Inc. -# Copyright (c) 2013-2016 Claudiu Popa -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015 Rene Zhang - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -""" -This module contains the classes for "scoped" node, i.e. which are opening a -new local scope in the language definition : Module, ClassDef, FunctionDef (and -Lambda, GeneratorExp, DictComp and SetComp to some extent). -""" - -import sys -import io -import itertools -import warnings - -import six - -from astroid import bases -from astroid import context as contextmod -from astroid import exceptions -from astroid import decorators as decorators_mod -from astroid.interpreter import objectmodel -from astroid.interpreter import dunder_lookup -from astroid import manager -from astroid import mixins -from astroid import node_classes -from astroid import util - - -BUILTINS = six.moves.builtins.__name__ -ITER_METHODS = ('__iter__', '__getitem__') - - -def _c3_merge(sequences, cls, context): - """Merges MROs in *sequences* to a single MRO using the C3 algorithm. - - Adapted from http://www.python.org/download/releases/2.3/mro/. - - """ - result = [] - while True: - sequences = [s for s in sequences if s] # purge empty sequences - if not sequences: - return result - for s1 in sequences: # find merge candidates among seq heads - candidate = s1[0] - for s2 in sequences: - if candidate in s2[1:]: - candidate = None - break # reject the current head, it appears later - else: - break - if not candidate: - # Show all the remaining bases, which were considered as - # candidates for the next mro sequence. - raise exceptions.InconsistentMroError( - message="Cannot create a consistent method resolution order " - "for MROs {mros} of class {cls!r}.", - mros=sequences, cls=cls, context=context) - - result.append(candidate) - # remove the chosen candidate - for seq in sequences: - if seq[0] == candidate: - del seq[0] - - -def _verify_duplicates_mro(sequences, cls, context): - for sequence in sequences: - names = [node.qname() for node in sequence] - if len(names) != len(set(names)): - raise exceptions.DuplicateBasesError( - message='Duplicates found in MROs {mros} for {cls!r}.', - mros=sequences, cls=cls, context=context) - - -def function_to_method(n, klass): - if isinstance(n, FunctionDef): - if n.type == 'classmethod': - return bases.BoundMethod(n, klass) - if n.type != 'staticmethod': - return bases.UnboundMethod(n) - return n - - -MANAGER = manager.AstroidManager() -def builtin_lookup(name): - """lookup a name into the builtin module - return the list of matching statements and the astroid for the builtin - module - """ - builtin_astroid = MANAGER.ast_from_module(six.moves.builtins) - if name == '__dict__': - return builtin_astroid, () - try: - stmts = builtin_astroid.locals[name] - except KeyError: - stmts = () - return builtin_astroid, stmts - - -# TODO move this Mixin to mixins.py; problem: 'FunctionDef' in _scope_lookup -class LocalsDictNodeNG(node_classes.LookupMixIn, - node_classes.NodeNG): - """ this class provides locals handling common to Module, FunctionDef - and ClassDef nodes, including a dict like interface for direct access - to locals information - """ - - # attributes below are set by the builder module or by raw factories - - # dictionary of locals with name as key and node defining the local as - # value - - locals = {} - - def qname(self): - """return the 'qualified' name of the node, eg module.name, - module.class.name ... - """ - # pylint: disable=no-member; github.com/pycqa/astroid/issues/278 - if self.parent is None: - return self.name - return '%s.%s' % (self.parent.frame().qname(), self.name) - - def frame(self): - """return the first parent frame node (i.e. Module, FunctionDef or ClassDef) - """ - return self - - def scope(self): - """return the first node defining a new scope (i.e. Module, - FunctionDef, ClassDef, Lambda but also GeneratorExp, DictComp and SetComp) - """ - return self - - def _scope_lookup(self, node, name, offset=0): - """XXX method for interfacing the scope lookup""" - try: - stmts = node._filter_stmts(self.locals[name], self, offset) - except KeyError: - stmts = () - if stmts: - return self, stmts - if self.parent: # i.e. not Module - # nested scope: if parent scope is a function, that's fine - # else jump to the module - pscope = self.parent.scope() - if not pscope.is_function: - pscope = pscope.root() - return pscope.scope_lookup(node, name) - return builtin_lookup(name) # Module - - def set_local(self, name, stmt): - """define in locals ( is the node defining the name) - if the node is a Module node (i.e. has globals), add the name to - globals - - if the name is already defined, ignore it - """ - #assert not stmt in self.locals.get(name, ()), (self, stmt) - self.locals.setdefault(name, []).append(stmt) - - __setitem__ = set_local - - def _append_node(self, child): - """append a child, linking it in the tree""" - # pylint: disable=no-member; depending by the class - # which uses the current class as a mixin or base class. - # It's rewritten in 2.0, so it makes no sense for now - # to spend development time on it. - self.body.append(child) - child.parent = self - - def add_local_node(self, child_node, name=None): - """append a child which should alter locals to the given node""" - if name != '__class__': - # add __class__ node as a child will cause infinite recursion later! - self._append_node(child_node) - self.set_local(name or child_node.name, child_node) - - def __getitem__(self, item): - """method from the `dict` interface returning the first node - associated with the given name in the locals dictionary - - :type item: str - :param item: the name of the locally defined object - :raises KeyError: if the name is not defined - """ - return self.locals[item][0] - - def __iter__(self): - """method from the `dict` interface returning an iterator on - `self.keys()` - """ - return iter(self.keys()) - - def keys(self): - """method from the `dict` interface returning a tuple containing - locally defined names - """ - return list(self.locals.keys()) - - def values(self): - """method from the `dict` interface returning a tuple containing - locally defined nodes which are instance of `FunctionDef` or `ClassDef` - """ - return [self[key] for key in self.keys()] - - def items(self): - """method from the `dict` interface returning a list of tuple - containing each locally defined name with its associated node, - which is an instance of `FunctionDef` or `ClassDef` - """ - return list(zip(self.keys(), self.values())) - - def __contains__(self, name): - return name in self.locals - - -class Module(LocalsDictNodeNG): - _astroid_fields = ('body',) - - fromlineno = 0 - lineno = 0 - - # attributes below are set by the builder module or by raw factories - - # the file from which as been extracted the astroid representation. It may - # be None if the representation has been built from a built-in module - file = None - # Alternatively, if built from a string/bytes, this can be set - file_bytes = None - # encoding of python source file, so we can get unicode out of it (python2 - # only) - file_encoding = None - # the module name - name = None - # boolean for astroid built from source (i.e. ast) - pure_python = None - # boolean for package module - package = None - # dictionary of globals with name as key and node defining the global - # as value - globals = None - - # Future imports - future_imports = None - special_attributes = objectmodel.ModuleModel() - - # names of python special attributes (handled by getattr impl.) - - # names of module attributes available through the global scope - scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) - - _other_fields = ('name', 'doc', 'file', 'path', 'package', - 'pure_python', 'future_imports') - _other_other_fields = ('locals', 'globals') - - def __init__(self, name, doc, file=None, path=None, package=None, - parent=None, pure_python=True): - self.name = name - self.doc = doc - self.file = file - self.path = path - self.package = package - self.parent = parent - self.pure_python = pure_python - self.locals = self.globals = {} - self.body = [] - self.future_imports = set() - # pylint: enable=redefined-builtin - - def postinit(self, body=None): - self.body = body - - def _get_stream(self): - if self.file_bytes is not None: - return io.BytesIO(self.file_bytes) - if self.file is not None: - stream = open(self.file, 'rb') - return stream - return None - - @property - def file_stream(self): - warnings.warn("file_stream property is deprecated and " - "it is slated for removal in astroid 1.6." - "Use the new method 'stream' instead.", - PendingDeprecationWarning, - stacklevel=2) - return self._get_stream() - - def stream(self): - """Get a stream to the underlying file or bytes.""" - return self._get_stream() - - def close(self): - """Close the underlying file streams.""" - warnings.warn("close method is deprecated and it is " - "slated for removal in astroid 1.6, along " - "with 'file_stream' property. " - "Its behaviour is replaced by managing each " - "file stream returned by the 'stream' method.", - PendingDeprecationWarning, - stacklevel=2) - - def block_range(self, lineno): - """return block line numbers. - - start from the beginning whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def scope_lookup(self, node, name, offset=0): - if name in self.scope_attrs and name not in self.locals: - try: - return self, self.getattr(name) - except exceptions.AttributeInferenceError: - return self, () - return self._scope_lookup(node, name, offset) - - def pytype(self): - return '%s.module' % BUILTINS - - def display_type(self): - return 'Module' - - def getattr(self, name, context=None, ignore_locals=False): - result = [] - name_in_locals = name in self.locals - - if name in self.special_attributes and not ignore_locals and not name_in_locals: - result = [self.special_attributes.lookup(name)] - elif not ignore_locals and name_in_locals: - result = self.locals[name] - elif self.package: - try: - result = [self.import_module(name, relative_only=True)] - except (exceptions.AstroidBuildingError, SyntaxError): - util.reraise(exceptions.AttributeInferenceError(target=self, - attribute=name, - context=context)) - result = [n for n in result if not isinstance(n, node_classes.DelName)] - if result: - return result - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - - def igetattr(self, name, context=None): - """inferred getattr""" - # set lookup name since this is necessary to infer on import nodes for - # instance - context = contextmod.copy_context(context) - context.lookupname = name - try: - return bases._infer_stmts(self.getattr(name, context), - context, frame=self) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) - - def fully_defined(self): - """return True if this module has been built from a .py file - and so contains a complete representation including the code - """ - return self.file is not None and self.file.endswith('.py') - - def statement(self): - """return the first parent node marked as statement node - consider a module as a statement... - """ - return self - - def previous_sibling(self): - """module has no sibling""" - return - - def next_sibling(self): - """module has no sibling""" - return - - if six.PY2: - @decorators_mod.cachedproperty - def _absolute_import_activated(self): - for stmt in self.locals.get('absolute_import', ()): - if isinstance(stmt, node_classes.ImportFrom) and stmt.modname == '__future__': - return True - return False - else: - _absolute_import_activated = True - - def absolute_import_activated(self): - return self._absolute_import_activated - - def import_module(self, modname, relative_only=False, level=None): - """import the given module considering self as context""" - if relative_only and level is None: - level = 0 - absmodname = self.relative_to_absolute_name(modname, level) - - try: - return MANAGER.ast_from_module_name(absmodname) - except exceptions.AstroidBuildingError: - # we only want to import a sub module or package of this module, - # skip here - if relative_only: - raise - return MANAGER.ast_from_module_name(modname) - - def relative_to_absolute_name(self, modname, level): - """return the absolute module name for a relative import. - - The relative import can be implicit or explicit. - """ - # XXX this returns non sens when called on an absolute import - # like 'pylint.checkers.astroid.utils' - # XXX doesn't return absolute name if self.name isn't absolute name - if self.absolute_import_activated() and level is None: - return modname - if level: - if self.package: - level = level - 1 - if level and self.name.count('.') < level: - raise exceptions.TooManyLevelsError( - level=level, name=self.name) - - package_name = self.name.rsplit('.', level)[0] - elif self.package: - package_name = self.name - else: - package_name = self.name.rsplit('.', 1)[0] - - if package_name: - if not modname: - return package_name - return '%s.%s' % (package_name, modname) - return modname - - def wildcard_import_names(self): - """return the list of imported names when this module is 'wildcard - imported' - - It doesn't include the '__builtins__' name which is added by the - current CPython implementation of wildcard imports. - """ - # We separate the different steps of lookup in try/excepts - # to avoid catching too many Exceptions - default = [name for name in self.keys() if not name.startswith('_')] - try: - all_values = self['__all__'] - except KeyError: - return default - - try: - explicit = next(all_values.assigned_stmts()) - except exceptions.InferenceError: - return default - except AttributeError: - # not an assignment node - # XXX infer? - return default - - # Try our best to detect the exported name. - inferred = [] - try: - explicit = next(explicit.infer()) - except exceptions.InferenceError: - return default - if not isinstance(explicit, (node_classes.Tuple, node_classes.List)): - return default - - str_const = lambda node: (isinstance(node, node_classes.Const) and - isinstance(node.value, six.string_types)) - for node in explicit.elts: - if str_const(node): - inferred.append(node.value) - else: - try: - inferred_node = next(node.infer()) - except exceptions.InferenceError: - continue - if str_const(inferred_node): - inferred.append(inferred_node.value) - return inferred - - def public_names(self): - """Get the list of the names which are publicly available in this module.""" - return [name for name in self.keys() if not name.startswith('_')] - - def bool_value(self): - return True - - -class ComprehensionScope(LocalsDictNodeNG): - def frame(self): - return self.parent.frame() - - scope_lookup = LocalsDictNodeNG._scope_lookup - - -class GeneratorExp(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - _other_other_fields = ('locals',) - elt = None - generators = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(GeneratorExp, self).__init__(lineno, col_offset, parent) - - def postinit(self, elt=None, generators=None): - self.elt = elt - if generators is None: - self.generators = [] - else: - self.generators = generators - - def bool_value(self): - return True - - -class DictComp(ComprehensionScope): - _astroid_fields = ('key', 'value', 'generators') - _other_other_fields = ('locals',) - key = None - value = None - generators = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(DictComp, self).__init__(lineno, col_offset, parent) - - def postinit(self, key=None, value=None, generators=None): - self.key = key - self.value = value - if generators is None: - self.generators = [] - else: - self.generators = generators - - def bool_value(self): - return util.Uninferable - - -class SetComp(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - _other_other_fields = ('locals',) - elt = None - generators = None - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(SetComp, self).__init__(lineno, col_offset, parent) - - def postinit(self, elt=None, generators=None): - self.elt = elt - if generators is None: - self.generators = [] - else: - self.generators = generators - - def bool_value(self): - return util.Uninferable - - -class _ListComp(node_classes.NodeNG): - """class representing a ListComp node""" - _astroid_fields = ('elt', 'generators') - elt = None - generators = None - - def postinit(self, elt=None, generators=None): - self.elt = elt - self.generators = generators - - def bool_value(self): - return util.Uninferable - - -if six.PY3: - class ListComp(_ListComp, ComprehensionScope): - """class representing a ListComp node""" - _other_other_fields = ('locals',) - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - super(ListComp, self).__init__(lineno, col_offset, parent) -else: - class ListComp(_ListComp): - """class representing a ListComp node""" - - -def _infer_decorator_callchain(node): - """Detect decorator call chaining and see if the end result is a - static or a classmethod. - """ - if not isinstance(node, FunctionDef): - return - if not node.parent: - return - try: - # TODO: We don't handle multiple inference results right now, - # because there's no flow to reason when the return - # is what we are looking for, a static or a class method. - result = next(node.infer_call_result(node.parent)) - except (StopIteration, exceptions.InferenceError): - return - if isinstance(result, bases.Instance): - result = result._proxied - if isinstance(result, ClassDef): - if result.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - if result.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - - -class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG): - _astroid_fields = ('args', 'body',) - _other_other_fields = ('locals',) - name = '' - - # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' - @property - def type(self): - # pylint: disable=no-member - if self.args.args and self.args.args[0].name == 'self': - if isinstance(self.parent.scope(), ClassDef): - return 'method' - return 'function' - - def __init__(self, lineno=None, col_offset=None, parent=None): - self.locals = {} - self.args = [] - self.body = [] - super(Lambda, self).__init__(lineno, col_offset, parent) - - def postinit(self, args, body): - self.args = args - self.body = body - - def pytype(self): - if 'method' in self.type: - return '%s.instancemethod' % BUILTINS - return '%s.function' % BUILTINS - - def display_type(self): - if 'method' in self.type: - return 'Method' - return 'Function' - - def callable(self): - return True - - def argnames(self): - """return a list of argument names""" - # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 - # args is in fact redefined later on by postinit. Can't be changed - # to None due to a strong interaction between Lambda and FunctionDef. - - if self.args.args: # maybe None with builtin functions - names = _rec_get_names(self.args.args) - else: - names = [] - if self.args.vararg: - names.append(self.args.vararg) - if self.args.kwarg: - names.append(self.args.kwarg) - return names - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 - # args is in fact redefined later on by postinit. Can't be changed - # to None due to a strong interaction between Lambda and FunctionDef. - - return self.body.infer(context) - - def scope_lookup(self, node, name, offset=0): - # pylint: disable=no-member; github.com/pycqa/astroid/issues/291 - # args is in fact redefined later on by postinit. Can't be changed - # to None due to a strong interaction between Lambda and FunctionDef. - - if node in self.args.defaults or node in self.args.kw_defaults: - frame = self.parent.frame() - # line offset to avoid that def func(f=func) resolve the default - # value to the defined function - offset = -1 - else: - # check this is not used in function decorators - frame = self - return frame._scope_lookup(node, name, offset) - - def bool_value(self): - return True - - -class FunctionDef(node_classes.Statement, Lambda): - if six.PY3: - _astroid_fields = ('decorators', 'args', 'returns', 'body') - returns = None - else: - _astroid_fields = ('decorators', 'args', 'body') - decorators = None - special_attributes = objectmodel.FunctionModel() - is_function = True - # attributes below are set by the builder module or by raw factories - _other_fields = ('name', 'doc') - _other_other_fields = ('locals', '_type') - _type = None - - def __init__(self, name=None, doc=None, lineno=None, - col_offset=None, parent=None): - self.name = name - self.doc = doc - self.instance_attrs = {} - super(FunctionDef, self).__init__(lineno, col_offset, parent) - if parent: - frame = parent.frame() - frame.set_local(name, self) - - # pylint: disable=arguments-differ; different than Lambdas - def postinit(self, args, body, decorators=None, returns=None): - self.args = args - self.body = body - self.decorators = decorators - self.returns = returns - - @decorators_mod.cachedproperty - def extra_decorators(self): - """Get the extra decorators that this function can haves - Additional decorators are considered when they are used as - assignments, as in `method = staticmethod(method)`. - The property will return all the callables that are used for - decoration. - """ - frame = self.parent.frame() - if not isinstance(frame, ClassDef): - return [] - - decorators = [] - for assign in frame.nodes_of_class(node_classes.Assign): - if (isinstance(assign.value, node_classes.Call) - and isinstance(assign.value.func, node_classes.Name)): - for assign_node in assign.targets: - if not isinstance(assign_node, node_classes.AssignName): - # Support only `name = callable(name)` - continue - - if assign_node.name != self.name: - # Interested only in the assignment nodes that - # decorates the current method. - continue - try: - meth = frame[self.name] - except KeyError: - continue - else: - # Must be a function and in the same frame as the - # original method. - if (isinstance(meth, FunctionDef) - and assign_node.frame() == frame): - decorators.append(assign.value) - return decorators - - @decorators_mod.cachedproperty - def type(self): - """Get the function type for this node. - - Possible values are: method, function, staticmethod, classmethod. - """ - builtin_descriptors = {'classmethod', 'staticmethod'} - - for decorator in self.extra_decorators: - if decorator.func.name in builtin_descriptors: - return decorator.func.name - - frame = self.parent.frame() - type_name = 'function' - if isinstance(frame, ClassDef): - if self.name == '__new__': - return 'classmethod' - elif sys.version_info >= (3, 6) and self.name == '__init_subclass__': - return 'classmethod' - else: - type_name = 'method' - - if not self.decorators: - return type_name - - for node in self.decorators.nodes: - if isinstance(node, node_classes.Name): - if node.name in builtin_descriptors: - return node.name - - if isinstance(node, node_classes.Call): - # Handle the following case: - # @some_decorator(arg1, arg2) - # def func(...) - # - try: - current = next(node.func.infer()) - except exceptions.InferenceError: - continue - _type = _infer_decorator_callchain(current) - if _type is not None: - return _type - - try: - for inferred in node.infer(): - # Check to see if this returns a static or a class method. - _type = _infer_decorator_callchain(inferred) - if _type is not None: - return _type - - if not isinstance(inferred, ClassDef): - continue - for ancestor in inferred.ancestors(): - if not isinstance(ancestor, ClassDef): - continue - if ancestor.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - except exceptions.InferenceError: - pass - return type_name - - @decorators_mod.cachedproperty - def fromlineno(self): - # lineno is the line number of the first decorator, we want the def - # statement lineno - lineno = self.lineno - if self.decorators is not None: - lineno += sum(node.tolineno - node.lineno + 1 - for node in self.decorators.nodes) - - return lineno - - @decorators_mod.cachedproperty - def blockstart_tolineno(self): - return self.args.tolineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "def" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - """ - if name in self.instance_attrs: - return self.instance_attrs[name] - if name in self.special_attributes: - return [self.special_attributes.lookup(name)] - raise exceptions.AttributeInferenceError(target=self, attribute=name) - - def igetattr(self, name, context=None): - """Inferred getattr, which returns an iterator of inferred statements.""" - try: - return bases._infer_stmts(self.getattr(name, context), - context, frame=self) - except exceptions.AttributeInferenceError as error: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) - - def is_method(self): - """return true if the function node should be considered as a method""" - # check we are defined in a ClassDef, because this is usually expected - # (e.g. pylint...) when is_method() return True - return self.type != 'function' and isinstance(self.parent.frame(), ClassDef) - - @decorators_mod.cached - def decoratornames(self): - """return a list of decorator qualified names""" - result = set() - decoratornodes = [] - if self.decorators is not None: - decoratornodes += self.decorators.nodes - decoratornodes += self.extra_decorators - for decnode in decoratornodes: - try: - for infnode in decnode.infer(): - result.add(infnode.qname()) - except exceptions.InferenceError: - continue - return result - - def is_bound(self): - """return true if the function is bound to an Instance or a class""" - return self.type == 'classmethod' - - def is_abstract(self, pass_is_abstract=True): - """Returns True if the method is abstract. - - A method is considered abstract if - - the only statement is 'raise NotImplementedError', or - - the only statement is 'pass' and pass_is_abstract is True, or - - the method is annotated with abc.astractproperty/abc.abstractmethod - """ - if self.decorators: - for node in self.decorators.nodes: - try: - inferred = next(node.infer()) - except exceptions.InferenceError: - continue - if inferred and inferred.qname() in ('abc.abstractproperty', - 'abc.abstractmethod'): - return True - - for child_node in self.body: - if isinstance(child_node, node_classes.Raise): - if child_node.raises_not_implemented(): - return True - return pass_is_abstract and isinstance(child_node, node_classes.Pass) - # empty function is the same as function with a single "pass" statement - if pass_is_abstract: - return True - - def is_generator(self): - """return true if this is a generator function""" - yield_nodes = (node_classes.Yield, node_classes.YieldFrom) - return next(self.nodes_of_class(yield_nodes, - skip_klass=(FunctionDef, Lambda)), False) - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - if self.is_generator(): - result = bases.Generator(self) - yield result - return - # This is really a gigantic hack to work around metaclass generators - # that return transient class-generating functions. Pylint's AST structure - # cannot handle a base class object that is only used for calling __new__, - # but does not contribute to the inheritance structure itself. We inject - # a fake class into the hierarchy here for several well-known metaclass - # generators, and filter it out later. - if (self.name == 'with_metaclass' and - len(self.args.args) == 1 and - self.args.vararg is not None): - metaclass = next(caller.args[0].infer(context)) - if isinstance(metaclass, ClassDef): - c = ClassDef('temporary_class', None) - c.hide = True - c.parent = self - class_bases = [next(b.infer(context)) for b in caller.args[1:]] - c.bases = [base for base in class_bases if base != util.Uninferable] - c._metaclass = metaclass - yield c - return - returns = self.nodes_of_class(node_classes.Return, skip_klass=FunctionDef) - for returnnode in returns: - if returnnode.value is None: - yield node_classes.Const(None) - else: - try: - for inferred in returnnode.value.infer(context): - yield inferred - except exceptions.InferenceError: - yield util.Uninferable - - def bool_value(self): - return True - - -class AsyncFunctionDef(FunctionDef): - """Asynchronous function created with the `async` keyword.""" - - -def _rec_get_names(args, names=None): - """return a list of all argument names""" - if names is None: - names = [] - for arg in args: - if isinstance(arg, node_classes.Tuple): - _rec_get_names(arg.elts, names) - else: - names.append(arg.name) - return names - - -def _is_metaclass(klass, seen=None): - """ Return if the given class can be - used as a metaclass. - """ - if klass.name == 'type': - return True - if seen is None: - seen = set() - for base in klass.bases: - try: - for baseobj in base.infer(): - baseobj_name = baseobj.qname() - if baseobj_name in seen: - continue - else: - seen.add(baseobj_name) - if isinstance(baseobj, bases.Instance): - # not abstract - return False - if baseobj is util.Uninferable: - continue - if baseobj is klass: - continue - if not isinstance(baseobj, ClassDef): - continue - if baseobj._type == 'metaclass': - return True - if _is_metaclass(baseobj, seen): - return True - except exceptions.InferenceError: - continue - return False - - -def _class_type(klass, ancestors=None): - """return a ClassDef node type to differ metaclass and exception - from 'regular' classes - """ - # XXX we have to store ancestors in case we have a ancestor loop - if klass._type is not None: - return klass._type - if _is_metaclass(klass): - klass._type = 'metaclass' - elif klass.name.endswith('Exception'): - klass._type = 'exception' - else: - if ancestors is None: - ancestors = set() - klass_name = klass.qname() - if klass_name in ancestors: - # XXX we are in loop ancestors, and have found no type - klass._type = 'class' - return 'class' - ancestors.add(klass_name) - for base in klass.ancestors(recurs=False): - name = _class_type(base, ancestors) - if name != 'class': - if name == 'metaclass' and not _is_metaclass(klass): - # don't propagate it if the current class - # can't be a metaclass - continue - klass._type = base.type - break - if klass._type is None: - klass._type = 'class' - return klass._type - - -def get_wrapping_class(node): - """Obtain the class that *wraps* this node - - We consider that a class wraps a node if the class - is a parent for the said node. - """ - - klass = node.frame() - while klass is not None and not isinstance(klass, ClassDef): - if klass.parent is None: - klass = None - else: - klass = klass.parent.frame() - return klass - - - -class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, - node_classes.Statement): - - # some of the attributes below are set by the builder module or - # by a raw factories - - # a dictionary of class instances attributes - _astroid_fields = ('decorators', 'bases', 'body') # name - - decorators = None - special_attributes = objectmodel.ClassModel() - - _type = None - _metaclass_hack = False - hide = False - type = property(_class_type, - doc="class'type, possible values are 'class' | " - "'metaclass' | 'exception'") - _other_fields = ('name', 'doc') - _other_other_fields = ('locals', '_newstyle') - _newstyle = None - - def __init__(self, name=None, doc=None, lineno=None, - col_offset=None, parent=None): - self.instance_attrs = {} - self.locals = {} - self.keywords = [] - self.bases = [] - self.body = [] - self.name = name - self.doc = doc - super(ClassDef, self).__init__(lineno, col_offset, parent) - if parent is not None: - parent.frame().set_local(name, self) - - # pylint: disable=redefined-outer-name - def postinit(self, bases, body, decorators, newstyle=None, metaclass=None, keywords=None): - self.keywords = keywords - self.bases = bases - self.body = body - self.decorators = decorators - if newstyle is not None: - self._newstyle = newstyle - if metaclass is not None: - self._metaclass = metaclass - - def _newstyle_impl(self, context=None): - if context is None: - context = contextmod.InferenceContext() - if self._newstyle is not None: - return self._newstyle - for base in self.ancestors(recurs=False, context=context): - if base._newstyle_impl(context): - self._newstyle = True - break - klass = self.declared_metaclass() - # could be any callable, we'd need to infer the result of klass(name, - # bases, dict). punt if it's not a class node. - if klass is not None and isinstance(klass, ClassDef): - self._newstyle = klass._newstyle_impl(context) - if self._newstyle is None: - self._newstyle = False - return self._newstyle - - _newstyle = None - newstyle = property(_newstyle_impl, - doc="boolean indicating if it's a new style class" - "or not") - - @decorators_mod.cachedproperty - def blockstart_tolineno(self): - if self.bases: - return self.bases[-1].tolineno - - return self.fromlineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "class" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def pytype(self): - if self.newstyle: - return '%s.type' % BUILTINS - return '%s.classobj' % BUILTINS - - def display_type(self): - return 'Class' - - def callable(self): - return True - - def is_subtype_of(self, type_name, context=None): - if self.qname() == type_name: - return True - for anc in self.ancestors(context=context): - if anc.qname() == type_name: - return True - - def _infer_type_call(self, caller, context): - name_node = next(caller.args[0].infer(context)) - if (isinstance(name_node, node_classes.Const) and - isinstance(name_node.value, six.string_types)): - name = name_node.value - else: - return util.Uninferable - - result = ClassDef(name, None) - - # Get the bases of the class. - class_bases = next(caller.args[1].infer(context)) - if isinstance(class_bases, (node_classes.Tuple, node_classes.List)): - result.bases = class_bases.itered() - else: - # There is currently no AST node that can represent an 'unknown' - # node (Uninferable is not an AST node), therefore we simply return Uninferable here - # although we know at least the name of the class. - return util.Uninferable - - # Get the members of the class - try: - members = next(caller.args[2].infer(context)) - except exceptions.InferenceError: - members = None - - if members and isinstance(members, node_classes.Dict): - for attr, value in members.items: - if (isinstance(attr, node_classes.Const) and - isinstance(attr.value, six.string_types)): - result.locals[attr.value] = [value] - - result.parent = caller.parent - return result - - def infer_call_result(self, caller, context=None): - """infer what a class is returning when called""" - if (self.is_subtype_of('%s.type' % (BUILTINS,), context) - and len(caller.args) == 3): - result = self._infer_type_call(caller, context) - yield result - else: - yield bases.Instance(self) - - def scope_lookup(self, node, name, offset=0): - # If the name looks like a builtin name, just try to look - # into the upper scope of this class. We might have a - # decorator that it's poorly named after a builtin object - # inside this class. - lookup_upper_frame = ( - isinstance(node.parent, node_classes.Decorators) and - name in MANAGER.astroid_cache[six.moves.builtins.__name__] - ) - if any(node == base or base.parent_of(node) - for base in self.bases) or lookup_upper_frame: - # Handle the case where we have either a name - # in the bases of a class, which exists before - # the actual definition or the case where we have - # a Getattr node, with that name. - # - # name = ... - # class A(name): - # def name(self): ... - # - # import name - # class A(name.Name): - # def name(self): ... - - frame = self.parent.frame() - # line offset to avoid that class A(A) resolve the ancestor to - # the defined class - offset = -1 - else: - frame = self - return frame._scope_lookup(node, name, offset) - - @property - def basenames(self): - """Get the list of parent class names, as they appear in the class definition.""" - return [bnode.as_string() for bnode in self.bases] - - def ancestors(self, recurs=True, context=None): - """return an iterator on the node base classes in a prefixed - depth first order - - :param recurs: - boolean indicating if it should recurse or return direct - ancestors only - """ - # FIXME: should be possible to choose the resolution order - # FIXME: inference make infinite loops possible here - yielded = set([self]) - if context is None: - context = contextmod.InferenceContext() - if six.PY3: - if not self.bases and self.qname() != 'builtins.object': - yield builtin_lookup("object")[1][0] - return - - for stmt in self.bases: - with context.restore_path(): - try: - for baseobj in stmt.infer(context): - if not isinstance(baseobj, ClassDef): - if isinstance(baseobj, bases.Instance): - baseobj = baseobj._proxied - else: - continue - if not baseobj.hide: - if baseobj in yielded: - continue - yielded.add(baseobj) - yield baseobj - if recurs: - for grandpa in baseobj.ancestors(recurs=True, - context=context): - if grandpa is self: - # This class is the ancestor of itself. - break - if grandpa in yielded: - continue - yielded.add(grandpa) - yield grandpa - except exceptions.InferenceError: - continue - - def local_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their locals - """ - if self.newstyle and all(n.newstyle for n in self.ancestors(context)): - # Look up in the mro if we can. This will result in the - # attribute being looked up just as Python does it. - try: - ancestors = self.mro(context)[1:] - except exceptions.MroError: - # Fallback to use ancestors, we can't determine - # a sane MRO. - ancestors = self.ancestors(context=context) - else: - ancestors = self.ancestors(context=context) - for astroid in ancestors: - if name in astroid: - yield astroid - - def instance_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their instance attribute dictionary - """ - for astroid in self.ancestors(context=context): - if name in astroid.instance_attrs: - yield astroid - - def has_base(self, node): - return node in self.bases - - def local_attr(self, name, context=None): - """return the list of assign node associated to name in this class - locals or in its parents - - :raises `AttributeInferenceError`: - if no attribute with this name has been find in this class or - its parent classes - """ - result = [] - if name in self.locals: - result = self.locals[name] - else: - class_node = next(self.local_attr_ancestors(name, context), ()) - if class_node: - result = class_node.locals[name] - result = [n for n in result if not isinstance(n, node_classes.DelAttr)] - if result: - return result - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - - def instance_attr(self, name, context=None): - """return the astroid nodes associated to name in this class instance - attributes dictionary and in its parents - - :raises `AttributeInferenceError`: - if no attribute with this name has been find in this class or - its parent classes - """ - # Return a copy, so we don't modify self.instance_attrs, - # which could lead to infinite loop. - values = list(self.instance_attrs.get(name, [])) - # get all values from parents - for class_node in self.instance_attr_ancestors(name, context): - values += class_node.instance_attrs[name] - values = [n for n in values if not isinstance(n, node_classes.DelAttr)] - if values: - return values - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - - def instantiate_class(self): - """return Instance of ClassDef node, else return self""" - return bases.Instance(self) - - def instanciate_class(self): - warnings.warn('%s.instanciate_class() is deprecated and slated for ' - 'removal in astroid 2.0, use %s.instantiate_class() ' - 'instead.' % (type(self).__name__, type(self).__name__), - PendingDeprecationWarning, stacklevel=2) - return self.instantiate_class() - - def getattr(self, name, context=None, class_context=True): - """Get an attribute from this class, using Python's attribute semantic - - This method doesn't look in the instance_attrs dictionary - since it's done by an Instance proxy at inference time. It - may return a Uninferable object if the attribute has not been actually - found but a __getattr__ or __getattribute__ method is defined. - If *class_context* is given, then it's considered that the - attribute is accessed from a class context, - e.g. ClassDef.attribute, otherwise it might have been accessed - from an instance as well. If *class_context* is used in that - case, then a lookup in the implicit metaclass and the explicit - metaclass will be done. - - """ - values = self.locals.get(name, []) - if name in self.special_attributes and class_context and not values: - result = [self.special_attributes.lookup(name)] - if name == '__bases__': - # Need special treatment, since they are mutable - # and we need to return all the values. - result += values - return result - - # don't modify the list in self.locals! - values = list(values) - for classnode in self.ancestors(recurs=True, context=context): - values += classnode.locals.get(name, []) - - if class_context: - values += self._metaclass_lookup_attribute(name, context) - - if not values: - raise exceptions.AttributeInferenceError(target=self, attribute=name, - context=context) - return values - - def _metaclass_lookup_attribute(self, name, context): - """Search the given name in the implicit and the explicit metaclass.""" - attrs = set() - implicit_meta = self.implicit_metaclass() - metaclass = self.metaclass() - for cls in {implicit_meta, metaclass}: - if cls and cls != self and isinstance(cls, ClassDef): - cls_attributes = self._get_attribute_from_metaclass( - cls, name, context) - attrs.update(set(cls_attributes)) - return attrs - - def _get_attribute_from_metaclass(self, cls, name, context): - try: - attrs = cls.getattr(name, context=context, - class_context=True) - except exceptions.AttributeInferenceError: - return - - for attr in bases._infer_stmts(attrs, context, frame=cls): - if not isinstance(attr, FunctionDef): - yield attr - continue - - if bases._is_property(attr): - # TODO(cpopa): don't use a private API. - for inferred in attr.infer_call_result(self, context): - yield inferred - continue - if attr.type == 'classmethod': - # If the method is a classmethod, then it will - # be bound to the metaclass, not to the class - # from where the attribute is retrieved. - # get_wrapping_class could return None, so just - # default to the current class. - frame = get_wrapping_class(attr) or self - yield bases.BoundMethod(attr, frame) - elif attr.type == 'staticmethod': - yield attr - else: - yield bases.BoundMethod(attr, self) - - def igetattr(self, name, context=None, class_context=True): - """inferred getattr, need special treatment in class to handle - descriptors - """ - # set lookup name since this is necessary to infer on import nodes for - # instance - context = contextmod.copy_context(context) - context.lookupname = name - try: - attrs = self.getattr(name, context, class_context=class_context) - for inferred in bases._infer_stmts(attrs, context, frame=self): - # yield Uninferable object instead of descriptors when necessary - if (not isinstance(inferred, node_classes.Const) - and isinstance(inferred, bases.Instance)): - try: - inferred._proxied.getattr('__get__', context) - except exceptions.AttributeInferenceError: - yield inferred - else: - yield util.Uninferable - else: - yield function_to_method(inferred, self) - except exceptions.AttributeInferenceError as error: - if not name.startswith('__') and self.has_dynamic_getattr(context): - # class handle some dynamic attributes, return a Uninferable object - yield util.Uninferable - else: - util.reraise(exceptions.InferenceError( - error.message, target=self, attribute=name, context=context)) - - def has_dynamic_getattr(self, context=None): - """ - Check if the current instance has a custom __getattr__ - or a custom __getattribute__. - - If any such method is found and it is not from - builtins, nor from an extension module, then the function - will return True. - """ - def _valid_getattr(node): - root = node.root() - return root.name != BUILTINS and getattr(root, 'pure_python', None) - - try: - return _valid_getattr(self.getattr('__getattr__', context)[0]) - except exceptions.AttributeInferenceError: - #if self.newstyle: XXX cause an infinite recursion error - try: - getattribute = self.getattr('__getattribute__', context)[0] - return _valid_getattr(getattribute) - except exceptions.AttributeInferenceError: - pass - return False - - def getitem(self, index, context=None): - """Return the inference of a subscript. - - This is basically looking up the method in the metaclass and calling it. - """ - try: - methods = dunder_lookup.lookup(self, '__getitem__') - except exceptions.AttributeInferenceError as exc: - util.reraise( - exceptions.AstroidTypeError( - node=self, error=exc, - context=context - ) - ) - - method = methods[0] - - # Create a new callcontext for providing index as an argument. - if context: - new_context = context.clone() - else: - new_context = contextmod.InferenceContext() - - new_context.callcontext = contextmod.CallContext(args=[index]) - new_context.boundnode = self - - return next(method.infer_call_result(self, new_context)) - - def methods(self): - """return an iterator on all methods defined in the class and - its ancestors - """ - done = {} - for astroid in itertools.chain(iter((self,)), self.ancestors()): - for meth in astroid.mymethods(): - if meth.name in done: - continue - done[meth.name] = None - yield meth - - def mymethods(self): - """return an iterator on all methods defined in the class""" - for member in self.values(): - if isinstance(member, FunctionDef): - yield member - - def implicit_metaclass(self): - """Get the implicit metaclass of the current class - - For newstyle classes, this will return an instance of builtins.type. - For oldstyle classes, it will simply return None, since there's - no implicit metaclass there. - """ - - if self.newstyle: - return builtin_lookup('type')[1][0] - - _metaclass = None - def declared_metaclass(self): - """Return the explicit declared metaclass for the current class. - - An explicit declared metaclass is defined - either by passing the ``metaclass`` keyword argument - in the class definition line (Python 3) or (Python 2) by - having a ``__metaclass__`` class attribute, or if there are - no explicit bases but there is a global ``__metaclass__`` variable. - """ - for base in self.bases: - try: - for baseobj in base.infer(): - if isinstance(baseobj, ClassDef) and baseobj.hide: - self._metaclass = baseobj._metaclass - self._metaclass_hack = True - break - except exceptions.InferenceError: - pass - - if self._metaclass: - # Expects this from Py3k TreeRebuilder - try: - return next(node for node in self._metaclass.infer() - if node is not util.Uninferable) - except (exceptions.InferenceError, StopIteration): - return None - if six.PY3: - return None - - if '__metaclass__' in self.locals: - assignment = self.locals['__metaclass__'][-1] - elif self.bases: - return None - elif '__metaclass__' in self.root().locals: - assignments = [ass for ass in self.root().locals['__metaclass__'] - if ass.lineno < self.lineno] - if not assignments: - return None - assignment = assignments[-1] - else: - return None - - try: - inferred = next(assignment.infer()) - except exceptions.InferenceError: - return - if inferred is util.Uninferable: # don't expose this - return None - return inferred - - def _find_metaclass(self, seen=None): - if seen is None: - seen = set() - seen.add(self) - - klass = self.declared_metaclass() - if klass is None: - for parent in self.ancestors(): - if parent not in seen: - klass = parent._find_metaclass(seen) - if klass is not None: - break - return klass - - def metaclass(self): - """Return the metaclass of this class. - - If this class does not define explicitly a metaclass, - then the first defined metaclass in ancestors will be used - instead. - """ - return self._find_metaclass() - - def has_metaclass_hack(self): - return self._metaclass_hack - - def _islots(self): - """ Return an iterator with the inferred slots. """ - if '__slots__' not in self.locals: - return - for slots in self.igetattr('__slots__'): - # check if __slots__ is a valid type - for meth in ITER_METHODS: - try: - slots.getattr(meth) - break - except exceptions.AttributeInferenceError: - continue - else: - continue - - if isinstance(slots, node_classes.Const): - # a string. Ignore the following checks, - # but yield the node, only if it has a value - if slots.value: - yield slots - continue - if not hasattr(slots, 'itered'): - # we can't obtain the values, maybe a .deque? - continue - - if isinstance(slots, node_classes.Dict): - values = [item[0] for item in slots.items] - else: - values = slots.itered() - if values is util.Uninferable: - continue - if not values: - # Stop the iteration, because the class - # has an empty list of slots. - raise StopIteration(values) - - for elt in values: - try: - for inferred in elt.infer(): - if inferred is util.Uninferable: - continue - if (not isinstance(inferred, node_classes.Const) or - not isinstance(inferred.value, - six.string_types)): - continue - if not inferred.value: - continue - yield inferred - except exceptions.InferenceError: - continue - - def _slots(self): - if not self.newstyle: - raise NotImplementedError( - "The concept of slots is undefined for old-style classes.") - - slots = self._islots() - try: - first = next(slots) - except StopIteration as exc: - # The class doesn't have a __slots__ definition or empty slots. - if exc.args and exc.args[0] not in ('', None): - return exc.args[0] - return None - return [first] + list(slots) - - # Cached, because inferring them all the time is expensive - @decorators_mod.cached - def slots(self): - """Get all the slots for this node. - - If the class doesn't define any slot, through `__slots__` - variable, then this function will return a None. - Also, it will return None in the case the slots weren't inferred. - Otherwise, it will return a list of slot names. - """ - def grouped_slots(): - # Not interested in object, since it can't have slots. - for cls in self.mro()[:-1]: - try: - cls_slots = cls._slots() - except NotImplementedError: - continue - if cls_slots is not None: - for slot in cls_slots: - yield slot - else: - yield None - - if not self.newstyle: - raise NotImplementedError( - "The concept of slots is undefined for old-style classes.") - - slots = list(grouped_slots()) - if not all(slot is not None for slot in slots): - return None - - return sorted(slots, key=lambda item: item.value) - - def _inferred_bases(self, context=None): - # TODO(cpopa): really similar with .ancestors, - # but the difference is when one base is inferred, - # only the first object is wanted. That's because - # we aren't interested in superclasses, as in the following - # example: - # - # class SomeSuperClass(object): pass - # class SomeClass(SomeSuperClass): pass - # class Test(SomeClass): pass - # - # Inferring SomeClass from the Test's bases will give - # us both SomeClass and SomeSuperClass, but we are interested - # only in SomeClass. - - if context is None: - context = contextmod.InferenceContext() - if six.PY3: - if not self.bases and self.qname() != 'builtins.object': - yield builtin_lookup("object")[1][0] - return - - for stmt in self.bases: - try: - baseobj = next(stmt.infer(context=context)) - except exceptions.InferenceError: - continue - if isinstance(baseobj, bases.Instance): - baseobj = baseobj._proxied - if not isinstance(baseobj, ClassDef): - continue - if not baseobj.hide: - yield baseobj - else: - for base in baseobj.bases: - yield base - - def _compute_mro(self, context=None): - inferred_bases = list(self._inferred_bases(context=context)) - bases_mro = [] - for base in inferred_bases: - if base is self: - continue - - try: - mro = base._compute_mro(context=context) - bases_mro.append(mro) - except NotImplementedError: - # Some classes have in their ancestors both newstyle and - # old style classes. For these we can't retrieve the .mro, - # although in Python it's possible, since the class we are - # currently working is in fact new style. - # So, we fallback to ancestors here. - ancestors = list(base.ancestors(context=context)) - bases_mro.append(ancestors) - - unmerged_mro = ([[self]] + bases_mro + [inferred_bases]) - _verify_duplicates_mro(unmerged_mro, self, context) - return _c3_merge(unmerged_mro, self, context) - - def mro(self, context=None): - """Get the method resolution order, using C3 linearization. - - It returns the list of ancestors sorted by the mro. - This will raise `NotImplementedError` for old-style classes, since - they don't have the concept of MRO. - """ - - if not self.newstyle: - raise NotImplementedError( - "Could not obtain mro for old-style classes.") - - return self._compute_mro(context=context) - - def bool_value(self): - return True - - -# Backwards-compatibility aliases -Class = util.proxy_alias('Class', ClassDef) -Function = util.proxy_alias('Function', FunctionDef) -GenExpr = util.proxy_alias('GenExpr', GeneratorExp) diff --git a/pymode/libs/astroid/test_utils.py b/pymode/libs/astroid/test_utils.py deleted file mode 100644 index e7e64b17..00000000 --- a/pymode/libs/astroid/test_utils.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2013-2014 Google, Inc. -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -"""Utility functions for test code that uses astroid ASTs as input.""" -import contextlib -import functools -import sys -import warnings - -from astroid import nodes -from astroid import util - - -def require_version(minver=None, maxver=None): - """ Compare version of python interpreter to the given one. Skip the test - if older. - """ - def parse(string, default=None): - string = string or default - try: - return tuple(int(v) for v in string.split('.')) - except ValueError: - util.reraise(ValueError('%s is not a correct version : should be X.Y[.Z].' % string)) - - def check_require_version(f): - current = sys.version_info[:3] - if parse(minver, "0") < current <= parse(maxver, "4"): - return f - - str_version = '.'.join(str(v) for v in sys.version_info) - @functools.wraps(f) - def new_f(self, *args, **kwargs): - if minver is not None: - self.skipTest('Needs Python > %s. Current version is %s.' - % (minver, str_version)) - elif maxver is not None: - self.skipTest('Needs Python <= %s. Current version is %s.' - % (maxver, str_version)) - return new_f - - - return check_require_version - -def get_name_node(start_from, name, index=0): - return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index] - - -@contextlib.contextmanager -def enable_warning(warning): - warnings.simplefilter('always', warning) - try: - yield - finally: - # Reset it to default value, so it will take - # into account the values from the -W flag. - warnings.simplefilter('default', warning) - \ No newline at end of file diff --git a/pymode/libs/astroid/transforms.py b/pymode/libs/astroid/transforms.py deleted file mode 100644 index 852b9854..00000000 --- a/pymode/libs/astroid/transforms.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - - -import collections -import warnings - - -class TransformVisitor(object): - """A visitor for handling transforms. - - The standard approach of using it is to call - :meth:`~visit` with an *astroid* module and the class - will take care of the rest, walking the tree and running the - transforms for each encountered node. - """ - - def __init__(self): - self.transforms = collections.defaultdict(list) - - def _transform(self, node): - """Call matching transforms for the given node if any and return the - transformed node. - """ - cls = node.__class__ - if cls not in self.transforms: - # no transform registered for this class of node - return node - - transforms = self.transforms[cls] - orig_node = node # copy the reference - for transform_func, predicate in transforms: - if predicate is None or predicate(node): - ret = transform_func(node) - # if the transformation function returns something, it's - # expected to be a replacement for the node - if ret is not None: - if node is not orig_node: - # node has already be modified by some previous - # transformation, warn about it - warnings.warn('node %s substituted multiple times' % node) - node = ret - return node - - def _visit(self, node): - if hasattr(node, '_astroid_fields'): - for field in node._astroid_fields: - value = getattr(node, field) - visited = self._visit_generic(value) - setattr(node, field, visited) - return self._transform(node) - - def _visit_generic(self, node): - if isinstance(node, list): - return [self._visit_generic(child) for child in node] - elif isinstance(node, tuple): - return tuple(self._visit_generic(child) for child in node) - - return self._visit(node) - - def register_transform(self, node_class, transform, predicate=None): - """Register `transform(node)` function to be applied on the given - astroid's `node_class` if `predicate` is None or returns true - when called with the node as argument. - - The transform function may return a value which is then used to - substitute the original node in the tree. - """ - self.transforms[node_class].append((transform, predicate)) - - def unregister_transform(self, node_class, transform, predicate=None): - """Unregister the given transform.""" - self.transforms[node_class].remove((transform, predicate)) - - def visit(self, module): - """Walk the given astroid *tree* and transform each encountered node - - Only the nodes which have transforms registered will actually - be replaced or changed. - """ - module.body = [self._visit(child) for child in module.body] - return self._transform(module) diff --git a/pymode/libs/astroid/util.py b/pymode/libs/astroid/util.py deleted file mode 100644 index 6d2d0177..00000000 --- a/pymode/libs/astroid/util.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2015-2016 Cara Vinson -# Copyright (c) 2015-2016 Claudiu Popa - -# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html -# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER - -import sys -import warnings - -import importlib -import lazy_object_proxy -import six - - -def lazy_descriptor(obj): - class DescriptorProxy(lazy_object_proxy.Proxy): - def __get__(self, instance, owner=None): - return self.__class__.__get__(self, instance) - return DescriptorProxy(obj) - - -def lazy_import(module_name): - return lazy_object_proxy.Proxy( - lambda: importlib.import_module('.' + module_name, 'astroid')) - - -def reraise(exception): - '''Reraises an exception with the traceback from the current exception - block.''' - six.reraise(type(exception), exception, sys.exc_info()[2]) - - -@object.__new__ -class Uninferable(object): - """Special inference object, which is returned when inference fails.""" - def __repr__(self): - return 'Uninferable' - __str__ = __repr__ - - def __getattribute__(self, name): - if name == 'next': - raise AttributeError('next method should not be called') - if name.startswith('__') and name.endswith('__'): - return object.__getattribute__(self, name) - if name == 'accept': - return object.__getattribute__(self, name) - return self - - def __call__(self, *args, **kwargs): - return self - - def __bool__(self): - return False - - __nonzero__ = __bool__ - - def accept(self, visitor): - func = getattr(visitor, "visit_uninferable") - return func(self) - -class BadOperationMessage(object): - """Object which describes a TypeError occurred somewhere in the inference chain - - This is not an exception, but a container object which holds the types and - the error which occurred. - """ - - -class BadUnaryOperationMessage(BadOperationMessage): - """Object which describes operational failures on UnaryOps.""" - - def __init__(self, operand, op, error): - self.operand = operand - self.op = op - self.error = error - - def __str__(self): - operand_type = self.operand.name - msg = "bad operand type for unary {}: {}" - return msg.format(self.op, operand_type) - - -class BadBinaryOperationMessage(BadOperationMessage): - """Object which describes type errors for BinOps.""" - - def __init__(self, left_type, op, right_type): - self.left_type = left_type - self.right_type = right_type - self.op = op - - def __str__(self): - msg = "unsupported operand type(s) for {}: {!r} and {!r}" - return msg.format(self.op, self.left_type.name, self.right_type.name) - - -def _instancecheck(cls, other): - wrapped = cls.__wrapped__ - other_cls = other.__class__ - is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped) - warnings.warn("%r is deprecated and slated for removal in astroid " - "2.0, use %r instead" % (cls.__class__.__name__, - wrapped.__name__), - PendingDeprecationWarning, stacklevel=2) - return is_instance_of - - -def proxy_alias(alias_name, node_type): - """Get a Proxy from the given name to the given node type.""" - proxy = type(alias_name, (lazy_object_proxy.Proxy,), - {'__class__': object.__dict__['__class__'], - '__instancecheck__': _instancecheck}) - return proxy(lambda: node_type) - - -# Backwards-compatibility aliases -YES = Uninferable diff --git a/pymode/libs/backports.functools_lru_cache-1.3-py3.5-nspkg.pth b/pymode/libs/backports.functools_lru_cache-1.3-py3.5-nspkg.pth deleted file mode 100644 index 0b1f79dd..00000000 --- a/pymode/libs/backports.functools_lru_cache-1.3-py3.5-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('backports',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and sys.modules.setdefault('backports', types.ModuleType('backports'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/pymode/libs/backports/configparser/__init__.py b/pymode/libs/backports/configparser/__init__.py deleted file mode 100644 index 06d7a085..00000000 --- a/pymode/libs/backports/configparser/__init__.py +++ /dev/null @@ -1,1390 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Configuration file parser. - -A configuration file consists of sections, lead by a "[section]" header, -and followed by "name: value" entries, with continuations and such in -the style of RFC 822. - -Intrinsic defaults can be specified by passing them into the -ConfigParser constructor as a dictionary. - -class: - -ConfigParser -- responsible for parsing a list of - configuration files, and managing the parsed database. - - methods: - - __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, - delimiters=('=', ':'), comment_prefixes=('#', ';'), - inline_comment_prefixes=None, strict=True, - empty_lines_in_values=True, default_section='DEFAULT', - interpolation=, converters=): - Create the parser. When `defaults' is given, it is initialized into the - dictionary or intrinsic defaults. The keys must be strings, the values - must be appropriate for %()s string interpolation. - - When `dict_type' is given, it will be used to create the dictionary - objects for the list of sections, for the options within a section, and - for the default values. - - When `delimiters' is given, it will be used as the set of substrings - that divide keys from values. - - When `comment_prefixes' is given, it will be used as the set of - substrings that prefix comments in empty lines. Comments can be - indented. - - When `inline_comment_prefixes' is given, it will be used as the set of - substrings that prefix comments in non-empty lines. - - When `strict` is True, the parser won't allow for any section or option - duplicates while reading from a single source (file, string or - dictionary). Default is True. - - When `empty_lines_in_values' is False (default: True), each empty line - marks the end of an option. Otherwise, internal empty lines of - a multiline option are kept as part of the value. - - When `allow_no_value' is True (default: False), options without - values are accepted; the value presented for these is None. - - sections() - Return all the configuration section names, sans DEFAULT. - - has_section(section) - Return whether the given section exists. - - has_option(section, option) - Return whether the given option exists in the given section. - - options(section) - Return list of configuration options for the named section. - - read(filenames, encoding=None) - Read and parse the list of named configuration files, given by - name. A single filename is also allowed. Non-existing files - are ignored. Return list of successfully read files. - - read_file(f, filename=None) - Read and parse one configuration file, given as a file object. - The filename defaults to f.name; it is only used in error - messages (if f has no `name' attribute, the string `' is used). - - read_string(string) - Read configuration from a given string. - - read_dict(dictionary) - Read configuration from a dictionary. Keys are section names, - values are dictionaries with keys and values that should be present - in the section. If the used dictionary type preserves order, sections - and their keys will be added in order. Values are automatically - converted to strings. - - get(section, option, raw=False, vars=None, fallback=_UNSET) - Return a string value for the named option. All % interpolations are - expanded in the return values, based on the defaults passed into the - constructor and the DEFAULT section. Additional substitutions may be - provided using the `vars' argument, which must be a dictionary whose - contents override any pre-existing defaults. If `option' is a key in - `vars', the value from `vars' is used. - - getint(section, options, raw=False, vars=None, fallback=_UNSET) - Like get(), but convert value to an integer. - - getfloat(section, options, raw=False, vars=None, fallback=_UNSET) - Like get(), but convert value to a float. - - getboolean(section, options, raw=False, vars=None, fallback=_UNSET) - Like get(), but convert value to a boolean (currently case - insensitively defined as 0, false, no, off for False, and 1, true, - yes, on for True). Returns False or True. - - items(section=_UNSET, raw=False, vars=None) - If section is given, return a list of tuples with (name, value) for - each option in the section. Otherwise, return a list of tuples with - (section_name, section_proxy) for each section, including DEFAULTSECT. - - remove_section(section) - Remove the given file section and all its options. - - remove_option(section, option) - Remove the given option from the given section. - - set(section, option, value) - Set the given option. - - write(fp, space_around_delimiters=True) - Write the configuration state in .ini format. If - `space_around_delimiters' is True (the default), delimiters - between keys and values are surrounded by spaces. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -from collections import MutableMapping -import functools -import io -import itertools -import re -import sys -import warnings - -from backports.configparser.helpers import OrderedDict as _default_dict -from backports.configparser.helpers import ChainMap as _ChainMap -from backports.configparser.helpers import from_none, open, str, PY2 - -__all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError", - "NoOptionError", "InterpolationError", "InterpolationDepthError", - "InterpolationMissingOptionError", "InterpolationSyntaxError", - "ParsingError", "MissingSectionHeaderError", - "ConfigParser", "SafeConfigParser", "RawConfigParser", - "Interpolation", "BasicInterpolation", "ExtendedInterpolation", - "LegacyInterpolation", "SectionProxy", "ConverterMapping", - "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] - -DEFAULTSECT = "DEFAULT" - -MAX_INTERPOLATION_DEPTH = 10 - - -# exception classes -class Error(Exception): - """Base class for ConfigParser exceptions.""" - - def __init__(self, msg=''): - self.message = msg - Exception.__init__(self, msg) - - def __repr__(self): - return self.message - - __str__ = __repr__ - - -class NoSectionError(Error): - """Raised when no section matches a requested option.""" - - def __init__(self, section): - Error.__init__(self, 'No section: %r' % (section,)) - self.section = section - self.args = (section, ) - - -class DuplicateSectionError(Error): - """Raised when a section is repeated in an input source. - - Possible repetitions that raise this exception are: multiple creation - using the API or in strict parsers when a section is found more than once - in a single input file, string or dictionary. - """ - - def __init__(self, section, source=None, lineno=None): - msg = [repr(section), " already exists"] - if source is not None: - message = ["While reading from ", repr(source)] - if lineno is not None: - message.append(" [line {0:2d}]".format(lineno)) - message.append(": section ") - message.extend(msg) - msg = message - else: - msg.insert(0, "Section ") - Error.__init__(self, "".join(msg)) - self.section = section - self.source = source - self.lineno = lineno - self.args = (section, source, lineno) - - -class DuplicateOptionError(Error): - """Raised by strict parsers when an option is repeated in an input source. - - Current implementation raises this exception only when an option is found - more than once in a single file, string or dictionary. - """ - - def __init__(self, section, option, source=None, lineno=None): - msg = [repr(option), " in section ", repr(section), - " already exists"] - if source is not None: - message = ["While reading from ", repr(source)] - if lineno is not None: - message.append(" [line {0:2d}]".format(lineno)) - message.append(": option ") - message.extend(msg) - msg = message - else: - msg.insert(0, "Option ") - Error.__init__(self, "".join(msg)) - self.section = section - self.option = option - self.source = source - self.lineno = lineno - self.args = (section, option, source, lineno) - - -class NoOptionError(Error): - """A requested option was not found.""" - - def __init__(self, option, section): - Error.__init__(self, "No option %r in section: %r" % - (option, section)) - self.option = option - self.section = section - self.args = (option, section) - - -class InterpolationError(Error): - """Base class for interpolation-related exceptions.""" - - def __init__(self, option, section, msg): - Error.__init__(self, msg) - self.option = option - self.section = section - self.args = (option, section, msg) - - -class InterpolationMissingOptionError(InterpolationError): - """A string substitution required a setting which was not available.""" - - def __init__(self, option, section, rawval, reference): - msg = ("Bad value substitution: option {0!r} in section {1!r} contains " - "an interpolation key {2!r} which is not a valid option name. " - "Raw value: {3!r}".format(option, section, reference, rawval)) - InterpolationError.__init__(self, option, section, msg) - self.reference = reference - self.args = (option, section, rawval, reference) - - -class InterpolationSyntaxError(InterpolationError): - """Raised when the source text contains invalid syntax. - - Current implementation raises this exception when the source text into - which substitutions are made does not conform to the required syntax. - """ - - -class InterpolationDepthError(InterpolationError): - """Raised when substitutions are nested too deeply.""" - - def __init__(self, option, section, rawval): - msg = ("Recursion limit exceeded in value substitution: option {0!r} " - "in section {1!r} contains an interpolation key which " - "cannot be substituted in {2} steps. Raw value: {3!r}" - "".format(option, section, MAX_INTERPOLATION_DEPTH, - rawval)) - InterpolationError.__init__(self, option, section, msg) - self.args = (option, section, rawval) - - -class ParsingError(Error): - """Raised when a configuration file does not follow legal syntax.""" - - def __init__(self, source=None, filename=None): - # Exactly one of `source'/`filename' arguments has to be given. - # `filename' kept for compatibility. - if filename and source: - raise ValueError("Cannot specify both `filename' and `source'. " - "Use `source'.") - elif not filename and not source: - raise ValueError("Required argument `source' not given.") - elif filename: - source = filename - Error.__init__(self, 'Source contains parsing errors: %r' % source) - self.source = source - self.errors = [] - self.args = (source, ) - - @property - def filename(self): - """Deprecated, use `source'.""" - warnings.warn( - "The 'filename' attribute will be removed in future versions. " - "Use 'source' instead.", - DeprecationWarning, stacklevel=2 - ) - return self.source - - @filename.setter - def filename(self, value): - """Deprecated, user `source'.""" - warnings.warn( - "The 'filename' attribute will be removed in future versions. " - "Use 'source' instead.", - DeprecationWarning, stacklevel=2 - ) - self.source = value - - def append(self, lineno, line): - self.errors.append((lineno, line)) - self.message += '\n\t[line %2d]: %s' % (lineno, line) - - -class MissingSectionHeaderError(ParsingError): - """Raised when a key-value pair is found before any section header.""" - - def __init__(self, filename, lineno, line): - Error.__init__( - self, - 'File contains no section headers.\nfile: %r, line: %d\n%r' % - (filename, lineno, line)) - self.source = filename - self.lineno = lineno - self.line = line - self.args = (filename, lineno, line) - - -# Used in parser getters to indicate the default behaviour when a specific -# option is not found it to raise an exception. Created to enable `None' as -# a valid fallback value. -_UNSET = object() - - -class Interpolation(object): - """Dummy interpolation that passes the value through with no changes.""" - - def before_get(self, parser, section, option, value, defaults): - return value - - def before_set(self, parser, section, option, value): - return value - - def before_read(self, parser, section, option, value): - return value - - def before_write(self, parser, section, option, value): - return value - - -class BasicInterpolation(Interpolation): - """Interpolation as implemented in the classic ConfigParser. - - The option values can contain format strings which refer to other values in - the same section, or values in the special default section. - - For example: - - something: %(dir)s/whatever - - would resolve the "%(dir)s" to the value of dir. All reference - expansions are done late, on demand. If a user needs to use a bare % in - a configuration file, she can escape it by writing %%. Other % usage - is considered a user error and raises `InterpolationSyntaxError'.""" - - _KEYCRE = re.compile(r"%\(([^)]+)\)s") - - def before_get(self, parser, section, option, value, defaults): - L = [] - self._interpolate_some(parser, option, L, value, section, defaults, 1) - return ''.join(L) - - def before_set(self, parser, section, option, value): - tmp_value = value.replace('%%', '') # escaped percent signs - tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax - if '%' in tmp_value: - raise ValueError("invalid interpolation syntax in %r at " - "position %d" % (value, tmp_value.find('%'))) - return value - - def _interpolate_some(self, parser, option, accum, rest, section, map, - depth): - rawval = parser.get(section, option, raw=True, fallback=rest) - if depth > MAX_INTERPOLATION_DEPTH: - raise InterpolationDepthError(option, section, rawval) - while rest: - p = rest.find("%") - if p < 0: - accum.append(rest) - return - if p > 0: - accum.append(rest[:p]) - rest = rest[p:] - # p is no longer used - c = rest[1:2] - if c == "%": - accum.append("%") - rest = rest[2:] - elif c == "(": - m = self._KEYCRE.match(rest) - if m is None: - raise InterpolationSyntaxError(option, section, - "bad interpolation variable reference %r" % rest) - var = parser.optionxform(m.group(1)) - rest = rest[m.end():] - try: - v = map[var] - except KeyError: - raise from_none(InterpolationMissingOptionError( - option, section, rawval, var)) - if "%" in v: - self._interpolate_some(parser, option, accum, v, - section, map, depth + 1) - else: - accum.append(v) - else: - raise InterpolationSyntaxError( - option, section, - "'%%' must be followed by '%%' or '(', " - "found: %r" % (rest,)) - - -class ExtendedInterpolation(Interpolation): - """Advanced variant of interpolation, supports the syntax used by - `zc.buildout'. Enables interpolation between sections.""" - - _KEYCRE = re.compile(r"\$\{([^}]+)\}") - - def before_get(self, parser, section, option, value, defaults): - L = [] - self._interpolate_some(parser, option, L, value, section, defaults, 1) - return ''.join(L) - - def before_set(self, parser, section, option, value): - tmp_value = value.replace('$$', '') # escaped dollar signs - tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax - if '$' in tmp_value: - raise ValueError("invalid interpolation syntax in %r at " - "position %d" % (value, tmp_value.find('$'))) - return value - - def _interpolate_some(self, parser, option, accum, rest, section, map, - depth): - rawval = parser.get(section, option, raw=True, fallback=rest) - if depth > MAX_INTERPOLATION_DEPTH: - raise InterpolationDepthError(option, section, rawval) - while rest: - p = rest.find("$") - if p < 0: - accum.append(rest) - return - if p > 0: - accum.append(rest[:p]) - rest = rest[p:] - # p is no longer used - c = rest[1:2] - if c == "$": - accum.append("$") - rest = rest[2:] - elif c == "{": - m = self._KEYCRE.match(rest) - if m is None: - raise InterpolationSyntaxError(option, section, - "bad interpolation variable reference %r" % rest) - path = m.group(1).split(':') - rest = rest[m.end():] - sect = section - opt = option - try: - if len(path) == 1: - opt = parser.optionxform(path[0]) - v = map[opt] - elif len(path) == 2: - sect = path[0] - opt = parser.optionxform(path[1]) - v = parser.get(sect, opt, raw=True) - else: - raise InterpolationSyntaxError( - option, section, - "More than one ':' found: %r" % (rest,)) - except (KeyError, NoSectionError, NoOptionError): - raise from_none(InterpolationMissingOptionError( - option, section, rawval, ":".join(path))) - if "$" in v: - self._interpolate_some(parser, opt, accum, v, sect, - dict(parser.items(sect, raw=True)), - depth + 1) - else: - accum.append(v) - else: - raise InterpolationSyntaxError( - option, section, - "'$' must be followed by '$' or '{', " - "found: %r" % (rest,)) - - -class LegacyInterpolation(Interpolation): - """Deprecated interpolation used in old versions of ConfigParser. - Use BasicInterpolation or ExtendedInterpolation instead.""" - - _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") - - def before_get(self, parser, section, option, value, vars): - rawval = value - depth = MAX_INTERPOLATION_DEPTH - while depth: # Loop through this until it's done - depth -= 1 - if value and "%(" in value: - replace = functools.partial(self._interpolation_replace, - parser=parser) - value = self._KEYCRE.sub(replace, value) - try: - value = value % vars - except KeyError as e: - raise from_none(InterpolationMissingOptionError( - option, section, rawval, e.args[0])) - else: - break - if value and "%(" in value: - raise InterpolationDepthError(option, section, rawval) - return value - - def before_set(self, parser, section, option, value): - return value - - @staticmethod - def _interpolation_replace(match, parser): - s = match.group(1) - if s is None: - return match.group() - else: - return "%%(%s)s" % parser.optionxform(s) - - -class RawConfigParser(MutableMapping): - """ConfigParser that does not do interpolation.""" - - # Regular expressions for parsing section headers and options - _SECT_TMPL = r""" - \[ # [ - (?P
[^]]+) # very permissive! - \] # ] - """ - _OPT_TMPL = r""" - (?P