diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 260501bb..00000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[bumpversion] -commit = True -current_version = 0.9.0 -files = plugin/pymode.vim -tag = True -tag_name = {new_version} - -[bumpversion:file:doc/pymode.txt] -search = Version: {current_version} -replace = Version: {new_version} - diff --git a/.gitignore b/.gitignore index f5674a78..9daeafb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1 @@ -*.py[cod] -*.sw? -*~ -.DS_Store -.bundle -.ropeproject -.vim-flavor -.vimrc -Gemfile.lock -VimFlavor.lock -_ -build -tags -test.py -todo.txt -vendor -vim.py +test 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 deleted file mode 100644 index cc3de277..00000000 --- a/AUTHORS +++ /dev/null @@ -1,64 +0,0 @@ -Maintainers: - -* Kirill Klenov -* Bryce Guinta (https://github.com/brycepg) - - -Contributors: - -* Alvin Francis (http://github.com/alvinfrancis); -* Andriy Kohut (https://github.com/andriykohut) -* Anler Hp (http://github.com/ikame); -* Anton Parkhomenko (http://github.com/chuwy); -* Ashley Hewson (http://github.com/ashleyh); -* Benjamin Ruston (http://github.com/bruston); -* Boris Filippov (http://github.com/frenzykryger); -* Brad Mease (http://github.com/bmease) -* Brendan Maguire (https://github.com/brendanmaguire) -* 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); -* Florent Xicluna (http://github.com/florentx); -* Fredrik Henrysson (http://github.com/fhenrysson); -* Igor Guerrero (http://github.com/igorgue); -* Jacob Niehus (https://github.com/wilywampa) -* Jason Harvey (http://github.com/alienth) -* Jay Rainey (https://github.com/jawrainey) -* Jonathan McCall (http://github.com/Jonnymcc); -* Kevin Deldycke (http://github.com/kdeldycke); -* Kurtis Rader (https://github.com/krader1961); -* Lawrence Akka (https://github.com/lawrenceakka); -* Lowe Thiderman (http://github.com/thiderman); -* Martin Brochhaus (http://github.com/mbrochh); -* Matt Dodge (https://github.com/mattdodge); -* Matthew Moses (http://github.com/mlmoses); -* Maxim (https://github.com/mpyatishev); -* Mel Boyce (http://github.com/syngin); -* Mohammed (http://github.com/mbadran); -* Naoya Inada (http://github.com/naoina); -* Nate Zhang (https://github.com/natezhang93); -* Paweł Korzeniewski (https://github.com/korzeniewskipl); -* Pedro Algarvio (http://github.com/s0undt3ch); -* Phillip Cloud (http://github.com/cpcloud); -* Piet Delport (http://github.com/pjdelport); -* Robert David Grant (http://github.com/bgrant); -* Robin Schneider (https://github.com/ypid); -* Ronald Andreu Kaiser (http://github.com/cathoderay);; -* Samir Benmendil (https://github.com/Ram-Z); -* Sorin Ionescu (sorin-ionescu); -* Steve Losh (http://github.com/sjl); -* Tommy Allen (https://github.com/tweekmonster); -* Tony Narlock (https://github.com/tony); -* 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); diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 00000000..36fa0201 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,4 @@ +This plugin was forked from +[`python-mode`](https://github.com/python-mode/python-mode). + +See the `python-mode` AUTHORS file for the original authors. 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 7706091b..00000000 --- a/Makefile +++ /dev/null @@ -1,90 +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 - -.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.md b/README.md new file mode 100644 index 00000000..b828a32d --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# python3-syntax + +Python 3 syntax highlighting for Vim. + +This syntax file is the same as that of +[`python-mode`](https://github.com/python-mode/python-mode), but properly +handles the Python 3 builtins and the `yield from` statement. diff --git a/README.rst b/README.rst deleted file mode 100644 index 867029d5..00000000 --- a/README.rst +++ /dev/null @@ -1,367 +0,0 @@ -|logo| Python-mode, Python in VIM -################################# - -.. image:: https://travis-ci.org/klen/python-mode.png?branch=develop - :target: https://travis-ci.org/klen/python-mode - ------ - -*The project needs contributors* - -** Python-mode Slack Channel is here: https://python-mode.herokuapp.com/ ** - ------ - -| -| Src: https://github.com/klen/python-mode -| Homepage: https://klen.github.io/python-mode/ -| Docs: https://github.com/klen/python-mode/blob/develop/doc/pymode.txt -| - -Python-mode is a vim plugin that helps you to create python code very quickly -by utilizing libraries including -`pylint`_, `rope`_, pydoc_, `pyflakes`_, `pep8`_, `autopep8`_, -`pep257`_ and `mccabe`_ -for features like static analysis, refactoring, folding, completion, -documentation, and more. - -The plugin contains all you need to develop python applications in Vim. - -There is no need to install `pylint`_, `rope`_ -or any other `Python Libraries`_ on your system. - -- Support Python version 2.6+ and 3.2+ -- Syntax highlighting -- Virtualenv support -- Run python code (``r``) -- Add/remove breakpoints (``b``) -- Improved Python indentation -- Python folding -- Python motions and operators (``]]``, ``3[[``, ``]]M``, ``vaC``, ``viM``, - ``daC``, ``ciM``, ...) -- Code checking (pylint_, pyflakes_, pylama_, ...) that can be run - simultaneously (``:PymodeLint``) -- Autofix PEP8 errors (``:PymodeLintAuto``) -- Search in python documentation (``K``) -- Code refactoring (rope_) -- Strong code completion (rope_) -- Go to definition (``g`` for `:RopeGotoDefinition`) -- And more, more ... - -See (very old) screencast here: http://www.youtube.com/watch?v=67OZNp9Z0CQ -(sorry for quality, this is my first screencast) Another old presentation here: -http://www.youtube.com/watch?v=YhqsjUUHj6g - -**To read python-mode documentation in Vim, see** ``:help pymode`` - - -.. contents:: - - -Requirements -============ - -- VIM >= 7.3 (mostly features needed `+python` or `+python3` support) - (also ``--with-features=big`` if you want ``g:pymode_lint_signs``) - - -How to install -============== - -Using pathogen (recommended) ----------------------------- -:: - - % cd ~/.vim - % mkdir -p bundle && cd bundle - % git clone https://github.com/klen/python-mode.git - -- Enable `pathogen `_ - in your ``~/.vimrc``: :: - - " Pathogen load - filetype off - - call pathogen#infect() - call pathogen#helptags() - - filetype plugin indent on - syntax on - - -Manually --------- -:: - - % git clone https://github.com/klen/python-mode.git - % cd python-mode - % cp -R * ~/.vim - -Then rebuild **helptags** in vim:: - - :helptags ~/.vim/doc/ - - -.. note:: **filetype-plugin** (``:help filetype-plugin-on``) and - **filetype-indent** (``:help filetype-indent-on``) - must be enabled to use python-mode. - - -Debian packages ---------------- -|Repository URL: https://klen.github.io/python-mode/deb/ - -Install with commands: - -:: - - add-apt-repository https://klen.github.io/python-mode/deb main - apt-get update - apt-get install vim-python-mode - -If you are getting the message: "The following signatures couldn't be verified because the public key is not available": :: - - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B5DF65307000E266 - -`vim-python-mode` using `vim-addons`, so after installation just enable -`python-mode` with command: :: - - vim-addons install python-mode - - -Troubleshooting -=============== - -If your python-mode doesn't work: - -1. Load Vim with only python-mode enabled (use `debug.vim` from pymode): :: - - vim -u /debug.vim - -And try to repeat your case. If no error occurs, seems like problem isn't in the -plugin. - -2. Type `:PymodeTroubleshooting` - -And fix any warnings or copy the output and send it to me. (For example, by -creating a `new github issue `_ -if one does not already exist for the problem). - - -Customization -============= - -You can override the default key bindings by redefining them in your `.vimrc`, for example: :: - - " Override go-to.definition key shortcut to Ctrl-] - let g:pymode_rope_goto_definition_bind = "" - - " Override run current python file key shortcut to Ctrl-Shift-e - let g:pymode_run_bind = "" - - " Override view python doc key shortcut to Ctrl-Shift-d - let g:pymode_doc_bind = "" - - -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/klen/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/klen/python-mode - -Please make a pull request to `development` branch and add yourself to -`AUTHORS`. - -Source Links -=================== -- `doc/pymode.txt - `__ - -- ``:help pymode`` -- `plugin/pymode.vim - `__ - -- python-mode VIM plugin -- `syntax/python.vim - `__ - -- python-mode ``python.vim`` VIM syntax -- `syntax/pyrex.vim - `__ - -- ``pyrex.vim`` VIM syntax (pyrex, Cython) -- `t/ - `__ - -- ``*.vim`` more python-mode VIM configuration -- `pymode/ - `__ - -- ``*.py`` -- python-mode Python module -- `pymode/libs/ - `__ - -- ``*.py`` -- `Python Libraries <#python-libraries>`__ - - -Python Libraries ------------------- -Vendored Python modules are located -mostly in -`pymode/libs/ `__. - - -====== -rope -====== -| PyPI: https://pypi.python.org/pypi/rope -| Src: https://github.com/python-rope/rope -| Docs: https://github.com/python-rope/rope/blob/master/docs/overview.rst -| Docs: https://github.com/python-rope/rope/blob/master/docs/library.rst - -======================== -ropemode -======================== -| PyPI: https://pypi.python.org/pypi/ropemode -| Src: https://github.com/python-rope/ropemode - -========= -ropevim -========= -| PyPI: https://pypi.python.org/pypi/ropevim -| Src: https://github.com/python-rope/ropevim -| Docs: https://github.com/python-rope/ropevim/blob/master/doc/ropevim.txt - -======= -pylama -======= -| PyPI: https://pypi.python.org/pypi/pylama -| Src: https://github.com/klen/pylama - -======== -pylint -======== -| PyPI: https://pypi.python.org/pypi/pylint -| Src: https://bitbucket.org/logilab/pylint -| Homepage: http://www.pylint.org/ -| Docs: http://docs.pylint.org/ -| Docs: http://docs.pylint.org/message-control.html -| Docs: http://docs.pylint.org/faq.html#message-control -| ErrCodes: http://pylint-messages.wikidot.com/all-codes -| ErrCodes: http://pylint-messages.wikidot.com/all-messages - -========== -pyflakes -========== -| PyPI: https://pypi.python.org/pypi/pyflakes -| Src: https://github.com/pyflakes/pyflakes -| ErrCodes: https://flake8.readthedocs.org/en/latest/warnings.html - -====== -pep8 -====== -| PyPI: https://pypi.python.org/pypi/pep8 -| Src: http://github.com/jcrocholl/pep8 -| PEP 8: http://www.python.org/dev/peps/pep-0008/ -| PEP 8: http://legacy.python.org/dev/peps/pep-0008/ -| Docs: https://pep8.readthedocs.org/en/latest/ -| Docs: https://pep8.readthedocs.org/en/latest/intro.html#configuration -| ErrCodes: https://pep8.readthedocs.org/en/latest/intro.html#error-codes - -========= -autopep8 -========= -| PyPI: https://pypi.python.org/pypi/autopep8 -| Src: https://github.com/hhatto/autopep8 - -======= -pep257 -======= -| PyPI: https://pypi.python.org/pypi/pep257 -| Src: http://github.com/GreenSteam/pep257 -| Docs: https://pep257.readthedocs.org/en/latest/ -| PEP 257: http://www.python.org/dev/peps/pep-0257/ -| ErrCodes: https://pep257.readthedocs.org/en/latest/error_codes.html - -======= -mccabe -======= -| PyPI: https://pypi.python.org/pypi/mccabe -| Src: https://github.com/flintwork/mccabe -| Docs: https://en.wikipedia.org/wiki/Cyclomatic_complexity - - -Vim Libraries ---------------- -Vendored Vim modules are located mostly in ``t/``. - -====================== -Python syntax for vim -====================== -| Src: http://www.hlabs.spb.ru/vim/python.vim - - -===================== -PEP8 VIM indentation -===================== -| Src: http://github.com/hynek/vim-python-pep8-indent - - - -Copyright -========= - -Copyright © 2013-2015 Kirill Klenov (klen_) - -License -======= - -Licensed under a `GNU lesser general public license`_. - -If you like this plugin, I would very appreciated if you kindly send me a postcard :) -My address is here: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill Klenov". -**Thanks for support!** - -.. _GNU lesser general public license: http://www.gnu.org/copyleft/lesser.html -.. _klen: https://klen.github.com/ -.. _pydoc: http://docs.python.org/library/pydoc.html -.. _pathogen: https://github.com/tpope/vim-pathogen -.. _rope_: https://pypi.python.org/pypi/rope -.. _pylama_: https://github.com/klen/pylama -.. _pylint_: https://bitbucket.org/logilab/pylint -.. _pyflakes_: https://pypi.python.org/pypi/pyflakes -.. _autopep8_: https://github.com/hhatto/autopep8 -.. _pep257_: http://github.com/GreenSteam/pep257 -.. _mccabe_: https://github.com/flintwork/mccabe -.. _pythonvim: http://www.hlabs.spb.ru/vim/python.vim -.. _pep8_: http://github.com/jcrocholl/pep8 -.. _pep8indent: http://github.com/hynek/vim-python-pep8-indent -.. |logo| image:: https://raw.github.com/klen/python-mode/develop/logo.png diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 63a3a361..00000000 --- a/Rakefile +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env rake - -task :ci => [:dump, :test] - -task :dump do - sh 'vim --version' -end - -task :test do - sh 'bundle exec vim-flavor test' -end diff --git a/after/ftplugin/pyrex.vim b/after/ftplugin/pyrex.vim deleted file mode 100644 index 61d43637..00000000 --- a/after/ftplugin/pyrex.vim +++ /dev/null @@ -1 +0,0 @@ -runtime after/ftplugin/python.vim diff --git a/after/ftplugin/python.vim b/after/ftplugin/python.vim deleted file mode 100644 index 0dec7542..00000000 --- a/after/ftplugin/python.vim +++ /dev/null @@ -1,58 +0,0 @@ -if !g:pymode - finish -endif - -if g:pymode_motion - - if !&magic - if g:pymode_warning - call pymode#error("Pymode motion requires `&magic` option. Enable them or disable g:pymode_motion") - endif - finish - endif - - nnoremap ]] :call pymode#motion#move('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) - -endif - -if g:pymode_rope && g:pymode_rope_completion - - setlocal omnifunc=pymode#rope#completions - - if g:pymode_rope_completion_bind != "" - exe "inoremap " . g:pymode_rope_completion_bind . " =pymode#rope#complete(0)" - if tolower(g:pymode_rope_completion_bind) == '' - exe "inoremap =pymode#rope#complete(0)" - endif - end - -end diff --git a/after/indent/pyrex.vim b/after/indent/pyrex.vim deleted file mode 100644 index ab2e54dd..00000000 --- a/after/indent/pyrex.vim +++ /dev/null @@ -1 +0,0 @@ -runtime after/indent/python.vim diff --git a/after/indent/python.vim b/after/indent/python.vim deleted file mode 100644 index 98399b40..00000000 --- a/after/indent/python.vim +++ /dev/null @@ -1,13 +0,0 @@ -if !g:pymode || !g:pymode_indent - finish -endif - -setlocal nolisp -setlocal tabstop=4 -setlocal softtabstop=4 -setlocal shiftwidth=4 -setlocal shiftround -setlocal expandtab -setlocal autoindent -setlocal indentexpr=pymode#indent#get_indent(v:lnum) -setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except diff --git a/autoload/pymode.vim b/autoload/pymode.vim deleted file mode 100644 index 723af9b5..00000000 --- a/autoload/pymode.vim +++ /dev/null @@ -1,133 +0,0 @@ -" Pymode core functions - -" DESC: Check variable and set default value if it not exists -fun! pymode#default(name, default) "{{{ - if !exists(a:name) - let {a:name} = a:default - return 0 - endif - return 1 -endfunction "}}} - -" DESC: Import python libs -fun! pymode#init(plugin_root, paths) "{{{ - - PymodePython import sys, vim - PymodePython sys.path.insert(0, vim.eval('a:plugin_root')) - PymodePython sys.path = vim.eval('a:paths') + sys.path - -endfunction "}}} - -" DESC: Show wide message -fun! pymode#wide_message(msg) "{{{ - let x=&ruler | let y=&showcmd - set noruler noshowcmd - redraw - echohl Debug | echo strpart("[Pymode] " . a:msg, 0, &columns-1) | echohl none - let &ruler=x | let &showcmd=y -endfunction "}}} - -" DESC: Show error -fun! pymode#error(msg) "{{{ - execute "normal \" - echohl ErrorMsg - echomsg "[Pymode]: error: " . a:msg - echohl None -endfunction "}}} - -" DESC: Open quickfix window -fun! pymode#quickfix_open(onlyRecognized, maxHeight, minHeight, jumpError) "{{{ - let numErrors = len(filter(getqflist(), 'v:val.valid')) - let numOthers = len(getqflist()) - numErrors - if numErrors > 0 || (!a:onlyRecognized && numOthers > 0) - let num = winnr() - botright copen - exe max([min([line("$"), a:maxHeight]), a:minHeight]) . "wincmd _" - if a:jumpError - cc - elseif num != winnr() - wincmd p - endif - else - cclose - endif - redraw - if numOthers > 0 - call pymode#wide_message(printf('Quickfix: %d(+%d)', numErrors, numOthers)) - elseif numErrors > 0 - call pymode#wide_message(printf('Quickfix: %d', numErrors)) - endif -endfunction "}}} - -" DESC: Open temp buffer. -fun! pymode#tempbuffer_open(name) "{{{ - pclose - exe "botright 8new " . a:name - setlocal buftype=nofile bufhidden=delete noswapfile nowrap previewwindow - redraw -endfunction "}}} - -" DESC: Remove unused whitespaces -fun! pymode#trim_whitespaces() "{{{ - if g:pymode_trim_whitespaces - let cursor_pos = getpos('.') - silent! %s/\s\+$// - call setpos('.', cursor_pos) - endif -endfunction "}}} - - -fun! pymode#save() "{{{ - if &modifiable && &modified - try - noautocmd write - catch /E212/ - call pymode#error("File modified and I can't save it. Please save it manually.") - return 0 - endtry - endif - return expand('%') != '' -endfunction "}}} - -fun! pymode#reload_buf_by_nr(nr) "{{{ - let cur = bufnr("") - try - exe "buffer " . a:nr - catch /E86/ - return - endtry - exe "e!" - exe "buffer " . cur -endfunction "}}} - -fun! pymode#buffer_pre_write() "{{{ - let b:pymode_modified = &modified -endfunction "}}} - -fun! pymode#buffer_post_write() "{{{ - if g:pymode_rope - if g:pymode_rope_regenerate_on_write && b:pymode_modified - call pymode#debug('regenerate') - call pymode#rope#regenerate() - endif - endif - if g:pymode_lint - if g:pymode_lint_unmodified || (g:pymode_lint_on_write && b:pymode_modified) - call pymode#debug('check code') - call pymode#lint#check() - endif - endif -endfunction "}}} - -fun! pymode#debug(msg) "{{{ - if g:pymode_debug - let g:pymode_debug += 1 - echom string(g:pymode_debug) . ': ' . string(a:msg) - endif -endfunction "}}} - -fun! pymode#quit() "{{{ - augroup pymode - au! * - augroup END -endfunction "}}} diff --git a/autoload/pymode/breakpoint.vim b/autoload/pymode/breakpoint.vim deleted file mode 100644 index c3189aad..00000000 --- a/autoload/pymode/breakpoint.vim +++ /dev/null @@ -1,51 +0,0 @@ -fun! pymode#breakpoint#init() "{{{ - - if !g:pymode_breakpoint - 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 - - PymodePython << EOF - -from imp import find_module - -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 - -EOF - -endfunction "}}} - -fun! pymode#breakpoint#operate(lnum) "{{{ - let line = getline(a:lnum) - if strridx(line, g:pymode_breakpoint_cmd) != -1 - normal dd - else - let plnum = prevnonblank(a:lnum) - if &expandtab - let indents = repeat(' ', indent(plnum)) - else - let indents = repeat("\t", plnum / &shiftwidth) - endif - - call append(line('.')-1, indents.g:pymode_breakpoint_cmd) - normal k - endif - - " Save file without any events - call pymode#save() - -endfunction "}}} diff --git a/autoload/pymode/doc.vim b/autoload/pymode/doc.vim deleted file mode 100644 index b89eb0e7..00000000 --- a/autoload/pymode/doc.vim +++ /dev/null @@ -1,37 +0,0 @@ -" Python-mode search by documentation -" -PymodePython import pymode - -fun! pymode#doc#find() "{{{ - " Extract the 'word' at the cursor, expanding leftwards across identifiers - " and the . operator, and rightwards across the identifier only. - " - " For example: - " import xml.dom.minidom - " ^ ! - " - " With the cursor at ^ this returns 'xml'; at ! it returns 'xml.dom'. - let l:line = getline(".") - let l:pre = l:line[:col(".") - 1] - let l:suf = l:line[col("."):] - let word = matchstr(pre, "[A-Za-z0-9_.]*$") . matchstr(suf, "^[A-Za-z0-9_]*") - call pymode#doc#show(word) -endfunction "}}} - -fun! pymode#doc#show(word) "{{{ - if a:word == '' - call pymode#error("No name/symbol under cursor!") - return 0 - endif - - call pymode#tempbuffer_open('__doc__') - PymodePython pymode.get_documentation() - setlocal nomodifiable - setlocal nomodified - setlocal filetype=rst - if g:pymode_doc_vertical - wincmd L - endif - wincmd p - -endfunction "}}} diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim deleted file mode 100644 index 399748e3..00000000 --- a/autoload/pymode/folding.vim +++ /dev/null @@ -1,274 +0,0 @@ -" Python-mode folding functions - -" Notice that folding is based on single line so complex regular expressions -" that take previous line into consideration are not fit for the job. - -" Regex definitions for correct folding -let s:def_regex = g:pymode_folding_regex -let s:blank_regex = '^\s*$' -" 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*[uU]\=\%("""\|''''''\)' -let s:doc_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*[uU]\=\("""\|''''''\).\+\1\s*$' -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 - let fs = nextnonblank(fs + 1) - endwhile - if getline(fs) =~ s:doc_end_regex && getline(fs) =~ s:doc_begin_regex - let fs = nextnonblank(fs + 1) - endif - let line = getline(fs) - - let has_numbers = &number || &relativenumber - let nucolwidth = &fdc + has_numbers * &numberwidth - let windowwidth = winwidth(0) - nucolwidth - 6 - let foldedlinecount = v:foldend - v:foldstart - - " expand tabs into spaces - let onetab = strpart(' ', 0, &tabstop) - let line = substitute(line, '\t', onetab, 'g') - - let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount)) - let line = substitute(line, '[uU]\=\%("""\|''''''\)', '', '') - let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + 1 - return line . ' ' . repeat(s:symbol, fillcharcount) . ' ' . foldedlinecount -endfunction "}}} - -fun! pymode#folding#expr(lnum) "{{{ - - let line = getline(a:lnum) - let indent = indent(a:lnum) - let prev_line = getline(a:lnum - 1) - let next_line = getline(a:lnum + 1) - - " Decorators {{{ - if line =~ s:decorator_regex - return ">".(indent / &shiftwidth + 1) - endif "}}} - - " Definition {{{ - if line =~ s:def_regex - " 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 - " Check if last decorator is before the last def - let decorated = 0 - let lnum = a:lnum - 1 - while lnum > 0 - if getline(lnum) =~ s:def_regex - break - elseif getline(lnum) =~ s:decorator_regex - let decorated = 1 - break - endif - let lnum -= 1 - endwhile - if decorated - return '=' - else - return ">".(indent / &shiftwidth + 1) - endif - endif "}}} - - " Docstrings {{{ - - " TODO: A while loop now counts the number of open and closed folding in - " order to determine if it is a closing or opening folding. - " It is working but looks like it is an overkill. - - " Notice that an effect of this is that other docstring matches will not - " be one liners. - if line =~ s:doc_line_regex - return "=" - endif - - if line =~ s:doc_begin_regex - " echom 'just entering' - if s:Is_opening_folding(a:lnum) - " echom 'entering at line ' . a:lnum - return ">".(indent / &shiftwidth + 1) - endif - endif - if line =~ s:doc_end_regex - if !s:Is_opening_folding(a:lnum) - " echom 'leaving at line ' . a:lnum - return "<".(indent / &shiftwidth + 1) - endif - 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 - endif - finally - call setpos('.', curpos) - endtry - endif " }}} - - " Blank Line {{{ - 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 '=' - endif - endif " }}} - - return '=' - -endfunction "}}} - -fun! s:BlockStart(lnum) "{{{ - " 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] - if last_def - let last_def_indent = indent(last_def) - call cursor(last_def, 0) - let next_stmt_at_def_indent = searchpos('\v^\s{'.last_def_indent.'}[^[:space:]#]', 'nW')[0] - else - let next_stmt_at_def_indent = -1 - endif - - " Now find the class/def one shiftwidth lower than the start of the - " aforementioned indent block. - if next_stmt_at_def_indent && next_stmt_at_def_indent < a:lnum - let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0]) - else - let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0]) - endif - return searchpos('\v^\s{,'.max_indent.'}(def |class )\w', 'bcnW')[0] -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! s:Is_opening_folding(lnum) "{{{ - " Helper function to see if docstring is opening or closing - - " Cache the result so the loop runs only once per change - if get(b:, 'fold_changenr', -1) == changenr() - return b:fold_cache[a:lnum] "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 - - " 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 - " 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 - 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 - let has_open_docstring = 0 - let number_of_folding = number_of_folding + 1 - - elseif i_line =~ s:doc_general_regex - " echom "extra docstrings on line " . i - let extra_docstrings = extra_docstrings + 1 - endif - endfor - - call add(b:fold_cache, number_of_folding % 2) - - return b:fold_cache[a:lnum] -endfunction "}}} - -" vim: fdm=marker:fdl=0 diff --git a/autoload/pymode/indent.vim b/autoload/pymode/indent.vim deleted file mode 100644 index efd41f29..00000000 --- a/autoload/pymode/indent.vim +++ /dev/null @@ -1,186 +0,0 @@ -" PEP8 compatible Python indent file -" Language: Python -" Maintainer: Hynek Schlawack -" Prev Maintainer: Eric Mc Sween (address invalid) -" Original Author: David Bustos (address invalid) -" Last Change: 2012-06-21 -" License: Public Domain - - -function! pymode#indent#get_indent(lnum) - - " First line has indent 0 - if a:lnum == 1 - return 0 - endif - - " If we can find an open parenthesis/bracket/brace, line up with it. - call cursor(a:lnum, 1) - let parlnum = s:SearchParensPair() - if parlnum > 0 - let parcol = col('.') - let closing_paren = match(getline(a:lnum), '^\s*[])}]') != -1 - if match(getline(parlnum), '[([{]\s*$', parcol - 1) != -1 - if closing_paren - return indent(parlnum) - else - return indent(parlnum) + &shiftwidth - endif - else - return parcol - endif - endif - - " Examine this line - let thisline = getline(a:lnum) - let thisindent = indent(a:lnum) - - " If the line starts with 'elif' or 'else', line up with 'if' or 'elif' - if thisline =~ '^\s*\(elif\|else\)\>' - let bslnum = s:BlockStarter(a:lnum, '^\s*\(if\|elif\)\>') - if bslnum > 0 - return indent(bslnum) - else - return -1 - endif - endif - - " If the line starts with 'except' or 'finally', line up with 'try' - " or 'except' - if thisline =~ '^\s*\(except\|finally\)\>' - let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\)\>') - if bslnum > 0 - return indent(bslnum) - else - return -1 - endif - endif - - " Examine previous line - let plnum = a:lnum - 1 - let pline = getline(plnum) - let sslnum = s:StatementStart(plnum) - - " If the previous line is blank, keep the same indentation - if pline =~ '^\s*$' - return -1 - endif - - " If this line is explicitly joined, find the first indentation that is a - " multiple of four and will distinguish itself from next logical line. - if pline =~ '\\$' - let maybe_indent = indent(sslnum) + &sw - let control_structure = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*' - if match(getline(sslnum), control_structure) != -1 - " add extra indent to avoid E125 - return maybe_indent + &sw - else - " control structure not found - return maybe_indent - endif - endif - - " If the previous line ended with a colon and is not a comment, indent - " relative to statement start. - if pline =~ '^[^#]*:\s*\(#.*\)\?$' - return indent(sslnum) + &sw - endif - - " If the previous line was a stop-execution statement or a pass - if getline(sslnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>' - " See if the user has already dedented - if indent(a:lnum) > indent(sslnum) - &sw - " If not, recommend one dedent - return indent(sslnum) - &sw - endif - " Otherwise, trust the user - return -1 - endif - - " In all other cases, line up with the start of the previous statement. - return indent(sslnum) -endfunction - - -" Find backwards the closest open parenthesis/bracket/brace. -function! s:SearchParensPair() " {{{ - let line = line('.') - let col = col('.') - - " Skip strings and comments and don't look too far - let skip = "line('.') < " . (line - 50) . " ? dummy :" . - \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? ' . - \ '"string\\|comment\\|doctest"' - - " Search for parentheses - call cursor(line, col) - let parlnum = searchpair('(', '', ')', 'bW', skip) - let parcol = col('.') - - " Search for brackets - call cursor(line, col) - let par2lnum = searchpair('\[', '', '\]', 'bW', skip) - let par2col = col('.') - - " Search for braces - call cursor(line, col) - let par3lnum = searchpair('{', '', '}', 'bW', skip) - let par3col = col('.') - - " Get the closest match - if par2lnum > parlnum || (par2lnum == parlnum && par2col > parcol) - let parlnum = par2lnum - let parcol = par2col - endif - if par3lnum > parlnum || (par3lnum == parlnum && par3col > parcol) - let parlnum = par3lnum - let parcol = par3col - endif - - " Put the cursor on the match - if parlnum > 0 - call cursor(parlnum, parcol) - endif - return parlnum -endfunction " }}} - - -" Find the start of a multi-line statement -function! s:StatementStart(lnum) " {{{ - let lnum = a:lnum - while 1 - if getline(lnum - 1) =~ '\\$' - let lnum = lnum - 1 - else - call cursor(lnum, 1) - let maybe_lnum = s:SearchParensPair() - if maybe_lnum < 1 - return lnum - else - let lnum = maybe_lnum - endif - endif - endwhile -endfunction " }}} - - -" Find the block starter that matches the current line -function! s:BlockStarter(lnum, block_start_re) " {{{ - let lnum = a:lnum - let maxindent = 10000 " whatever - while lnum > 1 - let lnum = prevnonblank(lnum - 1) - if indent(lnum) < maxindent - if getline(lnum) =~ a:block_start_re - return lnum - else - let maxindent = indent(lnum) - " It's not worth going further if we reached the top level - if maxindent == 0 - return -1 - endif - endif - endif - endwhile - return -1 -endfunction " }}} diff --git a/autoload/pymode/lint.vim b/autoload/pymode/lint.vim deleted file mode 100644 index e7dba8b5..00000000 --- a/autoload/pymode/lint.vim +++ /dev/null @@ -1,101 +0,0 @@ -PymodePython from pymode.lint import code_check - -call pymode#tools#signs#init() -call pymode#tools#loclist#init() - - -fun! pymode#lint#auto() "{{{ - if !pymode#save() - return 0 - endif - PymodePython from pymode import auto - PymodePython auto() - cclose - call g:PymodeSigns.clear() - edit - call pymode#wide_message("AutoPep8 done.") -endfunction "}}} - - -fun! pymode#lint#show_errormessage() "{{{ - let loclist = g:PymodeLocList.current() - if loclist.is_empty() - return - endif - - let l = line('.') - if l == b:pymode_error_line - return - endif - let b:pymode_error_line = l - if has_key(loclist._messages, l) - call pymode#wide_message(loclist._messages[l]) - else - echo - endif -endfunction "}}} - - -fun! pymode#lint#toggle() "{{{ - let g:pymode_lint = g:pymode_lint ? 0 : 1 - if g:pymode_lint - call pymode#wide_message("Code checking is enabled.") - else - call pymode#wide_message("Code checking is disabled.") - end -endfunction "}}} - - -fun! pymode#lint#check() "{{{ - " DESC: Run checkers on current file. - " - let loclist = g:PymodeLocList.current() - - let b:pymode_error_line = -1 - - call loclist.clear() - - call pymode#wide_message('Code checking is running ...') - - PymodePython code_check() - - if loclist.is_empty() - call pymode#wide_message('Code checking is completed. No errors found.') - endif - - call g:PymodeSigns.refresh(loclist) - - call loclist.show() - - call pymode#lint#show_errormessage() - call pymode#wide_message('Found errors and warnings: ' . len(loclist._loclist)) - -endfunction " }}} - - -fun! pymode#lint#tick_queue() "{{{ - - python import time - python print time.time() - - if mode() == 'i' - if col('.') == 1 - call feedkeys("\\", "n") - else - call feedkeys("\\", "n") - endif - else - call feedkeys("f\e", "n") - endif -endfunction "}}} - - -fun! pymode#lint#stop() "{{{ - au! pymode CursorHold -endfunction "}}} - - -fun! pymode#lint#start() "{{{ - au! pymode CursorHold call pymode#lint#tick_queue() - call pymode#lint#tick_queue() -endfunction "}}} diff --git a/autoload/pymode/motion.vim b/autoload/pymode/motion.vim deleted file mode 100644 index 67e99e6b..00000000 --- a/autoload/pymode/motion.vim +++ /dev/null @@ -1,97 +0,0 @@ -" Python-mode motion functions - - -fun! pymode#motion#move(pattern, flags, ...) "{{{ - let cnt = v:count1 - 1 - let [line, column] = searchpos(a:pattern, a:flags . 'sW') - let indent = indent(line) - while cnt && line - let [line, column] = searchpos(a:pattern, a:flags . 'W') - if indent(line) == indent - let cnt = cnt - 1 - endif - endwhile - return [line, column] -endfunction "}}} - - -fun! pymode#motion#vmove(pattern, flags) range "{{{ - call cursor(a:lastline, 0) - let end = pymode#motion#move(a:pattern, a:flags) - call cursor(a:firstline, 0) - normal! v - call cursor(end) -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) "{{{ - let cnt = v:count1 - 1 - let orig = getpos('.')[1:2] - let snum = s:BlockStart(orig[0], a:pattern) - if getline(snum) !~ a:pattern - return 0 - endif - let enum = s:BlockEnd(snum, indent(snum)) - while cnt - let lnum = search(a: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 a:inner - let snum = snum + 1 - let enum = prevnonblank(enum) - endif - - call cursor(snum, 1) - normal! v - call cursor(enum, len(getline(enum))) - endif -endfunction "}}} - - -fun! s:BlockStart(lnum, ...) "{{{ - let pattern = a:0 ? a:1 : '^\s*\(@\|class\s.*:\|def\s\)' - let lnum = a:lnum + 1 - let indent = 100 - while lnum - let lnum = prevnonblank(lnum - 1) - let test = indent(lnum) - let line = getline(lnum) - if line =~ '^\s*#' " Skip comments - continue - elseif !test " Zero-level regular line - return lnum - elseif test >= indent " Skip deeper or equal lines - continue - " Indent is strictly less at this point: check for def/class - elseif line =~ pattern && line !~ '^\s*@' - return lnum - endif - let indent = indent(lnum) - endwhile - return 0 -endfunction "}}} - - -fun! s:BlockEnd(lnum, ...) "{{{ - let indent = a:0 ? a:1 : indent(a:lnum) - let lnum = a:lnum - while lnum - let lnum = nextnonblank(lnum + 1) - if getline(lnum) =~ '^\s*#' | continue - elseif lnum && indent(lnum) <= indent - return lnum - 1 - endif - endwhile - return line('$') -endfunction "}}} -" vim: fdm=marker:fdl=0 diff --git a/autoload/pymode/rope.vim b/autoload/pymode/rope.vim deleted file mode 100644 index a82a46d9..00000000 --- a/autoload/pymode/rope.vim +++ /dev/null @@ -1,185 +0,0 @@ -" Python-mode Rope support -" -PymodePython from pymode import rope - -call pymode#tools#loclist#init() - - -fun! pymode#rope#completions(findstart, base) - PymodePython rope.completions() -endfunction - -fun! pymode#rope#complete(dot) - if pumvisible() - return "\" - end - if a:dot - PymodePython rope.complete(True) - else - PymodePython rope.complete() - end - return pumvisible() ? "\\" : "" -endfunction - -fun! pymode#rope#complete_on_dot() "{{{ - if !exists("*synstack") - return "" - end - for group in map(synstack(line('.'), col('.') - 1), 'synIDattr(v:val, "name")') - for name in ['pythonString', 'pythonComment', 'pythonNumber', 'pythonDocstring'] - if group == name - return "" - endif - endfor - endfor - if g:pymode_rope_autoimport_import_after_complete - PymodePython rope.complete_check() - endif - return pymode#rope#complete(1) -endfunction "}}} - -fun! pymode#rope#goto_definition() - PymodePython rope.goto() -endfunction - - -fun! pymode#rope#organize_imports() - if !pymode#save() - return 0 - endif - call pymode#wide_message('Organize imports ... ') - PymodePython rope.organize_imports() -endfunction - - -fun! pymode#rope#find_it() - let loclist = g:PymodeLocList.current() - let loclist._title = "Occurrences" - call pymode#wide_message('Finding Occurrences ...') - PymodePython rope.find_it() - call loclist.show() -endfunction - - -fun! pymode#rope#show_doc() - let l:output = [] - - PymodePython rope.show_doc() - - if !empty(l:output) - call pymode#tempbuffer_open('__doc____rope__') - call append(0, l:output) - setlocal nomodifiable - setlocal nomodified - setlocal filetype=rst - wincmd p - end -endfunction - - -fun! pymode#rope#regenerate() "{{{ - call pymode#wide_message('Regenerate Rope cache ... ') - PymodePython rope.regenerate() -endfunction "}}} - - -fun! pymode#rope#new(...) "{{{ - PymodePython rope.new() -endfunction "}}} - - -fun! pymode#rope#rename() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.RenameRefactoring().run() -endfunction "}}} - -fun! pymode#rope#rename_module() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.RenameRefactoring(True).run() -endfunction "}}} - -fun! pymode#rope#extract_method() range "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.ExtractMethodRefactoring().run() -endfunction "}}} - -fun! pymode#rope#extract_variable() range "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.ExtractVariableRefactoring().run() -endfunction "}}} - -fun! pymode#rope#undo() "{{{ - PymodePython rope.undo() -endfunction "}}} - -fun! pymode#rope#redo() "{{{ - PymodePython rope.redo() -endfunction "}}} - -fun! pymode#rope#inline() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.InlineRefactoring().run() -endfunction "}}} - -fun! pymode#rope#move() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.MoveRefactoring().run() -endfunction "}}} - -fun! pymode#rope#signature() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.ChangeSignatureRefactoring().run() -endfunction "}}} - -fun! pymode#rope#use_function() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.UseFunctionRefactoring().run() -endfunction "}}} - -fun! pymode#rope#module_to_package() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.ModuleToPackageRefactoring().run() -endfunction "}}} - -fun! pymode#rope#autoimport(word) "{{{ - PymodePython rope.autoimport() -endfunction "}}} - -fun! pymode#rope#generate_function() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.GenerateElementRefactoring('function').run() -endfunction "}}} - -fun! pymode#rope#generate_class() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.GenerateElementRefactoring('class').run() -endfunction "}}} - -fun! pymode#rope#generate_package() "{{{ - if !pymode#save() - return 0 - endif - PymodePython rope.GenerateElementRefactoring('package').run() -endfunction "}}} diff --git a/autoload/pymode/run.vim b/autoload/pymode/run.vim deleted file mode 100644 index 24c8729c..00000000 --- a/autoload/pymode/run.vim +++ /dev/null @@ -1,99 +0,0 @@ -" The following lines set Vim's errorformat variable, to allow the -" quickfix window to show Python tracebacks properly. It is much -" easier to use let than set, because set requires many more -" characters to be escaped. This is much easier to read and -" maintain. % escapes are still needed however before any regex meta -" characters. Hence \S (non-whitespace) becomes %\S etc. Note that -" * becomes %#, so .* (match any character) becomes %.%# Commas must -" also be escaped, with a backslash (\,). See the Vim help on -" quickfix for details. -" -" Python errors are multi-lined. They often start with 'Traceback', so -" we want to capture that (with +G) and show it in the quickfix window -" because it explains the order of error messages. -let s:efm = '%+GTraceback%.%#,' - -" The error message itself starts with a line with 'File' in it. There -" are a couple of variations, and we need to process a line beginning -" with whitespace followed by File, the filename in "", a line number, -" and optional further text. %E here indicates the start of a multi-line -" error message. The %\C at the end means that a case-sensitive search is -" required. -let s:efm .= '%E File "%f"\, line %l\,%m%\C,' -let s:efm .= '%E File "%f"\, line %l%\C,' - -" The possible continutation lines are idenitifed to Vim by %C. We deal -" with these in order of most to least specific to ensure a proper -" match. A pointer (^) identifies the column in which the error occurs -" (but will not be entirely accurate due to indention of Python code). -let s:efm .= '%C%p^,' - -" Any text, indented by more than two spaces contain useful information. -" We want this to appear in the quickfix window, hence %+. -let s:efm .= '%+C %.%#,' -let s:efm .= '%+C %.%#,' - -" The last line (%Z) does not begin with any whitespace. We use a zero -" width lookahead (\&) to check this. The line contains the error -" message itself (%m) -let s:efm .= '%Z%\S%\&%m,' - -" We can ignore any other lines (%-G) -let s:efm .= '%-G%.%#' - -PymodePython from pymode.run import run_code - - -" DESC: Run python code -fun! pymode#run#code_run(line1, line2) "{{{ - - let l:output = [] - let l:traceback = [] - call setqflist([]) - - call pymode#wide_message("Code running ...") - - try - - PymodePython run_code() - - if len(l:output) - call pymode#tempbuffer_open('__run__') - call append(line('$'), l:output) - normal dd - wincmd p - else - call pymode#wide_message("No output.") - endif - - cexpr "" - - let l:_efm = &efm - - let &efm = s:efm - - cgetexpr(l:traceback) - - " If a range is run (starting other than at line 1), fix the reported error line numbers for - " the current buffer - if a:line1 > 1 - let qflist = getqflist() - for i in qflist - if i.bufnr == bufnr("") - let i.lnum = i.lnum - 1 + a:line1 - endif - endfor - call setqflist(qflist) - endif - - call pymode#quickfix_open(0, g:pymode_quickfix_maxheight, g:pymode_quickfix_maxheight, 0) - - let &efm = l:_efm - - catch /E234/ - - echohl Error | echo "Run-time error." | echohl none - - endtry - -endfunction "}}} diff --git a/autoload/pymode/tools/loclist.vim b/autoload/pymode/tools/loclist.vim deleted file mode 100644 index 18b6d294..00000000 --- a/autoload/pymode/tools/loclist.vim +++ /dev/null @@ -1,81 +0,0 @@ -let g:PymodeLocList= {} - - -fun! pymode#tools#loclist#init() "{{{ - return -endfunction "}}} - - -fun! g:PymodeLocList.init(raw_list) "{{{ - let obj = copy(self) - let loc_list = filter(copy(a:raw_list), 'v:val["valid"] == 1') - call obj.clear() - let obj._title = 'CodeCheck' - return obj -endfunction "}}} - - -fun! g:PymodeLocList.current() "{{{ - if !exists("b:pymode_loclist") - let b:pymode_loclist = g:PymodeLocList.init([]) - endif - return b:pymode_loclist -endfunction "}}} - - -fun! g:PymodeLocList.is_empty() "{{{ - return empty(self._loclist) -endfunction "}}} - - -fun! g:PymodeLocList.clear() "{{{ - let self._loclist = [] - let self._messages = {} - let self._name = expand('%:t') -endfunction "}}} - - -fun! g:PymodeLocList.extend(raw_list) "{{{ - call extend(self._loclist, a:raw_list) - for issue in a:raw_list - let self._messages[issue.lnum] = issue.text - endfor - return self -endfunction "}}} - - -fun! g:PymodeLocList.filter(filters) "{{{ - let loclist = [] - for error in self._loclist - let passes_filters = 1 - for key in keys(a:filters) - if get(error, key, '') !=? a:filters[key] - let passes_filters = 0 - break - endif - endfor - - if passes_filters - call add(loclist, error) - endif - - endfor - return loclist -endfunction "}}} - - -fun! g:PymodeLocList.show() "{{{ - call setloclist(0, self._loclist) - if self.is_empty() - lclose - elseif g:pymode_lint_cwindow - let num = winnr() - lopen - setl nowrap - execute max([min([line("$"), g:pymode_quickfix_maxheight]), g:pymode_quickfix_minheight]) . "wincmd _" - if num != winnr() - call setwinvar(winnr(), 'quickfix_title', self._title . ' <' . self._name . '>') - exe num . "wincmd w" - endif - end -endfunction "}}} diff --git a/autoload/pymode/tools/signs.vim b/autoload/pymode/tools/signs.vim deleted file mode 100644 index 3487cf85..00000000 --- a/autoload/pymode/tools/signs.vim +++ /dev/null @@ -1,57 +0,0 @@ -let g:PymodeSigns = {} - - -fun! pymode#tools#signs#init() "{{{ - call g:PymodeSigns.setup() -endfunction "}}} - - -fun! g:PymodeSigns.enabled() "{{{ - return (g:pymode_lint_signs && has('signs')) -endfunction "}}} - - -fun! g:PymodeSigns.setup() "{{{ - if self.enabled() - execute 'sign define PymodeW text=' . g:pymode_lint_todo_symbol . " texthl=Todo" - execute 'sign define PymodeD text=' . g:pymode_lint_docs_symbol . " texthl=String" - execute 'sign define PymodeC text=' . g:pymode_lint_comment_symbol . " texthl=Comment" - execute 'sign define PymodeR text=' . g:pymode_lint_visual_symbol . " texthl=Visual" - execute 'sign define PymodeE text=' . g:pymode_lint_error_symbol . " texthl=Error" - execute 'sign define PymodeI text=' . g:pymode_lint_info_symbol . " texthl=Info" - execute 'sign define PymodeF text=' . g:pymode_lint_pyflakes_symbol . " texthl=Info" - endif - let self._sign_ids = [] - let self._next_id = 10000 - let self._messages = {} -endfunction "}}} - - -fun! g:PymodeSigns.refresh(loclist) "{{{ - if self.enabled() - call self.clear() - call self.place(a:loclist) - endif -endfunction "}}} - - -fun! g:PymodeSigns.clear() "{{{ - let ids = copy(self._sign_ids) - for i in ids - execute "sign unplace " . i - call remove(self._sign_ids, index(self._sign_ids, i)) - endfor -endfunction "}}} - - -fun! g:PymodeSigns.place(loclist) "{{{ - let seen = {} - for issue in a:loclist._loclist - if !has_key(seen, issue.lnum) - let seen[issue.lnum] = 1 - call add(self._sign_ids, self._next_id) - execute printf('sign place %d line=%d name=%s buffer=%d', self._next_id, issue.lnum, "Pymode".issue.type[0], issue.bufnr) - let self._next_id += 1 - endif - endfor -endfunction "}}} diff --git a/autoload/pymode/troubleshooting.vim b/autoload/pymode/troubleshooting.vim deleted file mode 100644 index 915a5c5e..00000000 --- a/autoload/pymode/troubleshooting.vim +++ /dev/null @@ -1,89 +0,0 @@ -" DESC: Get debug information about pymode problem -fun! pymode#troubleshooting#test() "{{{ - new - setlocal buftype=nofile bufhidden=delete noswapfile nowrap - - let os = "Unknown" - if has('win16') || has('win32') || has('win64') - let os = "Windows" - else - let os = substitute(system('uname'), "\n", "", "") - endif - - if !pymode#default('g:pymode_init', 1) - call pymode#init(expand(':p:h'), g:pymode_paths) - call pymode#virtualenv#init() - call pymode#breakpoint#init() - endif - - call append('0', ['Pymode diagnostic', - \ '===================', - \ 'VIM:' . v:version . ', OS: ' . os .', multi_byte:' . has('multi_byte') . ', pymode: ' . g:pymode_version . ', pymode-python: ' . g:pymode_python, - \ '']) - - if !exists('#filetypeplugin') - call append('$', ['WARNING: ', 'Python-mode required :filetype plugin indent on', '']) - endif - - call append('$', ['+python: ' . has('python')]) - call append('$', ['+python3: ' . has('python3'), '']) - - if g:pymode_python == 'disable' - - if !has('python') && !has('python3') - - call append('$', ['WARNING: Python-mode required vim compiled with +python or +python3.', - \ '"lint, rope, run, doc, virtualenv" features disabled.', '']) - - else - - call append('$', ['WARNING: Python is disabled by `pymode_python` option.', - \ '"lint, rope, run, doc, virtualenv" features disabled.', '']) - - endif - - else - - call append('$', 'VIM python paths:') - call append('$', '-----------------') - PymodePython << EOF -import vim -vim.command('let l:output = %s' % repr(sys.path)) -EOF - call append('$', output) - call append('$', '') - - endif - - call append('$', 'Pymode variables:') - call append('$', '-------------------') - call append('$', 'let pymode = ' . string(g:pymode)) - call append('$', 'let pymode_breakpoint = ' . string(g:pymode_breakpoint)) - call append('$', 'let pymode_breakpoint_bind = ' . string(g:pymode_breakpoint_bind)) - call append('$', 'let pymode_doc = ' . string(g:pymode_doc)) - call append('$', 'let pymode_doc_bind = ' . string(g:pymode_doc_bind)) - call append('$', 'let pymode_folding = ' . string(g:pymode_folding)) - call append('$', 'let pymode_indent = ' . string(g:pymode_indent)) - call append('$', 'let pymode_lint = ' . string(g:pymode_lint)) - call append('$', 'let pymode_lint_checkers = ' . string(g:pymode_lint_checkers)) - call append('$', 'let pymode_lint_cwindow = ' . string(g:pymode_lint_cwindow)) - call append('$', 'let pymode_lint_ignore = ' . string(g:pymode_lint_ignore)) - call append('$', 'let pymode_lint_message = ' . string(g:pymode_lint_message)) - call append('$', 'let pymode_lint_on_fly = ' . string(g:pymode_lint_on_fly)) - call append('$', 'let pymode_lint_on_write = ' . string(g:pymode_lint_on_write)) - call append('$', 'let pymode_lint_select = ' . string(g:pymode_lint_select)) - call append('$', 'let pymode_lint_signs = ' . string(g:pymode_lint_signs)) - call append('$', 'let pymode_motion = ' . string(g:pymode_motion)) - call append('$', 'let pymode_options = ' . string(g:pymode_options)) - call append('$', 'let pymode_paths = ' . string(g:pymode_paths)) - call append('$', 'let pymode_quickfix_maxheight = ' . string(g:pymode_quickfix_maxheight)) - call append('$', 'let pymode_quickfix_minheight = ' . string(g:pymode_quickfix_minheight)) - call append('$', 'let pymode_rope = ' . string(g:pymode_rope)) - call append('$', 'let pymode_run = ' . string(g:pymode_run)) - call append('$', 'let pymode_run_bind = ' . string(g:pymode_run_bind)) - call append('$', 'let pymode_trim_whitespaces = ' . string(g:pymode_trim_whitespaces)) - call append('$', 'let pymode_virtualenv = ' . string(g:pymode_virtualenv)) - call append('$', 'let pymode_virtualenv_enabled = ' . string(g:pymode_virtualenv_enabled)) - call append('$', 'let pymode_virtualenv_path = ' . string(g:pymode_virtualenv_path)) - -endfunction "}}} diff --git a/autoload/pymode/virtualenv.vim b/autoload/pymode/virtualenv.vim deleted file mode 100644 index 7401e94b..00000000 --- a/autoload/pymode/virtualenv.vim +++ /dev/null @@ -1,17 +0,0 @@ -" Support virtualenv -" -PymodePython from pymode.virtualenv import enable_virtualenv - -fun! pymode#virtualenv#init() "{{{ - if !g:pymode_virtualenv || g:pymode_virtualenv_path == "" - return - endif - - PymodePython enable_virtualenv() - -endfunction "}}} - -fun! pymode#virtualenv#activate(path) "{{{ - let g:pymode_virtualenv_path = a:path - call pymode#virtualenv#init() -endfunction "}}} diff --git a/debug.vim b/debug.vim deleted file mode 100644 index c7d32661..00000000 --- a/debug.vim +++ /dev/null @@ -1,13 +0,0 @@ -" Use this settings for testing the plugin. -" Run vim with command -" -" $ vim -u debug.py -" -" Only python-mode will be loaded. - - -execute('set rtp+='. expand(':p:h')) -set rtp -=$HOME/.vim -set rtp -=$HOME/.vim/after -set nocp -syntax enable diff --git a/doc/pymode.txt b/doc/pymode.txt deleted file mode 100644 index 1d7bb24f..00000000 --- a/doc/pymode.txt +++ /dev/null @@ -1,796 +0,0 @@ -*pymode.txt* *python-mode.txt* *pymode* *python-mode* - - ____ _ _ ____ _ _ _____ _ _ __ __ _____ ____ ____ ~ - ( _ \( \/ )(_ _)( )_( )( _ )( \( )___( \/ )( _ )( _ \( ___) ~ - )___/ \ / )( ) _ ( )(_)( ) ((___)) ( )(_)( )(_) ))__) ~ - (__) (__) (__) (_) (_)(_____)(_)\_) (_/\/\_)(_____)(____/(____) ~ - - - Version: 0.9.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.Credits...................................................|pymode-credits| - 8.License...................................................|pymode-license| - -============================================================================== -1. Intro ~ - *pymode-intro* - -Python-mode is a vim plugin that allows you to use the pylint, rope, and pydoc -libraries in vim to provide features like python code bug checking, -refactoring, and some other useful things. - -This plugin allows you to create python code in vim very easily. There is no -need to install the pylint or rope libraries on your system. - -Python-mode contains all you need to develop python applications in Vim. - -Features: *pymode-features* - -- 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 ... - - -============================================================================== -2. Common functionality ~ - *pymode-common* - -This script provides the following options that can customizes the behavior of -PythonMode. These options should be set in your |vimrc|. - - Below shows the default values. - - -Turn on the whole plugin *'g:pymode'* -> - let g:pymode = 1 - -Turn off plugin's warnings *'g:pymode_warnings'* -> - let g:pymode_warnings = 1 - -Add paths to `sys.path` *'g:pymode_paths'* -Value is list of path's strings. -> - let g:pymode_paths = [] - -Trim unused white spaces on save *'g:pymode_trim_whitespaces'* -> - let g:pymode_trim_whitespaces = 1 - -Setup default python options *'g:pymode_options'* -> - let g:pymode_options = 1 - -If this option is set to 1, pymode will enable the following options for -python buffers: > - - setlocal complete+=t - setlocal formatoptions-=t - if v:version > 702 && !&relativenumber - setlocal number - endif - setlocal nowrap - setlocal textwidth=79 - setlocal commentstring=#%s - setlocal define=^\s*\\(def\\\\|class\\) - -Setup max line length *'g:pymode_options_max_line_length'* -> - let g:pymode_options_max_line_length = 79 - -Enable colorcolumn display at max_line_length *'g:pymode_options_colorcolumn'* -> - let g:pymode_options_colorcolumn = 1 - -Setup pymode |quickfix| window - - *'g:pymode_quickfix_maxheight'* *'g:pymode_quickfix_minheight'* -> - let g:pymode_quickfix_minheight = 3 - let g:pymode_quickfix_maxheight = 6 - ------------------------------------------------------------------------------- -2.1. Python version ~ - *pymode-python-version* - -By default pymode looks for current python version supported in your Vim. -You could choose prefer version, but value will be tested on loading. - - *'g:pymode_python'* -> - let g:pymode_python = 'python' - -Values are `python`, `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 supports PEP8-compatible python indent. -Enable pymode indentation *'g:pymode_indent'* -> - let g:pymode_indent = 1 - ------------------------------------------------------------------------------- -2.3 Python folding ~ - *pymode-folding* - -Fast and usual python folding in Vim. -Enable pymode folding *'g:pymode_folding'* -> - let g:pymode_folding = 1 - ------------------------------------------------------------------------------- -2.4 Vim motion ~ - *pymode-motion* - -Support Vim motion (See |operator|) for python objects (such as functions, -class and methods). - -`C` — means class -`M` — means method or function - *pymode-motion-keys* - -================ ============================ -Key Command -================ ============================ -[[ Jump to previous class or function (normal, visual, operator modes) -]] Jump to next class or function (normal, visual, operator modes) -[M Jump to previous class or method (normal, visual, operator modes) -]M Jump to next class or method (normal, visual, operator modes) -aC Select a class. Ex: vaC, daC, yaC, caC (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'* -> - let g:pymode_motion = 1 - ------------------------------------------------------------------------------- -2.5 Show documentation ~ - *pymode-documentation* - -Pymode could show documentation for current word by `pydoc`. - -Commands: -*:PymodeDoc* — show documentation - -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'* -> - let g:pymode_doc_bind = 'K' - ------------------------------------------------------------------------------- -2.6 Support virtualenv ~ - *pymode-virtualenv* - -Commands: -*:PymodeVirtualenv* -- Activate virtualenv (path can be absolute or -relative to current working directory) - -Enable automatic virtualenv detection *'g:pymode_virtualenv'* -> - let g:pymode_virtualenv = 1 - -Set path to virtualenv manually *'g:pymode_virtualenv_path'* -> - let g:pymode_virtualenv_path = $VIRTUAL_ENV - ------------------------------------------------------------------------------- -2.7 Run code ~ - *pymode-run* - -Commands: -*:PymodeRun* -- Run current buffer or selection - -Turn on the run code script *'g:pymode_run'* -> - let g:pymode_run = 1 - -Binds keys to run python code *'g:pymode_run_bind'* -> - let g:pymode_run_bind = 'r' - ------------------------------------------------------------------------------- -2.8 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'* -> - let g:pymode_breakpoint = 1 - -Bind keys -> - let g:pymode_breakpoint_bind = 'b' - -Manually set breakpoint command (leave empty for automatic detection) -> - let g:pymode_breakpoint_cmd = '' - - -============================================================================== -3. Code checking ~ - *pymode-lint* - -Pymode supports `pylint`, `pep257`, `pep8`, `pyflakes`, `mccabe` code -checkers. You could run several similar checkers. - - Pymode uses Pylama library for code checking. Many options like skip - files, errors and etc could be defined in `pylama.ini` file or modelines. - Check Pylama documentation for details. - - Pylint options (ex. disable messages) may be defined in `$HOME/pylint.rc` - See pylint documentation. - -Commands: -*:PymodeLint* -- Check code in current buffer -*:PymodeLintToggle* -- Toggle code checking -*:PymodeLintAuto* -- Fix PEP8 errors in current buffer automatically - -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'* -> - let g:pymode_lint_on_write = 1 - -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'* -> - let g:pymode_lint_on_fly = 0 - -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'* -> - let g:pymode_lint_checkers = ['pyflakes', 'pep8', 'mccabe'] - -Values may be chosen from: `pylint`, `pep8`, `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 -> - let g:pymode_lint_ignore = "E501,W" - -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" - -Sort errors by relevance *'g:pymode_lint_sort'* -If not empty, errors will be sort by defined relevance -E.g. let g:pymode_lint_sort = ['E', 'C', 'I'] " Errors first 'E', -after them 'C' and ... -> - let g:pymode_lint_sort = [] - -Auto open cwindow (quickfix) if any errors have been found - *'g:pymode_lint_cwindow'* -> - let g:pymode_lint_cwindow = 1 - -Place error |signs| *'g:pymode_signs'* -> - let g:pymode_lint_signs = 1 - -Definitions for |signs| -> - let g:pymode_lint_todo_symbol = 'WW' - let g:pymode_lint_comment_symbol = 'CC' - let g:pymode_lint_visual_symbol = 'RR' - let g:pymode_lint_error_symbol = 'EE' - let g:pymode_lint_info_symbol = 'II' - let g:pymode_lint_pyflakes_symbol = 'FF' - ------------------------------------------------------------------------------- -3.1 Set code checkers options ~ - *pymode-lint-options* - -Pymode has the ability to set code checkers options from pymode variables: - -Set PEP8 options *'g:pymode_lint_options_pep8'* -> - let g:pymode_lint_options_pep8 = - \ {'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'* -> - let g:pymode_lint_options_pyflakes = { 'builtins': '_' } - -Set mccabe options *'g:pymode_lint_options_mccabe'* -> - let g:pymode_lint_options_mccabe = { 'complexity': 12 } - -Set pep257 options *'g:pymode_lint_options_pep257'* -> - let g:pymode_lint_options_pep257 = {} - -Set pylint options *'g:pymode_lint_options_pylint'* -> - let g:pymode_lint_options_pylint = - \ {'max-line-length': g:pymode_options_max_line_length}) - -See http://docs.pylint.org/features.html#options for more info. - - - -============================================================================== -3. Rope support ~ - *pymode-rope* - -Pymode supports Rope refactoring operations, code completion and code assists. - -Commands: -|:PymodeRopeAutoImport| -- Resolve import for element under cursor -|:PymodeRopeModuleToPackage| -- Convert current module to package -|:PymodeRopeNewProject| -- Open new Rope project in current working directory -|:PymodeRopeRedo| -- Redo changes from last refactoring -|:PymodeRopeRegenerate| -- Regenerate the project cache -|:PymodeRopeRenameModule| -- Rename current module -|:PymodeRopeUndo| -- Undo changes from last refactoring - - -Turn on the rope script *'g:pymode_rope'* -> - let g:pymode_rope = 1 - -.ropeproject Folder ~ - *.ropeproject* - -*:PymodeRopeNewProject* [] -- Open new Rope project in the given path -*:PymodeRopeRegenerate* -- Regenerate the project cache - -Rope uses a folder inside projects for holding project configuration and data. -Its default name is `.ropeproject`. It is recommended that you do not add the -.ropeproject folder to version control system. - -Currently it is used for things such as: - -* The config.py file in this folder contains project configuration. Have - a look at the default config.py file (which is created when it - does not exist) for more information. -* It can be used for saving project history, so that the next time you open the - project you can undo past changes. -* It can be used to save information about object inferences. -* It can be used to save a global name cache, which is used for auto-import. - -By default, if `.ropeproject` is not found in the current directory, rope will -look recursively for it in parent folders. - -Warning: If rope finds `.ropeproject` in a parent dir, it will use it with -all its child directories, which may slow scanning down (because of many, -possibly unrelated, files) - -Enable searching for |.ropeproject| in parent directories - *'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'* -> - let g:pymode_rope_project_root = "" - - -The location of the `.ropeproject` folder may also be overridden if you wish to -keep it outside of your project root. The rope library treats this folder as a -project resource, so the path will always be relative to your project root (a -leading '/' will be ignored). You may use `'..'` path segments to place the -folder outside of your project root. - *'g:pymode_rope_ropefolder'* -> - let g:pymode_rope_ropefolder='.ropeproject' - - - -Show documentation for element under cursor ~ - -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' - -Regenerate project cache on every save (if file has been modified) -> - let g:pymode_rope_regenerate_on_write = 1 - ------------------------------------------------------------------------------- -4.1 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 -your code. and / works too. - -Autocompletion is also called by typing a period in |Insert| mode by default. - - -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'* -> - let g:pymode_rope_complete_on_dot = 1 - -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'* -> - let g:pymode_rope_autoimport = 0 - -Load modules to autoimport by default *'g:pymode_rope_autoimport_modules'* -> - let g:pymode_rope_autoimport_modules = ['os', 'shutil', 'datetime'] - -Offer to unresolved import object after completion. -> - let g:pymode_rope_autoimport_import_after_complete = 0 - - ------------------------------------------------------------------------------- -4.2 Find definition ~ - *pymode-rope-findit* - -By default when you press *g* on any object in your code you will be moved -to definition. -Leave empty for disable key binding. *'g:pymode_rope_goto_definition_bind'* -> - let g:pymode_rope_goto_definition_bind = 'g' - -Command for open window when definition has been found -Values are (`e`, `new`, `vnew`) *'g:pymode_rope_goto_definition_cmd'* -> - let g:pymode_rope_goto_definition_cmd = 'new' - ------------------------------------------------------------------------------- -4.3 Refactoring ~ - *pymode-rope-refactoring* - -Rename method/function/class/variable in the project ~ - -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'* -> - let g:pymode_rope_rename_bind = 'rr' - - -Rename a current module/package ~ - -*:PymodeRopeRenameModule* -- Rename current module - -Keymap for rename current module *'g:pymode_rope_rename_module_bind'* -> - let g:pymode_rope_rename_module_bind = 'r1r' - - -Imports ~ - -*:PymodeRopeAutoImport* -- Resolve import for element under cursor - -Organize imports sorts imports, too. It does that according to PEP8. Unused -imports will be dropped. -Keymap *'g:pymode_rope_organize_imports_bind'* -> - let g:pymode_rope_organize_imports_bind = 'ro' - -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'* - -*:PymodeRopeModuleToPackage* -- convert current module to package - -Keybinding: -> - let g:pymode_rope_module_to_package_bind = 'r1p' - - -Extract method/variable ~ - *pymode-rope-extract* - -Extract method/variable from selected lines. - - *'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* - -It tries to find the places in which a function can be used and changes the -code to call it instead. -> - let g:pymode_rope_use_function_bind = 'ru' - - -Move method/fields ~ - *pymode-rope-move* - -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. -> - let g:pymode_rope_move_bind = 'rv' - -Change function signature ~ -> - let g:pymode_rope_change_signature_bind = 'rs' - - ------------------------------------------------------------------------------- -4.4 Undo/Redo changes ~ - *pymode-rope-undo* - *pymode-rope-redo* - -Commands: - -*:PymodeRopeUndo* -- Undo last changes in the project -*:PymodeRopeRedo* -- Redo last changes in the project - - -============================================================================== -5. Syntax ~ - *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'* -> - let g:pymode_syntax_slow_sync = 1 - -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'* -> - let g:pymode_syntax_print_as_function = 0 - -Highlight "async/await" keywords *'g:pymode_syntax_highlight_async_await'* -> - let g:pymode_syntax_highlight_async_await = g:pymode_syntax_all - -Highlight '=' operator *'g:pymode_syntax_highlight_equal_operator'* -> - let g:pymode_syntax_highlight_equal_operator = g:pymode_syntax_all - -Highlight '*' operator *'g:pymode_syntax_highlight_stars_operator'* -> - let g:pymode_syntax_highlight_stars_operator = g:pymode_syntax_all - -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'* -> - let g:pymode_syntax_indent_errors = g:pymode_syntax_all - -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'* -> - 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'* -> - let g:pymode_syntax_builtin_objs = g:pymode_syntax_all - -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'* -> - let g:pymode_syntax_highlight_exceptions = g:pymode_syntax_all - -Highlight docstrings as pythonDocstring (otherwise as pythonString) - *'g:pymode_syntax_docstrings'* -> - let g:pymode_syntax_docstrings = g:pymode_syntax_all - - -============================================================================== -6. FAQ ~ - *pymode-faq* - -Python-mode doesn't work ------------------------- - -Open any python file and run ":call pymode#troubleshooting#test()", -fix the warning or send me the output. - - -Rope completion is very slow *pymode-rope-slow* ----------------------------- - -Rope creates a project-level service directory in |.ropeproject| - -If ``.ropeproject`` is not found in the current directory, rope will walk -upwards looking for a ``.ropeproject`` in every dir of the parent path. If -rope finds ``.ropeproject`` in a parent dir, it sets the project for all child -dirs and the scan may be slow for so many dirs and files. - -Solutions: - -- Delete `.ropeproject` from the parent dir to make rope create `.ropeproject` - in the current dir. -- Run ``:PymodeRopeNewProject`` to make rope create ``.ropeproject`` in the - current dir. -- Set |'g:pymode_rope_lookup_project'| to 0 for prevent searching in parent - dirs. - -You may also set |'g:pymode_rope_project_root'| to manually specify the project -root path. - - - -Pylint check is very slow -------------------------- - -In some projects pylint may check slowly, because it also scans imported -modules if possible. Try using another code checker: see -|'g:pymode_lint_checkers'|. - -You may set |exrc| and |secure| in your |vimrc| to auto-set custom settings -from `.vimrc` from your projects directories. - - -OSX cannot import urandom -------------------------- - -See: https://groups.google.com/forum/?fromgroups=#!topic/vim_dev/2NXKF6kDONo - -The sequence of commands that fixed this: -> - brew unlink python - brew unlink macvim - brew remove macvim - brew install -v --force macvim - brew link macvim - brew link python -< - -============================================================================== -7. Credits ~ - *pymode-credits* - Kirill Klenov - http://klen.github.com/ - http://github.com/klen/ - - Rope - Copyright (C) 2006-2010 Ali Gholami Rudi - Copyright (C) 2009-2010 Anton Gritsay - - Pylint - Copyright (C) 2003-2011 LOGILAB S.A. (Paris, FRANCE). - http://www.logilab.fr/ - - Pyflakes: - Copyright (c) 2005 Divmod, Inc. - http://www.divmod.com/ - - PEP8: - Copyright (c) 2006 Johann C. Rocholl - http://github.com/jcrocholl/pep8 - - autopep8: - Copyright (c) 2012 hhatto - https://github.com/hhatto/autopep8 - - Python syntax for vim: - Copyright (c) 2010 Dmitry Vasiliev - http://www.hlabs.spb.ru/vim/python.vim - - PEP8 VIM indentation - Copyright (c) 2012 Hynek Schlawack - http://github.com/hynek/vim-python-pep8-indent - - -============================================================================== -8. 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 :) - -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: diff --git a/ftplugin/pyrex.vim b/ftplugin/pyrex.vim deleted file mode 100644 index 93e0556d..00000000 --- a/ftplugin/pyrex.vim +++ /dev/null @@ -1 +0,0 @@ -runtime ftplugin/python/pymode.vim diff --git a/ftplugin/python/pymode.vim b/ftplugin/python/pymode.vim deleted file mode 100644 index 97daecca..00000000 --- a/ftplugin/python/pymode.vim +++ /dev/null @@ -1,212 +0,0 @@ -if !g:pymode || pymode#default('b:pymode', 1) - finish -endif - -if g:pymode_python == 'disable' - - if g:pymode_warning - call pymode#error("Pymode requires vim compiled with +python. Most of features will be disabled.") - endif - - finish - -else - - -let b:pymode_modified = &modified - -" Init paths -if !pymode#default('g:pymode_init', 1) - - call pymode#init(expand(':p:h:h:h'), g:pymode_paths) - call pymode#virtualenv#init() - call pymode#breakpoint#init() - - PymodePython from pymode.utils import patch_paths - PymodePython patch_paths() - - endif - -endif - -command! -buffer -nargs=1 PymodeVirtualenv call pymode#virtualenv#activate() - -" Setup events for pymode -au! pymode BufWritePre call pymode#buffer_pre_write() -au! pymode BufWritePost call pymode#buffer_post_write() - -" Run python code -if g:pymode_run - - command! -buffer -nargs=0 -range=% PymodeRun call pymode#run#code_run(, ) - - exe "nnoremap " g:pymode_run_bind ":PymodeRun" - exe "vnoremap " g:pymode_run_bind ":PymodeRun" - -endif - -" Add/remove breakpoints -if g:pymode_breakpoint - - exe "nnoremap " g:pymode_breakpoint_bind ":call pymode#breakpoint#operate(line('.'))" - -endif - -" Python folding -if g:pymode_folding - - setlocal foldmethod=expr - setlocal foldexpr=pymode#folding#expr(v:lnum) - setlocal foldtext=pymode#folding#text() - -endif - -" Remove unused whitespaces -if g:pymode_trim_whitespaces - au BufWritePre call pymode#trim_whitespaces() -endif - -" Custom options -if g:pymode_options - setlocal complete+=t - setlocal formatoptions-=t - if v:version > 702 && !&relativenumber - setlocal number - endif - setlocal nowrap - exe "setlocal textwidth=" . g:pymode_options_max_line_length - if g:pymode_options_colorcolumn && exists('+colorcolumn') - setlocal colorcolumn=+1 - endif - setlocal commentstring=#%s - setlocal define=^\s*\\(def\\\\|class\\) -endif - -if g:pymode_lint - - command! -buffer -nargs=0 PymodeLintAuto :call pymode#lint#auto() - command! -buffer -nargs=0 PymodeLintToggle :call pymode#lint#toggle() - command! -buffer -nargs=0 PymodeLint :call pymode#lint#check() - - if v:version > 703 || (v:version == 703 && has('patch544')) - au! QuitPre call pymode#quit() - else - au! pymode BufWinLeave * silent! lclose - endif - - let b:pymode_error_line = -1 - - if g:pymode_lint_on_fly - au! pymode InsertLeave PymodeLint - endif - - if g:pymode_lint_message - au! pymode CursorMoved - au! pymode CursorMoved call pymode#lint#show_errormessage() - endif - - " Disabled for current release - if g:pymode_lint_async - " let &l:updatetime = g:pymode_lint_async_updatetime - " au! BufEnter call pymode#lint#start() - " au! BufLeave call pymode#lint#stop() - end - -endif - -" Show python documentation -if g:pymode_doc - - " Set commands - command! -buffer -nargs=1 PymodeDoc call pymode#doc#show("") - - " Set keys - exe "nnoremap " g:pymode_doc_bind ":call pymode#doc#find()" - exe "vnoremap " g:pymode_doc_bind ":call pymode#doc#show(@*)" - -end - -" Rope support -if g:pymode_rope - - if g:pymode_rope_goto_definition_bind != "" - exe "noremap " . g:pymode_rope_goto_definition_bind . " :call pymode#rope#goto_definition()" - endif - if g:pymode_rope_show_doc_bind != "" - exe "noremap " . g:pymode_rope_show_doc_bind . " :call pymode#rope#show_doc()" - end - if g:pymode_rope_find_it_bind != "" - exe "noremap " . g:pymode_rope_find_it_bind . " :call pymode#rope#find_it()" - end - if g:pymode_rope_organize_imports_bind != "" - exe "noremap " . g:pymode_rope_organize_imports_bind . " :call pymode#rope#organize_imports()" - end - - if g:pymode_rope_rename_bind != "" - exe "noremap " . g:pymode_rope_rename_bind . " :call pymode#rope#rename()" - end - - if g:pymode_rope_rename_module_bind != "" - exe "noremap " . g:pymode_rope_rename_module_bind . " :call pymode#rope#rename_module()" - end - - if g:pymode_rope_extract_method_bind != "" - exe "vnoremap " . g:pymode_rope_extract_method_bind . " :call pymode#rope#extract_method()" - end - - if g:pymode_rope_extract_variable_bind != "" - exe "vnoremap " . g:pymode_rope_extract_variable_bind . " :call pymode#rope#extract_variable()" - end - - if g:pymode_rope_inline_bind != "" - exe "noremap " . g:pymode_rope_inline_bind . " :call pymode#rope#inline()" - end - - if g:pymode_rope_move_bind != "" - exe "noremap " . g:pymode_rope_move_bind . " :call pymode#rope#move()" - end - - if g:pymode_rope_change_signature_bind != "" - exe "noremap " . g:pymode_rope_change_signature_bind . " :call pymode#rope#signature()" - end - - if g:pymode_rope_use_function_bind != "" - exe "noremap " . g:pymode_rope_use_function_bind . " :call pymode#rope#use_function()" - end - - if g:pymode_rope_generate_function_bind != "" - exe "noremap " . g:pymode_rope_generate_function_bind . " :call pymode#rope#generate_function()" - end - - if g:pymode_rope_generate_package_bind != "" - exe "noremap " . g:pymode_rope_generate_package_bind . " :call pymode#rope#generate_package()" - end - - if g:pymode_rope_generate_class_bind != "" - exe "noremap " . g:pymode_rope_generate_class_bind . " :call pymode#rope#generate_class()" - end - - if g:pymode_rope_module_to_package_bind != "" - exe "noremap " . g:pymode_rope_module_to_package_bind . " :call pymode#rope#module_to_package()" - end - - if g:pymode_rope_autoimport_bind != "" - exe "noremap " . g:pymode_rope_autoimport_bind . " :PymodeRopeAutoImport" - end - - if g:pymode_rope_completion && g:pymode_rope_complete_on_dot - inoremap . .=pymode#rope#complete_on_dot() - end - - command! -buffer -nargs=? PymodeRopeNewProject call pymode#rope#new() - command! -buffer PymodeRopeUndo call pymode#rope#undo() - command! -buffer PymodeRopeRedo call pymode#rope#redo() - command! -buffer PymodeRopeRenameModule call pymode#rope#rename_module() - command! -buffer PymodeRopeModuleToPackage call pymode#rope#module_to_package() - command! -buffer PymodeRopeRegenerate call pymode#rope#regenerate() - - if g:pymode_rope_autoimport - command! -buffer PymodeRopeAutoImport call pymode#rope#autoimport(expand('')) - end - -end diff --git a/logo.png b/logo.png deleted file mode 100644 index b873f5cb..00000000 Binary files a/logo.png and /dev/null differ diff --git a/plugin/pymode.vim b/plugin/pymode.vim deleted file mode 100644 index 5b6c51ee..00000000 --- a/plugin/pymode.vim +++ /dev/null @@ -1,316 +0,0 @@ -" vi: fdl=1 -let g:pymode_version = "0.9.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 - finish -endif - -" Pymode needs -filetype plugin on - -" OPTIONS: {{{ - -" Vim Python interpreter. Set to 'disable' for remove python features. -call pymode#default('g:pymode_python', '') - -" Disable pymode warnings -call pymode#default('g:pymode_warning', 1) - -" Additional python paths -call pymode#default('g:pymode_paths', []) - -" Python documentation support -call pymode#default('g:pymode_doc', 1) -call pymode#default('g:pymode_doc_bind', 'K') - -" Enable/Disable pymode PEP8 indentation -call pymode#default("g:pymode_indent", 1) - -" Enable/disable pymode folding for pyfiles. -call pymode#default("g:pymode_folding", 1) -" 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\)\@!') - -" Enable/disable python motion operators -call pymode#default("g:pymode_motion", 1) - -" Auto remove unused whitespaces on save -call pymode#default("g:pymode_trim_whitespaces", 1) - -" Set recomended python options -call pymode#default("g:pymode_options", 1) -call pymode#default("g:pymode_options_max_line_length", 80) -call pymode#default("g:pymode_options_colorcolumn", 1) - -" Enable/disable vertical display of python documentation -call pymode#default("g:pymode_doc_vertical", 0) - -" Minimal height of pymode quickfix window -call pymode#default('g:pymode_quickfix_maxheight', 6) - -" Maximal height of pymode quickfix window -call pymode#default('g:pymode_quickfix_minheight', 3) - -" LOAD VIRTUALENV {{{ -" -" Enable virtualenv support -call pymode#default('g:pymode_virtualenv', 1) - -" Get path to virtualenv (by default take from shell) -call pymode#default('g:pymode_virtualenv_path', $VIRTUAL_ENV) - -" Service variable (don't set it manually) -call pymode#default('g:pymode_virtualenv_enabled', '') - -" }}} - -" RUN PYTHON {{{ -" -" Enable code running support -call pymode#default('g:pymode_run', 1) - -" Key's map for run python code -call pymode#default('g:pymode_run_bind', 'r') - -" }}} - -" CHECK CODE {{{ -" -" Code checking -call pymode#default('g:pymode_lint', 1) - -" Check code asynchronously -call pymode#default('g:pymode_lint_async', 1) -call pymode#default('g:pymode_lint_async_updatetime', 1000) - -" Check code every save if file has been modified -call pymode#default("g:pymode_lint_on_write", 1) - -" Check code every save (every) -call pymode#default("g:pymode_lint_unmodified", 0) - -" Check code on fly -call pymode#default("g:pymode_lint_on_fly", 0) - -" Show message about error in command line -call pymode#default("g:pymode_lint_message", 1) - -" Choices are: pylint, pyflakes, pep8, mccabe -call pymode#default("g:pymode_lint_checkers", ['pyflakes', 'pep8', 'mccabe']) - -" Skip errors and warnings (e.g. E4,W) -call pymode#default("g:pymode_lint_ignore", "") - -" Select errors and warnings (e.g. E4,W) -call pymode#default("g:pymode_lint_select", "") - -" Auto open cwindow if any errors has been finded -call pymode#default("g:pymode_lint_cwindow", 1) - -" If not emply, errors will be sort by defined relevance -" E.g. let g:pymode_lint_sort = ['E', 'C', 'I'] " Errors first 'E', -" after them 'C' and ... -call pymode#default("g:pymode_lint_sort", []) - -" Place error signs -call pymode#default("g:pymode_lint_signs", 1) - -" Symbol's definitions -call pymode#default("g:pymode_lint_todo_symbol", "WW") -call pymode#default("g:pymode_lint_docs_symbol", "DD") -call pymode#default("g:pymode_lint_comment_symbol", "CC") -call pymode#default("g:pymode_lint_visual_symbol", "RR") -call pymode#default("g:pymode_lint_error_symbol", "EE") -call pymode#default("g:pymode_lint_info_symbol", "II") -call pymode#default("g:pymode_lint_pyflakes_symbol", "FF") - -" Code checkers options -call pymode#default("g:pymode_lint_options_pep8", - \ {'max_line_length': g:pymode_options_max_line_length}) - -call pymode#default("g:pymode_lint_options_pylint", - \ {'max-line-length': g:pymode_options_max_line_length}) - -call pymode#default("g:pymode_lint_options_mccabe", - \ {'complexity': 12}) - -call pymode#default("g:pymode_lint_options_pep257", {}) -call pymode#default("g:pymode_lint_options_pyflakes", { 'builtins': '_' }) - - -" }}} - -" SET/UNSET BREAKPOINTS {{{ -" - -" Create/remove breakpoints -call pymode#default('g:pymode_breakpoint', 1) - -" Key's map for add/remove breakpoint -call pymode#default('g:pymode_breakpoint_bind', 'b') - -" Default pattern for making breakpoints. Leave this empty for auto search available debuggers (pdb, ipdb, ...) -call pymode#default('g:pymode_breakpoint_cmd', '') - -" }}} - -" ROPE (refactoring, codeassist) {{{ -" -" Rope support -call pymode#default('g:pymode_rope', 1) - -" System plugin variable -call pymode#default('g:pymode_rope_current', '') - -" Configurable rope project root -call pymode#default('g:pymode_rope_project_root', '') - -" Configurable rope project folder (always relative to project root) -call pymode#default('g:pymode_rope_ropefolder', '.ropeproject') - -" If project hasnt been finded in current working directory, look at parents directory -call pymode#default('g:pymode_rope_lookup_project', 0) - -" Enable Rope completion -call pymode#default('g:pymode_rope_completion', 1) - -" Complete keywords from not imported modules (could make completion slower) -" Enable autoimport used modules -call pymode#default('g:pymode_rope_autoimport', 0) - -" Offer to import object after complete (if that not be imported before) -call pymode#default('g:pymode_rope_autoimport_import_after_complete', 0) - -" Autoimported modules -call pymode#default('g:pymode_rope_autoimport_modules', ['os', 'shutil', 'datetime']) - -" Bind keys to autoimport module for object under cursor -call pymode#default('g:pymode_rope_autoimport_bind', 'ra') - -" Automatic completion on dot -call pymode#default('g:pymode_rope_complete_on_dot', 1) - -" Bind keys for autocomplete (leave empty for disable) -call pymode#default('g:pymode_rope_completion_bind', '') - -" Bind keys for goto definition (leave empty for disable) -call pymode#default('g:pymode_rope_goto_definition_bind', 'g') - -" 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 find occurencies (leave empty for disable) -call pymode#default('g:pymode_rope_find_it_bind', 'f') - -" Bind keys for organize imports (leave empty for disable) -call pymode#default('g:pymode_rope_organize_imports_bind', '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 module -call pymode#default('g:pymode_rope_rename_module_bind', 'r1r') - -" Bind keys for convert module to package -call pymode#default('g:pymode_rope_module_to_package_bind', '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 variable from the selected lines -call pymode#default('g:pymode_rope_extract_variable_bind', 'rl') - -" Inline refactoring -call pymode#default('g:pymode_rope_inline_bind', 'ri') - -" Move refactoring -call pymode#default('g:pymode_rope_move_bind', 'rv') - -" Generate function -call pymode#default('g:pymode_rope_generate_function_bind', 'rnf') - -" Generate class -call pymode#default('g:pymode_rope_generate_class_bind', 'rnc') - -" Generate package -call pymode#default('g:pymode_rope_generate_package_bind', 'rnp') - -" Change signature -call pymode#default('g:pymode_rope_change_signature_bind', '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') - -" Regenerate project cache on every save -call pymode#default('g:pymode_rope_regenerate_on_write', 1) - -" }}} - -" }}} - -" Prepare to plugin loading -if &compatible - set nocompatible -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") - 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' - - command! -nargs=1 PymodePython python3 - let g:UltiSnipsUsePythonVersion = 3 - -else - - let g:pymode_doc = 0 - let g:pymode_lint = 0 - let g:pymode_path = 0 - let g:pymode_rope = 0 - let g:pymode_run = 0 - let g:pymode_virtualenv = 0 - - command! -nargs=1 PymodePython echo - -endif - - -command! PymodeVersion echomsg "Pymode version: " . g:pymode_version . " interpreter: " . g:pymode_python . " lint: " . g:pymode_lint . " rope: " . g:pymode_rope - -augroup pymode diff --git a/pylama.ini b/pylama.ini deleted file mode 100644 index 0394772f..00000000 --- a/pylama.ini +++ /dev/null @@ -1,8 +0,0 @@ -[pylama] -linters=pep8,pyflakes,pylint - -[pylama:pymode/libs*] -skip=1 - -[pylama:pylint] -disable=E1120,E1130,E1103,W1401,F0001 diff --git a/pymode/__init__.py b/pymode/__init__.py deleted file mode 100644 index d5e63ba3..00000000 --- a/pymode/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -""" Pymode support functions. """ - -from __future__ import absolute_import - -import sys -import vim # noqa - - -def auto(): - """ Fix PEP8 erorrs in current buffer. """ - from .autopep8 import fix_file - - class Options(object): - aggressive = 2 - diff = False - experimental = True - ignore = vim.eval('g:pymode_lint_ignore') - in_place = True - indent_size = int(vim.eval('&tabstop')) - line_range = None - max_line_length = int(vim.eval('g:pymode_options_max_line_length')) - pep8_passes = 100 - recursive = False - select = vim.eval('g:pymode_lint_select') - verbose = 0 - - fix_file(vim.current.buffer.name, Options) - - -def get_documentation(): - """ Search documentation and append to current buffer. """ - from ._compat import StringIO - - sys.stdout, _ = StringIO(), sys.stdout - help(vim.eval('a:word')) - sys.stdout, out = _, sys.stdout.getvalue() - vim.current.buffer.append(str(out).splitlines(), 0) 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 deleted file mode 100644 index dd314d76..00000000 --- a/pymode/async.py +++ /dev/null @@ -1,6 +0,0 @@ -""" Python-mode async support. """ - -from ._compat import Queue - - -RESULTS = Queue() diff --git a/pymode/autopep8.py b/pymode/autopep8.py deleted file mode 100644 index 13308751..00000000 --- a/pymode/autopep8.py +++ /dev/null @@ -1,3654 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2010-2011 Hideo Hattori -# Copyright (C) 2011-2013 Hideo Hattori, Steven Myint -# Copyright (C) 2013-2015 Hideo Hattori, Steven Myint, Bill Wendling -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Automatically formats Python code to conform to the PEP 8 style guide. - -Fixes that only need be done once can be added by adding a function of the form -"fix_(source)" to this module. They should return the fixed source code. -These fixes are picked up by apply_global_fixes(). - -Fixes that depend on pep8 should be added as methods to FixPEP8. See the class -documentation for more information. - -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import codecs -import collections -import copy -import difflib -import fnmatch -import inspect -import io -import keyword -import locale -import os -import re -import signal -import sys -import textwrap -import token -import tokenize - -import pep8 - - -try: - unicode -except NameError: - unicode = str - - -__version__ = '1.2.1a0' - - -CR = '\r' -LF = '\n' -CRLF = '\r\n' - - -PYTHON_SHEBANG_REGEX = re.compile(r'^#!.*\bpython[23]?\b\s*$') - - -# For generating line shortening candidates. -SHORTEN_OPERATOR_GROUPS = frozenset([ - frozenset([',']), - frozenset(['%']), - frozenset([',', '(', '[', '{']), - frozenset(['%', '(', '[', '{']), - frozenset([',', '(', '[', '{', '%', '+', '-', '*', '/', '//']), - frozenset(['%', '+', '-', '*', '/', '//']), -]) - - -DEFAULT_IGNORE = 'E24' -DEFAULT_INDENT_SIZE = 4 - - -# W602 is handled separately due to the need to avoid "with_traceback". -CODE_TO_2TO3 = { - 'E231': ['ws_comma'], - 'E721': ['idioms'], - 'W601': ['has_key'], - 'W603': ['ne'], - 'W604': ['repr'], - 'W690': ['apply', - 'except', - 'exitfunc', - 'numliterals', - 'operator', - 'paren', - 'reduce', - 'renames', - 'standarderror', - 'sys_exc', - 'throw', - 'tuple_params', - 'xreadlines']} - - -if sys.platform == 'win32': # pragma: no cover - DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8') -else: - DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or - os.path.expanduser('~/.config'), 'pep8') -PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8') - - -def open_with_encoding(filename, encoding=None, mode='r'): - """Return opened file with a specific encoding.""" - if not encoding: - encoding = detect_encoding(filename) - - return io.open(filename, mode=mode, encoding=encoding, - newline='') # Preserve line endings - - -def detect_encoding(filename): - """Return file encoding.""" - try: - with open(filename, 'rb') as input_file: - from lib2to3.pgen2 import tokenize as lib2to3_tokenize - encoding = lib2to3_tokenize.detect_encoding(input_file.readline)[0] - - # Check for correctness of encoding - with open_with_encoding(filename, encoding) as test_file: - test_file.read() - - return encoding - except (LookupError, SyntaxError, UnicodeDecodeError): - return 'latin-1' - - -def readlines_from_file(filename): - """Return contents of file.""" - with open_with_encoding(filename) as input_file: - return input_file.readlines() - - -def extended_blank_lines(logical_line, - blank_lines, - blank_before, - indent_level, - previous_logical): - """Check for missing blank lines after class declaration.""" - if previous_logical.startswith('class '): - if logical_line.startswith(('def ', 'class ', '@')): - if indent_level and not blank_lines and not blank_before: - yield (0, 'E309 expected 1 blank line after class declaration') - elif previous_logical.startswith('def '): - if blank_lines and pep8.DOCSTRING_REGEX.match(logical_line): - yield (0, 'E303 too many blank lines ({0})'.format(blank_lines)) - elif pep8.DOCSTRING_REGEX.match(previous_logical): - # Missing blank line between class docstring and method declaration. - if ( - indent_level and - not blank_lines and - not blank_before and - logical_line.startswith(('def ')) and - '(self' in logical_line - ): - yield (0, 'E301 expected 1 blank line, found 0') -pep8.register_check(extended_blank_lines) - - -def continued_indentation(logical_line, tokens, indent_level, indent_char, - noqa): - """Override pep8's function to provide indentation information.""" - first_row = tokens[0][2][0] - nrows = 1 + tokens[-1][2][0] - first_row - if noqa or nrows == 1: - return - - # indent_next tells us whether the next block is indented. Assuming - # that it is indented by 4 spaces, then we should not allow 4-space - # indents on the final continuation line. In turn, some other - # indents are allowed to have an extra 4 spaces. - indent_next = logical_line.endswith(':') - - row = depth = 0 - valid_hangs = ( - (DEFAULT_INDENT_SIZE,) - if indent_char != '\t' else (DEFAULT_INDENT_SIZE, - 2 * DEFAULT_INDENT_SIZE) - ) - - # Remember how many brackets were opened on each line. - parens = [0] * nrows - - # Relative indents of physical lines. - rel_indent = [0] * nrows - - # For each depth, collect a list of opening rows. - open_rows = [[0]] - # For each depth, memorize the hanging indentation. - hangs = [None] - - # Visual indents. - indent_chances = {} - last_indent = tokens[0][2] - indent = [last_indent[1]] - - last_token_multiline = None - line = None - last_line = '' - last_line_begins_with_multiline = False - for token_type, text, start, end, line in tokens: - - newline = row < start[0] - first_row - if newline: - row = start[0] - first_row - newline = (not last_token_multiline and - token_type not in (tokenize.NL, tokenize.NEWLINE)) - last_line_begins_with_multiline = last_token_multiline - - if newline: - # This is the beginning of a continuation line. - last_indent = start - - # Record the initial indent. - rel_indent[row] = pep8.expand_indent(line) - indent_level - - # Identify closing bracket. - close_bracket = (token_type == tokenize.OP and text in ']})') - - # Is the indent relative to an opening bracket line? - for open_row in reversed(open_rows[depth]): - hang = rel_indent[row] - rel_indent[open_row] - hanging_indent = hang in valid_hangs - if hanging_indent: - break - if hangs[depth]: - hanging_indent = (hang == hangs[depth]) - - visual_indent = (not close_bracket and hang > 0 and - indent_chances.get(start[1])) - - if close_bracket and indent[depth]: - # Closing bracket for visual indent. - if start[1] != indent[depth]: - yield (start, 'E124 {0}'.format(indent[depth])) - elif close_bracket and not hang: - pass - elif indent[depth] and start[1] < indent[depth]: - # Visual indent is broken. - yield (start, 'E128 {0}'.format(indent[depth])) - elif (hanging_indent or - (indent_next and - rel_indent[row] == 2 * DEFAULT_INDENT_SIZE)): - # Hanging indent is verified. - if close_bracket: - yield (start, 'E123 {0}'.format(indent_level + - rel_indent[open_row])) - hangs[depth] = hang - elif visual_indent is True: - # Visual indent is verified. - indent[depth] = start[1] - elif visual_indent in (text, unicode): - # Ignore token lined up with matching one from a previous line. - pass - else: - one_indented = (indent_level + rel_indent[open_row] + - DEFAULT_INDENT_SIZE) - # Indent is broken. - if hang <= 0: - error = ('E122', one_indented) - elif indent[depth]: - error = ('E127', indent[depth]) - elif hang > DEFAULT_INDENT_SIZE: - error = ('E126', one_indented) - else: - hangs[depth] = hang - error = ('E121', one_indented) - - yield (start, '{0} {1}'.format(*error)) - - # Look for visual indenting. - if ( - parens[row] and - token_type not in (tokenize.NL, tokenize.COMMENT) and - not indent[depth] - ): - indent[depth] = start[1] - indent_chances[start[1]] = True - # Deal with implicit string concatenation. - elif (token_type in (tokenize.STRING, tokenize.COMMENT) or - text in ('u', 'ur', 'b', 'br')): - indent_chances[start[1]] = unicode - # Special case for the "if" statement because len("if (") is equal to - # 4. - elif not indent_chances and not row and not depth and text == 'if': - indent_chances[end[1] + 1] = True - elif text == ':' and line[end[1]:].isspace(): - open_rows[depth].append(row) - - # Keep track of bracket depth. - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - hangs.append(None) - if len(open_rows) == depth: - open_rows.append([]) - open_rows[depth].append(row) - parens[row] += 1 - elif text in ')]}' and depth > 0: - # Parent indents should not be more than this one. - prev_indent = indent.pop() or last_indent[1] - hangs.pop() - for d in range(depth): - if indent[d] > prev_indent: - indent[d] = 0 - for ind in list(indent_chances): - if ind >= prev_indent: - del indent_chances[ind] - del open_rows[depth + 1:] - depth -= 1 - if depth: - indent_chances[indent[depth]] = True - for idx in range(row, -1, -1): - if parens[idx]: - parens[idx] -= 1 - break - assert len(indent) == depth + 1 - if ( - start[1] not in indent_chances and - # This is for purposes of speeding up E121 (GitHub #90). - not last_line.rstrip().endswith(',') - ): - # Allow to line up tokens. - indent_chances[start[1]] = text - - last_token_multiline = (start[0] != end[0]) - if last_token_multiline: - rel_indent[end[0] - first_row] = rel_indent[row] - - last_line = line - - if ( - indent_next and - not last_line_begins_with_multiline and - pep8.expand_indent(line) == indent_level + DEFAULT_INDENT_SIZE - ): - pos = (start[0], indent[0] + 4) - yield (pos, 'E125 {0}'.format(indent_level + - 2 * DEFAULT_INDENT_SIZE)) -del pep8._checks['logical_line'][pep8.continued_indentation] -pep8.register_check(continued_indentation) - - -class FixPEP8(object): - - """Fix invalid code. - - Fixer methods are prefixed "fix_". The _fix_source() method looks for these - automatically. - - The fixer method can take either one or two arguments (in addition to - self). The first argument is "result", which is the error information from - pep8. The second argument, "logical", is required only for logical-line - fixes. - - The fixer method can return the list of modified lines or None. An empty - list would mean that no changes were made. None would mean that only the - line reported in the pep8 error was modified. Note that the modified line - numbers that are returned are indexed at 1. This typically would correspond - with the line number reported in the pep8 error information. - - [fixed method list] - - e121,e122,e123,e124,e125,e126,e127,e128,e129 - - e201,e202,e203 - - e211 - - e221,e222,e223,e224,e225 - - e231 - - e251 - - e261,e262 - - e271,e272,e273,e274 - - e301,e302,e303 - - e401 - - e502 - - e701,e702 - - e711 - - w291 - - """ - - def __init__(self, filename, - options, - contents=None, - long_line_ignore_cache=None): - self.filename = filename - if contents is None: - self.source = readlines_from_file(filename) - else: - sio = io.StringIO(contents) - self.source = sio.readlines() - self.options = options - self.indent_word = _get_indentword(''.join(self.source)) - - self.long_line_ignore_cache = ( - set() if long_line_ignore_cache is None - else long_line_ignore_cache) - - # Many fixers are the same even though pep8 categorizes them - # differently. - self.fix_e115 = self.fix_e112 - self.fix_e116 = self.fix_e113 - self.fix_e121 = self._fix_reindent - self.fix_e122 = self._fix_reindent - self.fix_e123 = self._fix_reindent - self.fix_e124 = self._fix_reindent - self.fix_e126 = self._fix_reindent - self.fix_e127 = self._fix_reindent - self.fix_e128 = self._fix_reindent - self.fix_e129 = self._fix_reindent - self.fix_e202 = self.fix_e201 - self.fix_e203 = self.fix_e201 - self.fix_e211 = self.fix_e201 - self.fix_e221 = self.fix_e271 - self.fix_e222 = self.fix_e271 - self.fix_e223 = self.fix_e271 - self.fix_e226 = self.fix_e225 - self.fix_e227 = self.fix_e225 - self.fix_e228 = self.fix_e225 - self.fix_e241 = self.fix_e271 - self.fix_e242 = self.fix_e224 - self.fix_e261 = self.fix_e262 - self.fix_e272 = self.fix_e271 - self.fix_e273 = self.fix_e271 - self.fix_e274 = self.fix_e271 - self.fix_e309 = self.fix_e301 - self.fix_e501 = ( - self.fix_long_line_logically if - options and (options.aggressive >= 2 or options.experimental) else - self.fix_long_line_physically) - self.fix_e703 = self.fix_e702 - self.fix_w293 = self.fix_w291 - - def _fix_source(self, results): - try: - (logical_start, logical_end) = _find_logical(self.source) - logical_support = True - except (SyntaxError, tokenize.TokenError): # pragma: no cover - logical_support = False - - completed_lines = set() - for result in sorted(results, key=_priority_key): - if result['line'] in completed_lines: - continue - - fixed_methodname = 'fix_' + result['id'].lower() - if hasattr(self, fixed_methodname): - fix = getattr(self, fixed_methodname) - - line_index = result['line'] - 1 - original_line = self.source[line_index] - - is_logical_fix = len(inspect.getargspec(fix).args) > 2 - if is_logical_fix: - logical = None - if logical_support: - logical = _get_logical(self.source, - result, - logical_start, - logical_end) - if logical and set(range( - logical[0][0] + 1, - logical[1][0] + 1)).intersection( - completed_lines): - continue - - modified_lines = fix(result, logical) - else: - modified_lines = fix(result) - - if modified_lines is None: - # Force logical fixes to report what they modified. - assert not is_logical_fix - - if self.source[line_index] == original_line: - modified_lines = [] - - if modified_lines: - completed_lines.update(modified_lines) - elif modified_lines == []: # Empty list means no fix - if self.options.verbose >= 2: - print( - '---> Not fixing {f} on line {l}'.format( - f=result['id'], l=result['line']), - file=sys.stderr) - else: # We assume one-line fix when None. - completed_lines.add(result['line']) - else: - if self.options.verbose >= 3: - print( - "---> '{0}' is not defined.".format(fixed_methodname), - file=sys.stderr) - - info = result['info'].strip() - print('---> {0}:{1}:{2}:{3}'.format(self.filename, - result['line'], - result['column'], - info), - file=sys.stderr) - - def fix(self): - """Return a version of the source code with PEP 8 violations fixed.""" - pep8_options = { - 'ignore': self.options.ignore, - 'select': self.options.select, - 'max_line_length': self.options.max_line_length, - } - results = _execute_pep8(pep8_options, self.source) - - if self.options.verbose: - progress = {} - for r in results: - if r['id'] not in progress: - progress[r['id']] = set() - progress[r['id']].add(r['line']) - print('---> {n} issue(s) to fix {progress}'.format( - n=len(results), progress=progress), file=sys.stderr) - - if self.options.line_range: - start, end = self.options.line_range - results = [r for r in results - if start <= r['line'] <= end] - - self._fix_source(filter_results(source=''.join(self.source), - results=results, - aggressive=self.options.aggressive)) - - if self.options.line_range: - # If number of lines has changed then change line_range. - count = sum(sline.count('\n') - for sline in self.source[start - 1:end]) - self.options.line_range[1] = start + count - 1 - - return ''.join(self.source) - - def _fix_reindent(self, result): - """Fix a badly indented line. - - This is done by adding or removing from its initial indent only. - - """ - num_indent_spaces = int(result['info'].split()[1]) - line_index = result['line'] - 1 - target = self.source[line_index] - - self.source[line_index] = ' ' * num_indent_spaces + target.lstrip() - - def fix_e112(self, result): - """Fix under-indented comments.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - if not target.lstrip().startswith('#'): - # Don't screw with invalid syntax. - return [] - - self.source[line_index] = self.indent_word + target - - def fix_e113(self, result): - """Fix over-indented comments.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - indent = _get_indentation(target) - stripped = target.lstrip() - - if not stripped.startswith('#'): - # Don't screw with invalid syntax. - return [] - - self.source[line_index] = indent[1:] + stripped - - def fix_e125(self, result): - """Fix indentation undistinguish from the next logical line.""" - num_indent_spaces = int(result['info'].split()[1]) - line_index = result['line'] - 1 - target = self.source[line_index] - - spaces_to_add = num_indent_spaces - len(_get_indentation(target)) - indent = len(_get_indentation(target)) - modified_lines = [] - - while len(_get_indentation(self.source[line_index])) >= indent: - self.source[line_index] = (' ' * spaces_to_add + - self.source[line_index]) - modified_lines.append(1 + line_index) # Line indexed at 1. - line_index -= 1 - - return modified_lines - - def fix_e201(self, result): - """Remove extraneous whitespace.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - if is_probably_part_of_multiline(target): - return [] - - fixed = fix_whitespace(target, - offset=offset, - replacement='') - - self.source[line_index] = fixed - - def fix_e224(self, result): - """Remove extraneous whitespace around operator.""" - target = self.source[result['line'] - 1] - offset = result['column'] - 1 - fixed = target[:offset] + target[offset:].replace('\t', ' ') - self.source[result['line'] - 1] = fixed - - def fix_e225(self, result): - """Fix missing whitespace around operator.""" - target = self.source[result['line'] - 1] - offset = result['column'] - 1 - fixed = target[:offset] + ' ' + target[offset:] - - # Only proceed if non-whitespace characters match. - # And make sure we don't break the indentation. - if ( - fixed.replace(' ', '') == target.replace(' ', '') and - _get_indentation(fixed) == _get_indentation(target) - ): - self.source[result['line'] - 1] = fixed - else: - return [] - - def fix_e231(self, result): - """Add missing whitespace.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - fixed = target[:offset] + ' ' + target[offset:] - self.source[line_index] = fixed - - def fix_e251(self, result): - """Remove whitespace around parameter '=' sign.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - # This is necessary since pep8 sometimes reports columns that goes - # past the end of the physical line. This happens in cases like, - # foo(bar\n=None) - c = min(result['column'] - 1, - len(target) - 1) - - if target[c].strip(): - fixed = target - else: - fixed = target[:c].rstrip() + target[c:].lstrip() - - # There could be an escaped newline - # - # def foo(a=\ - # 1) - if fixed.endswith(('=\\\n', '=\\\r\n', '=\\\r')): - self.source[line_index] = fixed.rstrip('\n\r \t\\') - self.source[line_index + 1] = self.source[line_index + 1].lstrip() - return [line_index + 1, line_index + 2] # Line indexed at 1 - - self.source[result['line'] - 1] = fixed - - def fix_e262(self, result): - """Fix spacing after comment hash.""" - target = self.source[result['line'] - 1] - offset = result['column'] - - code = target[:offset].rstrip(' \t#') - comment = target[offset:].lstrip(' \t#') - - fixed = code + (' # ' + comment if comment.strip() else '\n') - - self.source[result['line'] - 1] = fixed - - def fix_e271(self, result): - """Fix extraneous whitespace around keywords.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - if is_probably_part_of_multiline(target): - return [] - - fixed = fix_whitespace(target, - offset=offset, - replacement=' ') - - if fixed == target: - return [] - else: - self.source[line_index] = fixed - - def fix_e301(self, result): - """Add missing blank line.""" - cr = '\n' - self.source[result['line'] - 1] = cr + self.source[result['line'] - 1] - - def fix_e302(self, result): - """Add missing 2 blank lines.""" - add_linenum = 2 - int(result['info'].split()[-1]) - cr = '\n' * add_linenum - self.source[result['line'] - 1] = cr + self.source[result['line'] - 1] - - def fix_e303(self, result): - """Remove extra blank lines.""" - delete_linenum = int(result['info'].split('(')[1].split(')')[0]) - 2 - delete_linenum = max(1, delete_linenum) - - # We need to count because pep8 reports an offset line number if there - # are comments. - cnt = 0 - line = result['line'] - 2 - modified_lines = [] - while cnt < delete_linenum and line >= 0: - if not self.source[line].strip(): - self.source[line] = '' - modified_lines.append(1 + line) # Line indexed at 1 - cnt += 1 - line -= 1 - - return modified_lines - - def fix_e304(self, result): - """Remove blank line following function decorator.""" - line = result['line'] - 2 - if not self.source[line].strip(): - self.source[line] = '' - - def fix_e401(self, result): - """Put imports on separate lines.""" - line_index = result['line'] - 1 - target = self.source[line_index] - offset = result['column'] - 1 - - if not target.lstrip().startswith('import'): - return [] - - indentation = re.split(pattern=r'\bimport\b', - string=target, maxsplit=1)[0] - fixed = (target[:offset].rstrip('\t ,') + '\n' + - indentation + 'import ' + target[offset:].lstrip('\t ,')) - self.source[line_index] = fixed - - def fix_long_line_logically(self, result, logical): - """Try to make lines fit within --max-line-length characters.""" - if ( - not logical or - len(logical[2]) == 1 or - self.source[result['line'] - 1].lstrip().startswith('#') - ): - return self.fix_long_line_physically(result) - - start_line_index = logical[0][0] - end_line_index = logical[1][0] - logical_lines = logical[2] - - previous_line = get_item(self.source, start_line_index - 1, default='') - next_line = get_item(self.source, end_line_index + 1, default='') - - single_line = join_logical_line(''.join(logical_lines)) - - try: - fixed = self.fix_long_line( - target=single_line, - previous_line=previous_line, - next_line=next_line, - original=''.join(logical_lines)) - except (SyntaxError, tokenize.TokenError): - return self.fix_long_line_physically(result) - - if fixed: - for line_index in range(start_line_index, end_line_index + 1): - self.source[line_index] = '' - self.source[start_line_index] = fixed - return range(start_line_index + 1, end_line_index + 1) - else: - return [] - - def fix_long_line_physically(self, result): - """Try to make lines fit within --max-line-length characters.""" - line_index = result['line'] - 1 - target = self.source[line_index] - - previous_line = get_item(self.source, line_index - 1, default='') - next_line = get_item(self.source, line_index + 1, default='') - - try: - fixed = self.fix_long_line( - target=target, - previous_line=previous_line, - next_line=next_line, - original=target) - except (SyntaxError, tokenize.TokenError): - return [] - - if fixed: - self.source[line_index] = fixed - return [line_index + 1] - else: - return [] - - def fix_long_line(self, target, previous_line, - next_line, original): - cache_entry = (target, previous_line, next_line) - if cache_entry in self.long_line_ignore_cache: - return [] - - if target.lstrip().startswith('#'): - # Wrap commented lines. - return shorten_comment( - line=target, - max_line_length=self.options.max_line_length, - last_comment=not next_line.lstrip().startswith('#')) - - fixed = get_fixed_long_line( - target=target, - previous_line=previous_line, - original=original, - indent_word=self.indent_word, - max_line_length=self.options.max_line_length, - aggressive=self.options.aggressive, - experimental=self.options.experimental, - verbose=self.options.verbose) - if fixed and not code_almost_equal(original, fixed): - return fixed - else: - self.long_line_ignore_cache.add(cache_entry) - return None - - def fix_e502(self, result): - """Remove extraneous escape of newline.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - self.source[line_index] = target.rstrip('\n\r \t\\') + '\n' - - def fix_e701(self, result): - """Put colon-separated compound statement on separate lines.""" - line_index = result['line'] - 1 - target = self.source[line_index] - c = result['column'] - - fixed_source = (target[:c] + '\n' + - _get_indentation(target) + self.indent_word + - target[c:].lstrip('\n\r \t\\')) - self.source[result['line'] - 1] = fixed_source - return [result['line'], result['line'] + 1] - - def fix_e702(self, result, logical): - """Put semicolon-separated compound statement on separate lines.""" - if not logical: - return [] # pragma: no cover - logical_lines = logical[2] - - line_index = result['line'] - 1 - target = self.source[line_index] - - if target.rstrip().endswith('\\'): - # Normalize '1; \\\n2' into '1; 2'. - self.source[line_index] = target.rstrip('\n \r\t\\') - self.source[line_index + 1] = self.source[line_index + 1].lstrip() - return [line_index + 1, line_index + 2] - - if target.rstrip().endswith(';'): - self.source[line_index] = target.rstrip('\n \r\t;') + '\n' - return [line_index + 1] - - offset = result['column'] - 1 - first = target[:offset].rstrip(';').rstrip() - second = (_get_indentation(logical_lines[0]) + - target[offset:].lstrip(';').lstrip()) - - # find inline commnet - inline_comment = None - if '# ' == target[offset:].lstrip(';').lstrip()[:2]: - inline_comment = target[offset:].lstrip(';') - - if inline_comment: - self.source[line_index] = first + inline_comment - else: - self.source[line_index] = first + '\n' + second - return [line_index + 1] - - def fix_e711(self, result): - """Fix comparison with None.""" - (line_index, offset, target) = get_index_offset_contents(result, - self.source) - - right_offset = offset + 2 - if right_offset >= len(target): - return [] - - left = target[:offset].rstrip() - center = target[offset:right_offset] - right = target[right_offset:].lstrip() - - if not right.startswith('None'): - return [] - - if center.strip() == '==': - new_center = 'is' - elif center.strip() == '!=': - new_center = 'is not' - else: - return [] - - self.source[line_index] = ' '.join([left, new_center, right]) - - def fix_e712(self, result): - """Fix (trivial case of) comparison with boolean.""" - (line_index, offset, target) = get_index_offset_contents(result, - self.source) - - # Handle very easy "not" special cases. - if re.match(r'^\s*if [\w.]+ == False:$', target): - self.source[line_index] = re.sub(r'if ([\w.]+) == False:', - r'if not \1:', target, count=1) - elif re.match(r'^\s*if [\w.]+ != True:$', target): - self.source[line_index] = re.sub(r'if ([\w.]+) != True:', - r'if not \1:', target, count=1) - else: - right_offset = offset + 2 - if right_offset >= len(target): - return [] - - left = target[:offset].rstrip() - center = target[offset:right_offset] - right = target[right_offset:].lstrip() - - # Handle simple cases only. - new_right = None - if center.strip() == '==': - if re.match(r'\bTrue\b', right): - new_right = re.sub(r'\bTrue\b *', '', right, count=1) - elif center.strip() == '!=': - if re.match(r'\bFalse\b', right): - new_right = re.sub(r'\bFalse\b *', '', right, count=1) - - if new_right is None: - return [] - - if new_right[0].isalnum(): - new_right = ' ' + new_right - - self.source[line_index] = left + new_right - - def fix_e713(self, result): - """Fix (trivial case of) non-membership check.""" - (line_index, _, target) = get_index_offset_contents(result, - self.source) - - # Handle very easy case only. - if re.match(r'^\s*if not [\w.]+ in [\w.]+:$', target): - self.source[line_index] = re.sub(r'if not ([\w.]+) in ([\w.]+):', - r'if \1 not in \2:', - target, - count=1) - - def fix_w291(self, result): - """Remove trailing whitespace.""" - fixed_line = self.source[result['line'] - 1].rstrip() - self.source[result['line'] - 1] = fixed_line + '\n' - - def fix_w391(self, _): - """Remove trailing blank lines.""" - blank_count = 0 - for line in reversed(self.source): - line = line.rstrip() - if line: - break - else: - blank_count += 1 - - original_length = len(self.source) - self.source = self.source[:original_length - blank_count] - return range(1, 1 + original_length) - - -def get_index_offset_contents(result, source): - """Return (line_index, column_offset, line_contents).""" - line_index = result['line'] - 1 - return (line_index, - result['column'] - 1, - source[line_index]) - - -def get_fixed_long_line(target, previous_line, original, - indent_word=' ', max_line_length=79, - aggressive=False, experimental=False, verbose=False): - """Break up long line and return result. - - Do this by generating multiple reformatted candidates and then - ranking the candidates to heuristically select the best option. - - """ - indent = _get_indentation(target) - source = target[len(indent):] - assert source.lstrip() == source - - # Check for partial multiline. - tokens = list(generate_tokens(source)) - - candidates = shorten_line( - tokens, source, indent, - indent_word, - max_line_length, - aggressive=aggressive, - experimental=experimental, - previous_line=previous_line) - - # Also sort alphabetically as a tie breaker (for determinism). - candidates = sorted( - sorted(set(candidates).union([target, original])), - key=lambda x: line_shortening_rank( - x, - indent_word, - max_line_length, - experimental=experimental)) - - if verbose >= 4: - print(('-' * 79 + '\n').join([''] + candidates + ['']), - file=wrap_output(sys.stderr, 'utf-8')) - - if candidates: - best_candidate = candidates[0] - # Don't allow things to get longer. - if longest_line_length(best_candidate) > longest_line_length(original): - return None - else: - return best_candidate - - -def longest_line_length(code): - """Return length of longest line.""" - return max(len(line) for line in code.splitlines()) - - -def join_logical_line(logical_line): - """Return single line based on logical line input.""" - indentation = _get_indentation(logical_line) - - return indentation + untokenize_without_newlines( - generate_tokens(logical_line.lstrip())) + '\n' - - -def untokenize_without_newlines(tokens): - """Return source code based on tokens.""" - text = '' - last_row = 0 - last_column = -1 - - for t in tokens: - token_string = t[1] - (start_row, start_column) = t[2] - (end_row, end_column) = t[3] - - if start_row > last_row: - last_column = 0 - if ( - (start_column > last_column or token_string == '\n') and - not text.endswith(' ') - ): - text += ' ' - - if token_string != '\n': - text += token_string - - last_row = end_row - last_column = end_column - - return text.rstrip() - - -def _find_logical(source_lines): - # Make a variable which is the index of all the starts of lines. - logical_start = [] - logical_end = [] - last_newline = True - parens = 0 - for t in generate_tokens(''.join(source_lines)): - if t[0] in [tokenize.COMMENT, tokenize.DEDENT, - tokenize.INDENT, tokenize.NL, - tokenize.ENDMARKER]: - continue - if not parens and t[0] in [tokenize.NEWLINE, tokenize.SEMI]: - last_newline = True - logical_end.append((t[3][0] - 1, t[2][1])) - continue - if last_newline and not parens: - logical_start.append((t[2][0] - 1, t[2][1])) - last_newline = False - if t[0] == tokenize.OP: - if t[1] in '([{': - parens += 1 - elif t[1] in '}])': - parens -= 1 - return (logical_start, logical_end) - - -def _get_logical(source_lines, result, logical_start, logical_end): - """Return the logical line corresponding to the result. - - Assumes input is already E702-clean. - - """ - row = result['line'] - 1 - col = result['column'] - 1 - ls = None - le = None - for i in range(0, len(logical_start), 1): - assert logical_end - x = logical_end[i] - if x[0] > row or (x[0] == row and x[1] > col): - le = x - ls = logical_start[i] - break - if ls is None: - return None - original = source_lines[ls[0]:le[0] + 1] - return ls, le, original - - -def get_item(items, index, default=None): - if 0 <= index < len(items): - return items[index] - else: - return default - - -def reindent(source, indent_size): - """Reindent all lines.""" - reindenter = Reindenter(source) - return reindenter.run(indent_size) - - -def code_almost_equal(a, b): - """Return True if code is similar. - - Ignore whitespace when comparing specific line. - - """ - split_a = split_and_strip_non_empty_lines(a) - split_b = split_and_strip_non_empty_lines(b) - - if len(split_a) != len(split_b): - return False - - for index in range(len(split_a)): - if ''.join(split_a[index].split()) != ''.join(split_b[index].split()): - return False - - return True - - -def split_and_strip_non_empty_lines(text): - """Return lines split by newline. - - Ignore empty lines. - - """ - return [line.strip() for line in text.splitlines() if line.strip()] - - -def fix_e265(source, aggressive=False): # pylint: disable=unused-argument - """Format block comments.""" - if '#' not in source: - # Optimization. - return source - - ignored_line_numbers = multiline_string_lines( - source, - include_docstrings=True) | set(commented_out_code_lines(source)) - - fixed_lines = [] - sio = io.StringIO(source) - for (line_number, line) in enumerate(sio.readlines(), start=1): - if ( - line.lstrip().startswith('#') and - line_number not in ignored_line_numbers - ): - indentation = _get_indentation(line) - line = line.lstrip() - - # Normalize beginning if not a shebang. - if len(line) > 1: - pos = next((index for index, c in enumerate(line) - if c != '#')) - if ( - # Leave multiple spaces like '# ' alone. - (line[:pos].count('#') > 1 or line[1].isalnum()) and - # Leave stylistic outlined blocks alone. - not line.rstrip().endswith('#') - ): - line = '# ' + line.lstrip('# \t') - - fixed_lines.append(indentation + line) - else: - fixed_lines.append(line) - - return ''.join(fixed_lines) - - -def refactor(source, fixer_names, ignore=None, filename=''): - """Return refactored code using lib2to3. - - Skip if ignore string is produced in the refactored code. - - """ - from lib2to3 import pgen2 - try: - new_text = refactor_with_2to3(source, - fixer_names=fixer_names, - filename=filename) - except (pgen2.parse.ParseError, - SyntaxError, - UnicodeDecodeError, - UnicodeEncodeError): - return source - - if ignore: - if ignore in new_text and ignore not in source: - return source - - return new_text - - -def code_to_2to3(select, ignore): - fixes = set() - for code, fix in CODE_TO_2TO3.items(): - if code_match(code, select=select, ignore=ignore): - fixes |= set(fix) - return fixes - - -def fix_2to3(source, - aggressive=True, select=None, ignore=None, filename=''): - """Fix various deprecated code (via lib2to3).""" - if not aggressive: - return source - - select = select or [] - ignore = ignore or [] - - return refactor(source, - code_to_2to3(select=select, - ignore=ignore), - filename=filename) - - -def fix_w602(source, aggressive=True): - """Fix deprecated form of raising exception.""" - if not aggressive: - return source - - return refactor(source, ['raise'], - ignore='with_traceback') - - -def find_newline(source): - """Return type of newline used in source. - - Input is a list of lines. - - """ - assert not isinstance(source, unicode) - - counter = collections.defaultdict(int) - for line in source: - if line.endswith(CRLF): - counter[CRLF] += 1 - elif line.endswith(CR): - counter[CR] += 1 - elif line.endswith(LF): - counter[LF] += 1 - - return (sorted(counter, key=counter.get, reverse=True) or [LF])[0] - - -def _get_indentword(source): - """Return indentation type.""" - indent_word = ' ' # Default in case source has no indentation - try: - for t in generate_tokens(source): - if t[0] == token.INDENT: - indent_word = t[1] - break - except (SyntaxError, tokenize.TokenError): - pass - return indent_word - - -def _get_indentation(line): - """Return leading whitespace.""" - if line.strip(): - non_whitespace_index = len(line) - len(line.lstrip()) - return line[:non_whitespace_index] - else: - return '' - - -def get_diff_text(old, new, filename): - """Return text of unified diff between old and new.""" - newline = '\n' - diff = difflib.unified_diff( - old, new, - 'original/' + filename, - 'fixed/' + filename, - lineterm=newline) - - text = '' - for line in diff: - text += line - - # Work around missing newline (http://bugs.python.org/issue2142). - if text and not line.endswith(newline): - text += newline + r'\ No newline at end of file' + newline - - return text - - -def _priority_key(pep8_result): - """Key for sorting PEP8 results. - - Global fixes should be done first. This is important for things like - indentation. - - """ - priority = [ - # Fix multiline colon-based before semicolon based. - 'e701', - # Break multiline statements early. - 'e702', - # Things that make lines longer. - 'e225', 'e231', - # Remove extraneous whitespace before breaking lines. - 'e201', - # Shorten whitespace in comment before resorting to wrapping. - 'e262' - ] - middle_index = 10000 - lowest_priority = [ - # We need to shorten lines last since the logical fixer can get in a - # loop, which causes us to exit early. - 'e501' - ] - key = pep8_result['id'].lower() - try: - return priority.index(key) - except ValueError: - try: - return middle_index + lowest_priority.index(key) + 1 - except ValueError: - return middle_index - - -def shorten_line(tokens, source, indentation, indent_word, max_line_length, - aggressive=False, experimental=False, previous_line=''): - """Separate line at OPERATOR. - - Multiple candidates will be yielded. - - """ - for candidate in _shorten_line(tokens=tokens, - source=source, - indentation=indentation, - indent_word=indent_word, - aggressive=aggressive, - previous_line=previous_line): - yield candidate - - if aggressive: - for key_token_strings in SHORTEN_OPERATOR_GROUPS: - shortened = _shorten_line_at_tokens( - tokens=tokens, - source=source, - indentation=indentation, - indent_word=indent_word, - key_token_strings=key_token_strings, - aggressive=aggressive) - - if shortened is not None and shortened != source: - yield shortened - - if experimental: - for shortened in _shorten_line_at_tokens_new( - tokens=tokens, - source=source, - indentation=indentation, - max_line_length=max_line_length): - - yield shortened - - -def _shorten_line(tokens, source, indentation, indent_word, - aggressive=False, previous_line=''): - """Separate line at OPERATOR. - - The input is expected to be free of newlines except for inside multiline - strings and at the end. - - Multiple candidates will be yielded. - - """ - for (token_type, - token_string, - start_offset, - end_offset) in token_offsets(tokens): - - if ( - token_type == tokenize.COMMENT and - not is_probably_part_of_multiline(previous_line) and - not is_probably_part_of_multiline(source) and - not source[start_offset + 1:].strip().lower().startswith( - ('noqa', 'pragma:', 'pylint:')) - ): - # Move inline comments to previous line. - first = source[:start_offset] - second = source[start_offset:] - yield (indentation + second.strip() + '\n' + - indentation + first.strip() + '\n') - elif token_type == token.OP and token_string != '=': - # Don't break on '=' after keyword as this violates PEP 8. - - assert token_type != token.INDENT - - first = source[:end_offset] - - second_indent = indentation - if first.rstrip().endswith('('): - second_indent += indent_word - elif '(' in first: - second_indent += ' ' * (1 + first.find('(')) - else: - second_indent += indent_word - - second = (second_indent + source[end_offset:].lstrip()) - if ( - not second.strip() or - second.lstrip().startswith('#') - ): - continue - - # Do not begin a line with a comma - if second.lstrip().startswith(','): - continue - # Do end a line with a dot - if first.rstrip().endswith('.'): - continue - if token_string in '+-*/': - fixed = first + ' \\' + '\n' + second - else: - fixed = first + '\n' + second - - # Only fix if syntax is okay. - if check_syntax(normalize_multiline(fixed) - if aggressive else fixed): - yield indentation + fixed - - -# A convenient way to handle tokens. -Token = collections.namedtuple('Token', ['token_type', 'token_string', - 'spos', 'epos', 'line']) - - -class ReformattedLines(object): - - """The reflowed lines of atoms. - - Each part of the line is represented as an "atom." They can be moved - around when need be to get the optimal formatting. - - """ - - ########################################################################### - # Private Classes - - class _Indent(object): - - """Represent an indentation in the atom stream.""" - - def __init__(self, indent_amt): - self._indent_amt = indent_amt - - def emit(self): - return ' ' * self._indent_amt - - @property - def size(self): - return self._indent_amt - - class _Space(object): - - """Represent a space in the atom stream.""" - - def emit(self): - return ' ' - - @property - def size(self): - return 1 - - class _LineBreak(object): - - """Represent a line break in the atom stream.""" - - def emit(self): - return '\n' - - @property - def size(self): - return 0 - - def __init__(self, max_line_length): - self._max_line_length = max_line_length - self._lines = [] - self._bracket_depth = 0 - self._prev_item = None - self._prev_prev_item = None - - def __repr__(self): - return self.emit() - - ########################################################################### - # Public Methods - - def add(self, obj, indent_amt, break_after_open_bracket): - if isinstance(obj, Atom): - self._add_item(obj, indent_amt) - return - - self._add_container(obj, indent_amt, break_after_open_bracket) - - def add_comment(self, item): - num_spaces = 2 - if len(self._lines) > 1: - if isinstance(self._lines[-1], self._Space): - num_spaces -= 1 - if len(self._lines) > 2: - if isinstance(self._lines[-2], self._Space): - num_spaces -= 1 - - while num_spaces > 0: - self._lines.append(self._Space()) - num_spaces -= 1 - self._lines.append(item) - - def add_indent(self, indent_amt): - self._lines.append(self._Indent(indent_amt)) - - def add_line_break(self, indent): - self._lines.append(self._LineBreak()) - self.add_indent(len(indent)) - - def add_line_break_at(self, index, indent_amt): - self._lines.insert(index, self._LineBreak()) - self._lines.insert(index + 1, self._Indent(indent_amt)) - - def add_space_if_needed(self, curr_text, equal=False): - if ( - not self._lines or isinstance( - self._lines[-1], (self._LineBreak, self._Indent, self._Space)) - ): - return - - prev_text = unicode(self._prev_item) - prev_prev_text = ( - unicode(self._prev_prev_item) if self._prev_prev_item else '') - - if ( - # The previous item was a keyword or identifier and the current - # item isn't an operator that doesn't require a space. - ((self._prev_item.is_keyword or self._prev_item.is_string or - self._prev_item.is_name or self._prev_item.is_number) and - (curr_text[0] not in '([{.,:}])' or - (curr_text[0] == '=' and equal))) or - - # Don't place spaces around a '.', unless it's in an 'import' - # statement. - ((prev_prev_text != 'from' and prev_text[-1] != '.' and - curr_text != 'import') and - - # Don't place a space before a colon. - curr_text[0] != ':' and - - # Don't split up ending brackets by spaces. - ((prev_text[-1] in '}])' and curr_text[0] not in '.,}])') or - - # Put a space after a colon or comma. - prev_text[-1] in ':,' or - - # Put space around '=' if asked to. - (equal and prev_text == '=') or - - # Put spaces around non-unary arithmetic operators. - ((self._prev_prev_item and - (prev_text not in '+-' and - (self._prev_prev_item.is_name or - self._prev_prev_item.is_number or - self._prev_prev_item.is_string)) and - prev_text in ('+', '-', '%', '*', '/', '//', '**', 'in'))))) - ): - self._lines.append(self._Space()) - - def previous_item(self): - """Return the previous non-whitespace item.""" - return self._prev_item - - def fits_on_current_line(self, item_extent): - return self.current_size() + item_extent <= self._max_line_length - - def current_size(self): - """The size of the current line minus the indentation.""" - size = 0 - for item in reversed(self._lines): - size += item.size - if isinstance(item, self._LineBreak): - break - - return size - - def line_empty(self): - return (self._lines and - isinstance(self._lines[-1], - (self._LineBreak, self._Indent))) - - def emit(self): - string = '' - for item in self._lines: - if isinstance(item, self._LineBreak): - string = string.rstrip() - string += item.emit() - - return string.rstrip() + '\n' - - ########################################################################### - # Private Methods - - def _add_item(self, item, indent_amt): - """Add an item to the line. - - Reflow the line to get the best formatting after the item is - inserted. The bracket depth indicates if the item is being - inserted inside of a container or not. - - """ - if self._prev_item and self._prev_item.is_string and item.is_string: - # Place consecutive string literals on separate lines. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - - item_text = unicode(item) - if self._lines and self._bracket_depth: - # Adding the item into a container. - self._prevent_default_initializer_splitting(item, indent_amt) - - if item_text in '.,)]}': - self._split_after_delimiter(item, indent_amt) - - elif self._lines and not self.line_empty(): - # Adding the item outside of a container. - if self.fits_on_current_line(len(item_text)): - self._enforce_space(item) - - else: - # Line break for the new item. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - - self._lines.append(item) - self._prev_item, self._prev_prev_item = item, self._prev_item - - if item_text in '([{': - self._bracket_depth += 1 - - elif item_text in '}])': - self._bracket_depth -= 1 - assert self._bracket_depth >= 0 - - def _add_container(self, container, indent_amt, break_after_open_bracket): - actual_indent = indent_amt + 1 - - if ( - unicode(self._prev_item) != '=' and - not self.line_empty() and - not self.fits_on_current_line( - container.size + self._bracket_depth + 2) - ): - - if unicode(container)[0] == '(' and self._prev_item.is_name: - # Don't split before the opening bracket of a call. - break_after_open_bracket = True - actual_indent = indent_amt + 4 - elif ( - break_after_open_bracket or - unicode(self._prev_item) not in '([{' - ): - # If the container doesn't fit on the current line and the - # current line isn't empty, place the container on the next - # line. - self._lines.append(self._LineBreak()) - self._lines.append(self._Indent(indent_amt)) - break_after_open_bracket = False - else: - actual_indent = self.current_size() + 1 - break_after_open_bracket = False - - if isinstance(container, (ListComprehension, IfExpression)): - actual_indent = indent_amt - - # Increase the continued indentation only if recursing on a - # container. - container.reflow(self, ' ' * actual_indent, - break_after_open_bracket=break_after_open_bracket) - - def _prevent_default_initializer_splitting(self, item, indent_amt): - """Prevent splitting between a default initializer. - - When there is a default initializer, it's best to keep it all on - the same line. It's nicer and more readable, even if it goes - over the maximum allowable line length. This goes back along the - current line to determine if we have a default initializer, and, - if so, to remove extraneous whitespaces and add a line - break/indent before it if needed. - - """ - if unicode(item) == '=': - # This is the assignment in the initializer. Just remove spaces for - # now. - self._delete_whitespace() - return - - if (not self._prev_item or not self._prev_prev_item or - unicode(self._prev_item) != '='): - return - - self._delete_whitespace() - prev_prev_index = self._lines.index(self._prev_prev_item) - - if ( - isinstance(self._lines[prev_prev_index - 1], self._Indent) or - self.fits_on_current_line(item.size + 1) - ): - # The default initializer is already the only item on this line. - # Don't insert a newline here. - return - - # Replace the space with a newline/indent combo. - if isinstance(self._lines[prev_prev_index - 1], self._Space): - del self._lines[prev_prev_index - 1] - - self.add_line_break_at(self._lines.index(self._prev_prev_item), - indent_amt) - - def _split_after_delimiter(self, item, indent_amt): - """Split the line only after a delimiter.""" - self._delete_whitespace() - - if self.fits_on_current_line(item.size): - return - - last_space = None - for item in reversed(self._lines): - if ( - last_space and - (not isinstance(item, Atom) or not item.is_colon) - ): - break - else: - last_space = None - if isinstance(item, self._Space): - last_space = item - if isinstance(item, (self._LineBreak, self._Indent)): - return - - if not last_space: - return - - self.add_line_break_at(self._lines.index(last_space), indent_amt) - - def _enforce_space(self, item): - """Enforce a space in certain situations. - - There are cases where we will want a space where normally we - wouldn't put one. This just enforces the addition of a space. - - """ - if isinstance(self._lines[-1], - (self._Space, self._LineBreak, self._Indent)): - return - - if not self._prev_item: - return - - item_text = unicode(item) - prev_text = unicode(self._prev_item) - - # Prefer a space around a '.' in an import statement, and between the - # 'import' and '('. - if ( - (item_text == '.' and prev_text == 'from') or - (item_text == 'import' and prev_text == '.') or - (item_text == '(' and prev_text == 'import') - ): - self._lines.append(self._Space()) - - def _delete_whitespace(self): - """Delete all whitespace from the end of the line.""" - while isinstance(self._lines[-1], (self._Space, self._LineBreak, - self._Indent)): - del self._lines[-1] - - -class Atom(object): - - """The smallest unbreakable unit that can be reflowed.""" - - def __init__(self, atom): - self._atom = atom - - def __repr__(self): - return self._atom.token_string - - def __len__(self): - return self.size - - def reflow( - self, reflowed_lines, continued_indent, extent, - break_after_open_bracket=False, - is_list_comp_or_if_expr=False, - next_is_dot=False - ): - if self._atom.token_type == tokenize.COMMENT: - reflowed_lines.add_comment(self) - return - - total_size = extent if extent else self.size - - if self._atom.token_string not in ',:([{}])': - # Some atoms will need an extra 1-sized space token after them. - total_size += 1 - - prev_item = reflowed_lines.previous_item() - if ( - not is_list_comp_or_if_expr and - not reflowed_lines.fits_on_current_line(total_size) and - not (next_is_dot and - reflowed_lines.fits_on_current_line(self.size + 1)) and - not reflowed_lines.line_empty() and - not self.is_colon and - not (prev_item and prev_item.is_name and - unicode(self) == '(') - ): - # Start a new line if there is already something on the line and - # adding this atom would make it go over the max line length. - reflowed_lines.add_line_break(continued_indent) - else: - reflowed_lines.add_space_if_needed(unicode(self)) - - reflowed_lines.add(self, len(continued_indent), - break_after_open_bracket) - - def emit(self): - return self.__repr__() - - @property - def is_keyword(self): - return keyword.iskeyword(self._atom.token_string) - - @property - def is_string(self): - return self._atom.token_type == tokenize.STRING - - @property - def is_name(self): - return self._atom.token_type == tokenize.NAME - - @property - def is_number(self): - return self._atom.token_type == tokenize.NUMBER - - @property - def is_comma(self): - return self._atom.token_string == ',' - - @property - def is_colon(self): - return self._atom.token_string == ':' - - @property - def size(self): - return len(self._atom.token_string) - - -class Container(object): - - """Base class for all container types.""" - - def __init__(self, items): - self._items = items - - def __repr__(self): - string = '' - last_was_keyword = False - - for item in self._items: - if item.is_comma: - string += ', ' - elif item.is_colon: - string += ': ' - else: - item_string = unicode(item) - if ( - string and - (last_was_keyword or - (not string.endswith(tuple('([{,.:}]) ')) and - not item_string.startswith(tuple('([{,.:}])')))) - ): - string += ' ' - string += item_string - - last_was_keyword = item.is_keyword - return string - - def __iter__(self): - for element in self._items: - yield element - - def __getitem__(self, idx): - return self._items[idx] - - def reflow(self, reflowed_lines, continued_indent, - break_after_open_bracket=False): - last_was_container = False - for (index, item) in enumerate(self._items): - next_item = get_item(self._items, index + 1) - - if isinstance(item, Atom): - is_list_comp_or_if_expr = ( - isinstance(self, (ListComprehension, IfExpression))) - item.reflow(reflowed_lines, continued_indent, - self._get_extent(index), - is_list_comp_or_if_expr=is_list_comp_or_if_expr, - next_is_dot=(next_item and - unicode(next_item) == '.')) - if last_was_container and item.is_comma: - reflowed_lines.add_line_break(continued_indent) - last_was_container = False - else: # isinstance(item, Container) - reflowed_lines.add(item, len(continued_indent), - break_after_open_bracket) - last_was_container = not isinstance(item, (ListComprehension, - IfExpression)) - - if ( - break_after_open_bracket and index == 0 and - # Prefer to keep empty containers together instead of - # separating them. - unicode(item) == self.open_bracket and - (not next_item or unicode(next_item) != self.close_bracket) and - (len(self._items) != 3 or not isinstance(next_item, Atom)) - ): - reflowed_lines.add_line_break(continued_indent) - break_after_open_bracket = False - else: - next_next_item = get_item(self._items, index + 2) - if ( - unicode(item) not in ['.', '%', 'in'] and - next_item and not isinstance(next_item, Container) and - unicode(next_item) != ':' and - next_next_item and (not isinstance(next_next_item, Atom) or - unicode(next_item) == 'not') and - not reflowed_lines.line_empty() and - not reflowed_lines.fits_on_current_line( - self._get_extent(index + 1) + 2) - ): - reflowed_lines.add_line_break(continued_indent) - - def _get_extent(self, index): - """The extent of the full element. - - E.g., the length of a function call or keyword. - - """ - extent = 0 - prev_item = get_item(self._items, index - 1) - seen_dot = prev_item and unicode(prev_item) == '.' - while index < len(self._items): - item = get_item(self._items, index) - index += 1 - - if isinstance(item, (ListComprehension, IfExpression)): - break - - if isinstance(item, Container): - if prev_item and prev_item.is_name: - if seen_dot: - extent += 1 - else: - extent += item.size - - prev_item = item - continue - elif (unicode(item) not in ['.', '=', ':', 'not'] and - not item.is_name and not item.is_string): - break - - if unicode(item) == '.': - seen_dot = True - - extent += item.size - prev_item = item - - return extent - - @property - def is_string(self): - return False - - @property - def size(self): - return len(self.__repr__()) - - @property - def is_keyword(self): - return False - - @property - def is_name(self): - return False - - @property - def is_comma(self): - return False - - @property - def is_colon(self): - return False - - @property - def open_bracket(self): - return None - - @property - def close_bracket(self): - return None - - -class Tuple(Container): - - """A high-level representation of a tuple.""" - - @property - def open_bracket(self): - return '(' - - @property - def close_bracket(self): - return ')' - - -class List(Container): - - """A high-level representation of a list.""" - - @property - def open_bracket(self): - return '[' - - @property - def close_bracket(self): - return ']' - - -class DictOrSet(Container): - - """A high-level representation of a dictionary or set.""" - - @property - def open_bracket(self): - return '{' - - @property - def close_bracket(self): - return '}' - - -class ListComprehension(Container): - - """A high-level representation of a list comprehension.""" - - @property - def size(self): - length = 0 - for item in self._items: - if isinstance(item, IfExpression): - break - length += item.size - return length - - -class IfExpression(Container): - - """A high-level representation of an if-expression.""" - - -def _parse_container(tokens, index, for_or_if=None): - """Parse a high-level container, such as a list, tuple, etc.""" - - # Store the opening bracket. - items = [Atom(Token(*tokens[index]))] - index += 1 - - num_tokens = len(tokens) - while index < num_tokens: - tok = Token(*tokens[index]) - - if tok.token_string in ',)]}': - # First check if we're at the end of a list comprehension or - # if-expression. Don't add the ending token as part of the list - # comprehension or if-expression, because they aren't part of those - # constructs. - if for_or_if == 'for': - return (ListComprehension(items), index - 1) - - elif for_or_if == 'if': - return (IfExpression(items), index - 1) - - # We've reached the end of a container. - items.append(Atom(tok)) - - # If not, then we are at the end of a container. - if tok.token_string == ')': - # The end of a tuple. - return (Tuple(items), index) - - elif tok.token_string == ']': - # The end of a list. - return (List(items), index) - - elif tok.token_string == '}': - # The end of a dictionary or set. - return (DictOrSet(items), index) - - elif tok.token_string in '([{': - # A sub-container is being defined. - (container, index) = _parse_container(tokens, index) - items.append(container) - - elif tok.token_string == 'for': - (container, index) = _parse_container(tokens, index, 'for') - items.append(container) - - elif tok.token_string == 'if': - (container, index) = _parse_container(tokens, index, 'if') - items.append(container) - - else: - items.append(Atom(tok)) - - index += 1 - - return (None, None) - - -def _parse_tokens(tokens): - """Parse the tokens. - - This converts the tokens into a form where we can manipulate them - more easily. - - """ - - index = 0 - parsed_tokens = [] - - num_tokens = len(tokens) - while index < num_tokens: - tok = Token(*tokens[index]) - - assert tok.token_type != token.INDENT - if tok.token_type == tokenize.NEWLINE: - # There's only one newline and it's at the end. - break - - if tok.token_string in '([{': - (container, index) = _parse_container(tokens, index) - if not container: - return None - parsed_tokens.append(container) - else: - parsed_tokens.append(Atom(tok)) - - index += 1 - - return parsed_tokens - - -def _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line): - """Reflow the lines so that it looks nice.""" - - if unicode(parsed_tokens[0]) == 'def': - # A function definition gets indented a bit more. - continued_indent = indentation + ' ' * 2 * DEFAULT_INDENT_SIZE - else: - continued_indent = indentation + ' ' * DEFAULT_INDENT_SIZE - - break_after_open_bracket = not start_on_prefix_line - - lines = ReformattedLines(max_line_length) - lines.add_indent(len(indentation.lstrip('\r\n'))) - - if not start_on_prefix_line: - # If splitting after the opening bracket will cause the first element - # to be aligned weirdly, don't try it. - first_token = get_item(parsed_tokens, 0) - second_token = get_item(parsed_tokens, 1) - - if ( - first_token and second_token and - unicode(second_token)[0] == '(' and - len(indentation) + len(first_token) + 1 == len(continued_indent) - ): - return None - - for item in parsed_tokens: - lines.add_space_if_needed(unicode(item), equal=True) - - save_continued_indent = continued_indent - if start_on_prefix_line and isinstance(item, Container): - start_on_prefix_line = False - continued_indent = ' ' * (lines.current_size() + 1) - - item.reflow(lines, continued_indent, break_after_open_bracket) - continued_indent = save_continued_indent - - return lines.emit() - - -def _shorten_line_at_tokens_new(tokens, source, indentation, - max_line_length): - """Shorten the line taking its length into account. - - The input is expected to be free of newlines except for inside - multiline strings and at the end. - - """ - # Yield the original source so to see if it's a better choice than the - # shortened candidate lines we generate here. - yield indentation + source - - parsed_tokens = _parse_tokens(tokens) - - if parsed_tokens: - # Perform two reflows. The first one starts on the same line as the - # prefix. The second starts on the line after the prefix. - fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line=True) - if fixed and check_syntax(normalize_multiline(fixed.lstrip())): - yield fixed - - fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, - start_on_prefix_line=False) - if fixed and check_syntax(normalize_multiline(fixed.lstrip())): - yield fixed - - -def _shorten_line_at_tokens(tokens, source, indentation, indent_word, - key_token_strings, aggressive): - """Separate line by breaking at tokens in key_token_strings. - - The input is expected to be free of newlines except for inside - multiline strings and at the end. - - """ - offsets = [] - for (index, _t) in enumerate(token_offsets(tokens)): - (token_type, - token_string, - start_offset, - end_offset) = _t - - assert token_type != token.INDENT - - if token_string in key_token_strings: - # Do not break in containers with zero or one items. - unwanted_next_token = { - '(': ')', - '[': ']', - '{': '}'}.get(token_string) - if unwanted_next_token: - if ( - get_item(tokens, - index + 1, - default=[None, None])[1] == unwanted_next_token or - get_item(tokens, - index + 2, - default=[None, None])[1] == unwanted_next_token - ): - continue - - if ( - index > 2 and token_string == '(' and - tokens[index - 1][1] in ',(%[' - ): - # Don't split after a tuple start, or before a tuple start if - # the tuple is in a list. - continue - - if end_offset < len(source) - 1: - # Don't split right before newline. - offsets.append(end_offset) - else: - # Break at adjacent strings. These were probably meant to be on - # separate lines in the first place. - previous_token = get_item(tokens, index - 1) - if ( - token_type == tokenize.STRING and - previous_token and previous_token[0] == tokenize.STRING - ): - offsets.append(start_offset) - - current_indent = None - fixed = None - for line in split_at_offsets(source, offsets): - if fixed: - fixed += '\n' + current_indent + line - - for symbol in '([{': - if line.endswith(symbol): - current_indent += indent_word - else: - # First line. - fixed = line - assert not current_indent - current_indent = indent_word - - assert fixed is not None - - if check_syntax(normalize_multiline(fixed) - if aggressive > 1 else fixed): - return indentation + fixed - else: - return None - - -def token_offsets(tokens): - """Yield tokens and offsets.""" - end_offset = 0 - previous_end_row = 0 - previous_end_column = 0 - for t in tokens: - token_type = t[0] - token_string = t[1] - (start_row, start_column) = t[2] - (end_row, end_column) = t[3] - - # Account for the whitespace between tokens. - end_offset += start_column - if previous_end_row == start_row: - end_offset -= previous_end_column - - # Record the start offset of the token. - start_offset = end_offset - - # Account for the length of the token itself. - end_offset += len(token_string) - - yield (token_type, - token_string, - start_offset, - end_offset) - - previous_end_row = end_row - previous_end_column = end_column - - -def normalize_multiline(line): - """Normalize multiline-related code that will cause syntax error. - - This is for purposes of checking syntax. - - """ - if line.startswith('def ') and line.rstrip().endswith(':'): - return line + ' pass' - elif line.startswith('return '): - return 'def _(): ' + line - elif line.startswith('@'): - return line + 'def _(): pass' - elif line.startswith('class '): - return line + ' pass' - elif line.startswith(('if ', 'elif ', 'for ', 'while ')): - return line + ' pass' - else: - return line - - -def fix_whitespace(line, offset, replacement): - """Replace whitespace at offset and return fixed line.""" - # Replace escaped newlines too - left = line[:offset].rstrip('\n\r \t\\') - right = line[offset:].lstrip('\n\r \t\\') - if right.startswith('#'): - return line - else: - return left + replacement + right - - -def _execute_pep8(pep8_options, source): - """Execute pep8 via python method calls.""" - class QuietReport(pep8.BaseReport): - - """Version of checker that does not print.""" - - def __init__(self, options): - super(QuietReport, self).__init__(options) - self.__full_error_results = [] - - def error(self, line_number, offset, text, check): - """Collect errors.""" - code = super(QuietReport, self).error(line_number, - offset, - text, - check) - if code: - self.__full_error_results.append( - {'id': code, - 'line': line_number, - 'column': offset + 1, - 'info': text}) - - def full_error_results(self): - """Return error results in detail. - - Results are in the form of a list of dictionaries. Each - dictionary contains 'id', 'line', 'column', and 'info'. - - """ - return self.__full_error_results - - checker = pep8.Checker('', lines=source, - reporter=QuietReport, **pep8_options) - checker.check_all() - return checker.report.full_error_results() - - -def _remove_leading_and_normalize(line): - return line.lstrip().rstrip(CR + LF) + '\n' - - -class Reindenter(object): - - """Reindents badly-indented code to uniformly use four-space indentation. - - Released to the public domain, by Tim Peters, 03 October 2000. - - """ - - def __init__(self, input_text): - sio = io.StringIO(input_text) - source_lines = sio.readlines() - - self.string_content_line_numbers = multiline_string_lines(input_text) - - # File lines, rstripped & tab-expanded. Dummy at start is so - # that we can use tokenize's 1-based line numbering easily. - # Note that a line is all-blank iff it is a newline. - self.lines = [] - for line_number, line in enumerate(source_lines, start=1): - # Do not modify if inside a multiline string. - if line_number in self.string_content_line_numbers: - self.lines.append(line) - else: - # Only expand leading tabs. - self.lines.append(_get_indentation(line).expandtabs() + - _remove_leading_and_normalize(line)) - - self.lines.insert(0, None) - self.index = 1 # index into self.lines of next line - self.input_text = input_text - - def run(self, indent_size=DEFAULT_INDENT_SIZE): - """Fix indentation and return modified line numbers. - - Line numbers are indexed at 1. - - """ - if indent_size < 1: - return self.input_text - - try: - stats = _reindent_stats(tokenize.generate_tokens(self.getline)) - except (SyntaxError, tokenize.TokenError): - return self.input_text - # Remove trailing empty lines. - lines = self.lines - # Sentinel. - stats.append((len(lines), 0)) - # Map count of leading spaces to # we want. - have2want = {} - # Program after transformation. - after = [] - # Copy over initial empty lines -- there's nothing to do until - # we see a line with *something* on it. - i = stats[0][0] - after.extend(lines[1:i]) - for i in range(len(stats) - 1): - thisstmt, thislevel = stats[i] - nextstmt = stats[i + 1][0] - have = _leading_space_count(lines[thisstmt]) - want = thislevel * indent_size - if want < 0: - # A comment line. - if have: - # An indented comment line. If we saw the same - # indentation before, reuse what it most recently - # mapped to. - want = have2want.get(have, -1) - if want < 0: - # Then it probably belongs to the next real stmt. - for j in range(i + 1, len(stats) - 1): - jline, jlevel = stats[j] - if jlevel >= 0: - if have == _leading_space_count(lines[jline]): - want = jlevel * indent_size - break - if want < 0: # Maybe it's a hanging - # comment like this one, - # in which case we should shift it like its base - # line got shifted. - for j in range(i - 1, -1, -1): - jline, jlevel = stats[j] - if jlevel >= 0: - want = (have + _leading_space_count( - after[jline - 1]) - - _leading_space_count(lines[jline])) - break - if want < 0: - # Still no luck -- leave it alone. - want = have - else: - want = 0 - assert want >= 0 - have2want[have] = want - diff = want - have - if diff == 0 or have == 0: - after.extend(lines[thisstmt:nextstmt]) - else: - for line_number, line in enumerate(lines[thisstmt:nextstmt], - start=thisstmt): - if line_number in self.string_content_line_numbers: - after.append(line) - elif diff > 0: - if line == '\n': - after.append(line) - else: - after.append(' ' * diff + line) - else: - remove = min(_leading_space_count(line), -diff) - after.append(line[remove:]) - - return ''.join(after) - - def getline(self): - """Line-getter for tokenize.""" - if self.index >= len(self.lines): - line = '' - else: - line = self.lines[self.index] - self.index += 1 - return line - - -def _reindent_stats(tokens): - """Return list of (lineno, indentlevel) pairs. - - One for each stmt and comment line. indentlevel is -1 for comment lines, as - a signal that tokenize doesn't know what to do about them; indeed, they're - our headache! - - """ - find_stmt = 1 # Next token begins a fresh stmt? - level = 0 # Current indent level. - stats = [] - - for t in tokens: - token_type = t[0] - sline = t[2][0] - line = t[4] - - if token_type == tokenize.NEWLINE: - # A program statement, or ENDMARKER, will eventually follow, - # after some (possibly empty) run of tokens of the form - # (NL | COMMENT)* (INDENT | DEDENT+)? - find_stmt = 1 - - elif token_type == tokenize.INDENT: - find_stmt = 1 - level += 1 - - elif token_type == tokenize.DEDENT: - find_stmt = 1 - level -= 1 - - elif token_type == tokenize.COMMENT: - if find_stmt: - stats.append((sline, -1)) - # But we're still looking for a new stmt, so leave - # find_stmt alone. - - elif token_type == tokenize.NL: - pass - - elif find_stmt: - # This is the first "real token" following a NEWLINE, so it - # must be the first token of the next program statement, or an - # ENDMARKER. - find_stmt = 0 - if line: # Not endmarker. - stats.append((sline, level)) - - return stats - - -def _leading_space_count(line): - """Return number of leading spaces in line.""" - i = 0 - while i < len(line) and line[i] == ' ': - i += 1 - return i - - -def refactor_with_2to3(source_text, fixer_names, filename=''): - """Use lib2to3 to refactor the source. - - Return the refactored source code. - - """ - from lib2to3.refactor import RefactoringTool - fixers = ['lib2to3.fixes.fix_' + name for name in fixer_names] - tool = RefactoringTool(fixer_names=fixers, explicit=fixers) - - from lib2to3.pgen2 import tokenize as lib2to3_tokenize - try: - # The name parameter is necessary particularly for the "import" fixer. - return unicode(tool.refactor_string(source_text, name=filename)) - except lib2to3_tokenize.TokenError: - return source_text - - -def check_syntax(code): - """Return True if syntax is okay.""" - try: - return compile(code, '', 'exec') - except (SyntaxError, TypeError, UnicodeDecodeError): - return False - - -def filter_results(source, results, aggressive): - """Filter out spurious reports from pep8. - - If aggressive is True, we allow possibly unsafe fixes (E711, E712). - - """ - non_docstring_string_line_numbers = multiline_string_lines( - source, include_docstrings=False) - all_string_line_numbers = multiline_string_lines( - source, include_docstrings=True) - - commented_out_code_line_numbers = commented_out_code_lines(source) - - has_e901 = any(result['id'].lower() == 'e901' for result in results) - - for r in results: - issue_id = r['id'].lower() - - if r['line'] in non_docstring_string_line_numbers: - if issue_id.startswith(('e1', 'e501', 'w191')): - continue - - if r['line'] in all_string_line_numbers: - if issue_id in ['e501']: - continue - - # We must offset by 1 for lines that contain the trailing contents of - # multiline strings. - if not aggressive and (r['line'] + 1) in all_string_line_numbers: - # Do not modify multiline strings in non-aggressive mode. Remove - # trailing whitespace could break doctests. - if issue_id.startswith(('w29', 'w39')): - continue - - if aggressive <= 0: - if issue_id.startswith(('e711', 'w6')): - continue - - if aggressive <= 1: - if issue_id.startswith(('e712', 'e713')): - continue - - if r['line'] in commented_out_code_line_numbers: - if issue_id.startswith(('e26', 'e501')): - continue - - # Do not touch indentation if there is a token error caused by - # incomplete multi-line statement. Otherwise, we risk screwing up the - # indentation. - if has_e901: - if issue_id.startswith(('e1', 'e7')): - continue - - yield r - - -def multiline_string_lines(source, include_docstrings=False): - """Return line numbers that are within multiline strings. - - The line numbers are indexed at 1. - - Docstrings are ignored. - - """ - line_numbers = set() - previous_token_type = '' - try: - for t in generate_tokens(source): - token_type = t[0] - start_row = t[2][0] - end_row = t[3][0] - - if token_type == tokenize.STRING and start_row != end_row: - if ( - include_docstrings or - previous_token_type != tokenize.INDENT - ): - # We increment by one since we want the contents of the - # string. - line_numbers |= set(range(1 + start_row, 1 + end_row)) - - previous_token_type = token_type - except (SyntaxError, tokenize.TokenError): - pass - - return line_numbers - - -def commented_out_code_lines(source): - """Return line numbers of comments that are likely code. - - Commented-out code is bad practice, but modifying it just adds even more - clutter. - - """ - line_numbers = [] - try: - for t in generate_tokens(source): - token_type = t[0] - token_string = t[1] - start_row = t[2][0] - line = t[4] - - # Ignore inline comments. - if not line.lstrip().startswith('#'): - continue - - if token_type == tokenize.COMMENT: - stripped_line = token_string.lstrip('#').strip() - if ( - ' ' in stripped_line and - '#' not in stripped_line and - check_syntax(stripped_line) - ): - line_numbers.append(start_row) - except (SyntaxError, tokenize.TokenError): - pass - - return line_numbers - - -def shorten_comment(line, max_line_length, last_comment=False): - """Return trimmed or split long comment line. - - If there are no comments immediately following it, do a text wrap. - Doing this wrapping on all comments in general would lead to jagged - comment text. - - """ - assert len(line) > max_line_length - line = line.rstrip() - - # PEP 8 recommends 72 characters for comment text. - indentation = _get_indentation(line) + '# ' - max_line_length = min(max_line_length, - len(indentation) + 72) - - MIN_CHARACTER_REPEAT = 5 - if ( - len(line) - len(line.rstrip(line[-1])) >= MIN_CHARACTER_REPEAT and - not line[-1].isalnum() - ): - # Trim comments that end with things like --------- - return line[:max_line_length] + '\n' - elif last_comment and re.match(r'\s*#+\s*\w+', line): - split_lines = textwrap.wrap(line.lstrip(' \t#'), - initial_indent=indentation, - subsequent_indent=indentation, - width=max_line_length, - break_long_words=False, - break_on_hyphens=False) - return '\n'.join(split_lines) + '\n' - else: - return line + '\n' - - -def normalize_line_endings(lines, newline): - """Return fixed line endings. - - All lines will be modified to use the most common line ending. - - """ - return [line.rstrip('\n\r') + newline for line in lines] - - -def mutual_startswith(a, b): - return b.startswith(a) or a.startswith(b) - - -def code_match(code, select, ignore): - if ignore: - assert not isinstance(ignore, unicode) - for ignored_code in [c.strip() for c in ignore]: - if mutual_startswith(code.lower(), ignored_code.lower()): - return False - - if select: - assert not isinstance(select, unicode) - for selected_code in [c.strip() for c in select]: - if mutual_startswith(code.lower(), selected_code.lower()): - return True - return False - - return True - - -def fix_code(source, options=None, encoding=None, apply_config=False): - """Return fixed source code. - - "encoding" will be used to decode "source" if it is a byte string. - - """ - options = _get_options(options, apply_config) - - if not isinstance(source, unicode): - source = source.decode(encoding or get_encoding()) - - sio = io.StringIO(source) - return fix_lines(sio.readlines(), options=options) - - -def _get_options(raw_options, apply_config): - """Return parsed options.""" - if not raw_options: - return parse_args([''], apply_config=apply_config) - - if isinstance(raw_options, dict): - options = parse_args([''], apply_config=apply_config) - for name, value in raw_options.items(): - if not hasattr(options, name): - raise ValueError("No such option '{}'".format(name)) - - # Check for very basic type errors. - expected_type = type(getattr(options, name)) - if not isinstance(expected_type, (str, unicode)): - if isinstance(value, (str, unicode)): - raise ValueError( - "Option '{}' should not be a string".format(name)) - setattr(options, name, value) - else: - options = raw_options - - return options - - -def fix_lines(source_lines, options, filename=''): - """Return fixed source code.""" - # Transform everything to line feed. Then change them back to original - # before returning fixed source code. - original_newline = find_newline(source_lines) - tmp_source = ''.join(normalize_line_endings(source_lines, '\n')) - - # Keep a history to break out of cycles. - previous_hashes = set() - - if options.line_range: - # Disable "apply_local_fixes()" for now due to issue #175. - fixed_source = tmp_source - else: - # Apply global fixes only once (for efficiency). - fixed_source = apply_global_fixes(tmp_source, - options, - filename=filename) - - passes = 0 - long_line_ignore_cache = set() - while hash(fixed_source) not in previous_hashes: - if options.pep8_passes >= 0 and passes > options.pep8_passes: - break - passes += 1 - - previous_hashes.add(hash(fixed_source)) - - tmp_source = copy.copy(fixed_source) - - fix = FixPEP8( - filename, - options, - contents=tmp_source, - long_line_ignore_cache=long_line_ignore_cache) - - fixed_source = fix.fix() - - sio = io.StringIO(fixed_source) - return ''.join(normalize_line_endings(sio.readlines(), original_newline)) - - -def fix_file(filename, options=None, output=None, apply_config=False): - if not options: - options = parse_args([filename], apply_config=apply_config) - - original_source = readlines_from_file(filename) - - fixed_source = original_source - - if options.in_place or output: - encoding = detect_encoding(filename) - - if output: - output = LineEndingWrapper(wrap_output(output, encoding=encoding)) - - fixed_source = fix_lines(fixed_source, options, filename=filename) - - if options.diff: - new = io.StringIO(fixed_source) - new = new.readlines() - diff = get_diff_text(original_source, new, filename) - if output: - output.write(diff) - output.flush() - else: - return diff - elif options.in_place: - fp = open_with_encoding(filename, encoding=encoding, - mode='w') - fp.write(fixed_source) - fp.close() - else: - if output: - output.write(fixed_source) - output.flush() - else: - return fixed_source - - -def global_fixes(): - """Yield multiple (code, function) tuples.""" - for function in list(globals().values()): - if inspect.isfunction(function): - arguments = inspect.getargspec(function)[0] - if arguments[:1] != ['source']: - continue - - code = extract_code_from_function(function) - if code: - yield (code, function) - - -def apply_global_fixes(source, options, where='global', filename=''): - """Run global fixes on source code. - - These are fixes that only need be done once (unlike those in - FixPEP8, which are dependent on pep8). - - """ - if any(code_match(code, select=options.select, ignore=options.ignore) - for code in ['E101', 'E111']): - source = reindent(source, - indent_size=options.indent_size) - - for (code, function) in global_fixes(): - if code_match(code, select=options.select, ignore=options.ignore): - if options.verbose: - print('---> Applying {0} fix for {1}'.format(where, - code.upper()), - file=sys.stderr) - source = function(source, - aggressive=options.aggressive) - - source = fix_2to3(source, - aggressive=options.aggressive, - select=options.select, - ignore=options.ignore, - filename=filename) - - return source - - -def extract_code_from_function(function): - """Return code handled by function.""" - if not function.__name__.startswith('fix_'): - return None - - code = re.sub('^fix_', '', function.__name__) - if not code: - return None - - try: - int(code[1:]) - except ValueError: - return None - - return code - - -def create_parser(): - """Return command-line parser.""" - # Do import locally to be friendly to those who use autopep8 as a library - # and are supporting Python 2.6. - import argparse - - parser = argparse.ArgumentParser(description=docstring_summary(__doc__), - prog='autopep8') - parser.add_argument('--version', action='version', - version='%(prog)s ' + __version__) - parser.add_argument('-v', '--verbose', action='count', - default=0, - help='print verbose messages; ' - 'multiple -v result in more verbose messages') - parser.add_argument('-d', '--diff', action='store_true', - help='print the diff for the fixed source') - parser.add_argument('-i', '--in-place', action='store_true', - help='make changes to files in place') - parser.add_argument('--global-config', metavar='filename', - default=DEFAULT_CONFIG, - help='path to a global pep8 config file; if this file ' - 'does not exist then this is ignored ' - '(default: {0})'.format(DEFAULT_CONFIG)) - parser.add_argument('--ignore-local-config', action='store_true', - help="don't look for and apply local config files; " - 'if not passed, defaults are updated with any ' - "config files in the project's root directory") - parser.add_argument('-r', '--recursive', action='store_true', - help='run recursively over directories; ' - 'must be used with --in-place or --diff') - parser.add_argument('-j', '--jobs', type=int, metavar='n', default=1, - help='number of parallel jobs; ' - 'match CPU count if value is less than 1') - parser.add_argument('-p', '--pep8-passes', metavar='n', - default=-1, type=int, - help='maximum number of additional pep8 passes ' - '(default: infinite)') - parser.add_argument('-a', '--aggressive', action='count', default=0, - help='enable non-whitespace changes; ' - 'multiple -a result in more aggressive changes') - parser.add_argument('--experimental', action='store_true', - help='enable experimental fixes') - parser.add_argument('--exclude', metavar='globs', - help='exclude file/directory names that match these ' - 'comma-separated globs') - parser.add_argument('--list-fixes', action='store_true', - help='list codes for fixes; ' - 'used by --ignore and --select') - parser.add_argument('--ignore', metavar='errors', default='', - help='do not fix these errors/warnings ' - '(default: {0})'.format(DEFAULT_IGNORE)) - parser.add_argument('--select', metavar='errors', default='', - help='fix only these errors/warnings (e.g. E4,W)') - parser.add_argument('--max-line-length', metavar='n', default=79, type=int, - help='set maximum allowed line length ' - '(default: %(default)s)') - parser.add_argument('--line-range', '--range', metavar='line', - default=None, type=int, nargs=2, - help='only fix errors found within this inclusive ' - 'range of line numbers (e.g. 1 99); ' - 'line numbers are indexed at 1') - parser.add_argument('--indent-size', default=DEFAULT_INDENT_SIZE, - type=int, metavar='n', - help='number of spaces per indent level ' - '(default %(default)s)') - parser.add_argument('files', nargs='*', - help="files to format or '-' for standard in") - - return parser - - -def parse_args(arguments, apply_config=False): - """Parse command-line options.""" - parser = create_parser() - args = parser.parse_args(arguments) - - if not args.files and not args.list_fixes: - parser.error('incorrect number of arguments') - - args.files = [decode_filename(name) for name in args.files] - - if apply_config: - parser = read_config(args, parser) - args = parser.parse_args(arguments) - args.files = [decode_filename(name) for name in args.files] - - if '-' in args.files: - if len(args.files) > 1: - parser.error('cannot mix stdin and regular files') - - if args.diff: - parser.error('--diff cannot be used with standard input') - - if args.in_place: - parser.error('--in-place cannot be used with standard input') - - if args.recursive: - parser.error('--recursive cannot be used with standard input') - - if len(args.files) > 1 and not (args.in_place or args.diff): - parser.error('autopep8 only takes one filename as argument ' - 'unless the "--in-place" or "--diff" args are ' - 'used') - - if args.recursive and not (args.in_place or args.diff): - parser.error('--recursive must be used with --in-place or --diff') - - if args.in_place and args.diff: - parser.error('--in-place and --diff are mutually exclusive') - - if args.max_line_length <= 0: - parser.error('--max-line-length must be greater than 0') - - if args.select: - args.select = _split_comma_separated(args.select) - - if args.ignore: - args.ignore = _split_comma_separated(args.ignore) - elif not args.select: - if args.aggressive: - # Enable everything by default if aggressive. - args.select = ['E', 'W'] - else: - args.ignore = _split_comma_separated(DEFAULT_IGNORE) - - if args.exclude: - args.exclude = _split_comma_separated(args.exclude) - else: - args.exclude = [] - - if args.jobs < 1: - # Do not import multiprocessing globally in case it is not supported - # on the platform. - import multiprocessing - args.jobs = multiprocessing.cpu_count() - - if args.jobs > 1 and not args.in_place: - parser.error('parallel jobs requires --in-place') - - if args.line_range: - if args.line_range[0] <= 0: - parser.error('--range must be positive numbers') - if args.line_range[0] > args.line_range[1]: - parser.error('First value of --range should be less than or equal ' - 'to the second') - - return args - - -def read_config(args, parser): - """Read both user configuration and local configuration.""" - try: - from configparser import ConfigParser as SafeConfigParser - from configparser import Error - except ImportError: - from ConfigParser import SafeConfigParser - from ConfigParser import Error - - config = SafeConfigParser() - - try: - config.read(args.global_config) - - if not args.ignore_local_config: - parent = tail = args.files and os.path.abspath( - os.path.commonprefix(args.files)) - while tail: - if config.read([os.path.join(parent, fn) - for fn in PROJECT_CONFIG]): - break - (parent, tail) = os.path.split(parent) - - defaults = dict((k.lstrip('-').replace('-', '_'), v) - for k, v in config.items('pep8')) - parser.set_defaults(**defaults) - except Error: - # Ignore for now. - pass - - return parser - - -def _split_comma_separated(string): - """Return a set of strings.""" - return set(text.strip() for text in string.split(',') if text.strip()) - - -def decode_filename(filename): - """Return Unicode filename.""" - if isinstance(filename, unicode): - return filename - else: - return filename.decode(sys.getfilesystemencoding()) - - -def supported_fixes(): - """Yield pep8 error codes that autopep8 fixes. - - Each item we yield is a tuple of the code followed by its - description. - - """ - yield ('E101', docstring_summary(reindent.__doc__)) - - instance = FixPEP8(filename=None, options=None, contents='') - for attribute in dir(instance): - code = re.match('fix_([ew][0-9][0-9][0-9])', attribute) - if code: - yield ( - code.group(1).upper(), - re.sub(r'\s+', ' ', - docstring_summary(getattr(instance, attribute).__doc__)) - ) - - for (code, function) in sorted(global_fixes()): - yield (code.upper() + (4 - len(code)) * ' ', - re.sub(r'\s+', ' ', docstring_summary(function.__doc__))) - - for code in sorted(CODE_TO_2TO3): - yield (code.upper() + (4 - len(code)) * ' ', - re.sub(r'\s+', ' ', docstring_summary(fix_2to3.__doc__))) - - -def docstring_summary(docstring): - """Return summary of docstring.""" - return docstring.split('\n')[0] - - -def line_shortening_rank(candidate, indent_word, max_line_length, - experimental=False): - """Return rank of candidate. - - This is for sorting candidates. - - """ - if not candidate.strip(): - return 0 - - rank = 0 - lines = candidate.rstrip().split('\n') - - offset = 0 - if ( - not lines[0].lstrip().startswith('#') and - lines[0].rstrip()[-1] not in '([{' - ): - for (opening, closing) in ('()', '[]', '{}'): - # Don't penalize empty containers that aren't split up. Things like - # this "foo(\n )" aren't particularly good. - opening_loc = lines[0].find(opening) - closing_loc = lines[0].find(closing) - if opening_loc >= 0: - if closing_loc < 0 or closing_loc != opening_loc + 1: - offset = max(offset, 1 + opening_loc) - - current_longest = max(offset + len(x.strip()) for x in lines) - - rank += 4 * max(0, current_longest - max_line_length) - - rank += len(lines) - - # Too much variation in line length is ugly. - rank += 2 * standard_deviation(len(line) for line in lines) - - bad_staring_symbol = { - '(': ')', - '[': ']', - '{': '}'}.get(lines[0][-1]) - - if len(lines) > 1: - if ( - bad_staring_symbol and - lines[1].lstrip().startswith(bad_staring_symbol) - ): - rank += 20 - - for lineno, current_line in enumerate(lines): - current_line = current_line.strip() - - if current_line.startswith('#'): - continue - - for bad_start in ['.', '%', '+', '-', '/']: - if current_line.startswith(bad_start): - rank += 100 - - # Do not tolerate operators on their own line. - if current_line == bad_start: - rank += 1000 - - if ( - current_line.endswith(('.', '%', '+', '-', '/')) and - "': " in current_line - ): - rank += 1000 - - if current_line.endswith(('(', '[', '{', '.')): - # Avoid lonely opening. They result in longer lines. - if len(current_line) <= len(indent_word): - rank += 100 - - # Avoid the ugliness of ", (\n". - if ( - current_line.endswith('(') and - current_line[:-1].rstrip().endswith(',') - ): - rank += 100 - - # Also avoid the ugliness of "foo.\nbar" - if current_line.endswith('.'): - rank += 100 - - if has_arithmetic_operator(current_line): - rank += 100 - - # Avoid breaking at unary operators. - if re.match(r'.*[(\[{]\s*[\-\+~]$', current_line.rstrip('\\ ')): - rank += 1000 - - if re.match(r'.*lambda\s*\*$', current_line.rstrip('\\ ')): - rank += 1000 - - if current_line.endswith(('%', '(', '[', '{')): - rank -= 20 - - # Try to break list comprehensions at the "for". - if current_line.startswith('for '): - rank -= 50 - - if current_line.endswith('\\'): - # If a line ends in \-newline, it may be part of a - # multiline string. In that case, we would like to know - # how long that line is without the \-newline. If it's - # longer than the maximum, or has comments, then we assume - # that the \-newline is an okay candidate and only - # penalize it a bit. - total_len = len(current_line) - lineno += 1 - while lineno < len(lines): - total_len += len(lines[lineno]) - - if lines[lineno].lstrip().startswith('#'): - total_len = max_line_length - break - - if not lines[lineno].endswith('\\'): - break - - lineno += 1 - - if total_len < max_line_length: - rank += 10 - else: - rank += 100 if experimental else 1 - - # Prefer breaking at commas rather than colon. - if ',' in current_line and current_line.endswith(':'): - rank += 10 - - # Avoid splitting dictionaries between key and value. - if current_line.endswith(':'): - rank += 100 - - rank += 10 * count_unbalanced_brackets(current_line) - - return max(0, rank) - - -def standard_deviation(numbers): - """Return standard devation.""" - numbers = list(numbers) - if not numbers: - return 0 - mean = sum(numbers) / len(numbers) - return (sum((n - mean) ** 2 for n in numbers) / - len(numbers)) ** .5 - - -def has_arithmetic_operator(line): - """Return True if line contains any arithmetic operators.""" - for operator in pep8.ARITHMETIC_OP: - if operator in line: - return True - - return False - - -def count_unbalanced_brackets(line): - """Return number of unmatched open/close brackets.""" - count = 0 - for opening, closing in ['()', '[]', '{}']: - count += abs(line.count(opening) - line.count(closing)) - - return count - - -def split_at_offsets(line, offsets): - """Split line at offsets. - - Return list of strings. - - """ - result = [] - - previous_offset = 0 - current_offset = 0 - for current_offset in sorted(offsets): - if current_offset < len(line) and previous_offset != current_offset: - result.append(line[previous_offset:current_offset].strip()) - previous_offset = current_offset - - result.append(line[current_offset:]) - - return result - - -class LineEndingWrapper(object): - - r"""Replace line endings to work with sys.stdout. - - It seems that sys.stdout expects only '\n' as the line ending, no matter - the platform. Otherwise, we get repeated line endings. - - """ - - def __init__(self, output): - self.__output = output - - def write(self, s): - self.__output.write(s.replace('\r\n', '\n').replace('\r', '\n')) - - def flush(self): - self.__output.flush() - - -def match_file(filename, exclude): - """Return True if file is okay for modifying/recursing.""" - base_name = os.path.basename(filename) - - if base_name.startswith('.'): - return False - - for pattern in exclude: - if fnmatch.fnmatch(base_name, pattern): - return False - if fnmatch.fnmatch(filename, pattern): - return False - - if not os.path.isdir(filename) and not is_python_file(filename): - return False - - return True - - -def find_files(filenames, recursive, exclude): - """Yield filenames.""" - while filenames: - name = filenames.pop(0) - if recursive and os.path.isdir(name): - for root, directories, children in os.walk(name): - filenames += [os.path.join(root, f) for f in children - if match_file(os.path.join(root, f), - exclude)] - directories[:] = [d for d in directories - if match_file(os.path.join(root, d), - exclude)] - else: - yield name - - -def _fix_file(parameters): - """Helper function for optionally running fix_file() in parallel.""" - if parameters[1].verbose: - print('[file:{0}]'.format(parameters[0]), file=sys.stderr) - try: - fix_file(*parameters) - except IOError as error: - print(unicode(error), file=sys.stderr) - - -def fix_multiple_files(filenames, options, output=None): - """Fix list of files. - - Optionally fix files recursively. - - """ - filenames = find_files(filenames, options.recursive, options.exclude) - if options.jobs > 1: - import multiprocessing - pool = multiprocessing.Pool(options.jobs) - pool.map(_fix_file, - [(name, options) for name in filenames]) - else: - for name in filenames: - _fix_file((name, options, output)) - - -def is_python_file(filename): - """Return True if filename is Python file.""" - if filename.endswith('.py'): - return True - - try: - with open_with_encoding(filename) as f: - first_line = f.readlines(1)[0] - except (IOError, IndexError): - return False - - if not PYTHON_SHEBANG_REGEX.match(first_line): - return False - - return True - - -def is_probably_part_of_multiline(line): - """Return True if line is likely part of a multiline string. - - When multiline strings are involved, pep8 reports the error as being - at the start of the multiline string, which doesn't work for us. - - """ - return ( - '"""' in line or - "'''" in line or - line.rstrip().endswith('\\') - ) - - -def wrap_output(output, encoding): - """Return output with specified encoding.""" - return codecs.getwriter(encoding)(output.buffer - if hasattr(output, 'buffer') - else output) - - -def get_encoding(): - """Return preferred encoding.""" - return locale.getpreferredencoding() or sys.getdefaultencoding() - - -def main(argv=None, apply_config=True): - """Command-line entry.""" - if argv is None: - argv = sys.argv - - try: - # Exit on broken pipe. - signal.signal(signal.SIGPIPE, signal.SIG_DFL) - except AttributeError: # pragma: no cover - # SIGPIPE is not available on Windows. - pass - - try: - args = parse_args(argv[1:], apply_config=apply_config) - - if args.list_fixes: - for code, description in sorted(supported_fixes()): - print('{code} - {description}'.format( - code=code, description=description)) - return 0 - - if args.files == ['-']: - assert not args.in_place - - encoding = sys.stdin.encoding or get_encoding() - - # LineEndingWrapper is unnecessary here due to the symmetry between - # standard in and standard out. - wrap_output(sys.stdout, encoding=encoding).write( - fix_code(sys.stdin.read(), args, encoding=encoding)) - else: - if args.in_place or args.diff: - args.files = list(set(args.files)) - else: - assert len(args.files) == 1 - assert not args.recursive - - fix_multiple_files(args.files, args, sys.stdout) - except KeyboardInterrupt: - return 1 # pragma: no cover - - -class CachedTokenizer(object): - - """A one-element cache around tokenize.generate_tokens(). - - Original code written by Ned Batchelder, in coverage.py. - - """ - - def __init__(self): - self.last_text = None - self.last_tokens = None - - def generate_tokens(self, text): - """A stand-in for tokenize.generate_tokens().""" - if text != self.last_text: - string_io = io.StringIO(text) - self.last_tokens = list( - tokenize.generate_tokens(string_io.readline) - ) - self.last_text = text - return self.last_tokens - -_cached_tokenizer = CachedTokenizer() -generate_tokens = _cached_tokenizer.generate_tokens - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/pymode/environment.py b/pymode/environment.py deleted file mode 100644 index c146ea6e..00000000 --- a/pymode/environment.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Define interfaces.""" - -from __future__ import print_function - -import json -import os.path -import time -import vim # noqa - -from ._compat import PY2 - - -class VimPymodeEnviroment(object): - - """Vim User interface.""" - - prefix = '[Pymode]' - - def __init__(self): - """Init VIM environment.""" - self.current = vim.current - self.options = dict(encoding=vim.eval('&enc')) - self.options['debug'] = self.var('g:pymode_debug', True) - - @property - def curdir(self): - """Return current working directory.""" - return self.var('getcwd()') - - @property - def curbuf(self): - """Return current buffer.""" - return self.current.buffer - - @property - def cursor(self): - """Return current window position. - - :return tuple: (row, col) - - """ - return self.current.window.cursor - - @property - def source(self): - """Return source of current buffer.""" - return "\n".join(self.lines) - - @property - def lines(self): - """Iterate by lines in current file. - - :return list: - - """ - if not PY2: - return self.curbuf - - return [l.decode(self.options.get('encoding')) for l in self.curbuf] - - @staticmethod - def var(name, to_bool=False, silence=False): - """Get vim variable. - - :return vimobj: - - """ - try: - value = vim.eval(name) - except vim.error: - if silence: - return None - raise - - if to_bool: - try: - value = bool(int(value)) - except ValueError: - value = value - return value - - @staticmethod - def message(msg, history=False): - """Show message to user. - - :return: :None - - """ - if history: - return vim.command('echom "%s"' % str(msg)) - - return vim.command('call pymode#wide_message("%s")' % str(msg)) - - def user_input(self, msg, default=''): - """Return user input or default. - - :return str: - - """ - msg = '%s %s ' % (self.prefix, msg) - - if default != '': - msg += '[%s] ' % default - - try: - vim.command('echohl Debug') - input_str = vim.eval('input("%s> ")' % msg) - vim.command('echohl none') - except KeyboardInterrupt: - input_str = '' - - return input_str or default - - def user_confirm(self, msg, yes=False): - """Get user confirmation. - - :return bool: - - """ - default = 'yes' if yes else 'no' - action = self.user_input(msg, default) - return action and 'yes'.startswith(action) - - def user_input_choices(self, msg, *options): - """Get one of many options. - - :return str: A choosen option - - """ - choices = ['%s %s' % (self.prefix, msg)] - choices += [ - "%s. %s" % (num, opt) for num, opt in enumerate(options, 1)] - try: - input_str = int( - vim.eval('inputlist(%s)' % self.prepare_value(choices))) - except (KeyboardInterrupt, ValueError): - input_str = 0 - - if not input_str: - self.message('Cancelled!') - return False - - try: - return options[input_str - 1] - except (IndexError, ValueError): - self.error('Invalid option: %s' % input_str) - return self.user_input_choices(msg, *options) - - @staticmethod - def error(msg): - """Show error to user.""" - vim.command('call pymode#error("%s")' % str(msg)) - - def debug(self, msg, *args): - """Print debug information.""" - if self.options.get('debug'): - print("%s %s [%s]" % ( - int(time.time()), msg, ', '.join([str(a) for a in args]))) - - def stop(self, value=None): - """Break Vim function.""" - cmd = 'return' - if value is not None: - cmd += ' ' + self.prepare_value(value) - vim.command(cmd) - - def catch_exceptions(self, func): - """Decorator. Make execution more silence. - - :return func: - - """ - def _wrapper(*args, **kwargs): - try: - return func(*args, **kwargs) - except (Exception, vim.error) as e: # noqa - if self.options.get('debug'): - raise - self.error(e) - return None - return _wrapper - - def run(self, name, *args): - """Run vim function.""" - vim.command('call %s(%s)' % (name, ", ".join([ - self.prepare_value(a) for a in args - ]))) - - def let(self, name, value): - """Set variable.""" - cmd = 'let %s = %s' % (name, self.prepare_value(value)) - self.debug(cmd) - vim.command(cmd) - - def prepare_value(self, value, dumps=True): - """Decode bstr to vim encoding. - - :return unicode string: - - """ - if dumps: - value = json.dumps(value) - - if PY2: - value = value.decode('utf-8').encode(self.options.get('encoding')) - - return value - - def get_offset_params(self, cursor=None, base=""): - """Calculate current offset. - - :return tuple: (source, offset) - - """ - row, col = cursor or env.cursor - source = "" - offset = 0 - for i, line in enumerate(self.lines, 1): - if i == row: - source += line[:col] + base - offset = len(source) - source += line[col:] - else: - source += line - source += '\n' - env.debug('Get offset', base or None, row, col, offset) - return source, offset - - @staticmethod - def goto_line(line): - """Go to line.""" - vim.command('normal %sggzz' % line) - - def goto_file(self, path, cmd='e', force=False): - """Open file by path.""" - if force or os.path.abspath(path) != self.curbuf.name: - self.debug('read', path) - if ' ' in path and os.name == 'posix': - path = path.replace(' ', '\\ ') - vim.command("%s %s" % (cmd, path)) - - @staticmethod - def goto_buffer(bufnr): - """Open buffer.""" - if str(bufnr) != '-1': - vim.command('buffer %s' % bufnr) - - -env = VimPymodeEnviroment() diff --git a/pymode/libs/_markerlib/__init__.py b/pymode/libs/_markerlib/__init__.py deleted file mode 100644 index e2b237b1..00000000 --- a/pymode/libs/_markerlib/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -try: - import ast - from _markerlib.markers import default_environment, compile, interpret -except ImportError: - if 'ast' in globals(): - raise - def default_environment(): - return {} - def compile(marker): - def marker_fn(environment=None, override=None): - # 'empty markers are True' heuristic won't install extra deps. - return not marker.strip() - marker_fn.__doc__ = marker - return marker_fn - def interpret(marker, environment=None, override=None): - return compile(marker)() diff --git a/pymode/libs/_markerlib/markers.py b/pymode/libs/_markerlib/markers.py deleted file mode 100644 index fa837061..00000000 --- a/pymode/libs/_markerlib/markers.py +++ /dev/null @@ -1,119 +0,0 @@ -# -*- coding: utf-8 -*- -"""Interpret PEP 345 environment markers. - -EXPR [in|==|!=|not in] EXPR [or|and] ... - -where EXPR belongs to any of those: - - python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1]) - python_full_version = sys.version.split()[0] - os.name = os.name - sys.platform = sys.platform - platform.version = platform.version() - platform.machine = platform.machine() - platform.python_implementation = platform.python_implementation() - a free string, like '2.6', or 'win32' -""" - -__all__ = ['default_environment', 'compile', 'interpret'] - -import ast -import os -import platform -import sys -import weakref - -_builtin_compile = compile - -try: - from platform import python_implementation -except ImportError: - if os.name == "java": - # Jython 2.5 has ast module, but not platform.python_implementation() function. - def python_implementation(): - return "Jython" - else: - raise - - -# restricted set of variables -_VARS = {'sys.platform': sys.platform, - 'python_version': '%s.%s' % sys.version_info[:2], - # FIXME parsing sys.platform is not reliable, but there is no other - # way to get e.g. 2.7.2+, and the PEP is defined with sys.version - 'python_full_version': sys.version.split(' ', 1)[0], - 'os.name': os.name, - 'platform.version': platform.version(), - 'platform.machine': platform.machine(), - 'platform.python_implementation': python_implementation(), - 'extra': None # wheel extension - } - -for var in list(_VARS.keys()): - if '.' in var: - _VARS[var.replace('.', '_')] = _VARS[var] - -def default_environment(): - """Return copy of default PEP 385 globals dictionary.""" - return dict(_VARS) - -class ASTWhitelist(ast.NodeTransformer): - def __init__(self, statement): - self.statement = statement # for error messages - - ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str) - # Bool operations - ALLOWED += (ast.And, ast.Or) - # Comparison operations - ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn) - - def visit(self, node): - """Ensure statement only contains allowed nodes.""" - if not isinstance(node, self.ALLOWED): - raise SyntaxError('Not allowed in environment markers.\n%s\n%s' % - (self.statement, - (' ' * node.col_offset) + '^')) - return ast.NodeTransformer.visit(self, node) - - def visit_Attribute(self, node): - """Flatten one level of attribute access.""" - new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx) - return ast.copy_location(new_node, node) - -def parse_marker(marker): - tree = ast.parse(marker, mode='eval') - new_tree = ASTWhitelist(marker).generic_visit(tree) - return new_tree - -def compile_marker(parsed_marker): - return _builtin_compile(parsed_marker, '', 'eval', - dont_inherit=True) - -_cache = weakref.WeakValueDictionary() - -def compile(marker): - """Return compiled marker as a function accepting an environment dict.""" - try: - return _cache[marker] - except KeyError: - pass - if not marker.strip(): - def marker_fn(environment=None, override=None): - """""" - return True - else: - compiled_marker = compile_marker(parse_marker(marker)) - def marker_fn(environment=None, override=None): - """override updates environment""" - if override is None: - override = {} - if environment is None: - environment = default_environment() - environment.update(override) - return eval(compiled_marker, environment) - marker_fn.__doc__ = marker - _cache[marker] = marker_fn - return _cache[marker] - -def interpret(marker, environment=None): - return compile(marker)(environment) diff --git a/pymode/libs/astroid/__init__.py b/pymode/libs/astroid/__init__.py deleted file mode 100644 index d4fd12c5..00000000 --- a/pymode/libs/astroid/__init__.py +++ /dev/null @@ -1,131 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""Python Abstract Syntax Tree New Generation - -The aim of this module is to provide a common base representation of -python source code for projects such as pychecker, pyreverse, -pylint... Well, actually the development of this library is essentially -governed by pylint's needs. - -It extends class defined in the python's _ast module with some -additional methods and attributes. Instance attributes are added by a -builder object, which can either generate extended ast (let's call -them astroid ;) by visiting an existent ast tree or by inspecting living -object. Methods are added by monkey patching ast classes. - -Main modules are: - -* nodes and scoped_nodes for more information about methods and - attributes added to different node classes - -* the manager contains a high level object to get astroid trees from - source files and living objects. It maintains a cache of previously - constructed tree for quick access - -* builder contains the class responsible to build astroid trees -""" -__doctype__ = "restructuredtext en" - -import sys -import re -from operator import attrgetter - -# WARNING: internal imports order matters ! - -# make all exception classes accessible from astroid package -from astroid.exceptions import * - -# make all node classes accessible from astroid package -from astroid.nodes import * - -# trigger extra monkey-patching -from astroid import inference - -# more stuff available -from astroid import raw_building -from astroid.bases import YES, Instance, BoundMethod, UnboundMethod -from astroid.node_classes import are_exclusive, unpack_infer -from astroid.scoped_nodes import builtin_lookup - -# make a manager instance (borg) as well as Project and Package classes -# accessible from astroid package -from astroid.manager import AstroidManager, Project -MANAGER = AstroidManager() -del AstroidManager - -# transform utilities (filters and decorator) - -class AsStringRegexpPredicate(object): - """Class to be used as predicate that may be given to `register_transform` - - First argument is a regular expression that will be searched against the `as_string` - representation of the node onto which it's applied. - - If specified, the second argument is an `attrgetter` expression that will be - applied on the node first to get the actual node on which `as_string` should - be called. - - WARNING: This can be fairly slow, as it has to convert every AST node back - to Python code; you should consider examining the AST directly instead. - """ - def __init__(self, regexp, expression=None): - self.regexp = re.compile(regexp) - self.expression = expression - - def __call__(self, node): - if self.expression is not None: - node = attrgetter(self.expression)(node) - return self.regexp.search(node.as_string()) - -def inference_tip(infer_function): - """Given an instance specific inference function, return a function to be - given to MANAGER.register_transform to set this inference function. - - Typical usage - - .. sourcecode:: python - - MANAGER.register_transform(CallFunc, inference_tip(infer_named_tuple), - predicate) - """ - def transform(node, infer_function=infer_function): - node._explicit_inference = infer_function - return node - return transform - - -def register_module_extender(manager, module_name, get_extension_mod): - def transform(node): - extension_module = get_extension_mod() - for name, obj in extension_module.locals.items(): - node.locals[name] = obj - - manager.register_transform(Module, transform, lambda n: n.name == module_name) - - -# load brain plugins -from os import listdir -from os.path import join, dirname -BRAIN_MODULES_DIR = join(dirname(__file__), 'brain') -if BRAIN_MODULES_DIR not in sys.path: - # add it to the end of the list so user path take precedence - sys.path.append(BRAIN_MODULES_DIR) -# load modules in this directory -for module in listdir(BRAIN_MODULES_DIR): - if module.endswith('.py'): - __import__(module[:-3]) diff --git a/pymode/libs/astroid/__pkginfo__.py b/pymode/libs/astroid/__pkginfo__.py deleted file mode 100644 index 3fb45aa4..00000000 --- a/pymode/libs/astroid/__pkginfo__.py +++ /dev/null @@ -1,42 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""astroid packaging information""" -distname = 'astroid' - -modname = 'astroid' - -numversion = (1, 3, 8) -version = '.'.join([str(num) for num in numversion]) - -install_requires = ['logilab-common>=0.63.0', 'six'] - -license = 'LGPL' - -author = 'Logilab' -author_email = 'pylint-dev@lists.logilab.org' -mailinglist = "mailto://%s" % author_email -web = 'http://bitbucket.org/logilab/astroid' - -description = "A abstract syntax tree for Python with inference support." - -classifiers = ["Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: Quality Assurance", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 3", - ] diff --git a/pymode/libs/astroid/as_string.py b/pymode/libs/astroid/as_string.py deleted file mode 100644 index f627f9e8..00000000 --- a/pymode/libs/astroid/as_string.py +++ /dev/null @@ -1,499 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module renders Astroid nodes as string: - -* :func:`to_code` function return equivalent (hopefuly valid) python string - -* :func:`dump` function return an internal representation of nodes found - in the tree, useful for debugging or understanding the tree structure -""" - -import sys - -INDENT = ' ' # 4 spaces ; keep indentation variable - - -def dump(node, ids=False): - """print a nice astroid tree representation. - - :param ids: if true, we also print the ids (usefull for debugging) - """ - result = [] - _repr_tree(node, result, ids=ids) - return "\n".join(result) - -def _repr_tree(node, result, indent='', _done=None, ids=False): - """built a tree representation of a node as a list of lines""" - if _done is None: - _done = set() - if not hasattr(node, '_astroid_fields'): # not a astroid node - return - if node in _done: - result.append(indent + 'loop in tree: %s' % node) - return - _done.add(node) - node_str = str(node) - if ids: - node_str += ' . \t%x' % id(node) - result.append(indent + node_str) - indent += INDENT - for field in node._astroid_fields: - value = getattr(node, field) - if isinstance(value, (list, tuple)): - result.append(indent + field + " = [") - for child in value: - if isinstance(child, (list, tuple)): - # special case for Dict # FIXME - _repr_tree(child[0], result, indent, _done, ids) - _repr_tree(child[1], result, indent, _done, ids) - result.append(indent + ',') - else: - _repr_tree(child, result, indent, _done, ids) - result.append(indent + "]") - else: - result.append(indent + field + " = ") - _repr_tree(value, result, indent, _done, ids) - - -class AsStringVisitor(object): - """Visitor to render an Astroid node as a valid python code string""" - - def __call__(self, node): - """Makes this visitor behave as a simple function""" - return node.accept(self) - - def _stmt_list(self, stmts): - """return a list of nodes to string""" - stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr]) - return INDENT + stmts.replace('\n', '\n'+INDENT) - - - ## visit_ methods ########################################### - - def visit_arguments(self, node): - """return an astroid.Function node as string""" - return node.format_args() - - def visit_assattr(self, node): - """return an astroid.AssAttr node as string""" - return self.visit_getattr(node) - - def visit_assert(self, node): - """return an astroid.Assert node as string""" - if node.fail: - return 'assert %s, %s' % (node.test.accept(self), - node.fail.accept(self)) - return 'assert %s' % node.test.accept(self) - - def visit_assname(self, node): - """return an astroid.AssName node as string""" - return node.name - - def visit_assign(self, node): - """return an astroid.Assign node as string""" - lhs = ' = '.join([n.accept(self) for n in node.targets]) - return '%s = %s' % (lhs, node.value.accept(self)) - - def visit_augassign(self, node): - """return an astroid.AugAssign node as string""" - return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) - - def visit_backquote(self, node): - """return an astroid.Backquote node as string""" - return '`%s`' % node.value.accept(self) - - def visit_binop(self, node): - """return an astroid.BinOp node as string""" - return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self)) - - def visit_boolop(self, node): - """return an astroid.BoolOp node as string""" - return (' %s ' % node.op).join(['(%s)' % n.accept(self) - for n in node.values]) - - def visit_break(self, node): - """return an astroid.Break node as string""" - return 'break' - - def visit_callfunc(self, node): - """return an astroid.CallFunc node as string""" - expr_str = node.func.accept(self) - args = [arg.accept(self) for arg in node.args] - if node.starargs: - args.append('*' + node.starargs.accept(self)) - if node.kwargs: - args.append('**' + node.kwargs.accept(self)) - return '%s(%s)' % (expr_str, ', '.join(args)) - - def visit_class(self, node): - """return an astroid.Class node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - bases = ', '.join([n.accept(self) for n in node.bases]) - if sys.version_info[0] == 2: - bases = bases and '(%s)' % bases or '' - else: - metaclass = node.metaclass() - if metaclass and not node.has_metaclass_hack(): - if bases: - bases = '(%s, metaclass=%s)' % (bases, metaclass.name) - else: - bases = '(metaclass=%s)' % metaclass.name - else: - bases = bases and '(%s)' % bases or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, - self._stmt_list(node.body)) - - def visit_compare(self, node): - """return an astroid.Compare node as string""" - rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) - for op, expr in node.ops]) - return '%s %s' % (node.left.accept(self), rhs_str) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - ifs = ''.join([' if %s' % n.accept(self) for n in node.ifs]) - return 'for %s in %s%s' % (node.target.accept(self), - node.iter.accept(self), ifs) - - def visit_const(self, node): - """return an astroid.Const node as string""" - return repr(node.value) - - def visit_continue(self, node): - """return an astroid.Continue node as string""" - return 'continue' - - def visit_delete(self, node): # XXX check if correct - """return an astroid.Delete node as string""" - return 'del %s' % ', '.join([child.accept(self) - for child in node.targets]) - - def visit_delattr(self, node): - """return an astroid.DelAttr node as string""" - return self.visit_getattr(node) - - def visit_delname(self, node): - """return an astroid.DelName node as string""" - return node.name - - def visit_decorators(self, node): - """return an astroid.Decorators node as string""" - return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) - - def visit_dict(self, node): - """return an astroid.Dict node as string""" - return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), - value.accept(self)) - for key, value in node.items]) - - def visit_dictcomp(self, node): - """return an astroid.DictComp node as string""" - return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_discard(self, node): - """return an astroid.Discard node as string""" - return node.value.accept(self) - - def visit_emptynode(self, node): - """dummy method for visiting an Empty node""" - return '' - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s, %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_ellipsis(self, node): - """return an astroid.Ellipsis node as string""" - return '...' - - def visit_empty(self, node): - """return an Empty node as string""" - return '' - - def visit_exec(self, node): - """return an astroid.Exec node as string""" - if node.locals: - return 'exec %s in %s, %s' % (node.expr.accept(self), - node.locals.accept(self), - node.globals.accept(self)) - if node.globals: - return 'exec %s in %s' % (node.expr.accept(self), - node.globals.accept(self)) - return 'exec %s' % node.expr.accept(self) - - def visit_extslice(self, node): - """return an astroid.ExtSlice node as string""" - return ','.join([dim.accept(self) for dim in node.dims]) - - def visit_for(self, node): - """return an astroid.For node as string""" - fors = 'for %s in %s:\n%s' % (node.target.accept(self), - node.iter.accept(self), - self._stmt_list(node.body)) - if node.orelse: - fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) - return fors - - def visit_from(self, node): - """return an astroid.From node as string""" - return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, - _import_string(node.names)) - - def visit_function(self, node): - """return an astroid.Function node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self), - docs, self._stmt_list(node.body)) - - def visit_genexpr(self, node): - """return an astroid.GenExpr node as string""" - return '(%s %s)' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_getattr(self, node): - """return an astroid.Getattr node as string""" - return '%s.%s' % (node.expr.accept(self), node.attrname) - - def visit_global(self, node): - """return an astroid.Global node as string""" - return 'global %s' % ', '.join(node.names) - - def visit_if(self, node): - """return an astroid.If node as string""" - ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))] - if node.orelse:# XXX use elif ??? - ifs.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(ifs) - - def visit_ifexp(self, node): - """return an astroid.IfExp node as string""" - return '%s if %s else %s' % (node.body.accept(self), - node.test.accept(self), - node.orelse.accept(self)) - - def visit_import(self, node): - """return an astroid.Import node as string""" - return 'import %s' % _import_string(node.names) - - def visit_keyword(self, node): - """return an astroid.Keyword node as string""" - return '%s=%s' % (node.arg, node.value.accept(self)) - - def visit_lambda(self, node): - """return an astroid.Lambda node as string""" - return 'lambda %s: %s' % (node.args.accept(self), - node.body.accept(self)) - - def visit_list(self, node): - """return an astroid.List node as string""" - return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_listcomp(self, node): - """return an astroid.ListComp node as string""" - return '[%s %s]' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_module(self, node): - """return an astroid.Module node as string""" - docs = node.doc and '"""%s"""\n\n' % node.doc or '' - return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' - - def visit_name(self, node): - """return an astroid.Name node as string""" - return node.name - - def visit_pass(self, node): - """return an astroid.Pass node as string""" - return 'pass' - - def visit_print(self, node): - """return an astroid.Print node as string""" - nodes = ', '.join([n.accept(self) for n in node.values]) - if not node.nl: - nodes = '%s,' % nodes - if node.dest: - return 'print >> %s, %s' % (node.dest.accept(self), nodes) - return 'print %s' % nodes - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.inst: - if node.tback: - return 'raise %s, %s, %s' % (node.exc.accept(self), - node.inst.accept(self), - node.tback.accept(self)) - return 'raise %s, %s' % (node.exc.accept(self), - node.inst.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_return(self, node): - """return an astroid.Return node as string""" - if node.value: - return 'return %s' % node.value.accept(self) - else: - return 'return' - - def visit_index(self, node): - """return a astroid.Index node as string""" - return node.value.accept(self) - - def visit_set(self, node): - """return an astroid.Set node as string""" - return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_setcomp(self, node): - """return an astroid.SetComp node as string""" - return '{%s %s}' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_slice(self, node): - """return a astroid.Slice node as string""" - lower = node.lower and node.lower.accept(self) or '' - upper = node.upper and node.upper.accept(self) or '' - step = node.step and node.step.accept(self) or '' - if step: - return '%s:%s:%s' % (lower, upper, step) - return '%s:%s' % (lower, upper) - - def visit_subscript(self, node): - """return an astroid.Subscript node as string""" - return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) - - def visit_tryexcept(self, node): - """return an astroid.TryExcept node as string""" - trys = ['try:\n%s' % self._stmt_list(node.body)] - for handler in node.handlers: - trys.append(handler.accept(self)) - if node.orelse: - trys.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(trys) - - def visit_tryfinally(self, node): - """return an astroid.TryFinally node as string""" - return 'try:\n%s\nfinally:\n%s' % (self._stmt_list(node.body), - self._stmt_list(node.finalbody)) - - def visit_tuple(self, node): - """return an astroid.Tuple node as string""" - if len(node.elts) == 1: - return '(%s, )' % node.elts[0].accept(self) - return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_unaryop(self, node): - """return an astroid.UnaryOp node as string""" - if node.op == 'not': - operator = 'not ' - else: - operator = node.op - return '%s%s' % (operator, node.operand.accept(self)) - - def visit_while(self, node): - """return an astroid.While node as string""" - whiles = 'while %s:\n%s' % (node.test.accept(self), - self._stmt_list(node.body)) - if node.orelse: - whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) - return whiles - - def visit_with(self, node): # 'with' without 'as' is possible - """return an astroid.With node as string""" - items = ', '.join(('(%s)' % expr.accept(self)) + - (vars and ' as (%s)' % (vars.accept(self)) or '') - for expr, vars in node.items) - return 'with %s:\n%s' % (items, self._stmt_list(node.body)) - - def visit_yield(self, node): - """yield an ast.Yield node as string""" - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -class AsStringVisitor3k(AsStringVisitor): - """AsStringVisitor3k overwrites some AsStringVisitor methods""" - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s as %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_nonlocal(self, node): - """return an astroid.Nonlocal node as string""" - return 'nonlocal %s' % ', '.join(node.names) - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.cause: - return 'raise %s from %s' % (node.exc.accept(self), - node.cause.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_starred(self, node): - """return Starred node as string""" - return "*" + node.value.accept(self) - - def visit_yieldfrom(self, node): - """ Return an astroid.YieldFrom node as string. """ - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield from' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -def _import_string(names): - """return a list of (name, asname) formatted as a string""" - _names = [] - for name, asname in names: - if asname is not None: - _names.append('%s as %s' % (name, asname)) - else: - _names.append(name) - return ', '.join(_names) - - -if sys.version_info >= (3, 0): - AsStringVisitor = AsStringVisitor3k - -# this visitor is stateless, thus it can be reused -to_code = AsStringVisitor() - diff --git a/pymode/libs/astroid/astpeephole.py b/pymode/libs/astroid/astpeephole.py deleted file mode 100644 index af03462a..00000000 --- a/pymode/libs/astroid/astpeephole.py +++ /dev/null @@ -1,86 +0,0 @@ -# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""Small AST optimizations.""" - -import _ast - -from astroid import nodes - - -__all__ = ('ASTPeepholeOptimizer', ) - - -try: - _TYPES = (_ast.Str, _ast.Bytes) -except AttributeError: - _TYPES = (_ast.Str, ) - - -class ASTPeepholeOptimizer(object): - """Class for applying small optimizations to generate new AST.""" - - def optimize_binop(self, node): - """Optimize BinOps with string Const nodes on the lhs. - - This fixes an infinite recursion crash, where multiple - strings are joined using the addition operator. With a - sufficient number of such strings, astroid will fail - with a maximum recursion limit exceeded. The - function will return a Const node with all the strings - already joined. - Return ``None`` if no AST node can be obtained - through optimization. - """ - ast_nodes = [] - current = node - while isinstance(current, _ast.BinOp): - # lhs must be a BinOp with the addition operand. - if not isinstance(current.left, _ast.BinOp): - return - if (not isinstance(current.left.op, _ast.Add) - or not isinstance(current.op, _ast.Add)): - return - - # rhs must a str / bytes. - if not isinstance(current.right, _TYPES): - return - - ast_nodes.append(current.right.s) - current = current.left - - if (isinstance(current, _ast.BinOp) - and isinstance(current.left, _TYPES) - and isinstance(current.right, _TYPES)): - # Stop early if we are at the last BinOp in - # the operation - ast_nodes.append(current.right.s) - ast_nodes.append(current.left.s) - break - - if not ast_nodes: - return - - # If we have inconsistent types, bail out. - known = type(ast_nodes[0]) - if any(type(element) is not known - for element in ast_nodes[1:]): - return - - value = known().join(reversed(ast_nodes)) - newnode = nodes.Const(value) - return newnode diff --git a/pymode/libs/astroid/bases.py b/pymode/libs/astroid/bases.py deleted file mode 100644 index ee8ee1c3..00000000 --- a/pymode/libs/astroid/bases.py +++ /dev/null @@ -1,652 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module contains base classes and functions for the nodes and some -inference utils. -""" - -__docformat__ = "restructuredtext en" - -import sys -from contextlib import contextmanager - -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import (InferenceError, AstroidError, NotFoundError, - UnresolvableName, UseInferenceDefault) - - -if sys.version_info >= (3, 0): - BUILTINS = 'builtins' -else: - BUILTINS = '__builtin__' - - -class Proxy(object): - """a simple proxy object""" - - _proxied = None # proxied object may be set by class or by instance - - def __init__(self, proxied=None): - if proxied is not None: - self._proxied = proxied - - def __getattr__(self, name): - if name == '_proxied': - return getattr(self.__class__, '_proxied') - if name in self.__dict__: - return self.__dict__[name] - return getattr(self._proxied, name) - - def infer(self, context=None): - yield self - - -# Inference ################################################################## - -class InferenceContext(object): - __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'infered') - - def __init__(self, path=None, infered=None): - self.path = path or set() - self.lookupname = None - self.callcontext = None - self.boundnode = None - self.infered = infered or {} - - def push(self, node): - name = self.lookupname - if (node, name) in self.path: - raise StopIteration() - self.path.add((node, name)) - - def clone(self): - # XXX copy lookupname/callcontext ? - clone = InferenceContext(self.path, infered=self.infered) - clone.callcontext = self.callcontext - clone.boundnode = self.boundnode - return clone - - def cache_generator(self, key, generator): - results = [] - for result in generator: - results.append(result) - yield result - - self.infered[key] = tuple(results) - return - - @contextmanager - def restore_path(self): - path = set(self.path) - yield - self.path = path - -def copy_context(context): - if context is not None: - return context.clone() - else: - return InferenceContext() - - -def _infer_stmts(stmts, context, frame=None): - """return an iterator on statements inferred by each statement in - """ - stmt = None - infered = False - if context is not None: - name = context.lookupname - context = context.clone() - else: - name = None - context = InferenceContext() - for stmt in stmts: - if stmt is YES: - yield stmt - infered = True - continue - context.lookupname = stmt._infer_name(frame, name) - try: - for infered in stmt.infer(context): - yield infered - infered = True - except UnresolvableName: - continue - except InferenceError: - yield YES - infered = True - if not infered: - raise InferenceError(str(stmt)) - - -# special inference objects (e.g. may be returned as nodes by .infer()) ####### - -class _Yes(object): - """a yes object""" - def __repr__(self): - return 'YES' - def __getattribute__(self, name): - if name == 'next': - raise AttributeError('next method should not be called') - if name.startswith('__') and name.endswith('__'): - # to avoid inspection pb - return super(_Yes, self).__getattribute__(name) - return self - def __call__(self, *args, **kwargs): - return self - - -YES = _Yes() - - -class Instance(Proxy): - """a special node representing a class instance""" - def getattr(self, name, context=None, lookupclass=True): - try: - values = self._proxied.instance_attr(name, context) - except NotFoundError: - if name == '__class__': - return [self._proxied] - if lookupclass: - # class attributes not available through the instance - # unless they are explicitly defined - if name in ('__name__', '__bases__', '__mro__', '__subclasses__'): - return self._proxied.local_attr(name) - return self._proxied.getattr(name, context) - raise NotFoundError(name) - # since we've no context information, return matching class members as - # well - if lookupclass: - try: - return values + self._proxied.getattr(name, context) - except NotFoundError: - pass - return values - - def igetattr(self, name, context=None): - """inferred getattr""" - if not context: - context = InferenceContext() - try: - # avoid recursively inferring the same attr on the same class - - context.push((self._proxied, name)) - # XXX frame should be self._proxied, or not ? - get_attr = self.getattr(name, context, lookupclass=False) - return _infer_stmts( - self._wrap_attr(get_attr, context), - context, - frame=self, - ) - except NotFoundError: - try: - # fallback to class'igetattr since it has some logic to handle - # descriptors - return self._wrap_attr(self._proxied.igetattr(name, context), - context) - except NotFoundError: - raise InferenceError(name) - - def _wrap_attr(self, attrs, context=None): - """wrap bound methods of attrs in a InstanceMethod proxies""" - for attr in attrs: - if isinstance(attr, UnboundMethod): - if BUILTINS + '.property' in attr.decoratornames(): - for infered in attr.infer_call_result(self, context): - yield infered - else: - yield BoundMethod(attr, self) - else: - yield attr - - def infer_call_result(self, caller, context=None): - """infer what a class instance is returning when called""" - infered = False - for node in self._proxied.igetattr('__call__', context): - if node is YES: - continue - for res in node.infer_call_result(caller, context): - infered = True - yield res - if not infered: - raise InferenceError() - - def __repr__(self): - return '' % (self._proxied.root().name, - self._proxied.name, - id(self)) - def __str__(self): - return 'Instance of %s.%s' % (self._proxied.root().name, - self._proxied.name) - - def callable(self): - try: - self._proxied.getattr('__call__') - return True - except NotFoundError: - return False - - def pytype(self): - return self._proxied.qname() - - def display_type(self): - return 'Instance of' - - -class UnboundMethod(Proxy): - """a special node representing a method not bound to an instance""" - def __repr__(self): - frame = self._proxied.parent.frame() - return '<%s %s of %s at 0x%s' % (self.__class__.__name__, - self._proxied.name, - frame.qname(), id(self)) - - def is_bound(self): - return False - - def getattr(self, name, context=None): - if name == 'im_func': - return [self._proxied] - return super(UnboundMethod, self).getattr(name, context) - - def igetattr(self, name, context=None): - if name == 'im_func': - return iter((self._proxied,)) - return super(UnboundMethod, self).igetattr(name, context) - - def infer_call_result(self, caller, context): - # If we're unbound method __new__ of builtin object, the result is an - # instance of the class given as first argument. - if (self._proxied.name == '__new__' and - self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - infer = caller.args[0].infer() if caller.args else [] - return ((x is YES and x or Instance(x)) for x in infer) - return self._proxied.infer_call_result(caller, context) - - -class BoundMethod(UnboundMethod): - """a special node representing a method bound to an instance""" - def __init__(self, proxy, bound): - UnboundMethod.__init__(self, proxy) - self.bound = bound - - def is_bound(self): - return True - - def infer_call_result(self, caller, context): - context = context.clone() - context.boundnode = self.bound - return self._proxied.infer_call_result(caller, context) - - -class Generator(Instance): - """a special node representing a generator. - - Proxied class is set once for all in raw_building. - """ - def callable(self): - return False - - def pytype(self): - return '%s.generator' % BUILTINS - - def display_type(self): - return 'Generator' - - def __repr__(self): - return '' % (self._proxied.name, self.lineno, id(self)) - - def __str__(self): - return 'Generator(%s)' % (self._proxied.name) - - -# decorators ################################################################## - -def path_wrapper(func): - """return the given infer function wrapped to handle the path""" - def wrapped(node, context=None, _func=func, **kwargs): - """wrapper function handling context""" - if context is None: - context = InferenceContext() - context.push(node) - yielded = set() - for res in _func(node, context, **kwargs): - # unproxy only true instance, not const, tuple, dict... - if res.__class__ is Instance: - ares = res._proxied - else: - ares = res - if not ares in yielded: - yield res - yielded.add(ares) - return wrapped - -def yes_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - yield YES - return wrapper - -def raise_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - raise InferenceError() - return wrapper - - -# Node ###################################################################### - -class NodeNG(object): - """Base Class for all Astroid node classes. - - It represents a node of the new abstract syntax tree. - """ - is_statement = False - optional_assign = False # True for For (and for Comprehension if py <3.0) - is_function = False # True for Function nodes - # attributes below are set by the builder module or by raw factories - lineno = None - fromlineno = None - tolineno = None - col_offset = None - # parent node in the tree - parent = None - # attributes containing child node(s) redefined in most concrete classes: - _astroid_fields = () - # instance specific inference function infer(node, context) - _explicit_inference = None - - def infer(self, context=None, **kwargs): - """main interface to the interface system, return a generator on infered - values. - - If the instance has some explicit inference function set, it will be - called instead of the default interface. - """ - if self._explicit_inference is not None: - # explicit_inference is not bound, give it self explicitly - try: - return self._explicit_inference(self, context, **kwargs) - except UseInferenceDefault: - pass - - if not context: - return self._infer(context, **kwargs) - - key = (self, context.lookupname, - context.callcontext, context.boundnode) - if key in context.infered: - return iter(context.infered[key]) - - return context.cache_generator(key, self._infer(context, **kwargs)) - - def _repr_name(self): - """return self.name or self.attrname or '' for nice representation""" - return getattr(self, 'name', getattr(self, 'attrname', '')) - - def __str__(self): - return '%s(%s)' % (self.__class__.__name__, self._repr_name()) - - def __repr__(self): - return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__, - self._repr_name(), - self.fromlineno, - self.root().name, - id(self)) - - - def accept(self, visitor): - func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) - return func(self) - - def get_children(self): - for field in self._astroid_fields: - attr = getattr(self, field) - if attr is None: - continue - if isinstance(attr, (list, tuple)): - for elt in attr: - yield elt - else: - yield attr - - def last_child(self): - """an optimized version of list(get_children())[-1]""" - for field in self._astroid_fields[::-1]: - attr = getattr(self, field) - if not attr: # None or empty listy / tuple - continue - if attr.__class__ in (list, tuple): - return attr[-1] - else: - return attr - return None - - def parent_of(self, node): - """return true if i'm a parent of the given node""" - parent = node.parent - while parent is not None: - if self is parent: - return True - parent = parent.parent - return False - - def statement(self): - """return the first parent node marked as statement node""" - if self.is_statement: - return self - return self.parent.statement() - - def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) - """ - return self.parent.frame() - - def scope(self): - """return the first node defining a new scope (i.e. Module, Function, - Class, Lambda but also GenExpr) - """ - return self.parent.scope() - - def root(self): - """return the root node of the tree, (i.e. a Module)""" - if self.parent: - return self.parent.root() - return self - - def child_sequence(self, child): - """search for the right sequence where the child lies in""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - if node_or_sequence is child: - return [node_or_sequence] - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: - return node_or_sequence - else: - msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) - - def locate_child(self, child): - """return a 2-uple (child attribute name, sequence or node)""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if child is node_or_sequence: - return field, child - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: - return field, node_or_sequence - msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) - # FIXME : should we merge child_sequence and locate_child ? locate_child - # is only used in are_exclusive, child_sequence one time in pylint. - - def next_sibling(self): - """return the next sibling statement""" - return self.parent.next_sibling() - - def previous_sibling(self): - """return the previous sibling statement""" - return self.parent.previous_sibling() - - def nearest(self, nodes): - """return the node which is the nearest before this one in the - given list of nodes - """ - myroot = self.root() - mylineno = self.fromlineno - nearest = None, 0 - for node in nodes: - assert node.root() is myroot, \ - 'nodes %s and %s are not from the same module' % (self, node) - lineno = node.fromlineno - if node.fromlineno > mylineno: - break - if lineno > nearest[1]: - nearest = node, lineno - # FIXME: raise an exception if nearest is None ? - return nearest[0] - - # these are lazy because they're relatively expensive to compute for every - # single node, and they rarely get looked at - - @cachedproperty - def fromlineno(self): - if self.lineno is None: - return self._fixed_source_line() - else: - return self.lineno - - @cachedproperty - def tolineno(self): - if not self._astroid_fields: - # can't have children - lastchild = None - else: - lastchild = self.last_child() - if lastchild is None: - return self.fromlineno - else: - return lastchild.tolineno - - # TODO / FIXME: - assert self.fromlineno is not None, self - assert self.tolineno is not None, self - - def _fixed_source_line(self): - """return the line number where the given node appears - - we need this method since not all nodes have the lineno attribute - correctly set... - """ - line = self.lineno - _node = self - try: - while line is None: - _node = next(_node.get_children()) - line = _node.lineno - except StopIteration: - _node = self.parent - while _node and line is None: - line = _node.lineno - _node = _node.parent - return line - - def block_range(self, lineno): - """handle block line numbers range for non block opening statements - """ - return lineno, self.tolineno - - def set_local(self, name, stmt): - """delegate to a scoped parent handling a locals dictionary""" - self.parent.set_local(name, stmt) - - def nodes_of_class(self, klass, skip_klass=None): - """return an iterator on nodes which are instance of the given class(es) - - klass may be a class object or a tuple of class objects - """ - if isinstance(self, klass): - yield self - for child_node in self.get_children(): - if skip_klass is not None and isinstance(child_node, skip_klass): - continue - for matching in child_node.nodes_of_class(klass, skip_klass): - yield matching - - def _infer_name(self, frame, name): - # overridden for From, Import, Global, TryExcept and Arguments - return None - - def _infer(self, context=None): - """we don't know how to resolve a statement by default""" - # this method is overridden by most concrete classes - raise InferenceError(self.__class__.__name__) - - def infered(self): - '''return list of infered values for a more simple inference usage''' - return list(self.infer()) - - def instanciate_class(self): - """instanciate a node if it is a Class node, else return self""" - return self - - def has_base(self, node): - return False - - def callable(self): - return False - - def eq(self, value): - return False - - def as_string(self): - from astroid.as_string import to_code - return to_code(self) - - def repr_tree(self, ids=False): - from astroid.as_string import dump - return dump(self) - - -class Statement(NodeNG): - """Statement node adding a few attributes""" - is_statement = True - - def next_sibling(self): - """return the next sibling statement""" - stmts = self.parent.child_sequence(self) - index = stmts.index(self) - try: - return stmts[index +1] - except IndexError: - pass - - def previous_sibling(self): - """return the previous sibling statement""" - stmts = self.parent.child_sequence(self) - index = stmts.index(self) - if index >= 1: - return stmts[index -1] diff --git a/pymode/libs/astroid/brain/builtin_inference.py b/pymode/libs/astroid/brain/builtin_inference.py deleted file mode 100644 index f60e7913..00000000 --- a/pymode/libs/astroid/brain/builtin_inference.py +++ /dev/null @@ -1,245 +0,0 @@ -"""Astroid hooks for various builtins.""" - -import sys -from functools import partial -from textwrap import dedent - -import six -from astroid import (MANAGER, UseInferenceDefault, - inference_tip, YES, InferenceError, UnresolvableName) -from astroid import nodes -from astroid.builder import AstroidBuilder - - -def _extend_str(class_node, rvalue): - """function to extend builtin str/unicode class""" - # TODO(cpopa): this approach will make astroid to believe - # that some arguments can be passed by keyword, but - # unfortunately, strings and bytes don't accept keyword arguments. - code = dedent(''' - class whatever(object): - def join(self, iterable): - return {rvalue} - def replace(self, old, new, count=None): - return {rvalue} - def format(self, *args, **kwargs): - return {rvalue} - def encode(self, encoding='ascii', errors=None): - return '' - def decode(self, encoding='ascii', errors=None): - return u'' - def capitalize(self): - return {rvalue} - def title(self): - return {rvalue} - def lower(self): - return {rvalue} - def upper(self): - return {rvalue} - def swapcase(self): - return {rvalue} - def index(self, sub, start=None, end=None): - return 0 - def find(self, sub, start=None, end=None): - return 0 - def count(self, sub, start=None, end=None): - return 0 - def strip(self, chars=None): - return {rvalue} - def lstrip(self, chars=None): - return {rvalue} - def rstrip(self, chars=None): - return {rvalue} - def rjust(self, width, fillchar=None): - return {rvalue} - def center(self, width, fillchar=None): - return {rvalue} - def ljust(self, width, fillchar=None): - return {rvalue} - ''') - code = code.format(rvalue=rvalue) - fake = AstroidBuilder(MANAGER).string_build(code)['whatever'] - for method in fake.mymethods(): - class_node.locals[method.name] = [method] - method.parent = class_node - -def extend_builtins(class_transforms): - from astroid.bases import BUILTINS - builtin_ast = MANAGER.astroid_cache[BUILTINS] - for class_name, transform in class_transforms.items(): - transform(builtin_ast[class_name]) - -if sys.version_info > (3, 0): - extend_builtins({'bytes': partial(_extend_str, rvalue="b''"), - 'str': partial(_extend_str, rvalue="''")}) -else: - extend_builtins({'str': partial(_extend_str, rvalue="''"), - 'unicode': partial(_extend_str, rvalue="u''")}) - - -def register_builtin_transform(transform, builtin_name): - """Register a new transform function for the given *builtin_name*. - - The transform function must accept two parameters, a node and - an optional context. - """ - def _transform_wrapper(node, context=None): - result = transform(node, context=context) - if result: - result.parent = node - result.lineno = node.lineno - result.col_offset = node.col_offset - return iter([result]) - - MANAGER.register_transform(nodes.CallFunc, - inference_tip(_transform_wrapper), - lambda n: (isinstance(n.func, nodes.Name) and - n.func.name == builtin_name)) - - -def _generic_inference(node, context, node_type, transform): - args = node.args - if not args: - return node_type() - if len(node.args) > 1: - raise UseInferenceDefault() - - arg, = args - transformed = transform(arg) - if not transformed: - try: - infered = next(arg.infer(context=context)) - except (InferenceError, StopIteration): - raise UseInferenceDefault() - if infered is YES: - raise UseInferenceDefault() - transformed = transform(infered) - if not transformed or transformed is YES: - raise UseInferenceDefault() - return transformed - - -def _generic_transform(arg, klass, iterables, build_elts): - if isinstance(arg, klass): - return arg - elif isinstance(arg, iterables): - if not all(isinstance(elt, nodes.Const) - for elt in arg.elts): - # TODO(cpopa): Don't support heterogenous elements. - # Not yet, though. - raise UseInferenceDefault() - elts = [elt.value for elt in arg.elts] - elif isinstance(arg, nodes.Dict): - if not all(isinstance(elt[0], nodes.Const) - for elt in arg.items): - raise UseInferenceDefault() - elts = [item[0].value for item in arg.items] - elif (isinstance(arg, nodes.Const) and - isinstance(arg.value, (six.string_types, six.binary_type))): - elts = arg.value - else: - return - return klass(elts=build_elts(elts)) - - -def _infer_builtin(node, context, - klass=None, iterables=None, - build_elts=None): - transform_func = partial( - _generic_transform, - klass=klass, - iterables=iterables, - build_elts=build_elts) - - return _generic_inference(node, context, klass, transform_func) - -# pylint: disable=invalid-name -infer_tuple = partial( - _infer_builtin, - klass=nodes.Tuple, - iterables=(nodes.List, nodes.Set), - build_elts=tuple) - -infer_list = partial( - _infer_builtin, - klass=nodes.List, - iterables=(nodes.Tuple, nodes.Set), - build_elts=list) - -infer_set = partial( - _infer_builtin, - klass=nodes.Set, - iterables=(nodes.List, nodes.Tuple), - build_elts=set) - - -def _get_elts(arg, context): - is_iterable = lambda n: isinstance(n, - (nodes.List, nodes.Tuple, nodes.Set)) - try: - infered = next(arg.infer(context)) - except (InferenceError, UnresolvableName): - raise UseInferenceDefault() - if isinstance(infered, nodes.Dict): - items = infered.items - elif is_iterable(infered): - items = [] - for elt in infered.elts: - # If an item is not a pair of two items, - # then fallback to the default inference. - # Also, take in consideration only hashable items, - # tuples and consts. We are choosing Names as well. - if not is_iterable(elt): - raise UseInferenceDefault() - if len(elt.elts) != 2: - raise UseInferenceDefault() - if not isinstance(elt.elts[0], - (nodes.Tuple, nodes.Const, nodes.Name)): - raise UseInferenceDefault() - items.append(tuple(elt.elts)) - else: - raise UseInferenceDefault() - return items - -def infer_dict(node, context=None): - """Try to infer a dict call to a Dict node. - - The function treats the following cases: - - * dict() - * dict(mapping) - * dict(iterable) - * dict(iterable, **kwargs) - * dict(mapping, **kwargs) - * dict(**kwargs) - - If a case can't be infered, we'll fallback to default inference. - """ - has_keywords = lambda args: all(isinstance(arg, nodes.Keyword) - for arg in args) - if not node.args and not node.kwargs: - # dict() - return nodes.Dict() - elif has_keywords(node.args) and node.args: - # dict(a=1, b=2, c=4) - items = [(nodes.Const(arg.arg), arg.value) for arg in node.args] - elif (len(node.args) >= 2 and - has_keywords(node.args[1:])): - # dict(some_iterable, b=2, c=4) - elts = _get_elts(node.args[0], context) - keys = [(nodes.Const(arg.arg), arg.value) for arg in node.args[1:]] - items = elts + keys - elif len(node.args) == 1: - items = _get_elts(node.args[0], context) - else: - raise UseInferenceDefault() - - empty = nodes.Dict() - empty.items = items - return empty - -# Builtins inference -register_builtin_transform(infer_tuple, 'tuple') -register_builtin_transform(infer_set, 'set') -register_builtin_transform(infer_list, 'list') -register_builtin_transform(infer_dict, 'dict') diff --git a/pymode/libs/astroid/brain/py2gi.py b/pymode/libs/astroid/brain/py2gi.py deleted file mode 100644 index 6747898d..00000000 --- a/pymode/libs/astroid/brain/py2gi.py +++ /dev/null @@ -1,155 +0,0 @@ -"""Astroid hooks for the Python 2 GObject introspection bindings. - -Helps with understanding everything imported from 'gi.repository' -""" - -import inspect -import itertools -import sys -import re - -from astroid import MANAGER, AstroidBuildingException -from astroid.builder import AstroidBuilder - - -_inspected_modules = {} - -_identifier_re = r'^[A-Za-z_]\w*$' - -def _gi_build_stub(parent): - """ - Inspect the passed module recursively and build stubs for functions, - classes, etc. - """ - classes = {} - functions = {} - constants = {} - methods = {} - for name in dir(parent): - if name.startswith("__"): - continue - - # Check if this is a valid name in python - if not re.match(_identifier_re, name): - continue - - try: - obj = getattr(parent, name) - except: - continue - - if inspect.isclass(obj): - classes[name] = obj - elif (inspect.isfunction(obj) or - inspect.isbuiltin(obj)): - functions[name] = obj - elif (inspect.ismethod(obj) or - inspect.ismethoddescriptor(obj)): - methods[name] = obj - elif type(obj) in [int, str]: - constants[name] = obj - elif (str(obj).startswith(" (3, 0) -PY33 = sys.version_info >= (3, 3) - -# general function - -def infer_func_form(node, base_type, context=None, enum=False): - """Specific inference function for namedtuple or Python 3 enum. """ - def infer_first(node): - try: - value = next(node.infer(context=context)) - if value is YES: - raise UseInferenceDefault() - else: - return value - except StopIteration: - raise InferenceError() - - # node is a CallFunc node, class name as first argument and generated class - # attributes as second argument - if len(node.args) != 2: - # something weird here, go back to class implementation - raise UseInferenceDefault() - # namedtuple or enums list of attributes can be a list of strings or a - # whitespace-separate string - try: - name = infer_first(node.args[0]).value - names = infer_first(node.args[1]) - try: - attributes = names.value.replace(',', ' ').split() - except AttributeError: - if not enum: - attributes = [infer_first(const).value for const in names.elts] - else: - # Enums supports either iterator of (name, value) pairs - # or mappings. - # TODO: support only list, tuples and mappings. - if hasattr(names, 'items') and isinstance(names.items, list): - attributes = [infer_first(const[0]).value - for const in names.items - if isinstance(const[0], nodes.Const)] - elif hasattr(names, 'elts'): - # Enums can support either ["a", "b", "c"] - # or [("a", 1), ("b", 2), ...], but they can't - # be mixed. - if all(isinstance(const, nodes.Tuple) - for const in names.elts): - attributes = [infer_first(const.elts[0]).value - for const in names.elts - if isinstance(const, nodes.Tuple)] - else: - attributes = [infer_first(const).value - for const in names.elts] - else: - raise AttributeError - if not attributes: - raise AttributeError - except (AttributeError, exceptions.InferenceError) as exc: - raise UseInferenceDefault() - # we want to return a Class node instance with proper attributes set - class_node = nodes.Class(name, 'docstring') - class_node.parent = node.parent - # set base class=tuple - class_node.bases.append(base_type) - # XXX add __init__(*attributes) method - for attr in attributes: - fake_node = nodes.EmptyNode() - fake_node.parent = class_node - class_node.instance_attrs[attr] = [fake_node] - return class_node, name, attributes - - -# module specific transformation functions ##################################### - -def hashlib_transform(): - template = ''' - -class %(name)s(object): - def __init__(self, value=''): pass - def digest(self): - return %(digest)s - def copy(self): - return self - def update(self, value): pass - def hexdigest(self): - return '' - @property - def name(self): - return %(name)r - @property - def block_size(self): - return 1 - @property - def digest_size(self): - return 1 -''' - algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') - classes = "".join( - template % {'name': hashfunc, 'digest': 'b""' if PY3K else '""'} - for hashfunc in algorithms) - return AstroidBuilder(MANAGER).string_build(classes) - - -def collections_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -class defaultdict(dict): - default_factory = None - def __missing__(self, key): pass - -class deque(object): - maxlen = 0 - def __init__(self, iterable=None, maxlen=None): pass - def append(self, x): pass - def appendleft(self, x): pass - def clear(self): pass - def count(self, x): return 0 - def extend(self, iterable): pass - def extendleft(self, iterable): pass - def pop(self): pass - def popleft(self): pass - def remove(self, value): pass - def reverse(self): pass - def rotate(self, n): pass - def __iter__(self): return self - -''') - - -def pkg_resources_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -def resource_exists(package_or_requirement, resource_name): - pass - -def resource_isdir(package_or_requirement, resource_name): - pass - -def resource_filename(package_or_requirement, resource_name): - pass - -def resource_stream(package_or_requirement, resource_name): - pass - -def resource_string(package_or_requirement, resource_name): - pass - -def resource_listdir(package_or_requirement, resource_name): - pass - -def extraction_error(): - pass - -def get_cache_path(archive_name, names=()): - pass - -def postprocess(tempname, filename): - pass - -def set_extraction_path(path): - pass - -def cleanup_resources(force=False): - pass - -''') - - -def subprocess_transform(): - if PY3K: - communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=()): - pass - """ - else: - communicate = ('string', 'string') - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - pass - """ - if PY33: - wait_signature = 'def wait(self, timeout=None)' - else: - wait_signature = 'def wait(self)' - return AstroidBuilder(MANAGER).string_build(''' - -class Popen(object): - returncode = pid = 0 - stdin = stdout = stderr = file() - - %(init)s - - def communicate(self, input=None): - return %(communicate)r - %(wait_signature)s: - return self.returncode - def poll(self): - return self.returncode - def send_signal(self, signal): - pass - def terminate(self): - pass - def kill(self): - pass - ''' % {'init': init, - 'communicate': communicate, - 'wait_signature': wait_signature}) - - -# namedtuple support ########################################################### - -def looks_like_namedtuple(node): - func = node.func - if type(func) is nodes.Getattr: - return func.attrname == 'namedtuple' - if type(func) is nodes.Name: - return func.name == 'namedtuple' - return False - -def infer_named_tuple(node, context=None): - """Specific inference function for namedtuple CallFunc node""" - class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, - context=context) - fake = AstroidBuilder(MANAGER).string_build(''' -class %(name)s(tuple): - _fields = %(fields)r - def _asdict(self): - return self.__dict__ - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - return new(cls, iterable) - def _replace(_self, **kwds): - result = _self._make(map(kwds.pop, %(fields)r, _self)) - if kwds: - raise ValueError('Got unexpected field names: %%r' %% list(kwds)) - return result - ''' % {'name': name, 'fields': attributes}) - class_node.locals['_asdict'] = fake.body[0].locals['_asdict'] - class_node.locals['_make'] = fake.body[0].locals['_make'] - class_node.locals['_replace'] = fake.body[0].locals['_replace'] - class_node.locals['_fields'] = fake.body[0].locals['_fields'] - # we use UseInferenceDefault, we can't be a generator so return an iterator - return iter([class_node]) - -def infer_enum(node, context=None): - """ Specific inference function for enum CallFunc node. """ - enum_meta = nodes.Class("EnumMeta", 'docstring') - class_node = infer_func_form(node, enum_meta, - context=context, enum=True)[0] - return iter([class_node.instanciate_class()]) - -def infer_enum_class(node): - """ Specific inference for enums. """ - names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) - for basename in node.basenames: - # TODO: doesn't handle subclasses yet. This implementation - # is a hack to support enums. - if basename not in names: - continue - if node.root().name == 'enum': - # Skip if the class is directly from enum module. - break - for local, values in node.locals.items(): - if any(not isinstance(value, nodes.AssName) - for value in values): - continue - - stmt = values[0].statement() - if isinstance(stmt.targets[0], nodes.Tuple): - targets = stmt.targets[0].itered() - else: - targets = stmt.targets - - new_targets = [] - for target in targets: - # Replace all the assignments with our mocked class. - classdef = dedent(''' - class %(name)s(object): - @property - def value(self): - # Not the best return. - return None - @property - def name(self): - return %(name)r - ''' % {'name': target.name}) - fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] - fake.parent = target.parent - for method in node.mymethods(): - fake.locals[method.name] = [method] - new_targets.append(fake.instanciate_class()) - node.locals[local] = new_targets - break - return node - - -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_named_tuple), - looks_like_namedtuple) -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_enum), - AsStringRegexpPredicate('Enum', 'func')) -MANAGER.register_transform(nodes.Class, infer_enum_class) -register_module_extender(MANAGER, 'hashlib', hashlib_transform) -register_module_extender(MANAGER, 'collections', collections_transform) -register_module_extender(MANAGER, 'pkg_resources', pkg_resources_transform) -register_module_extender(MANAGER, 'subprocess', subprocess_transform) diff --git a/pymode/libs/astroid/brain/pynose.py b/pymode/libs/astroid/brain/pynose.py deleted file mode 100644 index 67a6fb8f..00000000 --- a/pymode/libs/astroid/brain/pynose.py +++ /dev/null @@ -1,79 +0,0 @@ -# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . - -"""Hooks for nose library.""" - -import re -import textwrap - -import astroid -import astroid.builder - -_BUILDER = astroid.builder.AstroidBuilder(astroid.MANAGER) - - -def _pep8(name, caps=re.compile('([A-Z])')): - return caps.sub(lambda m: '_' + m.groups()[0].lower(), name) - - -def _nose_tools_functions(): - """Get an iterator of names and bound methods.""" - module = _BUILDER.string_build(textwrap.dedent(''' - import unittest - - class Test(unittest.TestCase): - pass - a = Test() - ''')) - try: - case = next(module['a'].infer()) - except astroid.InferenceError: - return - for method in case.methods(): - if method.name.startswith('assert') and '_' not in method.name: - pep8_name = _pep8(method.name) - yield pep8_name, astroid.BoundMethod(method, case) - - -def _nose_tools_transform(node): - for method_name, method in _nose_tools_functions(): - node.locals[method_name] = [method] - - -def _nose_tools_trivial_transform(): - """Custom transform for the nose.tools module.""" - stub = _BUILDER.string_build('''__all__ = []''') - all_entries = ['ok_', 'eq_'] - - for pep8_name, method in _nose_tools_functions(): - all_entries.append(pep8_name) - stub[pep8_name] = method - - # Update the __all__ variable, since nose.tools - # does this manually with .append. - all_assign = stub['__all__'].parent - all_object = astroid.List(all_entries) - all_object.parent = all_assign - all_assign.value = all_object - return stub - - -astroid.register_module_extender(astroid.MANAGER, 'nose.tools.trivial', - _nose_tools_trivial_transform) -astroid.MANAGER.register_transform(astroid.Module, _nose_tools_transform, - lambda n: n.name == 'nose.tools') diff --git a/pymode/libs/astroid/brain/pysix_moves.py b/pymode/libs/astroid/brain/pysix_moves.py deleted file mode 100644 index 548d9761..00000000 --- a/pymode/libs/astroid/brain/pysix_moves.py +++ /dev/null @@ -1,261 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# astroid is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . - -"""Astroid hooks for six.moves.""" - -import sys -from textwrap import dedent - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder -from astroid.exceptions import AstroidBuildingException - -def _indent(text, prefix, predicate=None): - """Adds 'prefix' to the beginning of selected lines in 'text'. - - If 'predicate' is provided, 'prefix' will only be added to the lines - where 'predicate(line)' is True. If 'predicate' is not provided, - it will default to adding 'prefix' to all non-empty lines that do not - consist solely of whitespace characters. - """ - if predicate is None: - predicate = lambda line: line.strip() - - def prefixed_lines(): - for line in text.splitlines(True): - yield prefix + line if predicate(line) else line - return ''.join(prefixed_lines()) - - -if sys.version_info[0] == 2: - _IMPORTS_2 = """ - import BaseHTTPServer - import CGIHTTPServer - import SimpleHTTPServer - - from StringIO import StringIO - from cStringIO import StringIO as cStringIO - from UserDict import UserDict - from UserList import UserList - from UserString import UserString - - import __builtin__ as builtins - import thread as _thread - import dummy_thread as _dummy_thread - import ConfigParser as configparser - import copy_reg as copyreg - from itertools import (imap as map, - ifilter as filter, - ifilterfalse as filterfalse, - izip_longest as zip_longest, - izip as zip) - import htmlentitydefs as html_entities - import HTMLParser as html_parser - import httplib as http_client - import cookielib as http_cookiejar - import Cookie as http_cookies - import Queue as queue - import repr as reprlib - from pipes import quote as shlex_quote - import SocketServer as socketserver - import SimpleXMLRPCServer as xmlrpc_server - import xmlrpclib as xmlrpc_client - import _winreg as winreg - import robotparser as urllib_robotparser - import Tkinter as tkinter - import tkFileDialog as tkinter_tkfiledialog - - input = raw_input - intern = intern - range = xrange - xrange = xrange - reduce = reduce - reload_module = reload - - class UrllibParse(object): - import urlparse as _urlparse - import urllib as _urllib - ParseResult = _urlparse.ParseResult - SplitResult = _urlparse.SplitResult - parse_qs = _urlparse.parse_qs - parse_qsl = _urlparse.parse_qsl - urldefrag = _urlparse.urldefrag - urljoin = _urlparse.urljoin - urlparse = _urlparse.urlparse - urlsplit = _urlparse.urlsplit - urlunparse = _urlparse.urlunparse - urlunsplit = _urlparse.urlunsplit - quote = _urllib.quote - quote_plus = _urllib.quote_plus - unquote = _urllib.unquote - unquote_plus = _urllib.unquote_plus - urlencode = _urllib.urlencode - splitquery = _urllib.splitquery - splittag = _urllib.splittag - splituser = _urllib.splituser - uses_fragment = _urlparse.uses_fragment - uses_netloc = _urlparse.uses_netloc - uses_params = _urlparse.uses_params - uses_query = _urlparse.uses_query - uses_relative = _urlparse.uses_relative - - class UrllibError(object): - import urllib2 as _urllib2 - import urllib as _urllib - URLError = _urllib2.URLError - HTTPError = _urllib2.HTTPError - ContentTooShortError = _urllib.ContentTooShortError - - class DummyModule(object): - pass - - class UrllibRequest(object): - import urlparse as _urlparse - import urllib2 as _urllib2 - import urllib as _urllib - urlopen = _urllib2.urlopen - install_opener = _urllib2.install_opener - build_opener = _urllib2.build_opener - pathname2url = _urllib.pathname2url - url2pathname = _urllib.url2pathname - getproxies = _urllib.getproxies - Request = _urllib2.Request - OpenerDirector = _urllib2.OpenerDirector - HTTPDefaultErrorHandler = _urllib2.HTTPDefaultErrorHandler - HTTPRedirectHandler = _urllib2.HTTPRedirectHandler - HTTPCookieProcessor = _urllib2.HTTPCookieProcessor - ProxyHandler = _urllib2.ProxyHandler - BaseHandler = _urllib2.BaseHandler - HTTPPasswordMgr = _urllib2.HTTPPasswordMgr - HTTPPasswordMgrWithDefaultRealm = _urllib2.HTTPPasswordMgrWithDefaultRealm - AbstractBasicAuthHandler = _urllib2.AbstractBasicAuthHandler - HTTPBasicAuthHandler = _urllib2.HTTPBasicAuthHandler - ProxyBasicAuthHandler = _urllib2.ProxyBasicAuthHandler - AbstractDigestAuthHandler = _urllib2.AbstractDigestAuthHandler - HTTPDigestAuthHandler = _urllib2.HTTPDigestAuthHandler - ProxyDigestAuthHandler = _urllib2.ProxyDigestAuthHandler - HTTPHandler = _urllib2.HTTPHandler - HTTPSHandler = _urllib2.HTTPSHandler - FileHandler = _urllib2.FileHandler - FTPHandler = _urllib2.FTPHandler - CacheFTPHandler = _urllib2.CacheFTPHandler - UnknownHandler = _urllib2.UnknownHandler - HTTPErrorProcessor = _urllib2.HTTPErrorProcessor - urlretrieve = _urllib.urlretrieve - urlcleanup = _urllib.urlcleanup - proxy_bypass = _urllib.proxy_bypass - - urllib_parse = UrllibParse() - urllib_error = UrllibError() - urllib = DummyModule() - urllib.request = UrllibRequest() - urllib.parse = UrllibParse() - urllib.error = UrllibError() - """ -else: - _IMPORTS_3 = """ - import _io - cStringIO = _io.StringIO - filter = filter - from itertools import filterfalse - input = input - from sys import intern - map = map - range = range - from imp import reload as reload_module - from functools import reduce - from shlex import quote as shlex_quote - from io import StringIO - from collections import UserDict, UserList, UserString - xrange = range - zip = zip - from itertools import zip_longest - import builtins - import configparser - import copyreg - import _dummy_thread - import http.cookiejar as http_cookiejar - import http.cookies as http_cookies - import html.entities as html_entities - import html.parser as html_parser - import http.client as http_client - import http.server - BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server - import pickle as cPickle - import queue - import reprlib - import socketserver - import _thread - import winreg - import xmlrpc.server as xmlrpc_server - import xmlrpc.client as xmlrpc_client - import urllib.robotparser as urllib_robotparser - import email.mime.multipart as email_mime_multipart - import email.mime.nonmultipart as email_mime_nonmultipart - import email.mime.text as email_mime_text - import email.mime.base as email_mime_base - import urllib.parse as urllib_parse - import urllib.error as urllib_error - import tkinter - import tkinter.dialog as tkinter_dialog - import tkinter.filedialog as tkinter_filedialog - import tkinter.scrolledtext as tkinter_scrolledtext - import tkinter.simpledialog as tkinder_simpledialog - import tkinter.tix as tkinter_tix - import tkinter.ttk as tkinter_ttk - import tkinter.constants as tkinter_constants - import tkinter.dnd as tkinter_dnd - import tkinter.colorchooser as tkinter_colorchooser - import tkinter.commondialog as tkinter_commondialog - import tkinter.filedialog as tkinter_tkfiledialog - import tkinter.font as tkinter_font - import tkinter.messagebox as tkinter_messagebox - import urllib.request - import urllib.robotparser as urllib_robotparser - import urllib.parse as urllib_parse - import urllib.error as urllib_error - """ -if sys.version_info[0] == 2: - _IMPORTS = dedent(_IMPORTS_2) -else: - _IMPORTS = dedent(_IMPORTS_3) - - -def six_moves_transform(): - code = dedent(''' - class Moves(object): - {} - moves = Moves() - ''').format(_indent(_IMPORTS, " ")) - module = AstroidBuilder(MANAGER).string_build(code) - module.name = 'six.moves' - return module - - -def _six_fail_hook(modname): - if modname != 'six.moves': - raise AstroidBuildingException - module = AstroidBuilder(MANAGER).string_build(_IMPORTS) - module.name = 'six.moves' - return module - - -register_module_extender(MANAGER, 'six', six_moves_transform) -register_module_extender(MANAGER, 'requests.packages.urllib3.packages.six', - six_moves_transform) -MANAGER.register_failed_import_hook(_six_fail_hook) diff --git a/pymode/libs/astroid/builder.py b/pymode/libs/astroid/builder.py deleted file mode 100644 index 1fe7a36d..00000000 --- a/pymode/libs/astroid/builder.py +++ /dev/null @@ -1,240 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""The AstroidBuilder makes astroid from living object and / or from _ast - -The builder is not thread safe and can't be used to parse different sources -at the same time. -""" -from __future__ import with_statement - -__docformat__ = "restructuredtext en" - -import sys -from os.path import splitext, basename, exists, abspath - -from astroid.exceptions import AstroidBuildingException, InferenceError -from astroid.raw_building import InspectBuilder -from astroid.rebuilder import TreeRebuilder -from astroid.manager import AstroidManager -from astroid.bases import YES, Instance -from astroid.modutils import modpath_from_file - -from _ast import PyCF_ONLY_AST -def parse(string): - return compile(string, "", 'exec', PyCF_ONLY_AST) - -if sys.version_info >= (3, 0): - from tokenize import detect_encoding - - def open_source_file(filename): - with open(filename, 'rb') as byte_stream: - encoding = detect_encoding(byte_stream.readline)[0] - stream = open(filename, 'r', newline=None, encoding=encoding) - try: - data = stream.read() - except UnicodeError: # wrong encodingg - # detect_encoding returns utf-8 if no encoding specified - msg = 'Wrong (%s) or no encoding specified' % encoding - raise AstroidBuildingException(msg) - return stream, encoding, data - -else: - import re - - _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)") - - def _guess_encoding(string): - """get encoding from a python file as string or return None if not found - """ - # check for UTF-8 byte-order mark - if string.startswith('\xef\xbb\xbf'): - return 'UTF-8' - for line in string.split('\n', 2)[:2]: - # check for encoding declaration - match = _ENCODING_RGX.match(line) - if match is not None: - return match.group(1) - - def open_source_file(filename): - """get data for parsing a file""" - stream = open(filename, 'U') - data = stream.read() - encoding = _guess_encoding(data) - return stream, encoding, data - -# ast NG builder ############################################################## - -MANAGER = AstroidManager() - -class AstroidBuilder(InspectBuilder): - """provide astroid building methods""" - - def __init__(self, manager=None): - InspectBuilder.__init__(self) - self._manager = manager or MANAGER - - def module_build(self, module, modname=None): - """build an astroid from a living module instance - """ - node = None - path = getattr(module, '__file__', None) - if path is not None: - path_, ext = splitext(module.__file__) - if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'): - node = self.file_build(path_ + '.py', modname) - if node is None: - # this is a built-in module - # get a partial representation by introspection - node = self.inspect_build(module, modname=modname, path=path) - # we have to handle transformation by ourselves since the rebuilder - # isn't called for builtin nodes - # - # XXX it's then only called for Module nodes, not for underlying - # nodes - node = self._manager.transform(node) - return node - - def file_build(self, path, modname=None): - """build astroid from a source code file (i.e. from an ast) - - path is expected to be a python source file - """ - try: - stream, encoding, data = open_source_file(path) - except IOError as exc: - msg = 'Unable to load file %r (%s)' % (path, exc) - raise AstroidBuildingException(msg) - except SyntaxError as exc: # py3k encoding specification error - raise AstroidBuildingException(exc) - except LookupError as exc: # unknown encoding - raise AstroidBuildingException(exc) - with stream: - # get module name if necessary - if modname is None: - try: - modname = '.'.join(modpath_from_file(path)) - except ImportError: - modname = splitext(basename(path))[0] - # build astroid representation - module = self._data_build(data, modname, path) - return self._post_build(module, encoding) - - def string_build(self, data, modname='', path=None): - """build astroid from source code string and return rebuilded astroid""" - module = self._data_build(data, modname, path) - module.file_bytes = data.encode('utf-8') - return self._post_build(module, 'utf-8') - - def _post_build(self, module, encoding): - """handles encoding and delayed nodes - after a module has been built - """ - module.file_encoding = encoding - self._manager.cache_module(module) - # post tree building steps after we stored the module in the cache: - for from_node in module._from_nodes: - if from_node.modname == '__future__': - for symbol, _ in from_node.names: - module.future_imports.add(symbol) - self.add_from_names_to_locals(from_node) - # handle delayed assattr nodes - for delayed in module._delayed_assattr: - self.delayed_assattr(delayed) - return module - - def _data_build(self, data, modname, path): - """build tree node from data and add some informations""" - # this method could be wrapped with a pickle/cache function - try: - node = parse(data + '\n') - except TypeError as exc: - raise AstroidBuildingException(exc) - if path is not None: - node_file = abspath(path) - else: - node_file = '' - if modname.endswith('.__init__'): - modname = modname[:-9] - package = True - else: - package = path and path.find('__init__.py') > -1 or False - rebuilder = TreeRebuilder(self._manager) - module = rebuilder.visit_module(node, modname, node_file, package) - module._from_nodes = rebuilder._from_nodes - module._delayed_assattr = rebuilder._delayed_assattr - return module - - def add_from_names_to_locals(self, node): - """store imported names to the locals; - resort the locals if coming from a delayed node - """ - - _key_func = lambda node: node.fromlineno - def sort_locals(my_list): - my_list.sort(key=_key_func) - for (name, asname) in node.names: - if name == '*': - try: - imported = node.do_import_module() - except InferenceError: - continue - for name in imported.wildcard_import_names(): - node.parent.set_local(name, node) - sort_locals(node.parent.scope().locals[name]) - else: - node.parent.set_local(asname or name, node) - sort_locals(node.parent.scope().locals[asname or name]) - - def delayed_assattr(self, node): - """visit a AssAttr node -> add name to locals, handle members - definition - """ - try: - frame = node.frame() - for infered in node.expr.infer(): - if infered is YES: - continue - try: - if infered.__class__ is Instance: - infered = infered._proxied - iattrs = infered.instance_attrs - elif isinstance(infered, Instance): - # Const, Tuple, ... we may be wrong, may be not, but - # anyway we don't want to pollute builtin's namespace - continue - elif infered.is_function: - iattrs = infered.instance_attrs - else: - iattrs = infered.locals - except AttributeError: - # XXX log error - #import traceback - #traceback.print_exc() - continue - values = iattrs.setdefault(node.attrname, []) - if node in values: - continue - # get assign in __init__ first XXX useful ? - if frame.name == '__init__' and values and not \ - values[0].frame().name == '__init__': - values.insert(0, node) - else: - values.append(node) - except InferenceError: - pass - diff --git a/pymode/libs/astroid/exceptions.py b/pymode/libs/astroid/exceptions.py deleted file mode 100644 index 3889e2e7..00000000 --- a/pymode/libs/astroid/exceptions.py +++ /dev/null @@ -1,51 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains exceptions used in the astroid library - -""" - -__doctype__ = "restructuredtext en" - -class AstroidError(Exception): - """base exception class for all astroid related exceptions""" - -class AstroidBuildingException(AstroidError): - """exception class when we are unable to build an astroid representation""" - -class ResolveError(AstroidError): - """base class of astroid resolution/inference error""" - -class NotFoundError(ResolveError): - """raised when we are unable to resolve a name""" - -class InferenceError(ResolveError): - """raised when we are unable to infer a node""" - -class UseInferenceDefault(Exception): - """exception to be raised in custom inference function to indicate that it - should go back to the default behaviour - """ - -class UnresolvableName(InferenceError): - """raised when we are unable to resolve a name""" - -class NoDefault(AstroidError): - """raised by function's `default_value` method when an argument has - no default value - """ - diff --git a/pymode/libs/astroid/inference.py b/pymode/libs/astroid/inference.py deleted file mode 100644 index 22807049..00000000 --- a/pymode/libs/astroid/inference.py +++ /dev/null @@ -1,405 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to handle inference on astroid trees -""" - -__doctype__ = "restructuredtext en" - -from itertools import chain - -from astroid import nodes - -from astroid.manager import AstroidManager -from astroid.exceptions import (AstroidError, InferenceError, NoDefault, - NotFoundError, UnresolvableName) -from astroid.bases import (YES, Instance, InferenceContext, - _infer_stmts, copy_context, path_wrapper, - raise_if_nothing_infered) -from astroid.protocols import ( - _arguments_infer_argname, - BIN_OP_METHOD, UNARY_OP_METHOD) - -MANAGER = AstroidManager() - - -class CallContext(object): - """when inferring a function call, this class is used to remember values - given as argument - """ - def __init__(self, args, starargs, dstarargs): - self.args = [] - self.nargs = {} - for arg in args: - if isinstance(arg, nodes.Keyword): - self.nargs[arg.arg] = arg.value - else: - self.args.append(arg) - self.starargs = starargs - self.dstarargs = dstarargs - - def infer_argument(self, funcnode, name, context): - """infer a function argument value according to the call context""" - # 1. search in named keywords - try: - return self.nargs[name].infer(context) - except KeyError: - # Function.args.args can be None in astroid (means that we don't have - # information on argnames) - argindex = funcnode.args.find_argname(name)[0] - if argindex is not None: - # 2. first argument of instance/class method - if argindex == 0 and funcnode.type in ('method', 'classmethod'): - if context.boundnode is not None: - boundnode = context.boundnode - else: - # XXX can do better ? - boundnode = funcnode.parent.frame() - if funcnode.type == 'method': - if not isinstance(boundnode, Instance): - boundnode = Instance(boundnode) - return iter((boundnode,)) - if funcnode.type == 'classmethod': - return iter((boundnode,)) - # if we have a method, extract one position - # from the index, so we'll take in account - # the extra parameter represented by `self` or `cls` - if funcnode.type in ('method', 'classmethod'): - argindex -= 1 - # 2. search arg index - try: - return self.args[argindex].infer(context) - except IndexError: - pass - # 3. search in *args (.starargs) - if self.starargs is not None: - its = [] - for infered in self.starargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(argindex, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 4. XXX search in **kwargs (.dstarargs) - if self.dstarargs is not None: - its = [] - for infered in self.dstarargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(name, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 5. */** argument, (Tuple or Dict) - if name == funcnode.args.vararg: - return iter((nodes.const_factory(()))) - if name == funcnode.args.kwarg: - return iter((nodes.const_factory({}))) - # 6. return default value if any - try: - return funcnode.args.default_value(name).infer(context) - except NoDefault: - raise InferenceError(name) - - -# .infer method ############################################################### - - -def infer_end(self, context=None): - """inference's end for node such as Module, Class, Function, Const... - """ - yield self -nodes.Module._infer = infer_end -nodes.Class._infer = infer_end -nodes.Function._infer = infer_end -nodes.Lambda._infer = infer_end -nodes.Const._infer = infer_end -nodes.List._infer = infer_end -nodes.Tuple._infer = infer_end -nodes.Dict._infer = infer_end -nodes.Set._infer = infer_end - -def _higher_function_scope(node): - """ Search for the first function which encloses the given - scope. This can be used for looking up in that function's - scope, in case looking up in a lower scope for a particular - name fails. - - :param node: A scope node. - :returns: - ``None``, if no parent function scope was found, - otherwise an instance of :class:`astroid.scoped_nodes.Function`, - which encloses the given node. - """ - current = node - while current.parent and not isinstance(current.parent, nodes.Function): - current = current.parent - if current and current.parent: - return current.parent - -def infer_name(self, context=None): - """infer a Name: use name lookup rules""" - frame, stmts = self.lookup(self.name) - if not stmts: - # Try to see if the name is enclosed in a nested function - # and use the higher (first function) scope for searching. - # TODO: should this be promoted to other nodes as well? - parent_function = _higher_function_scope(self.scope()) - if parent_function: - _, stmts = parent_function.lookup(self.name) - - if not stmts: - raise UnresolvableName(self.name) - context = context.clone() - context.lookupname = self.name - return _infer_stmts(stmts, context, frame) -nodes.Name._infer = path_wrapper(infer_name) -nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper - - -def infer_callfunc(self, context=None): - """infer a CallFunc node by trying to guess what the function returns""" - callcontext = context.clone() - callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs) - callcontext.boundnode = None - for callee in self.func.infer(context): - if callee is YES: - yield callee - continue - try: - if hasattr(callee, 'infer_call_result'): - for infered in callee.infer_call_result(self, callcontext): - yield infered - except InferenceError: - ## XXX log error ? - continue -nodes.CallFunc._infer = path_wrapper(raise_if_nothing_infered(infer_callfunc)) - - -def infer_import(self, context=None, asname=True): - """infer an Import node: return the imported module/object""" - name = context.lookupname - if name is None: - raise InferenceError() - if asname: - yield self.do_import_module(self.real_name(name)) - else: - yield self.do_import_module(name) -nodes.Import._infer = path_wrapper(infer_import) - -def infer_name_module(self, name): - context = InferenceContext() - context.lookupname = name - return self.infer(context, asname=False) -nodes.Import.infer_name_module = infer_name_module - - -def infer_from(self, context=None, asname=True): - """infer a From nodes: return the imported module/object""" - name = context.lookupname - if name is None: - raise InferenceError() - if asname: - name = self.real_name(name) - module = self.do_import_module() - try: - context = copy_context(context) - context.lookupname = name - return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context) - except NotFoundError: - raise InferenceError(name) -nodes.From._infer = path_wrapper(infer_from) - - -def infer_getattr(self, context=None): - """infer a Getattr node by using getattr on the associated object""" - for owner in self.expr.infer(context): - if owner is YES: - yield owner - continue - try: - context.boundnode = owner - for obj in owner.igetattr(self.attrname, context): - yield obj - context.boundnode = None - except (NotFoundError, InferenceError): - context.boundnode = None - except AttributeError: - # XXX method / function - context.boundnode = None -nodes.Getattr._infer = path_wrapper(raise_if_nothing_infered(infer_getattr)) -nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper - - -def infer_global(self, context=None): - if context.lookupname is None: - raise InferenceError() - try: - return _infer_stmts(self.root().getattr(context.lookupname), context) - except NotFoundError: - raise InferenceError() -nodes.Global._infer = path_wrapper(infer_global) - - -def infer_subscript(self, context=None): - """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]""" - value = next(self.value.infer(context)) - if value is YES: - yield YES - return - - index = next(self.slice.infer(context)) - if index is YES: - yield YES - return - - if isinstance(index, nodes.Const): - try: - assigned = value.getitem(index.value, context) - except AttributeError: - raise InferenceError() - except (IndexError, TypeError): - yield YES - return - - # Prevent inferring if the infered subscript - # is the same as the original subscripted object. - if self is assigned: - yield YES - return - for infered in assigned.infer(context): - yield infered - else: - raise InferenceError() -nodes.Subscript._infer = path_wrapper(infer_subscript) -nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript) - -def infer_unaryop(self, context=None): - for operand in self.operand.infer(context): - try: - yield operand.infer_unary_op(self.op) - except TypeError: - continue - except AttributeError: - meth = UNARY_OP_METHOD[self.op] - if meth is None: - yield YES - else: - try: - # XXX just suppose if the type implement meth, returned type - # will be the same - operand.getattr(meth) - yield operand - except GeneratorExit: - raise - except: - yield YES -nodes.UnaryOp._infer = path_wrapper(infer_unaryop) - -def _infer_binop(operator, operand1, operand2, context, failures=None): - if operand1 is YES: - yield operand1 - return - try: - for valnode in operand1.infer_binary_op(operator, operand2, context): - yield valnode - except AttributeError: - try: - # XXX just suppose if the type implement meth, returned type - # will be the same - operand1.getattr(BIN_OP_METHOD[operator]) - yield operand1 - except: - if failures is None: - yield YES - else: - failures.append(operand1) - -def infer_binop(self, context=None): - failures = [] - for lhs in self.left.infer(context): - for val in _infer_binop(self.op, lhs, self.right, context, failures): - yield val - for lhs in failures: - for rhs in self.right.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): - yield val -nodes.BinOp._infer = path_wrapper(infer_binop) - - -def infer_arguments(self, context=None): - name = context.lookupname - if name is None: - raise InferenceError() - return _arguments_infer_argname(self, name, context) -nodes.Arguments._infer = infer_arguments - - -def infer_ass(self, context=None): - """infer a AssName/AssAttr: need to inspect the RHS part of the - assign node - """ - stmt = self.statement() - if isinstance(stmt, nodes.AugAssign): - return stmt.infer(context) - stmts = list(self.assigned_stmts(context=context)) - return _infer_stmts(stmts, context) -nodes.AssName._infer = path_wrapper(infer_ass) -nodes.AssAttr._infer = path_wrapper(infer_ass) - -def infer_augassign(self, context=None): - failures = [] - for lhs in self.target.infer_lhs(context): - for val in _infer_binop(self.op, lhs, self.value, context, failures): - yield val - for lhs in failures: - for rhs in self.value.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): - yield val -nodes.AugAssign._infer = path_wrapper(infer_augassign) - - -# no infer method on DelName and DelAttr (expected InferenceError) - - -def infer_empty_node(self, context=None): - if not self.has_underlying_object(): - yield YES - else: - try: - for infered in MANAGER.infer_ast_from_something(self.object, - context=context): - yield infered - except AstroidError: - yield YES -nodes.EmptyNode._infer = path_wrapper(infer_empty_node) - - -def infer_index(self, context=None): - return self.value.infer(context) -nodes.Index._infer = infer_index diff --git a/pymode/libs/astroid/inspector.py b/pymode/libs/astroid/inspector.py deleted file mode 100644 index 1fc31926..00000000 --- a/pymode/libs/astroid/inspector.py +++ /dev/null @@ -1,273 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""visitor doing some postprocessing on the astroid tree. -Try to resolve definitions (namespace) dictionary, relationship... - -This module has been imported from pyreverse -""" - -__docformat__ = "restructuredtext en" - -from os.path import dirname - -import astroid -from astroid.exceptions import InferenceError -from astroid.utils import LocalsVisitor -from astroid.modutils import get_module_part, is_relative, is_standard_module - -class IdGeneratorMixIn(object): - """ - Mixin adding the ability to generate integer uid - """ - def __init__(self, start_value=0): - self.id_count = start_value - - def init_counter(self, start_value=0): - """init the id counter - """ - self.id_count = start_value - - def generate_id(self): - """generate a new identifier - """ - self.id_count += 1 - return self.id_count - - -class Linker(IdGeneratorMixIn, LocalsVisitor): - """ - walk on the project tree and resolve relationships. - - According to options the following attributes may be added to visited nodes: - - * uid, - a unique identifier for the node (on astroid.Project, astroid.Module, - astroid.Class and astroid.locals_type). Only if the linker has been instantiated - with tag=True parameter (False by default). - - * Function - a mapping from locals names to their bounded value, which may be a - constant like a string or an integer, or an astroid node (on astroid.Module, - astroid.Class and astroid.Function). - - * instance_attrs_type - as locals_type but for klass member attributes (only on astroid.Class) - - * implements, - list of implemented interface _objects_ (only on astroid.Class nodes) - """ - - def __init__(self, project, inherited_interfaces=0, tag=False): - IdGeneratorMixIn.__init__(self) - LocalsVisitor.__init__(self) - # take inherited interface in consideration or not - self.inherited_interfaces = inherited_interfaces - # tag nodes or not - self.tag = tag - # visited project - self.project = project - - - def visit_project(self, node): - """visit an astroid.Project node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for module in node.modules: - self.visit(module) - - def visit_package(self, node): - """visit an astroid.Package node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for subelmt in node.values(): - self.visit(subelmt) - - def visit_module(self, node): - """visit an astroid.Module node - - * set the locals_type mapping - * set the depends mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - node.depends = [] - if self.tag: - node.uid = self.generate_id() - - def visit_class(self, node): - """visit an astroid.Class node - - * set the locals_type and instance_attrs_type mappings - * set the implements list and build it - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - # resolve ancestors - for baseobj in node.ancestors(recurs=False): - specializations = getattr(baseobj, 'specializations', []) - specializations.append(node) - baseobj.specializations = specializations - # resolve instance attributes - node.instance_attrs_type = {} - for assattrs in node.instance_attrs.values(): - for assattr in assattrs: - self.handle_assattr_type(assattr, node) - # resolve implemented interface - try: - node.implements = list(node.interfaces(self.inherited_interfaces)) - except InferenceError: - node.implements = () - - def visit_function(self, node): - """visit an astroid.Function node - - * set the locals_type mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - - link_project = visit_project - link_module = visit_module - link_class = visit_class - link_function = visit_function - - def visit_assname(self, node): - """visit an astroid.AssName node - - handle locals_type - """ - # avoid double parsing done by different Linkers.visit - # running over the same project: - if hasattr(node, '_handled'): - return - node._handled = True - if node.name in node.frame(): - frame = node.frame() - else: - # the name has been defined as 'global' in the frame and belongs - # there. Btw the frame is not yet visited as the name is in the - # root locals; the frame hence has no locals_type attribute - frame = node.root() - try: - values = node.infered() - try: - already_infered = frame.locals_type[node.name] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - frame.locals_type[node.name] = values - except astroid.InferenceError: - pass - - def handle_assattr_type(self, node, parent): - """handle an astroid.AssAttr node - - handle instance_attrs_type - """ - try: - values = list(node.infer()) - try: - already_infered = parent.instance_attrs_type[node.attrname] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - parent.instance_attrs_type[node.attrname] = values - except astroid.InferenceError: - pass - - def visit_import(self, node): - """visit an astroid.Import node - - resolve module dependencies - """ - context_file = node.root().file - for name in node.names: - relative = is_relative(name[0], context_file) - self._imported_module(node, name[0], relative) - - - def visit_from(self, node): - """visit an astroid.From node - - resolve module dependencies - """ - basename = node.modname - context_file = node.root().file - if context_file is not None: - relative = is_relative(basename, context_file) - else: - relative = False - for name in node.names: - if name[0] == '*': - continue - # analyze dependencies - fullname = '%s.%s' % (basename, name[0]) - if fullname.find('.') > -1: - try: - # XXX: don't use get_module_part, missing package precedence - fullname = get_module_part(fullname, context_file) - except ImportError: - continue - if fullname != basename: - self._imported_module(node, fullname, relative) - - - def compute_module(self, context_name, mod_path): - """return true if the module should be added to dependencies""" - package_dir = dirname(self.project.path) - if context_name == mod_path: - return 0 - elif is_standard_module(mod_path, (package_dir,)): - return 1 - return 0 - - # protected methods ######################################################## - - def _imported_module(self, node, mod_path, relative): - """notify an imported module, used to analyze dependencies - """ - module = node.root() - context_name = module.name - if relative: - mod_path = '%s.%s' % ('.'.join(context_name.split('.')[:-1]), - mod_path) - if self.compute_module(context_name, mod_path): - # handle dependencies - if not hasattr(module, 'depends'): - module.depends = [] - mod_paths = module.depends - if not mod_path in mod_paths: - mod_paths.append(mod_path) diff --git a/pymode/libs/astroid/manager.py b/pymode/libs/astroid/manager.py deleted file mode 100644 index b1fb3058..00000000 --- a/pymode/libs/astroid/manager.py +++ /dev/null @@ -1,391 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""astroid manager: avoid multiple astroid build of a same module when -possible by providing a class responsible to get astroid representation -from various source and using a cache of built modules) -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import collections -import imp -import os -from os.path import dirname, join, isdir, exists -from warnings import warn -import zipimport - -from logilab.common.configuration import OptionsProviderMixIn - -from astroid.exceptions import AstroidBuildingException -from astroid import modutils - - -def astroid_wrapper(func, modname): - """wrapper to give to AstroidManager.project_from_files""" - print('parsing %s...' % modname) - try: - return func(modname) - except AstroidBuildingException as exc: - print(exc) - except Exception as exc: - import traceback - traceback.print_exc() - -def _silent_no_wrap(func, modname): - """silent wrapper that doesn't do anything; can be used for tests""" - return func(modname) - -def safe_repr(obj): - try: - return repr(obj) - except: - return '???' - - - -class AstroidManager(OptionsProviderMixIn): - """the astroid manager, responsible to build astroid from files - or modules. - - Use the Borg pattern. - """ - - name = 'astroid loader' - options = (("ignore", - {'type' : "csv", 'metavar' : "", - 'dest' : "black_list", "default" : ('CVS',), - 'help' : "add (may be a directory) to the black list\ -. It should be a base name, not a path. You may set this option multiple times\ -."}), - ("project", - {'default': "No Name", 'type' : 'string', 'short': 'p', - 'metavar' : '', - 'help' : 'set the project name.'}), - ) - brain = {} - def __init__(self): - self.__dict__ = AstroidManager.brain - if not self.__dict__: - OptionsProviderMixIn.__init__(self) - self.load_defaults() - # NOTE: cache entries are added by the [re]builder - self.astroid_cache = {} - self._mod_file_cache = {} - self.transforms = collections.defaultdict(list) - self._failed_import_hooks = [] - self.always_load_extensions = False - self.optimize_ast = False - self.extension_package_whitelist = set() - - def ast_from_file(self, filepath, modname=None, fallback=True, source=False): - """given a module name, return the astroid object""" - try: - filepath = modutils.get_source_file(filepath, include_no_ext=True) - source = True - except modutils.NoSourceFile: - pass - if modname is None: - try: - modname = '.'.join(modutils.modpath_from_file(filepath)) - except ImportError: - modname = filepath - if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: - return self.astroid_cache[modname] - if source: - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).file_build(filepath, modname) - elif fallback and modname: - return self.ast_from_module_name(modname) - raise AstroidBuildingException('unable to get astroid for file %s' % - filepath) - - def _build_stub_module(self, modname): - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).string_build('', modname) - - def _can_load_extension(self, modname): - if self.always_load_extensions: - return True - if modutils.is_standard_module(modname): - return True - parts = modname.split('.') - return any( - '.'.join(parts[:x]) in self.extension_package_whitelist - for x in range(1, len(parts) + 1)) - - def ast_from_module_name(self, modname, context_file=None): - """given a module name, return the astroid object""" - if modname in self.astroid_cache: - return self.astroid_cache[modname] - if modname == '__main__': - return self._build_stub_module(modname) - old_cwd = os.getcwd() - if context_file: - os.chdir(dirname(context_file)) - try: - filepath, mp_type = self.file_from_module_name(modname, context_file) - if mp_type == modutils.PY_ZIPMODULE: - module = self.zip_import_data(filepath) - if module is not None: - return module - elif mp_type in (imp.C_BUILTIN, imp.C_EXTENSION): - if mp_type == imp.C_EXTENSION and not self._can_load_extension(modname): - return self._build_stub_module(modname) - try: - module = modutils.load_module_from_name(modname) - except Exception as ex: - msg = 'Unable to load module %s (%s)' % (modname, ex) - raise AstroidBuildingException(msg) - return self.ast_from_module(module, modname) - elif mp_type == imp.PY_COMPILED: - raise AstroidBuildingException("Unable to load compiled module %s" % (modname,)) - if filepath is None: - raise AstroidBuildingException("Unable to load module %s" % (modname,)) - return self.ast_from_file(filepath, modname, fallback=False) - except AstroidBuildingException as e: - for hook in self._failed_import_hooks: - try: - return hook(modname) - except AstroidBuildingException: - pass - raise e - finally: - os.chdir(old_cwd) - - def zip_import_data(self, filepath): - if zipimport is None: - return None - from astroid.builder import AstroidBuilder - builder = AstroidBuilder(self) - for ext in ('.zip', '.egg'): - try: - eggpath, resource = filepath.rsplit(ext + os.path.sep, 1) - except ValueError: - continue - try: - importer = zipimport.zipimporter(eggpath + ext) - zmodname = resource.replace(os.path.sep, '.') - if importer.is_package(resource): - zmodname = zmodname + '.__init__' - module = builder.string_build(importer.get_source(resource), - zmodname, filepath) - return module - except: - continue - return None - - def file_from_module_name(self, modname, contextfile): - try: - value = self._mod_file_cache[(modname, contextfile)] - except KeyError: - try: - value = modutils.file_info_from_modpath( - modname.split('.'), context_file=contextfile) - except ImportError as ex: - msg = 'Unable to load module %s (%s)' % (modname, ex) - value = AstroidBuildingException(msg) - self._mod_file_cache[(modname, contextfile)] = value - if isinstance(value, AstroidBuildingException): - raise value - return value - - def ast_from_module(self, module, modname=None): - """given an imported module, return the astroid object""" - modname = modname or module.__name__ - if modname in self.astroid_cache: - return self.astroid_cache[modname] - try: - # some builtin modules don't have __file__ attribute - filepath = module.__file__ - if modutils.is_python_source(filepath): - return self.ast_from_file(filepath, modname) - except AttributeError: - pass - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).module_build(module, modname) - - def ast_from_class(self, klass, modname=None): - """get astroid for the given class""" - if modname is None: - try: - modname = klass.__module__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for class %s' % safe_repr(klass)) - modastroid = self.ast_from_module_name(modname) - return modastroid.getattr(klass.__name__)[0] # XXX - - - def infer_ast_from_something(self, obj, context=None): - """infer astroid for the given class""" - if hasattr(obj, '__class__') and not isinstance(obj, type): - klass = obj.__class__ - else: - klass = obj - try: - modname = klass.__module__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for %s' % safe_repr(klass)) - except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving module for %s: %s' - % (safe_repr(klass), ex)) - try: - name = klass.__name__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get name for %s' % safe_repr(klass)) - except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving name for %s: %s' - % (safe_repr(klass), ex)) - # take care, on living object __module__ is regularly wrong :( - modastroid = self.ast_from_module_name(modname) - if klass is obj: - for infered in modastroid.igetattr(name, context): - yield infered - else: - for infered in modastroid.igetattr(name, context): - yield infered.instanciate_class() - - def project_from_files(self, files, func_wrapper=astroid_wrapper, - project_name=None, black_list=None): - """return a Project from a list of files or modules""" - # build the project representation - project_name = project_name or self.config.project - black_list = black_list or self.config.black_list - project = Project(project_name) - for something in files: - if not exists(something): - fpath = modutils.file_from_modpath(something.split('.')) - elif isdir(something): - fpath = join(something, '__init__.py') - else: - fpath = something - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None: - continue - # XXX why is first file defining the project.path ? - project.path = project.path or astroid.file - project.add_module(astroid) - base_name = astroid.name - # recurse in package except if __init__ was explicitly given - if astroid.package and something.find('__init__') == -1: - # recurse on others packages / modules if this is a package - for fpath in modutils.get_module_files(dirname(astroid.file), - black_list): - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None or astroid.name == base_name: - continue - project.add_module(astroid) - return project - - def register_transform(self, node_class, transform, predicate=None): - """Register `transform(node)` function to be applied on the given - Astroid's `node_class` if `predicate` is None or returns true - when called with the node as argument. - - The transform function may return a value which is then used to - substitute the original node in the tree. - """ - self.transforms[node_class].append((transform, predicate)) - - def unregister_transform(self, node_class, transform, predicate=None): - """Unregister the given transform.""" - self.transforms[node_class].remove((transform, predicate)) - - def register_failed_import_hook(self, hook): - """Registers a hook to resolve imports that cannot be found otherwise. - - `hook` must be a function that accepts a single argument `modname` which - contains the name of the module or package that could not be imported. - If `hook` can resolve the import, must return a node of type `astroid.Module`, - otherwise, it must raise `AstroidBuildingException`. - """ - self._failed_import_hooks.append(hook) - - def transform(self, node): - """Call matching transforms for the given node if any and return the - transformed node. - """ - cls = node.__class__ - if cls not in self.transforms: - # no transform registered for this class of node - return node - - transforms = self.transforms[cls] - orig_node = node # copy the reference - for transform_func, predicate in transforms: - if predicate is None or predicate(node): - ret = transform_func(node) - # if the transformation function returns something, it's - # expected to be a replacement for the node - if ret is not None: - if node is not orig_node: - # node has already be modified by some previous - # transformation, warn about it - warn('node %s substituted multiple times' % node) - node = ret - return node - - def cache_module(self, module): - """Cache a module if no module with the same name is known yet.""" - self.astroid_cache.setdefault(module.name, module) - - def clear_cache(self, astroid_builtin=None): - # XXX clear transforms - self.astroid_cache.clear() - # force bootstrap again, else we may ends up with cache inconsistency - # between the manager and CONST_PROXY, making - # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the - # test order - import astroid.raw_building - astroid.raw_building._astroid_bootstrapping( - astroid_builtin=astroid_builtin) - - -class Project(object): - """a project handle a set of modules / packages""" - def __init__(self, name=''): - self.name = name - self.path = None - self.modules = [] - self.locals = {} - self.__getitem__ = self.locals.__getitem__ - self.__iter__ = self.locals.__iter__ - self.values = self.locals.values - self.keys = self.locals.keys - self.items = self.locals.items - - def add_module(self, node): - self.locals[node.name] = node - self.modules.append(node) - - def get_module(self, name): - return self.locals[name] - - def get_children(self): - return self.modules - - def __repr__(self): - return '' % (self.name, id(self), - len(self.modules)) - - diff --git a/pymode/libs/astroid/mixins.py b/pymode/libs/astroid/mixins.py deleted file mode 100644 index dbf1673a..00000000 --- a/pymode/libs/astroid/mixins.py +++ /dev/null @@ -1,124 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module contains some mixins for the different nodes. -""" - -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import (AstroidBuildingException, InferenceError, - NotFoundError) - - -class BlockRangeMixIn(object): - """override block range """ - - @cachedproperty - def blockstart_tolineno(self): - return self.lineno - - def _elsed_block_range(self, lineno, orelse, last=None): - """handle block line numbers range for try/finally, for, if and while - statements - """ - if lineno == self.fromlineno: - return lineno, lineno - if orelse: - if lineno >= orelse[0].fromlineno: - return lineno, orelse[-1].tolineno - return lineno, orelse[0].fromlineno - 1 - return lineno, last or self.tolineno - - -class FilterStmtsMixin(object): - """Mixin for statement filtering and assignment type""" - - def _get_filtered_stmts(self, _, node, _stmts, mystmt): - """method used in _filter_stmts to get statemtents and trigger break""" - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - def ass_type(self): - return self - - -class AssignTypeMixin(object): - - def ass_type(self): - return self - - def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - return _stmts, True - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - -class ParentAssignTypeMixin(AssignTypeMixin): - - def ass_type(self): - return self.parent.ass_type() - - -class FromImportMixIn(FilterStmtsMixin): - """MixIn for From and Import Nodes""" - - def _infer_name(self, frame, name): - return name - - def do_import_module(self, modname=None): - """return the ast for a module whose name is imported by - """ - # handle special case where we are on a package node importing a module - # using the same name as the package, which may end in an infinite loop - # on relative imports - # XXX: no more needed ? - mymodule = self.root() - level = getattr(self, 'level', None) # Import as no level - if modname is None: - modname = self.modname - # XXX we should investigate deeper if we really want to check - # importing itself: modname and mymodule.name be relative or absolute - if mymodule.relative_to_absolute_name(modname, level) == mymodule.name: - # FIXME: we used to raise InferenceError here, but why ? - return mymodule - try: - return mymodule.import_module(modname, level=level) - except AstroidBuildingException: - raise InferenceError(modname) - except SyntaxError as ex: - raise InferenceError(str(ex)) - - def real_name(self, asname): - """get name from 'as' name""" - for name, _asname in self.names: - if name == '*': - return asname - if not _asname: - name = name.split('.', 1)[0] - _asname = name - if asname == _asname: - return name - raise NotFoundError(asname) - diff --git a/pymode/libs/astroid/modutils.py b/pymode/libs/astroid/modutils.py deleted file mode 100644 index c547f3e6..00000000 --- a/pymode/libs/astroid/modutils.py +++ /dev/null @@ -1,670 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# astroid is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""Python modules manipulation utility functions. - -:type PY_SOURCE_EXTS: tuple(str) -:var PY_SOURCE_EXTS: list of possible python source file extension - -:type STD_LIB_DIRS: set of str -:var STD_LIB_DIRS: directories where standard modules are located - -:type BUILTIN_MODULES: dict -:var BUILTIN_MODULES: dictionary with builtin module names has key -""" -from __future__ import with_statement - -__docformat__ = "restructuredtext en" - -import imp -import os -import sys -from distutils.sysconfig import get_python_lib -from distutils.errors import DistutilsPlatformError -import zipimport - -try: - import pkg_resources -except ImportError: - pkg_resources = None - -from logilab.common import _handle_blacklist - -PY_ZIPMODULE = object() - -if sys.platform.startswith('win'): - PY_SOURCE_EXTS = ('py', 'pyw') - PY_COMPILED_EXTS = ('dll', 'pyd') -else: - PY_SOURCE_EXTS = ('py',) - PY_COMPILED_EXTS = ('so',) - -# Notes about STD_LIB_DIRS -# Consider arch-specific installation for STD_LIB_DIRS definition -# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on -# -# :see: `Problems with /usr/lib64 builds `_ -# :see: `FHS `_ -try: - # The explicit sys.prefix is to work around a patch in virtualenv that - # replaces the 'real' sys.prefix (i.e. the location of the binary) - # with the prefix from which the virtualenv was created. This throws - # off the detection logic for standard library modules, thus the - # workaround. - STD_LIB_DIRS = set([ - get_python_lib(standard_lib=True, prefix=sys.prefix), - # Take care of installations where exec_prefix != prefix. - get_python_lib(standard_lib=True, prefix=sys.exec_prefix), - get_python_lib(standard_lib=True)]) - if os.name == 'nt': - STD_LIB_DIRS.add(os.path.join(sys.prefix, 'dlls')) - try: - # real_prefix is defined when running inside virtualenv. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'dlls')) - except AttributeError: - pass -# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to -# non-valid path, see https://bugs.pypy.org/issue1164 -except DistutilsPlatformError: - STD_LIB_DIRS = set() - -EXT_LIB_DIR = get_python_lib() - -BUILTIN_MODULES = dict(zip(sys.builtin_module_names, - [1]*len(sys.builtin_module_names))) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - -def _normalize_path(path): - return os.path.normcase(os.path.abspath(path)) - - -_NORM_PATH_CACHE = {} - -def _cache_normalize_path(path): - """abspath with caching""" - # _module_file calls abspath on every path in sys.path every time it's - # called; on a larger codebase this easily adds up to half a second just - # assembling path components. This cache alleviates that. - try: - return _NORM_PATH_CACHE[path] - except KeyError: - if not path: # don't cache result for '' - return _normalize_path(path) - result = _NORM_PATH_CACHE[path] = _normalize_path(path) - return result - -def load_module_from_name(dotted_name, path=None, use_sys=1): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=1): - """Load a python module from its splitted name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package splitted on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = imp.find_module(part, path) - module = imp.load_module(curname, mp_file, mp_filename, mp_desc) - # mp_file still needs to be closed. - if mp_file: - mp_file.close() - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, '__file__', '') - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):])) - path = [os.path.dirname(_file)] - prevmodule = module - return module - - -def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def _check_init(path, mod_path): - """check there are some __init__.py all along the way""" - for part in mod_path: - path = os.path.join(path, part) - if not _has_init(path): - return False - return True - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding splitted module's name - (i.e name of a module or package splitted on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package splitted in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding splitted module's name - """ - base = os.path.splitext(os.path.abspath(filename))[0] - if extrapath is not None: - for path_ in extrapath: - path = os.path.abspath(path_) - if path and os.path.normcase(base[:len(path)]) == os.path.normcase(path): - submodpath = [pkg for pkg in base[len(path):].split(os.sep) - if pkg] - if _check_init(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - for path in sys.path: - path = _cache_normalize_path(path) - if path and os.path.normcase(base).startswith(path): - modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] - if _check_init(path, modpath[:-1]): - return modpath - raise ImportError('Unable to find module for %s in %s' % ( - filename, ', \n'.join(sys.path))) - - -def file_from_modpath(modpath, path=None, context_file=None): - return file_info_from_modpath(modpath, path, context_file)[0] - -def file_info_from_modpath(modpath, path=None, context_file=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file, giving priority to source file over precompiled - file if it exists - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.') - (this means explicit relative imports that start with dots have - empty strings in this list!) - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - :raise ImportError: if there is no such module in the directory - - :rtype: (str or None, import type) - :return: - the path to the module's file or None if it's an integrated - builtin module such as 'sys' - """ - if context_file is not None: - context = os.path.dirname(context_file) - else: - context = context_file - if modpath[0] == 'xml': - # handle _xmlplus - try: - return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context) - except ImportError: - return _file_from_modpath(modpath, path, context) - elif modpath == ['os', 'path']: - # FIXME: currently ignoring search_path... - return os.path.__file__, imp.PY_SOURCE - return _file_from_modpath(modpath, path, context) - - -def get_module_part(dotted_name, context_file=None): - """given a dotted name return the module part of the name : - - >>> get_module_part('logilab.common.modutils.get_module_part') - 'logilab.common.modutils' - - :type dotted_name: str - :param dotted_name: full name of the identifier we are interested in - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the module part of the name or None if we have not been able at - all to import the given name - - XXX: deprecated, since it doesn't handle package precedence over module - (see #10066) - """ - # os.path trick - if dotted_name.startswith('os.path'): - return 'os.path' - parts = dotted_name.split('.') - if context_file is not None: - # first check for builtin module which won't be considered latter - # in that case (path != None) - if parts[0] in BUILTIN_MODULES: - if len(parts) > 2: - raise ImportError(dotted_name) - return parts[0] - # don't use += or insert, we want a new list to be created ! - path = None - starti = 0 - if parts[0] == '': - assert context_file is not None, \ - 'explicit relative import, but no context_file?' - path = [] # prevent resolving the import non-relatively - starti = 1 - while parts[starti] == '': # for all further dots: change context - starti += 1 - context_file = os.path.dirname(context_file) - for i in range(starti, len(parts)): - try: - file_from_modpath(parts[starti:i+1], path=path, - context_file=context_file) - except ImportError: - if not i >= max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_module_files(src_directory, blacklist): - """given a package directory return a list of all available python - module's files in the package and its subpackages - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all available python module's files in the package and - its subpackages - """ - files = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not '__init__.py' in filenames: - dirnames[:] = () - continue - for filename in filenames: - if _is_python_file(filename): - src = os.path.join(directory, filename) - files.append(src) - return files - - -def get_source_file(filename, include_no_ext=False): - """given a python module's file name return the matching source file - name (the filename will be returned identically if it's a already an - absolute path to a python source file...) - - :type filename: str - :param filename: python module's file name - - - :raise NoSourceFile: if no source file exists on the file system - - :rtype: str - :return: the absolute path of the source file if it exists - """ - base, orig_ext = os.path.splitext(os.path.abspath(filename)) - for ext in PY_SOURCE_EXTS: - source_path = '%s.%s' % (base, ext) - if os.path.exists(source_path): - return source_path - if include_no_ext and not orig_ext and os.path.exists(base): - return base - raise NoSourceFile(filename) - - -def is_python_source(filename): - """ - rtype: bool - return: True if the filename is a python source file - """ - return os.path.splitext(filename)[1][1:] in PY_SOURCE_EXTS - - -def is_standard_module(modname, std_path=None): - """try to guess if a module is a standard python module (by default, - see `std_path` parameter's description) - - :type modname: str - :param modname: name of the module we are interested in - - :type std_path: list(str) or tuple(str) - :param std_path: list of path considered has standard - - - :rtype: bool - :return: - true if the module: - - is located on the path listed in one of the directory in `std_path` - - is a built-in module - """ - modname = modname.split('.')[0] - try: - filename = file_from_modpath([modname]) - except ImportError: - # import failed, i'm probably not so wrong by supposing it's - # not standard... - return False - # modules which are not living in a file are considered standard - # (sys and __builtin__ for instance) - if filename is None: - return True - filename = _normalize_path(filename) - if filename.startswith(_cache_normalize_path(EXT_LIB_DIR)): - return False - if std_path is None: - std_path = STD_LIB_DIRS - for path in std_path: - if filename.startswith(_cache_normalize_path(path)): - return True - return False - - - -def is_relative(modname, from_file): - """return true if the given module name is relative to the given - file name - - :type modname: str - :param modname: name of the module we are interested in - - :type from_file: str - :param from_file: - path of the module from which modname has been imported - - :rtype: bool - :return: - true if the module has been imported relatively to `from_file` - """ - if not os.path.isdir(from_file): - from_file = os.path.dirname(from_file) - if from_file in sys.path: - return False - try: - stream, _, _ = imp.find_module(modname.split('.')[0], [from_file]) - - # Close the stream to avoid ResourceWarnings. - if stream: - stream.close() - return True - except ImportError: - return False - - -# internal only functions ##################################################### - -def _file_from_modpath(modpath, path=None, context=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file - - this function is used internally, see `file_from_modpath`'s - documentation for more information - """ - assert len(modpath) > 0 - if context is not None: - try: - mtype, mp_filename = _module_file(modpath, [context]) - except ImportError: - mtype, mp_filename = _module_file(modpath, path) - else: - mtype, mp_filename = _module_file(modpath, path) - if mtype == imp.PY_COMPILED: - try: - return get_source_file(mp_filename), imp.PY_SOURCE - except NoSourceFile: - return mp_filename, imp.PY_COMPILED - elif mtype == imp.C_BUILTIN: - # integrated builtin module - return None, imp.C_BUILTIN - elif mtype == imp.PKG_DIRECTORY: - mp_filename = _has_init(mp_filename) - mtype = imp.PY_SOURCE - return mp_filename, mtype - -def _search_zip(modpath, pic): - for filepath, importer in pic.items(): - if importer is not None: - if importer.find_module(modpath[0]): - if not importer.find_module(os.path.sep.join(modpath)): - raise ImportError('No module named %s in %s/%s' % ( - '.'.join(modpath[1:]), filepath, modpath)) - return PY_ZIPMODULE, os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), filepath - raise ImportError('No module named %s' % '.'.join(modpath)) - - -def _module_file(modpath, path=None): - """get a module type / file path - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.'), with leading empty strings for explicit relative import - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - - :rtype: tuple(int, str) - :return: the module type flag and the file path for a module - """ - # egg support compat - try: - pic = sys.path_importer_cache - _path = (path is None and sys.path or path) - for __path in _path: - if not __path in pic: - try: - pic[__path] = zipimport.zipimporter(__path) - except zipimport.ZipImportError: - pic[__path] = None - checkeggs = True - except AttributeError: - checkeggs = False - # pkg_resources support (aka setuptools namespace packages) - if (pkg_resources is not None - and modpath[0] in pkg_resources._namespace_packages - and modpath[0] in sys.modules - and len(modpath) > 1): - # setuptools has added into sys.modules a module object with proper - # __path__, get back information from there - module = sys.modules[modpath.pop(0)] - path = module.__path__ - imported = [] - while modpath: - modname = modpath[0] - # take care to changes in find_module implementation wrt builtin modules - # - # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) - # >>> imp.find_module('posix') - # (None, 'posix', ('', '', 6)) - # - # Python 3.3.1 (default, Apr 26 2013, 12:08:46) - # >>> imp.find_module('posix') - # (None, None, ('', '', 6)) - try: - stream, mp_filename, mp_desc = imp.find_module(modname, path) - except ImportError: - if checkeggs: - return _search_zip(modpath, pic)[:2] - raise - else: - # Don't forget to close the stream to avoid - # spurious ResourceWarnings. - if stream: - stream.close() - - if checkeggs and mp_filename: - fullabspath = [_cache_normalize_path(x) for x in _path] - try: - pathindex = fullabspath.index(os.path.dirname(_normalize_path(mp_filename))) - emtype, emp_filename, zippath = _search_zip(modpath, pic) - if pathindex > _path.index(zippath): - # an egg takes priority - return emtype, emp_filename - except ValueError: - # XXX not in _path - pass - except ImportError: - pass - checkeggs = False - imported.append(modpath.pop(0)) - mtype = mp_desc[2] - if modpath: - if mtype != imp.PKG_DIRECTORY: - raise ImportError('No module %s in %s' % ('.'.join(modpath), - '.'.join(imported))) - # XXX guess if package is using pkgutil.extend_path by looking for - # those keywords in the first four Kbytes - try: - with open(os.path.join(mp_filename, '__init__.py'), 'rb') as stream: - data = stream.read(4096) - except IOError: - path = [mp_filename] - else: - if b'pkgutil' in data and b'extend_path' in data: - # extend_path is called, search sys.path for module/packages - # of this name see pkgutil.extend_path documentation - path = [os.path.join(p, *imported) for p in sys.path - if os.path.isdir(os.path.join(p, *imported))] - else: - path = [mp_filename] - return mtype, mp_filename - -def _is_python_file(filename): - """return true if the given filename should be considered as a python file - - .pyc and .pyo are ignored - """ - for ext in ('.py', '.so', '.pyd', '.pyw'): - if filename.endswith(ext): - return True - return False - - -def _has_init(directory): - """if the given directory has a valid __init__ file, return its path, - else return None - """ - mod_or_pack = os.path.join(directory, '__init__') - for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'): - if os.path.exists(mod_or_pack + '.' + ext): - return mod_or_pack + '.' + ext - return None diff --git a/pymode/libs/astroid/node_classes.py b/pymode/libs/astroid/node_classes.py deleted file mode 100644 index 4b413ef8..00000000 --- a/pymode/libs/astroid/node_classes.py +++ /dev/null @@ -1,966 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""Module for some node classes. More nodes in scoped_nodes.py -""" - -import sys - -import six -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import NoDefault -from astroid.bases import (NodeNG, Statement, Instance, InferenceContext, - _infer_stmts, YES, BUILTINS) -from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin, - ParentAssignTypeMixin, FromImportMixIn) - -PY3K = sys.version_info >= (3, 0) - - -def unpack_infer(stmt, context=None): - """recursively generate nodes inferred by the given statement. - If the inferred value is a list or a tuple, recurse on the elements - """ - if isinstance(stmt, (List, Tuple)): - for elt in stmt.elts: - for infered_elt in unpack_infer(elt, context): - yield infered_elt - return - # if infered is a final node, return it and stop - infered = next(stmt.infer(context)) - if infered is stmt: - yield infered - return - # else, infer recursivly, except YES object that should be returned as is - for infered in stmt.infer(context): - if infered is YES: - yield infered - else: - for inf_inf in unpack_infer(infered, context): - yield inf_inf - - -def are_exclusive(stmt1, stmt2, exceptions=None): - """return true if the two given statements are mutually exclusive - - `exceptions` may be a list of exception names. If specified, discard If - branches and check one of the statement is in an exception handler catching - one of the given exceptions. - - algorithm : - 1) index stmt1's parents - 2) climb among stmt2's parents until we find a common parent - 3) if the common parent is a If or TryExcept statement, look if nodes are - in exclusive branches - """ - # index stmt1's parents - stmt1_parents = {} - children = {} - node = stmt1.parent - previous = stmt1 - while node: - stmt1_parents[node] = 1 - children[node] = previous - previous = node - node = node.parent - # climb among stmt2's parents until we find a common parent - node = stmt2.parent - previous = stmt2 - while node: - if node in stmt1_parents: - # if the common parent is a If or TryExcept statement, look if - # nodes are in exclusive branches - if isinstance(node, If) and exceptions is None: - if (node.locate_child(previous)[1] - is not node.locate_child(children[node])[1]): - return True - elif isinstance(node, TryExcept): - c2attr, c2node = node.locate_child(previous) - c1attr, c1node = node.locate_child(children[node]) - if c1node is not c2node: - if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'orelse') or - (c2attr == 'orelse' and c1attr == 'handlers')): - return True - elif c2attr == 'handlers' and c1attr == 'handlers': - return previous is not children[node] - return False - previous = node - node = node.parent - return False - - -class LookupMixIn(object): - """Mixin looking up a name in the right scope - """ - - def lookup(self, name): - """lookup a variable name - - return the scope node and the list of assignments associated to the - given name according to the scope where it has been found (locals, - globals or builtin) - - The lookup is starting from self's scope. If self is not a frame itself - and the name is found in the inner frame locals, statements will be - filtered to remove ignorable statements according to self's location - """ - return self.scope().scope_lookup(self, name) - - def ilookup(self, name): - """infered lookup - - return an iterator on infered values of the statements returned by - the lookup method - """ - frame, stmts = self.lookup(name) - context = InferenceContext() - return _infer_stmts(stmts, context, frame) - - def _filter_stmts(self, stmts, frame, offset): - """filter statements to remove ignorable statements. - - If self is not a frame itself and the name is found in the inner - frame locals, statements will be filtered to remove ignorable - statements according to self's location - """ - # if offset == -1, my actual frame is not the inner frame but its parent - # - # class A(B): pass - # - # we need this to resolve B correctly - if offset == -1: - myframe = self.frame().parent.frame() - else: - myframe = self.frame() - # If the frame of this node is the same as the statement - # of this node, then the node is part of a class or - # a function definition and the frame of this node should be the - # the upper frame, not the frame of the definition. - # For more information why this is important, - # see Pylint issue #295. - # For example, for 'b', the statement is the same - # as the frame / scope: - # - # def test(b=1): - # ... - - if self.statement() is myframe and myframe.parent: - myframe = myframe.parent.frame() - if not myframe is frame or self is frame: - return stmts - mystmt = self.statement() - # line filtering if we are in the same frame - # - # take care node may be missing lineno information (this is the case for - # nodes inserted for living objects) - if myframe is frame and mystmt.fromlineno is not None: - assert mystmt.fromlineno is not None, mystmt - mylineno = mystmt.fromlineno + offset - else: - # disabling lineno filtering - mylineno = 0 - _stmts = [] - _stmt_parents = [] - for node in stmts: - stmt = node.statement() - # line filtering is on and we have reached our location, break - if mylineno > 0 and stmt.fromlineno > mylineno: - break - assert hasattr(node, 'ass_type'), (node, node.scope(), - node.scope().locals) - ass_type = node.ass_type() - - if node.has_base(self): - break - - _stmts, done = ass_type._get_filtered_stmts(self, node, _stmts, mystmt) - if done: - break - - optional_assign = ass_type.optional_assign - if optional_assign and ass_type.parent_of(self): - # we are inside a loop, loop var assigment is hidding previous - # assigment - _stmts = [node] - _stmt_parents = [stmt.parent] - continue - - # XXX comment various branches below!!! - try: - pindex = _stmt_parents.index(stmt.parent) - except ValueError: - pass - else: - # we got a parent index, this means the currently visited node - # is at the same block level as a previously visited node - if _stmts[pindex].ass_type().parent_of(ass_type): - # both statements are not at the same block level - continue - # if currently visited node is following previously considered - # assignement and both are not exclusive, we can drop the - # previous one. For instance in the following code :: - # - # if a: - # x = 1 - # else: - # x = 2 - # print x - # - # we can't remove neither x = 1 nor x = 2 when looking for 'x' - # of 'print x'; while in the following :: - # - # x = 1 - # x = 2 - # print x - # - # we can remove x = 1 when we see x = 2 - # - # moreover, on loop assignment types, assignment won't - # necessarily be done if the loop has no iteration, so we don't - # want to clear previous assigments if any (hence the test on - # optional_assign) - if not (optional_assign or are_exclusive(_stmts[pindex], node)): - del _stmt_parents[pindex] - del _stmts[pindex] - if isinstance(node, AssName): - if not optional_assign and stmt.parent is mystmt.parent: - _stmts = [] - _stmt_parents = [] - elif isinstance(node, DelName): - _stmts = [] - _stmt_parents = [] - continue - if not are_exclusive(self, node): - _stmts.append(node) - _stmt_parents.append(stmt.parent) - return _stmts - -# Name classes - -class AssName(LookupMixIn, ParentAssignTypeMixin, NodeNG): - """class representing an AssName node""" - - -class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG): - """class representing a DelName node""" - - -class Name(LookupMixIn, NodeNG): - """class representing a Name node""" - - - - -##################### node classes ######################################## - -class Arguments(NodeNG, AssignTypeMixin): - """class representing an Arguments node""" - if PY3K: - # Python 3.4+ uses a different approach regarding annotations, - # each argument is a new class, _ast.arg, which exposes an - # 'annotation' attribute. In astroid though, arguments are exposed - # as is in the Arguments node and the only way to expose annotations - # is by using something similar with Python 3.3: - # - we expose 'varargannotation' and 'kwargannotation' of annotations - # of varargs and kwargs. - # - we expose 'annotation', a list with annotations for - # for each normal argument. If an argument doesn't have an - # annotation, its value will be None. - - _astroid_fields = ('args', 'defaults', 'kwonlyargs', - 'kw_defaults', 'annotations', - 'varargannotation', 'kwargannotation') - annotations = None - varargannotation = None - kwargannotation = None - else: - _astroid_fields = ('args', 'defaults', 'kwonlyargs', 'kw_defaults') - args = None - defaults = None - kwonlyargs = None - kw_defaults = None - - def __init__(self, vararg=None, kwarg=None): - self.vararg = vararg - self.kwarg = kwarg - - def _infer_name(self, frame, name): - if self.parent is frame: - return name - return None - - @cachedproperty - def fromlineno(self): - lineno = super(Arguments, self).fromlineno - return max(lineno, self.parent.fromlineno or 0) - - def format_args(self): - """return arguments formatted as string""" - result = [] - if self.args: - result.append(_format_args(self.args, self.defaults)) - if self.vararg: - result.append('*%s' % self.vararg) - if self.kwarg: - result.append('**%s' % self.kwarg) - if self.kwonlyargs: - if not self.vararg: - result.append('*') - result.append(_format_args(self.kwonlyargs, self.kw_defaults)) - return ', '.join(result) - - def default_value(self, argname): - """return the default value for an argument - - :raise `NoDefault`: if there is no default value defined - """ - i = _find_arg(argname, self.args)[0] - if i is not None: - idx = i - (len(self.args) - len(self.defaults)) - if idx >= 0: - return self.defaults[idx] - i = _find_arg(argname, self.kwonlyargs)[0] - if i is not None and self.kw_defaults[i] is not None: - return self.kw_defaults[i] - raise NoDefault() - - def is_argument(self, name): - """return True if the name is defined in arguments""" - if name == self.vararg: - return True - if name == self.kwarg: - return True - return self.find_argname(name, True)[1] is not None - - def find_argname(self, argname, rec=False): - """return index and Name node with given name""" - if self.args: # self.args may be None in some cases (builtin function) - return _find_arg(argname, self.args, rec) - return None, None - - def get_children(self): - """override get_children to skip over None elements in kw_defaults""" - for child in super(Arguments, self).get_children(): - if child is not None: - yield child - - -def _find_arg(argname, args, rec=False): - for i, arg in enumerate(args): - if isinstance(arg, Tuple): - if rec: - found = _find_arg(argname, arg.elts) - if found[0] is not None: - return found - elif arg.name == argname: - return i, arg - return None, None - - -def _format_args(args, defaults=None): - values = [] - if args is None: - return '' - if defaults is not None: - default_offset = len(args) - len(defaults) - for i, arg in enumerate(args): - if isinstance(arg, Tuple): - values.append('(%s)' % _format_args(arg.elts)) - else: - values.append(arg.name) - if defaults is not None and i >= default_offset: - if defaults[i-default_offset] is not None: - values[-1] += '=' + defaults[i-default_offset].as_string() - return ', '.join(values) - - -class AssAttr(NodeNG, ParentAssignTypeMixin): - """class representing an AssAttr node""" - _astroid_fields = ('expr',) - expr = None - -class Assert(Statement): - """class representing an Assert node""" - _astroid_fields = ('test', 'fail',) - test = None - fail = None - -class Assign(Statement, AssignTypeMixin): - """class representing an Assign node""" - _astroid_fields = ('targets', 'value',) - targets = None - value = None - -class AugAssign(Statement, AssignTypeMixin): - """class representing an AugAssign node""" - _astroid_fields = ('target', 'value',) - target = None - value = None - -class Backquote(NodeNG): - """class representing a Backquote node""" - _astroid_fields = ('value',) - value = None - -class BinOp(NodeNG): - """class representing a BinOp node""" - _astroid_fields = ('left', 'right',) - left = None - right = None - -class BoolOp(NodeNG): - """class representing a BoolOp node""" - _astroid_fields = ('values',) - values = None - -class Break(Statement): - """class representing a Break node""" - - -class CallFunc(NodeNG): - """class representing a CallFunc node""" - _astroid_fields = ('func', 'args', 'starargs', 'kwargs') - func = None - args = None - starargs = None - kwargs = None - - def __init__(self): - self.starargs = None - self.kwargs = None - -class Compare(NodeNG): - """class representing a Compare node""" - _astroid_fields = ('left', 'ops',) - left = None - ops = None - - def get_children(self): - """override get_children for tuple fields""" - yield self.left - for _, comparator in self.ops: - yield comparator # we don't want the 'op' - - def last_child(self): - """override last_child""" - # XXX maybe if self.ops: - return self.ops[-1][1] - #return self.left - -class Comprehension(NodeNG): - """class representing a Comprehension node""" - _astroid_fields = ('target', 'iter', 'ifs') - target = None - iter = None - ifs = None - - optional_assign = True - def ass_type(self): - return self - - def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - if isinstance(lookup_node, (Const, Name)): - return [lookup_node], True - - elif self.statement() is mystmt: - # original node's statement is the assignment, only keeps - # current node (gen exp, list comp) - - return [node], True - - return stmts, False - - -class Const(NodeNG, Instance): - """represent a constant node like num, str, bool, None, bytes""" - - def __init__(self, value=None): - self.value = value - - def getitem(self, index, context=None): - if isinstance(self.value, six.string_types): - return Const(self.value[index]) - raise TypeError('%r (value=%s)' % (self, self.value)) - - def has_dynamic_getattr(self): - return False - - def itered(self): - if isinstance(self.value, six.string_types): - return self.value - raise TypeError() - - def pytype(self): - return self._proxied.qname() - - -class Continue(Statement): - """class representing a Continue node""" - - -class Decorators(NodeNG): - """class representing a Decorators node""" - _astroid_fields = ('nodes',) - nodes = None - - def __init__(self, nodes=None): - self.nodes = nodes - - def scope(self): - # skip the function node to go directly to the upper level scope - return self.parent.parent.scope() - -class DelAttr(NodeNG, ParentAssignTypeMixin): - """class representing a DelAttr node""" - _astroid_fields = ('expr',) - expr = None - - -class Delete(Statement, AssignTypeMixin): - """class representing a Delete node""" - _astroid_fields = ('targets',) - targets = None - - -class Dict(NodeNG, Instance): - """class representing a Dict node""" - _astroid_fields = ('items',) - - def __init__(self, items=None): - if items is None: - self.items = [] - else: - self.items = [(const_factory(k), const_factory(v)) - for k, v in items.items()] - - def pytype(self): - return '%s.dict' % BUILTINS - - def get_children(self): - """get children of a Dict node""" - # overrides get_children - for key, value in self.items: - yield key - yield value - - def last_child(self): - """override last_child""" - if self.items: - return self.items[-1][1] - return None - - def itered(self): - return self.items[::2] - - def getitem(self, lookup_key, context=None): - for key, value in self.items: - for inferedkey in key.infer(context): - if inferedkey is YES: - continue - if isinstance(inferedkey, Const) \ - and inferedkey.value == lookup_key: - return value - # This should raise KeyError, but all call sites only catch - # IndexError. Let's leave it like that for now. - raise IndexError(lookup_key) - - -class Discard(Statement): - """class representing a Discard node""" - _astroid_fields = ('value',) - value = None - - -class Ellipsis(NodeNG): - """class representing an Ellipsis node""" - - -class EmptyNode(NodeNG): - """class representing an EmptyNode node""" - - -class ExceptHandler(Statement, AssignTypeMixin): - """class representing an ExceptHandler node""" - _astroid_fields = ('type', 'name', 'body',) - type = None - name = None - body = None - - @cachedproperty - def blockstart_tolineno(self): - if self.name: - return self.name.tolineno - elif self.type: - return self.type.tolineno - else: - return self.lineno - - def catch(self, exceptions): - if self.type is None or exceptions is None: - return True - for node in self.type.nodes_of_class(Name): - if node.name in exceptions: - return True - - -class Exec(Statement): - """class representing an Exec node""" - _astroid_fields = ('expr', 'globals', 'locals',) - expr = None - globals = None - locals = None - - -class ExtSlice(NodeNG): - """class representing an ExtSlice node""" - _astroid_fields = ('dims',) - dims = None - -class For(BlockRangeMixIn, AssignTypeMixin, Statement): - """class representing a For node""" - _astroid_fields = ('target', 'iter', 'body', 'orelse',) - target = None - iter = None - body = None - orelse = None - - optional_assign = True - @cachedproperty - def blockstart_tolineno(self): - return self.iter.tolineno - - -class From(FromImportMixIn, Statement): - """class representing a From node""" - - def __init__(self, fromname, names, level=0): - self.modname = fromname - self.names = names - self.level = level - -class Getattr(NodeNG): - """class representing a Getattr node""" - _astroid_fields = ('expr',) - expr = None - - -class Global(Statement): - """class representing a Global node""" - - def __init__(self, names): - self.names = names - - def _infer_name(self, frame, name): - return name - - -class If(BlockRangeMixIn, Statement): - """class representing an If node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - @cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for if statements""" - if lineno == self.body[0].fromlineno: - return lineno, lineno - if lineno <= self.body[-1].tolineno: - return lineno, self.body[-1].tolineno - return self._elsed_block_range(lineno, self.orelse, - self.body[0].fromlineno - 1) - - -class IfExp(NodeNG): - """class representing an IfExp node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - -class Import(FromImportMixIn, Statement): - """class representing an Import node""" - - -class Index(NodeNG): - """class representing an Index node""" - _astroid_fields = ('value',) - value = None - - -class Keyword(NodeNG): - """class representing a Keyword node""" - _astroid_fields = ('value',) - value = None - - -class List(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a List node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] - - def pytype(self): - return '%s.list' % BUILTINS - - def getitem(self, index, context=None): - return self.elts[index] - - def itered(self): - return self.elts - - -class Nonlocal(Statement): - """class representing a Nonlocal node""" - - def __init__(self, names): - self.names = names - - def _infer_name(self, frame, name): - return name - - -class Pass(Statement): - """class representing a Pass node""" - - -class Print(Statement): - """class representing a Print node""" - _astroid_fields = ('dest', 'values',) - dest = None - values = None - - -class Raise(Statement): - """class representing a Raise node""" - exc = None - if sys.version_info < (3, 0): - _astroid_fields = ('exc', 'inst', 'tback') - inst = None - tback = None - else: - _astroid_fields = ('exc', 'cause') - exc = None - cause = None - - def raises_not_implemented(self): - if not self.exc: - return - for name in self.exc.nodes_of_class(Name): - if name.name == 'NotImplementedError': - return True - - -class Return(Statement): - """class representing a Return node""" - _astroid_fields = ('value',) - value = None - - -class Set(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a Set node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] - - def pytype(self): - return '%s.set' % BUILTINS - - def itered(self): - return self.elts - - -class Slice(NodeNG): - """class representing a Slice node""" - _astroid_fields = ('lower', 'upper', 'step') - lower = None - upper = None - step = None - -class Starred(NodeNG, ParentAssignTypeMixin): - """class representing a Starred node""" - _astroid_fields = ('value',) - value = None - - -class Subscript(NodeNG): - """class representing a Subscript node""" - _astroid_fields = ('value', 'slice') - value = None - slice = None - - -class TryExcept(BlockRangeMixIn, Statement): - """class representing a TryExcept node""" - _astroid_fields = ('body', 'handlers', 'orelse',) - body = None - handlers = None - orelse = None - - def _infer_name(self, frame, name): - return name - - def block_range(self, lineno): - """handle block line numbers range for try/except statements""" - last = None - for exhandler in self.handlers: - if exhandler.type and lineno == exhandler.type.fromlineno: - return lineno, lineno - if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: - return lineno, exhandler.body[-1].tolineno - if last is None: - last = exhandler.body[0].fromlineno - 1 - return self._elsed_block_range(lineno, self.orelse, last) - - -class TryFinally(BlockRangeMixIn, Statement): - """class representing a TryFinally node""" - _astroid_fields = ('body', 'finalbody',) - body = None - finalbody = None - - def block_range(self, lineno): - """handle block line numbers range for try/finally statements""" - child = self.body[0] - # py2.5 try: except: finally: - if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno - and lineno > self.fromlineno and lineno <= child.tolineno): - return child.block_range(lineno) - return self._elsed_block_range(lineno, self.finalbody) - - -class Tuple(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a Tuple node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] - - def pytype(self): - return '%s.tuple' % BUILTINS - - def getitem(self, index, context=None): - return self.elts[index] - - def itered(self): - return self.elts - - -class UnaryOp(NodeNG): - """class representing an UnaryOp node""" - _astroid_fields = ('operand',) - operand = None - - -class While(BlockRangeMixIn, Statement): - """class representing a While node""" - _astroid_fields = ('test', 'body', 'orelse',) - test = None - body = None - orelse = None - - @cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for for and while statements""" - return self. _elsed_block_range(lineno, self.orelse) - - -class With(BlockRangeMixIn, AssignTypeMixin, Statement): - """class representing a With node""" - _astroid_fields = ('items', 'body') - items = None - body = None - - @cachedproperty - def blockstart_tolineno(self): - return self.items[-1][0].tolineno - - def get_children(self): - for expr, var in self.items: - yield expr - if var: - yield var - for elt in self.body: - yield elt - -class Yield(NodeNG): - """class representing a Yield node""" - _astroid_fields = ('value',) - value = None - -class YieldFrom(Yield): - """ Class representing a YieldFrom node. """ - -# constants ############################################################## - -CONST_CLS = { - list: List, - tuple: Tuple, - dict: Dict, - set: Set, - type(None): Const, - } - -def _update_const_classes(): - """update constant classes, so the keys of CONST_CLS can be reused""" - klasses = (bool, int, float, complex, str) - if sys.version_info < (3, 0): - klasses += (unicode, long) - if sys.version_info >= (2, 6): - klasses += (bytes,) - for kls in klasses: - CONST_CLS[kls] = Const -_update_const_classes() - -def const_factory(value): - """return an astroid node for a python value""" - # XXX we should probably be stricter here and only consider stuff in - # CONST_CLS or do better treatment: in case where value is not in CONST_CLS, - # we should rather recall the builder on this value than returning an empty - # node (another option being that const_factory shouldn't be called with something - # not in CONST_CLS) - assert not isinstance(value, NodeNG) - try: - return CONST_CLS[value.__class__](value) - except (KeyError, AttributeError): - node = EmptyNode() - node.object = value - return node diff --git a/pymode/libs/astroid/nodes.py b/pymode/libs/astroid/nodes.py deleted file mode 100644 index 67c2f8e8..00000000 --- a/pymode/libs/astroid/nodes.py +++ /dev/null @@ -1,74 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -""" -on all nodes : - .is_statement, returning true if the node should be considered as a - statement node - .root(), returning the root node of the tree (i.e. a Module) - .previous_sibling(), returning previous sibling statement node - .next_sibling(), returning next sibling statement node - .statement(), returning the first parent node marked as statement node - .frame(), returning the first node defining a new local scope (i.e. - Module, Function or Class) - .set_local(name, node), define an identifier on the first parent frame, - with the node defining it. This is used by the astroid builder and should not - be used from out there. - -on From and Import : - .real_name(name), - - -""" -# pylint: disable=unused-import - -__docformat__ = "restructuredtext en" - -from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \ - AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \ - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \ - Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \ - From, Getattr, Global, If, IfExp, Import, Index, Keyword, \ - List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \ - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \ - const_factory -from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \ - ListComp, SetComp, Function, Class - -ALL_NODE_CLASSES = ( - Arguments, AssAttr, Assert, Assign, AssName, AugAssign, - Backquote, BinOp, BoolOp, Break, - CallFunc, Class, Compare, Comprehension, Const, Continue, - Decorators, DelAttr, DelName, Delete, - Dict, DictComp, Discard, - Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, - For, From, Function, - Getattr, GenExpr, Global, - If, IfExp, Import, Index, - Keyword, - Lambda, List, ListComp, - Name, Nonlocal, - Module, - Pass, Print, - Raise, Return, - Set, SetComp, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, - UnaryOp, - While, With, - Yield, YieldFrom - ) - diff --git a/pymode/libs/astroid/protocols.py b/pymode/libs/astroid/protocols.py deleted file mode 100644 index 4c11f9cf..00000000 --- a/pymode/libs/astroid/protocols.py +++ /dev/null @@ -1,415 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to handle python protocols for nodes -where it makes sense. -""" - -__doctype__ = "restructuredtext en" -import collections - -from astroid.exceptions import InferenceError, NoDefault, NotFoundError -from astroid.node_classes import unpack_infer -from astroid.bases import InferenceContext, copy_context, \ - raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES -from astroid.nodes import const_factory -from astroid import nodes - -BIN_OP_METHOD = {'+': '__add__', - '-': '__sub__', - '/': '__div__', - '//': '__floordiv__', - '*': '__mul__', - '**': '__power__', - '%': '__mod__', - '&': '__and__', - '|': '__or__', - '^': '__xor__', - '<<': '__lshift__', - '>>': '__rshift__', - } - -UNARY_OP_METHOD = {'+': '__pos__', - '-': '__neg__', - '~': '__invert__', - 'not': None, # XXX not '__nonzero__' - } - -# unary operations ############################################################ - -def tl_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not bool(self.elts)) - raise TypeError() # XXX log unsupported operation -nodes.Tuple.infer_unary_op = tl_infer_unary_op -nodes.List.infer_unary_op = tl_infer_unary_op - - -def dict_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not bool(self.items)) - raise TypeError() # XXX log unsupported operation -nodes.Dict.infer_unary_op = dict_infer_unary_op - - -def const_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not self.value) - # XXX log potentially raised TypeError - elif operator == '+': - return const_factory(+self.value) - else: # operator == '-': - return const_factory(-self.value) -nodes.Const.infer_unary_op = const_infer_unary_op - - -# binary operations ########################################################### - -BIN_OP_IMPL = {'+': lambda a, b: a + b, - '-': lambda a, b: a - b, - '/': lambda a, b: a / b, - '//': lambda a, b: a // b, - '*': lambda a, b: a * b, - '**': lambda a, b: a ** b, - '%': lambda a, b: a % b, - '&': lambda a, b: a & b, - '|': lambda a, b: a | b, - '^': lambda a, b: a ^ b, - '<<': lambda a, b: a << b, - '>>': lambda a, b: a >> b, - } -for key, impl in list(BIN_OP_IMPL.items()): - BIN_OP_IMPL[key+'='] = impl - -def const_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, nodes.Const): - try: - impl = BIN_OP_IMPL[operator] - - try: - yield const_factory(impl(self.value, other.value)) - except Exception: - # ArithmeticError is not enough: float >> float is a TypeError - # TODO : let pylint know about the problem - pass - except TypeError: - # XXX log TypeError - continue - elif other is YES: - yield other - else: - try: - for val in other.infer_binary_op(operator, self, context): - yield val - except AttributeError: - yield YES -nodes.Const.infer_binary_op = yes_if_nothing_infered(const_infer_binary_op) - - -def tl_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, self.__class__) and operator == '+': - node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] - elts += [n for elt in other.elts for n in elt.infer(context) - if not n is YES] - node.elts = elts - yield node - elif isinstance(other, nodes.Const) and operator == '*': - if not isinstance(other.value, int): - yield YES - continue - node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] * other.value - node.elts = elts - yield node - elif isinstance(other, Instance) and not isinstance(other, nodes.Const): - yield YES - # XXX else log TypeError -nodes.Tuple.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) -nodes.List.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) - - -def dict_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, Instance) and isinstance(other._proxied, nodes.Class): - yield YES - # XXX else log TypeError -nodes.Dict.infer_binary_op = yes_if_nothing_infered(dict_infer_binary_op) - -def instance_infer_binary_op(self, operator, other, context): - try: - methods = self.getattr(BIN_OP_METHOD[operator]) - except (NotFoundError, KeyError): - # Unknown operator - yield YES - else: - for method in methods: - if not isinstance(method, nodes.Function): - continue - for result in method.infer_call_result(self, context): - if result is not YES: - yield result - # We are interested only in the first infered method, - # don't go looking in the rest of the methods of the ancestors. - break - -Instance.infer_binary_op = yes_if_nothing_infered(instance_infer_binary_op) - - -# assignment ################################################################## - -"""the assigned_stmts method is responsible to return the assigned statement -(e.g. not inferred) according to the assignment type. - -The `asspath` argument is used to record the lhs path of the original node. -For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath -will be [1, 1] once arrived to the Assign node. - -The `context` argument is the current inference context which should be given -to any intermediary inference necessary. -""" - -def _resolve_looppart(parts, asspath, context): - """recursive function to resolve multiple assignments on loops""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if part is YES: - continue - # XXX handle __iter__ and log potentially detected errors - if not hasattr(part, 'itered'): - continue - try: - itered = part.itered() - except TypeError: - continue # XXX log error - for stmt in itered: - try: - assigned = stmt.getitem(index, context) - except (AttributeError, IndexError): - continue - except TypeError: # stmt is unsubscriptable Const - continue - if not asspath: - # we achieved to resolved the assignment path, - # don't infer the last part - yield assigned - elif assigned is YES: - break - else: - # we are not yet on the last part of the path - # search on each possibly inferred value - try: - for infered in _resolve_looppart(assigned.infer(context), - asspath, context): - yield infered - except InferenceError: - break - - -def for_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - for lst in self.iter.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.elts: - yield item - else: - for infered in _resolve_looppart(self.iter.infer(context), - asspath, context): - yield infered - -nodes.For.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) -nodes.Comprehension.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) - - -def mulass_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - asspath = [] - asspath.insert(0, self.elts.index(node)) - return self.parent.assigned_stmts(self, context, asspath) -nodes.Tuple.assigned_stmts = mulass_assigned_stmts -nodes.List.assigned_stmts = mulass_assigned_stmts - - -def assend_assigned_stmts(self, context=None): - return self.parent.assigned_stmts(self, context=context) -nodes.AssName.assigned_stmts = assend_assigned_stmts -nodes.AssAttr.assigned_stmts = assend_assigned_stmts - - -def _arguments_infer_argname(self, name, context): - # arguments information may be missing, in which case we can't do anything - # more - if not (self.args or self.vararg or self.kwarg): - yield YES - return - # first argument of instance/class method - if self.args and getattr(self.args[0], 'name', None) == name: - functype = self.parent.type - if functype == 'method': - yield Instance(self.parent.parent.frame()) - return - if functype == 'classmethod': - yield self.parent.parent.frame() - return - if name == self.vararg: - vararg = const_factory(()) - vararg.parent = self - yield vararg - return - if name == self.kwarg: - kwarg = const_factory({}) - kwarg.parent = self - yield kwarg - return - # if there is a default value, yield it. And then yield YES to reflect - # we can't guess given argument value - try: - context = copy_context(context) - for infered in self.default_value(name).infer(context): - yield infered - yield YES - except NoDefault: - yield YES - - -def arguments_assigned_stmts(self, node, context, asspath=None): - if context.callcontext: - # reset call context/name - callcontext = context.callcontext - context = copy_context(context) - context.callcontext = None - return callcontext.infer_argument(self.parent, node.name, context) - return _arguments_infer_argname(self, node.name, context) -nodes.Arguments.assigned_stmts = arguments_assigned_stmts - - -def assign_assigned_stmts(self, node, context=None, asspath=None): - if not asspath: - yield self.value - return - for infered in _resolve_asspart(self.value.infer(context), asspath, context): - yield infered -nodes.Assign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) -nodes.AugAssign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) - - -def _resolve_asspart(parts, asspath, context): - """recursive function to resolve multiple assignments""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if hasattr(part, 'getitem'): - try: - assigned = part.getitem(index, context) - # XXX raise a specific exception to avoid potential hiding of - # unexpected exception ? - except (TypeError, IndexError): - return - if not asspath: - # we achieved to resolved the assignment path, don't infer the - # last part - yield assigned - elif assigned is YES: - return - else: - # we are not yet on the last part of the path search on each - # possibly inferred value - try: - for infered in _resolve_asspart(assigned.infer(context), - asspath, context): - yield infered - except InferenceError: - return - - -def excepthandler_assigned_stmts(self, node, context=None, asspath=None): - for assigned in unpack_infer(self.type): - if isinstance(assigned, nodes.Class): - assigned = Instance(assigned) - yield assigned -nodes.ExceptHandler.assigned_stmts = raise_if_nothing_infered(excepthandler_assigned_stmts) - - -def with_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - for _, vars in self.items: - if vars is None: - continue - for lst in vars.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.nodes: - yield item -nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts) - - -def starred_assigned_stmts(self, node=None, context=None, asspath=None): - stmt = self.statement() - if not isinstance(stmt, (nodes.Assign, nodes.For)): - raise InferenceError() - - if isinstance(stmt, nodes.Assign): - value = stmt.value - lhs = stmt.targets[0] - - if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1: - # Too many starred arguments in the expression. - raise InferenceError() - - if context is None: - context = InferenceContext() - try: - rhs = next(value.infer(context)) - except InferenceError: - yield YES - return - if rhs is YES or not hasattr(rhs, 'elts'): - # Not interested in inferred values without elts. - yield YES - return - - elts = collections.deque(rhs.elts[:]) - if len(lhs.elts) > len(rhs.elts): - # a, *b, c = (1, 2) - raise InferenceError() - - # Unpack iteratively the values from the rhs of the assignment, - # until the find the starred node. What will remain will - # be the list of values which the Starred node will represent - # This is done in two steps, from left to right to remove - # anything before the starred node and from right to left - # to remvoe anything after the starred node. - - for index, node in enumerate(lhs.elts): - if not isinstance(node, nodes.Starred): - elts.popleft() - continue - lhs_elts = collections.deque(reversed(lhs.elts[index:])) - for node in lhs_elts: - if not isinstance(node, nodes.Starred): - elts.pop() - continue - # We're done - for elt in elts: - yield elt - break - -nodes.Starred.assigned_stmts = starred_assigned_stmts diff --git a/pymode/libs/astroid/raw_building.py b/pymode/libs/astroid/raw_building.py deleted file mode 100644 index 99a026a7..00000000 --- a/pymode/libs/astroid/raw_building.py +++ /dev/null @@ -1,366 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains a set of functions to create astroid trees from scratch -(build_* functions) or from living object (object_build_* functions) -""" - -__docformat__ = "restructuredtext en" - -import sys -from os.path import abspath -from inspect import (getargspec, isdatadescriptor, isfunction, ismethod, - ismethoddescriptor, isclass, isbuiltin, ismodule) -import six - -from astroid.node_classes import CONST_CLS -from astroid.nodes import (Module, Class, Const, const_factory, From, - Function, EmptyNode, Name, Arguments) -from astroid.bases import BUILTINS, Generator -from astroid.manager import AstroidManager -MANAGER = AstroidManager() - -_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types - -def _io_discrepancy(member): - # _io module names itself `io`: http://bugs.python.org/issue18602 - member_self = getattr(member, '__self__', None) - return (member_self and - ismodule(member_self) and - member_self.__name__ == '_io' and - member.__module__ == 'io') - -def _attach_local_node(parent, node, name): - node.name = name # needed by add_local_node - parent.add_local_node(node) - -_marker = object() - -def attach_dummy_node(node, name, object=_marker): - """create a dummy node and register it in the locals of the given - node with the specified name - """ - enode = EmptyNode() - enode.object = object - _attach_local_node(node, enode, name) - -def _has_underlying_object(self): - return hasattr(self, 'object') and self.object is not _marker - -EmptyNode.has_underlying_object = _has_underlying_object - -def attach_const_node(node, name, value): - """create a Const node and register it in the locals of the given - node with the specified name - """ - if not name in node.special_attributes: - _attach_local_node(node, const_factory(value), name) - -def attach_import_node(node, modname, membername): - """create a From node and register it in the locals of the given - node with the specified name - """ - from_node = From(modname, [(membername, None)]) - _attach_local_node(node, from_node, membername) - - -def build_module(name, doc=None): - """create and initialize a astroid Module node""" - node = Module(name, doc, pure_python=False) - node.package = False - node.parent = None - return node - -def build_class(name, basenames=(), doc=None): - """create and initialize a astroid Class node""" - node = Class(name, doc) - for base in basenames: - basenode = Name() - basenode.name = base - node.bases.append(basenode) - basenode.parent = node - return node - -def build_function(name, args=None, defaults=None, flag=0, doc=None): - """create and initialize a astroid Function node""" - args, defaults = args or [], defaults or [] - # first argument is now a list of decorators - func = Function(name, doc) - func.args = argsnode = Arguments() - argsnode.args = [] - for arg in args: - argsnode.args.append(Name()) - argsnode.args[-1].name = arg - argsnode.args[-1].parent = argsnode - argsnode.defaults = [] - for default in defaults: - argsnode.defaults.append(const_factory(default)) - argsnode.defaults[-1].parent = argsnode - argsnode.kwarg = None - argsnode.vararg = None - argsnode.parent = func - if args: - register_arguments(func) - return func - - -def build_from_import(fromname, names): - """create and initialize an astroid From import statement""" - return From(fromname, [(name, None) for name in names]) - -def register_arguments(func, args=None): - """add given arguments to local - - args is a list that may contains nested lists - (i.e. def func(a, (b, c, d)): ...) - """ - if args is None: - args = func.args.args - if func.args.vararg: - func.set_local(func.args.vararg, func.args) - if func.args.kwarg: - func.set_local(func.args.kwarg, func.args) - for arg in args: - if isinstance(arg, Name): - func.set_local(arg.name, arg) - else: - register_arguments(func, arg.elts) - -def object_build_class(node, member, localname): - """create astroid for a living class object""" - basenames = [base.__name__ for base in member.__bases__] - return _base_class_object_build(node, member, basenames, - localname=localname) - -def object_build_function(node, member, localname): - """create astroid for a living function object""" - args, varargs, varkw, defaults = getargspec(member) - if varargs is not None: - args.append(varargs) - if varkw is not None: - args.append(varkw) - func = build_function(getattr(member, '__name__', None) or localname, args, - defaults, six.get_function_code(member).co_flags, member.__doc__) - node.add_local_node(func, localname) - -def object_build_datadescriptor(node, member, name): - """create astroid for a living data descriptor object""" - return _base_class_object_build(node, member, [], name) - -def object_build_methoddescriptor(node, member, localname): - """create astroid for a living method descriptor object""" - # FIXME get arguments ? - func = build_function(getattr(member, '__name__', None) or localname, - doc=member.__doc__) - # set node's arguments to None to notice that we have no information, not - # and empty argument list - func.args.args = None - node.add_local_node(func, localname) - -def _base_class_object_build(node, member, basenames, name=None, localname=None): - """create astroid for a living class object, with a given set of base names - (e.g. ancestors) - """ - klass = build_class(name or getattr(member, '__name__', None) or localname, - basenames, member.__doc__) - klass._newstyle = isinstance(member, type) - node.add_local_node(klass, localname) - try: - # limit the instantiation trick since it's too dangerous - # (such as infinite test execution...) - # this at least resolves common case such as Exception.args, - # OSError.errno - if issubclass(member, Exception): - instdict = member().__dict__ - else: - raise TypeError - except: - pass - else: - for name, obj in instdict.items(): - valnode = EmptyNode() - valnode.object = obj - valnode.parent = klass - valnode.lineno = 1 - klass.instance_attrs[name] = [valnode] - return klass - - - - -class InspectBuilder(object): - """class for building nodes from living object - - this is actually a really minimal representation, including only Module, - Function and Class nodes and some others as guessed. - """ - - # astroid from living objects ############################################### - - def __init__(self): - self._done = {} - self._module = None - - def inspect_build(self, module, modname=None, path=None): - """build astroid from a living module (i.e. using inspect) - this is used when there is no python source code available (either - because it's a built-in module or because the .py is not available) - """ - self._module = module - if modname is None: - modname = module.__name__ - try: - node = build_module(modname, module.__doc__) - except AttributeError: - # in jython, java modules have no __doc__ (see #109562) - node = build_module(modname) - node.file = node.path = path and abspath(path) or path - node.name = modname - MANAGER.cache_module(node) - node.package = hasattr(module, '__path__') - self._done = {} - self.object_build(node, module) - return node - - def object_build(self, node, obj): - """recursive method which create a partial ast from real objects - (only function, class, and method are handled) - """ - if obj in self._done: - return self._done[obj] - self._done[obj] = node - for name in dir(obj): - try: - member = getattr(obj, name) - except AttributeError: - # damned ExtensionClass.Base, I know you're there ! - attach_dummy_node(node, name) - continue - if ismethod(member): - member = six.get_method_function(member) - if isfunction(member): - # verify this is not an imported function - filename = getattr(six.get_function_code(member), - 'co_filename', None) - if filename is None: - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif filename != getattr(self._module, '__file__', None): - attach_dummy_node(node, name, member) - else: - object_build_function(node, member, name) - elif isbuiltin(member): - if (not _io_discrepancy(member) and - self.imported_member(node, member, name)): - continue - object_build_methoddescriptor(node, member, name) - elif isclass(member): - if self.imported_member(node, member, name): - continue - if member in self._done: - class_node = self._done[member] - if not class_node in node.locals.get(name, ()): - node.add_local_node(class_node, name) - else: - class_node = object_build_class(node, member, name) - # recursion - self.object_build(class_node, member) - if name == '__class__' and class_node.parent is None: - class_node.parent = self._done[self._module] - elif ismethoddescriptor(member): - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif isdatadescriptor(member): - assert isinstance(member, object) - object_build_datadescriptor(node, member, name) - elif type(member) in _CONSTANTS: - attach_const_node(node, name, member) - else: - # create an empty node so that the name is actually defined - attach_dummy_node(node, name, member) - - def imported_member(self, node, member, name): - """verify this is not an imported class or handle it""" - # /!\ some classes like ExtensionClass doesn't have a __module__ - # attribute ! Also, this may trigger an exception on badly built module - # (see http://www.logilab.org/ticket/57299 for instance) - try: - modname = getattr(member, '__module__', None) - except: - # XXX use logging - print('unexpected error while building astroid from living object') - import traceback - traceback.print_exc() - modname = None - if modname is None: - if name in ('__new__', '__subclasshook__'): - # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14) - # >>> print object.__new__.__module__ - # None - modname = BUILTINS - else: - attach_dummy_node(node, name, member) - return True - if {'gtk': 'gtk._gtk'}.get(modname, modname) != self._module.__name__: - # check if it sounds valid and then add an import node, else use a - # dummy node - try: - getattr(sys.modules[modname], name) - except (KeyError, AttributeError): - attach_dummy_node(node, name, member) - else: - attach_import_node(node, modname, name) - return True - return False - - -### astroid bootstrapping ###################################################### -Astroid_BUILDER = InspectBuilder() - -_CONST_PROXY = {} -def _astroid_bootstrapping(astroid_builtin=None): - """astroid boot strapping the builtins module""" - # this boot strapping is necessary since we need the Const nodes to - # inspect_build builtins, and then we can proxy Const - if astroid_builtin is None: - from logilab.common.compat import builtins - astroid_builtin = Astroid_BUILDER.inspect_build(builtins) - - for cls, node_cls in CONST_CLS.items(): - if cls is type(None): - proxy = build_class('NoneType') - proxy.parent = astroid_builtin - else: - proxy = astroid_builtin.getattr(cls.__name__)[0] - if cls in (dict, list, set, tuple): - node_cls._proxied = proxy - else: - _CONST_PROXY[cls] = proxy - -_astroid_bootstrapping() - -# TODO : find a nicer way to handle this situation; -# However __proxied introduced an -# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870) -def _set_proxied(const): - return _CONST_PROXY[const.value.__class__] -Const._proxied = property(_set_proxied) - -from types import GeneratorType -Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__) -Astroid_BUILDER.object_build(Generator._proxied, GeneratorType) - diff --git a/pymode/libs/astroid/rebuilder.py b/pymode/libs/astroid/rebuilder.py deleted file mode 100644 index 013479a8..00000000 --- a/pymode/libs/astroid/rebuilder.py +++ /dev/null @@ -1,926 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains utilities for rebuilding a _ast tree in -order to get a single Astroid representation -""" - -import sys -from _ast import ( - Expr as Discard, Str, - # binary operators - Add, BinOp, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor, - LShift, RShift, - # logical operators - And, Or, - # unary operators - UAdd, USub, Not, Invert, - # comparison operators - Eq, Gt, GtE, In, Is, IsNot, Lt, LtE, NotEq, NotIn, - ) - -from astroid import nodes as new -from astroid import astpeephole - - -_BIN_OP_CLASSES = {Add: '+', - BitAnd: '&', - BitOr: '|', - BitXor: '^', - Div: '/', - FloorDiv: '//', - Mod: '%', - Mult: '*', - Pow: '**', - Sub: '-', - LShift: '<<', - RShift: '>>', - } - -_BOOL_OP_CLASSES = {And: 'and', - Or: 'or', - } - -_UNARY_OP_CLASSES = {UAdd: '+', - USub: '-', - Not: 'not', - Invert: '~', - } - -_CMP_OP_CLASSES = {Eq: '==', - Gt: '>', - GtE: '>=', - In: 'in', - Is: 'is', - IsNot: 'is not', - Lt: '<', - LtE: '<=', - NotEq: '!=', - NotIn: 'not in', - } - -CONST_NAME_TRANSFORMS = {'None': None, - 'True': True, - 'False': False, - } - -REDIRECT = {'arguments': 'Arguments', - 'Attribute': 'Getattr', - 'comprehension': 'Comprehension', - 'Call': 'CallFunc', - 'ClassDef': 'Class', - "ListCompFor": 'Comprehension', - "GenExprFor": 'Comprehension', - 'excepthandler': 'ExceptHandler', - 'Expr': 'Discard', - 'FunctionDef': 'Function', - 'GeneratorExp': 'GenExpr', - 'ImportFrom': 'From', - 'keyword': 'Keyword', - 'Repr': 'Backquote', - } -PY3K = sys.version_info >= (3, 0) -PY34 = sys.version_info >= (3, 4) - -def _init_set_doc(node, newnode): - newnode.doc = None - try: - if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str): - newnode.doc = node.body[0].value.s - node.body = node.body[1:] - - except IndexError: - pass # ast built from scratch - -def _lineno_parent(oldnode, newnode, parent): - newnode.parent = parent - newnode.lineno = oldnode.lineno - newnode.col_offset = oldnode.col_offset - -def _set_infos(oldnode, newnode, parent): - newnode.parent = parent - if hasattr(oldnode, 'lineno'): - newnode.lineno = oldnode.lineno - if hasattr(oldnode, 'col_offset'): - newnode.col_offset = oldnode.col_offset - -def _create_yield_node(node, parent, rebuilder, factory): - newnode = factory() - _lineno_parent(node, newnode, parent) - if node.value is not None: - newnode.value = rebuilder.visit(node.value, newnode) - return newnode - - -class TreeRebuilder(object): - """Rebuilds the _ast tree to become an Astroid tree""" - - def __init__(self, manager): - self._manager = manager - self.asscontext = None - self._global_names = [] - self._from_nodes = [] - self._delayed_assattr = [] - self._visit_meths = {} - self._transform = manager.transform - self._peepholer = astpeephole.ASTPeepholeOptimizer() - - def visit_module(self, node, modname, modpath, package): - """visit a Module node by returning a fresh instance of it""" - newnode = new.Module(modname, None) - newnode.package = package - newnode.parent = None - _init_set_doc(node, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.file = newnode.path = modpath - return self._transform(newnode) - - def visit(self, node, parent): - cls = node.__class__ - if cls in self._visit_meths: - visit_method = self._visit_meths[cls] - else: - cls_name = cls.__name__ - visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower() - visit_method = getattr(self, visit_name) - self._visit_meths[cls] = visit_method - return self._transform(visit_method(node, parent)) - - def _save_assignment(self, node, name=None): - """save assignement situation since node.parent is not available yet""" - if self._global_names and node.name in self._global_names[-1]: - node.root().set_local(node.name, node) - else: - node.parent.set_local(node.name, node) - - - def visit_arguments(self, node, parent): - """visit a Arguments node by returning a fresh instance of it""" - newnode = new.Arguments() - newnode.parent = parent - self.asscontext = "Ass" - newnode.args = [self.visit(child, newnode) for child in node.args] - self.asscontext = None - newnode.defaults = [self.visit(child, newnode) for child in node.defaults] - newnode.kwonlyargs = [] - newnode.kw_defaults = [] - vararg, kwarg = node.vararg, node.kwarg - # change added in 82732 (7c5c678e4164), vararg and kwarg - # are instances of `_ast.arg`, not strings - if vararg: - if PY34: - if vararg.annotation: - newnode.varargannotation = self.visit(vararg.annotation, - newnode) - vararg = vararg.arg - elif PY3K and node.varargannotation: - newnode.varargannotation = self.visit(node.varargannotation, - newnode) - if kwarg: - if PY34: - if kwarg.annotation: - newnode.kwargannotation = self.visit(kwarg.annotation, - newnode) - kwarg = kwarg.arg - elif PY3K: - if node.kwargannotation: - newnode.kwargannotation = self.visit(node.kwargannotation, - newnode) - newnode.vararg = vararg - newnode.kwarg = kwarg - # save argument names in locals: - if vararg: - newnode.parent.set_local(vararg, newnode) - if kwarg: - newnode.parent.set_local(kwarg, newnode) - return newnode - - def visit_assattr(self, node, parent): - """visit a AssAttr node by returning a fresh instance of it""" - assc, self.asscontext = self.asscontext, None - newnode = new.AssAttr() - _lineno_parent(node, newnode, parent) - newnode.expr = self.visit(node.expr, newnode) - self.asscontext = assc - self._delayed_assattr.append(newnode) - return newnode - - def visit_assert(self, node, parent): - """visit a Assert node by returning a fresh instance of it""" - newnode = new.Assert() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - if node.msg is not None: - newnode.fail = self.visit(node.msg, newnode) - return newnode - - def visit_assign(self, node, parent): - """visit a Assign node by returning a fresh instance of it""" - newnode = new.Assign() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None - newnode.value = self.visit(node.value, newnode) - # set some function or metaclass infos XXX explain ? - klass = newnode.parent.frame() - if (isinstance(klass, new.Class) - and isinstance(newnode.value, new.CallFunc) - and isinstance(newnode.value.func, new.Name)): - func_name = newnode.value.func.name - for ass_node in newnode.targets: - try: - meth = klass[ass_node.name] - if isinstance(meth, new.Function): - if func_name in ('classmethod', 'staticmethod'): - meth.type = func_name - elif func_name == 'classproperty': # see lgc.decorators - meth.type = 'classmethod' - meth.extra_decorators.append(newnode.value) - except (AttributeError, KeyError): - continue - return newnode - - def visit_assname(self, node, parent, node_name=None): - '''visit a node and return a AssName node''' - newnode = new.AssName() - _set_infos(node, newnode, parent) - newnode.name = node_name - self._save_assignment(newnode) - return newnode - - def visit_augassign(self, node, parent): - """visit a AugAssign node by returning a fresh instance of it""" - newnode = new.AugAssign() - _lineno_parent(node, newnode, parent) - newnode.op = _BIN_OP_CLASSES[node.op.__class__] + "=" - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_backquote(self, node, parent): - """visit a Backquote node by returning a fresh instance of it""" - newnode = new.Backquote() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_binop(self, node, parent): - """visit a BinOp node by returning a fresh instance of it""" - if isinstance(node.left, BinOp) and self._manager.optimize_ast: - # Optimize BinOp operations in order to remove - # redundant recursion. For instance, if the - # following code is parsed in order to obtain - # its ast, then the rebuilder will fail with an - # infinite recursion, the same will happen with the - # inference engine as well. There's no need to hold - # so many objects for the BinOp if they can be reduced - # to something else (also, the optimization - # might handle only Const binops, which isn't a big - # problem for the correctness of the program). - # - # ("a" + "b" + # one thousand more + "c") - newnode = self._peepholer.optimize_binop(node) - if newnode: - _lineno_parent(node, newnode, parent) - return newnode - - newnode = new.BinOp() - _lineno_parent(node, newnode, parent) - newnode.left = self.visit(node.left, newnode) - newnode.right = self.visit(node.right, newnode) - newnode.op = _BIN_OP_CLASSES[node.op.__class__] - return newnode - - def visit_boolop(self, node, parent): - """visit a BoolOp node by returning a fresh instance of it""" - newnode = new.BoolOp() - _lineno_parent(node, newnode, parent) - newnode.values = [self.visit(child, newnode) for child in node.values] - newnode.op = _BOOL_OP_CLASSES[node.op.__class__] - return newnode - - def visit_break(self, node, parent): - """visit a Break node by returning a fresh instance of it""" - newnode = new.Break() - _set_infos(node, newnode, parent) - return newnode - - def visit_callfunc(self, node, parent): - """visit a CallFunc node by returning a fresh instance of it""" - newnode = new.CallFunc() - _lineno_parent(node, newnode, parent) - newnode.func = self.visit(node.func, newnode) - newnode.args = [self.visit(child, newnode) for child in node.args] - if node.starargs is not None: - newnode.starargs = self.visit(node.starargs, newnode) - if node.kwargs is not None: - newnode.kwargs = self.visit(node.kwargs, newnode) - for child in node.keywords: - newnode.args.append(self.visit(child, newnode)) - return newnode - - def visit_class(self, node, parent): - """visit a Class node to become astroid""" - newnode = new.Class(node.name, None) - _lineno_parent(node, newnode, parent) - _init_set_doc(node, newnode) - newnode.bases = [self.visit(child, newnode) for child in node.bases] - newnode.body = [self.visit(child, newnode) for child in node.body] - if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6 - newnode.decorators = self.visit_decorators(node, newnode) - newnode.parent.frame().set_local(newnode.name, newnode) - return newnode - - def visit_const(self, node, parent): - """visit a Const node by returning a fresh instance of it""" - newnode = new.Const(node.value) - _set_infos(node, newnode, parent) - return newnode - - def visit_continue(self, node, parent): - """visit a Continue node by returning a fresh instance of it""" - newnode = new.Continue() - _set_infos(node, newnode, parent) - return newnode - - def visit_compare(self, node, parent): - """visit a Compare node by returning a fresh instance of it""" - newnode = new.Compare() - _lineno_parent(node, newnode, parent) - newnode.left = self.visit(node.left, newnode) - newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode)) - for (op, expr) in zip(node.ops, node.comparators)] - return newnode - - def visit_comprehension(self, node, parent): - """visit a Comprehension node by returning a fresh instance of it""" - newnode = new.Comprehension() - newnode.parent = parent - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.ifs = [self.visit(child, newnode) for child in node.ifs] - return newnode - - def visit_decorators(self, node, parent): - """visit a Decorators node by returning a fresh instance of it""" - # /!\ node is actually a _ast.Function node while - # parent is a astroid.nodes.Function node - newnode = new.Decorators() - _lineno_parent(node, newnode, parent) - if 'decorators' in node._fields: # py < 2.6, i.e. 2.5 - decorators = node.decorators - else: - decorators = node.decorator_list - newnode.nodes = [self.visit(child, newnode) for child in decorators] - return newnode - - def visit_delete(self, node, parent): - """visit a Delete node by returning a fresh instance of it""" - newnode = new.Delete() - _lineno_parent(node, newnode, parent) - self.asscontext = "Del" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None - return newnode - - def visit_dict(self, node, parent): - """visit a Dict node by returning a fresh instance of it""" - newnode = new.Dict() - _lineno_parent(node, newnode, parent) - newnode.items = [(self.visit(key, newnode), self.visit(value, newnode)) - for key, value in zip(node.keys, node.values)] - return newnode - - def visit_dictcomp(self, node, parent): - """visit a DictComp node by returning a fresh instance of it""" - newnode = new.DictComp() - _lineno_parent(node, newnode, parent) - newnode.key = self.visit(node.key, newnode) - newnode.value = self.visit(node.value, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - return newnode - - def visit_discard(self, node, parent): - """visit a Discard node by returning a fresh instance of it""" - newnode = new.Discard() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_ellipsis(self, node, parent): - """visit an Ellipsis node by returning a fresh instance of it""" - newnode = new.Ellipsis() - _set_infos(node, newnode, parent) - return newnode - - def visit_emptynode(self, node, parent): - """visit an EmptyNode node by returning a fresh instance of it""" - newnode = new.EmptyNode() - _set_infos(node, newnode, parent) - return newnode - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = new.ExceptHandler() - _lineno_parent(node, newnode, parent) - if node.type is not None: - newnode.type = self.visit(node.type, newnode) - if node.name is not None: - # /!\ node.name can be a tuple - self.asscontext = "Ass" - newnode.name = self.visit(node.name, newnode) - self.asscontext = None - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_exec(self, node, parent): - """visit an Exec node by returning a fresh instance of it""" - newnode = new.Exec() - _lineno_parent(node, newnode, parent) - newnode.expr = self.visit(node.body, newnode) - if node.globals is not None: - newnode.globals = self.visit(node.globals, newnode) - if node.locals is not None: - newnode.locals = self.visit(node.locals, newnode) - return newnode - - def visit_extslice(self, node, parent): - """visit an ExtSlice node by returning a fresh instance of it""" - newnode = new.ExtSlice() - newnode.parent = parent - newnode.dims = [self.visit(dim, newnode) for dim in node.dims] - return newnode - - def visit_for(self, node, parent): - """visit a For node by returning a fresh instance of it""" - newnode = new.For() - _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_from(self, node, parent): - """visit a From node by returning a fresh instance of it""" - names = [(alias.name, alias.asname) for alias in node.names] - newnode = new.From(node.module or '', names, node.level or None) - _set_infos(node, newnode, parent) - # store From names to add them to locals after building - self._from_nodes.append(newnode) - return newnode - - def visit_function(self, node, parent): - """visit an Function node to become astroid""" - self._global_names.append({}) - newnode = new.Function(node.name, None) - _lineno_parent(node, newnode, parent) - _init_set_doc(node, newnode) - newnode.args = self.visit(node.args, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - if 'decorators' in node._fields: # py < 2.6 - attr = 'decorators' - else: - attr = 'decorator_list' - decorators = getattr(node, attr) - if decorators: - newnode.decorators = self.visit_decorators(node, newnode) - if PY3K and node.returns: - newnode.returns = self.visit(node.returns, newnode) - self._global_names.pop() - frame = newnode.parent.frame() - if isinstance(frame, new.Class): - if newnode.name == '__new__': - newnode._type = 'classmethod' - else: - newnode._type = 'method' - if newnode.decorators is not None: - for decorator_expr in newnode.decorators.nodes: - if isinstance(decorator_expr, new.Name): - if decorator_expr.name in ('classmethod', 'staticmethod'): - newnode._type = decorator_expr.name - elif decorator_expr.name == 'classproperty': - newnode._type = 'classmethod' - frame.set_local(newnode.name, newnode) - return newnode - - def visit_genexpr(self, node, parent): - """visit a GenExpr node by returning a fresh instance of it""" - newnode = new.GenExpr() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) for child in node.generators] - return newnode - - def visit_getattr(self, node, parent): - """visit a Getattr node by returning a fresh instance of it""" - if self.asscontext == "Del": - # FIXME : maybe we should reintroduce and visit_delattr ? - # for instance, deactivating asscontext - newnode = new.DelAttr() - elif self.asscontext == "Ass": - # FIXME : maybe we should call visit_assattr ? - newnode = new.AssAttr() - self._delayed_assattr.append(newnode) - else: - newnode = new.Getattr() - _lineno_parent(node, newnode, parent) - asscontext, self.asscontext = self.asscontext, None - newnode.expr = self.visit(node.value, newnode) - self.asscontext = asscontext - newnode.attrname = node.attr - return newnode - - def visit_global(self, node, parent): - """visit an Global node to become astroid""" - newnode = new.Global(node.names) - _set_infos(node, newnode, parent) - if self._global_names: # global at the module level, no effect - for name in node.names: - self._global_names[-1].setdefault(name, []).append(newnode) - return newnode - - def visit_if(self, node, parent): - """visit a If node by returning a fresh instance of it""" - newnode = new.If() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_ifexp(self, node, parent): - """visit a IfExp node by returning a fresh instance of it""" - newnode = new.IfExp() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = self.visit(node.body, newnode) - newnode.orelse = self.visit(node.orelse, newnode) - return newnode - - def visit_import(self, node, parent): - """visit a Import node by returning a fresh instance of it""" - newnode = new.Import() - _set_infos(node, newnode, parent) - newnode.names = [(alias.name, alias.asname) for alias in node.names] - # save import names in parent's locals: - for (name, asname) in newnode.names: - name = asname or name - newnode.parent.set_local(name.split('.')[0], newnode) - return newnode - - def visit_index(self, node, parent): - """visit a Index node by returning a fresh instance of it""" - newnode = new.Index() - newnode.parent = parent - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_keyword(self, node, parent): - """visit a Keyword node by returning a fresh instance of it""" - newnode = new.Keyword() - newnode.parent = parent - newnode.arg = node.arg - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_lambda(self, node, parent): - """visit a Lambda node by returning a fresh instance of it""" - newnode = new.Lambda() - _lineno_parent(node, newnode, parent) - newnode.args = self.visit(node.args, newnode) - newnode.body = self.visit(node.body, newnode) - return newnode - - def visit_list(self, node, parent): - """visit a List node by returning a fresh instance of it""" - newnode = new.List() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - return newnode - - def visit_listcomp(self, node, parent): - """visit a ListComp node by returning a fresh instance of it""" - newnode = new.ListComp() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - return newnode - - def visit_name(self, node, parent): - """visit a Name node by returning a fresh instance of it""" - # True and False can be assigned to something in py2x, so we have to - # check first the asscontext - if self.asscontext == "Del": - newnode = new.DelName() - elif self.asscontext is not None: # Ass - assert self.asscontext == "Ass" - newnode = new.AssName() - elif node.id in CONST_NAME_TRANSFORMS: - newnode = new.Const(CONST_NAME_TRANSFORMS[node.id]) - _set_infos(node, newnode, parent) - return newnode - else: - newnode = new.Name() - _lineno_parent(node, newnode, parent) - newnode.name = node.id - # XXX REMOVE me : - if self.asscontext in ('Del', 'Ass'): # 'Aug' ?? - self._save_assignment(newnode) - return newnode - - def visit_bytes(self, node, parent): - """visit a Bytes node by returning a fresh instance of Const""" - newnode = new.Const(node.s) - _set_infos(node, newnode, parent) - return newnode - - def visit_num(self, node, parent): - """visit a Num node by returning a fresh instance of Const""" - newnode = new.Const(node.n) - _set_infos(node, newnode, parent) - return newnode - - def visit_pass(self, node, parent): - """visit a Pass node by returning a fresh instance of it""" - newnode = new.Pass() - _set_infos(node, newnode, parent) - return newnode - - def visit_str(self, node, parent): - """visit a Str node by returning a fresh instance of Const""" - newnode = new.Const(node.s) - _set_infos(node, newnode, parent) - return newnode - - def visit_print(self, node, parent): - """visit a Print node by returning a fresh instance of it""" - newnode = new.Print() - _lineno_parent(node, newnode, parent) - newnode.nl = node.nl - if node.dest is not None: - newnode.dest = self.visit(node.dest, newnode) - newnode.values = [self.visit(child, newnode) for child in node.values] - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = new.Raise() - _lineno_parent(node, newnode, parent) - if node.type is not None: - newnode.exc = self.visit(node.type, newnode) - if node.inst is not None: - newnode.inst = self.visit(node.inst, newnode) - if node.tback is not None: - newnode.tback = self.visit(node.tback, newnode) - return newnode - - def visit_return(self, node, parent): - """visit a Return node by returning a fresh instance of it""" - newnode = new.Return() - _lineno_parent(node, newnode, parent) - if node.value is not None: - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_set(self, node, parent): - """visit a Set node by returning a fresh instance of it""" - newnode = new.Set() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - return newnode - - def visit_setcomp(self, node, parent): - """visit a SetComp node by returning a fresh instance of it""" - newnode = new.SetComp() - _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) - for child in node.generators] - return newnode - - def visit_slice(self, node, parent): - """visit a Slice node by returning a fresh instance of it""" - newnode = new.Slice() - newnode.parent = parent - if node.lower is not None: - newnode.lower = self.visit(node.lower, newnode) - if node.upper is not None: - newnode.upper = self.visit(node.upper, newnode) - if node.step is not None: - newnode.step = self.visit(node.step, newnode) - return newnode - - def visit_subscript(self, node, parent): - """visit a Subscript node by returning a fresh instance of it""" - newnode = new.Subscript() - _lineno_parent(node, newnode, parent) - subcontext, self.asscontext = self.asscontext, None - newnode.value = self.visit(node.value, newnode) - newnode.slice = self.visit(node.slice, newnode) - self.asscontext = subcontext - return newnode - - def visit_tryexcept(self, node, parent): - """visit a TryExcept node by returning a fresh instance of it""" - newnode = new.TryExcept() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.handlers = [self.visit(child, newnode) for child in node.handlers] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_tryfinally(self, node, parent): - """visit a TryFinally node by returning a fresh instance of it""" - newnode = new.TryFinally() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] - return newnode - - def visit_tuple(self, node, parent): - """visit a Tuple node by returning a fresh instance of it""" - newnode = new.Tuple() - _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] - return newnode - - def visit_unaryop(self, node, parent): - """visit a UnaryOp node by returning a fresh instance of it""" - newnode = new.UnaryOp() - _lineno_parent(node, newnode, parent) - newnode.operand = self.visit(node.operand, newnode) - newnode.op = _UNARY_OP_CLASSES[node.op.__class__] - return newnode - - def visit_while(self, node, parent): - """visit a While node by returning a fresh instance of it""" - newnode = new.While() - _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_with(self, node, parent): - newnode = new.With() - _lineno_parent(node, newnode, parent) - expr = self.visit(node.context_expr, newnode) - self.asscontext = "Ass" - if node.optional_vars is not None: - vars = self.visit(node.optional_vars, newnode) - else: - vars = None - self.asscontext = None - newnode.items = [(expr, vars)] - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_yield(self, node, parent): - """visit a Yield node by returning a fresh instance of it""" - return _create_yield_node(node, parent, self, new.Yield) - -class TreeRebuilder3k(TreeRebuilder): - """extend and overwrite TreeRebuilder for python3k""" - - def visit_arg(self, node, parent): - """visit a arg node by returning a fresh AssName instance""" - # the node is coming from py>=3.0, but we use AssName in py2.x - # XXX or we should instead introduce a Arg node in astroid ? - return self.visit_assname(node, parent, node.arg) - - def visit_nameconstant(self, node, parent): - # in Python 3.4 we have NameConstant for True / False / None - newnode = new.Const(node.value) - _set_infos(node, newnode, parent) - return newnode - - def visit_arguments(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_arguments(node, parent) - self.asscontext = "Ass" - newnode.kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs] - self.asscontext = None - newnode.kw_defaults = [self.visit(child, newnode) if child else None for child in node.kw_defaults] - newnode.annotations = [ - self.visit(arg.annotation, newnode) if arg.annotation else None - for arg in node.args] - return newnode - - def visit_excepthandler(self, node, parent): - """visit an ExceptHandler node by returning a fresh instance of it""" - newnode = new.ExceptHandler() - _lineno_parent(node, newnode, parent) - if node.type is not None: - newnode.type = self.visit(node.type, newnode) - if node.name is not None: - newnode.name = self.visit_assname(node, newnode, node.name) - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_nonlocal(self, node, parent): - """visit a Nonlocal node and return a new instance of it""" - newnode = new.Nonlocal(node.names) - _set_infos(node, newnode, parent) - return newnode - - def visit_raise(self, node, parent): - """visit a Raise node by returning a fresh instance of it""" - newnode = new.Raise() - _lineno_parent(node, newnode, parent) - # no traceback; anyway it is not used in Pylint - if node.exc is not None: - newnode.exc = self.visit(node.exc, newnode) - if node.cause is not None: - newnode.cause = self.visit(node.cause, newnode) - return newnode - - def visit_starred(self, node, parent): - """visit a Starred node and return a new instance of it""" - newnode = new.Starred() - _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_try(self, node, parent): - # python 3.3 introduce a new Try node replacing TryFinally/TryExcept nodes - if node.finalbody: - newnode = new.TryFinally() - _lineno_parent(node, newnode, parent) - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] - if node.handlers: - excnode = new.TryExcept() - _lineno_parent(node, excnode, newnode) - excnode.body = [self.visit(child, excnode) for child in node.body] - excnode.handlers = [self.visit(child, excnode) for child in node.handlers] - excnode.orelse = [self.visit(child, excnode) for child in node.orelse] - newnode.body = [excnode] - else: - newnode.body = [self.visit(child, newnode) for child in node.body] - elif node.handlers: - newnode = new.TryExcept() - _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.handlers = [self.visit(child, newnode) for child in node.handlers] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] - return newnode - - def visit_with(self, node, parent): - if 'items' not in node._fields: - # python < 3.3 - return super(TreeRebuilder3k, self).visit_with(node, parent) - - newnode = new.With() - _lineno_parent(node, newnode, parent) - def visit_child(child): - expr = self.visit(child.context_expr, newnode) - self.asscontext = 'Ass' - if child.optional_vars: - var = self.visit(child.optional_vars, newnode) - else: - var = None - self.asscontext = None - return expr, var - newnode.items = [visit_child(child) - for child in node.items] - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_yieldfrom(self, node, parent): - return _create_yield_node(node, parent, self, new.YieldFrom) - - def visit_class(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_class(node, parent) - newnode._newstyle = True - for keyword in node.keywords: - if keyword.arg == 'metaclass': - newnode._metaclass = self.visit(keyword, newnode).value - break - return newnode - -if sys.version_info >= (3, 0): - TreeRebuilder = TreeRebuilder3k - - diff --git a/pymode/libs/astroid/scoped_nodes.py b/pymode/libs/astroid/scoped_nodes.py deleted file mode 100644 index ac90f878..00000000 --- a/pymode/libs/astroid/scoped_nodes.py +++ /dev/null @@ -1,1484 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""This module contains the classes for "scoped" node, i.e. which are opening a -new local scope in the language definition : Module, Class, Function (and -Lambda, GenExpr, DictComp and SetComp to some extent). -""" -from __future__ import with_statement - -__doctype__ = "restructuredtext en" - -import sys -import warnings -from itertools import chain -try: - from io import BytesIO -except ImportError: - from cStringIO import StringIO as BytesIO - -import six -from logilab.common.compat import builtins -from logilab.common.decorators import cached, cachedproperty - -from astroid.exceptions import NotFoundError, \ - AstroidBuildingException, InferenceError, ResolveError -from astroid.node_classes import Const, DelName, DelAttr, \ - Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ - LookupMixIn, const_factory as cf, unpack_infer, CallFunc -from astroid.bases import NodeNG, InferenceContext, Instance, copy_context, \ - YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, \ - BUILTINS -from astroid.mixins import FilterStmtsMixin -from astroid.bases import Statement -from astroid.manager import AstroidManager - -ITER_METHODS = ('__iter__', '__getitem__') -PY3K = sys.version_info >= (3, 0) - -def _c3_merge(sequences): - """Merges MROs in *sequences* to a single MRO using the C3 algorithm. - - Adapted from http://www.python.org/download/releases/2.3/mro/. - - """ - result = [] - while True: - sequences = [s for s in sequences if s] # purge empty sequences - if not sequences: - return result - for s1 in sequences: # find merge candidates among seq heads - candidate = s1[0] - for s2 in sequences: - if candidate in s2[1:]: - candidate = None - break # reject the current head, it appears later - else: - break - if not candidate: - # Show all the remaining bases, which were considered as - # candidates for the next mro sequence. - bases = ["({})".format(", ".join(base.name - for base in subsequence)) - for subsequence in sequences] - raise ResolveError("Cannot create a consistent method resolution " - "order for bases %s" % ", ".join(bases)) - result.append(candidate) - # remove the chosen candidate - for seq in sequences: - if seq[0] == candidate: - del seq[0] - - -def _verify_duplicates_mro(sequences): - for sequence in sequences: - names = [node.qname() for node in sequence] - if len(names) != len(set(names)): - raise ResolveError('Duplicates found in the mro.') - - -def remove_nodes(func, cls): - def wrapper(*args, **kwargs): - nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)] - if not nodes: - raise NotFoundError() - return nodes - return wrapper - - -def function_to_method(n, klass): - if isinstance(n, Function): - if n.type == 'classmethod': - return BoundMethod(n, klass) - if n.type != 'staticmethod': - return UnboundMethod(n) - return n - -def std_special_attributes(self, name, add_locals=True): - if add_locals: - locals = self.locals - else: - locals = {} - if name == '__name__': - return [cf(self.name)] + locals.get(name, []) - if name == '__doc__': - return [cf(self.doc)] + locals.get(name, []) - if name == '__dict__': - return [Dict()] + locals.get(name, []) - raise NotFoundError(name) - -MANAGER = AstroidManager() -def builtin_lookup(name): - """lookup a name into the builtin module - return the list of matching statements and the astroid for the builtin - module - """ - builtin_astroid = MANAGER.ast_from_module(builtins) - if name == '__dict__': - return builtin_astroid, () - try: - stmts = builtin_astroid.locals[name] - except KeyError: - stmts = () - return builtin_astroid, stmts - - -# TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup -class LocalsDictNodeNG(LookupMixIn, NodeNG): - """ this class provides locals handling common to Module, Function - and Class nodes, including a dict like interface for direct access - to locals information - """ - - # attributes below are set by the builder module or by raw factories - - # dictionary of locals with name as key and node defining the local as - # value - - def qname(self): - """return the 'qualified' name of the node, eg module.name, - module.class.name ... - """ - if self.parent is None: - return self.name - return '%s.%s' % (self.parent.frame().qname(), self.name) - - def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) - """ - return self - - def scope(self): - """return the first node defining a new scope (i.e. Module, - Function, Class, Lambda but also GenExpr, DictComp and SetComp) - """ - return self - - - def _scope_lookup(self, node, name, offset=0): - """XXX method for interfacing the scope lookup""" - try: - stmts = node._filter_stmts(self.locals[name], self, offset) - except KeyError: - stmts = () - if stmts: - return self, stmts - if self.parent: # i.e. not Module - # nested scope: if parent scope is a function, that's fine - # else jump to the module - pscope = self.parent.scope() - if not pscope.is_function: - pscope = pscope.root() - return pscope.scope_lookup(node, name) - return builtin_lookup(name) # Module - - - - def set_local(self, name, stmt): - """define in locals ( is the node defining the name) - if the node is a Module node (i.e. has globals), add the name to - globals - - if the name is already defined, ignore it - """ - #assert not stmt in self.locals.get(name, ()), (self, stmt) - self.locals.setdefault(name, []).append(stmt) - - __setitem__ = set_local - - def _append_node(self, child): - """append a child, linking it in the tree""" - self.body.append(child) - child.parent = self - - def add_local_node(self, child_node, name=None): - """append a child which should alter locals to the given node""" - if name != '__class__': - # add __class__ node as a child will cause infinite recursion later! - self._append_node(child_node) - self.set_local(name or child_node.name, child_node) - - - def __getitem__(self, item): - """method from the `dict` interface returning the first node - associated with the given name in the locals dictionary - - :type item: str - :param item: the name of the locally defined object - :raises KeyError: if the name is not defined - """ - return self.locals[item][0] - - def __iter__(self): - """method from the `dict` interface returning an iterator on - `self.keys()` - """ - return iter(self.keys()) - - def keys(self): - """method from the `dict` interface returning a tuple containing - locally defined names - """ - return list(self.locals.keys()) - - def values(self): - """method from the `dict` interface returning a tuple containing - locally defined nodes which are instance of `Function` or `Class` - """ - return [self[key] for key in self.keys()] - - def items(self): - """method from the `dict` interface returning a list of tuple - containing each locally defined name with its associated node, - which is an instance of `Function` or `Class` - """ - return list(zip(self.keys(), self.values())) - - - def __contains__(self, name): - return name in self.locals - has_key = __contains__ - -# Module ##################################################################### - -class Module(LocalsDictNodeNG): - _astroid_fields = ('body',) - - fromlineno = 0 - lineno = 0 - - # attributes below are set by the builder module or by raw factories - - # the file from which as been extracted the astroid representation. It may - # be None if the representation has been built from a built-in module - file = None - # Alternatively, if built from a string/bytes, this can be set - file_bytes = None - # encoding of python source file, so we can get unicode out of it (python2 - # only) - file_encoding = None - # the module name - name = None - # boolean for astroid built from source (i.e. ast) - pure_python = None - # boolean for package module - package = None - # dictionary of globals with name as key and node defining the global - # as value - globals = None - - # Future imports - future_imports = None - - # names of python special attributes (handled by getattr impl.) - special_attributes = set(('__name__', '__doc__', '__file__', '__path__', - '__dict__')) - # names of module attributes available through the global scope - scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) - - def __init__(self, name, doc, pure_python=True): - self.name = name - self.doc = doc - self.pure_python = pure_python - self.locals = self.globals = {} - self.body = [] - self.future_imports = set() - - def _get_stream(self): - if self.file_bytes is not None: - return BytesIO(self.file_bytes) - if self.file is not None: - stream = open(self.file, 'rb') - return stream - return None - - @property - def file_stream(self): - warnings.warn("file_stream property is deprecated and " - "it is slated for removal in astroid 1.6." - "Use the new method 'stream' instead.", - PendingDeprecationWarning, - stacklevel=2) - return self._get_stream() - - def stream(self): - """Get a stream to the underlying file or bytes.""" - return self._get_stream() - - def close(self): - """Close the underlying file streams.""" - warnings.warn("close method is deprecated and it is " - "slated for removal in astroid 1.6, along " - "with 'file_stream' property. " - "Its behaviour is replaced by managing each " - "file stream returned by the 'stream' method.", - PendingDeprecationWarning, - stacklevel=2) - - def block_range(self, lineno): - """return block line numbers. - - start from the beginning whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def scope_lookup(self, node, name, offset=0): - if name in self.scope_attrs and not name in self.locals: - try: - return self, self.getattr(name) - except NotFoundError: - return self, () - return self._scope_lookup(node, name, offset) - - def pytype(self): - return '%s.module' % BUILTINS - - def display_type(self): - return 'Module' - - def getattr(self, name, context=None, ignore_locals=False): - if name in self.special_attributes: - if name == '__file__': - return [cf(self.file)] + self.locals.get(name, []) - if name == '__path__' and self.package: - return [List()] + self.locals.get(name, []) - return std_special_attributes(self, name) - if not ignore_locals and name in self.locals: - return self.locals[name] - if self.package: - try: - return [self.import_module(name, relative_only=True)] - except AstroidBuildingException: - raise NotFoundError(name) - except SyntaxError: - raise NotFoundError(name) - except Exception:# XXX pylint tests never pass here; do we need it? - import traceback - traceback.print_exc() - raise NotFoundError(name) - getattr = remove_nodes(getattr, DelName) - - def igetattr(self, name, context=None): - """inferred getattr""" - # set lookup name since this is necessary to infer on import nodes for - # instance - context = copy_context(context) - context.lookupname = name - try: - return _infer_stmts(self.getattr(name, context), context, frame=self) - except NotFoundError: - raise InferenceError(name) - - def fully_defined(self): - """return True if this module has been built from a .py file - and so contains a complete representation including the code - """ - return self.file is not None and self.file.endswith('.py') - - def statement(self): - """return the first parent node marked as statement node - consider a module as a statement... - """ - return self - - def previous_sibling(self): - """module has no sibling""" - return - - def next_sibling(self): - """module has no sibling""" - return - - if sys.version_info < (2, 8): - @cachedproperty - def _absolute_import_activated(self): - for stmt in self.locals.get('absolute_import', ()): - if isinstance(stmt, From) and stmt.modname == '__future__': - return True - return False - else: - _absolute_import_activated = True - - def absolute_import_activated(self): - return self._absolute_import_activated - - def import_module(self, modname, relative_only=False, level=None): - """import the given module considering self as context""" - if relative_only and level is None: - level = 0 - absmodname = self.relative_to_absolute_name(modname, level) - try: - return MANAGER.ast_from_module_name(absmodname) - except AstroidBuildingException: - # we only want to import a sub module or package of this module, - # skip here - if relative_only: - raise - return MANAGER.ast_from_module_name(modname) - - def relative_to_absolute_name(self, modname, level): - """return the absolute module name for a relative import. - - The relative import can be implicit or explicit. - """ - # XXX this returns non sens when called on an absolute import - # like 'pylint.checkers.astroid.utils' - # XXX doesn't return absolute name if self.name isn't absolute name - if self.absolute_import_activated() and level is None: - return modname - if level: - if self.package: - level = level - 1 - package_name = self.name.rsplit('.', level)[0] - elif self.package: - package_name = self.name - else: - package_name = self.name.rsplit('.', 1)[0] - if package_name: - if not modname: - return package_name - return '%s.%s' % (package_name, modname) - return modname - - - def wildcard_import_names(self): - """return the list of imported names when this module is 'wildcard - imported' - - It doesn't include the '__builtins__' name which is added by the - current CPython implementation of wildcard imports. - """ - # take advantage of a living module if it exists - try: - living = sys.modules[self.name] - except KeyError: - pass - else: - try: - return living.__all__ - except AttributeError: - return [name for name in living.__dict__.keys() - if not name.startswith('_')] - # else lookup the astroid - # - # We separate the different steps of lookup in try/excepts - # to avoid catching too many Exceptions - default = [name for name in self.keys() if not name.startswith('_')] - try: - all = self['__all__'] - except KeyError: - return default - try: - explicit = next(all.assigned_stmts()) - except InferenceError: - return default - except AttributeError: - # not an assignment node - # XXX infer? - return default - - # Try our best to detect the exported name. - infered = [] - try: - explicit = next(explicit.infer()) - except InferenceError: - return default - if not isinstance(explicit, (Tuple, List)): - return default - - str_const = lambda node: (isinstance(node, Const) and - isinstance(node.value, six.string_types)) - for node in explicit.elts: - if str_const(node): - infered.append(node.value) - else: - try: - infered_node = next(node.infer()) - except InferenceError: - continue - if str_const(infered_node): - infered.append(infered_node.value) - return infered - - - -class ComprehensionScope(LocalsDictNodeNG): - def frame(self): - return self.parent.frame() - - scope_lookup = LocalsDictNodeNG._scope_lookup - - -class GenExpr(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - - def __init__(self): - self.locals = {} - self.elt = None - self.generators = [] - - -class DictComp(ComprehensionScope): - _astroid_fields = ('key', 'value', 'generators') - - def __init__(self): - self.locals = {} - self.key = None - self.value = None - self.generators = [] - - -class SetComp(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - - def __init__(self): - self.locals = {} - self.elt = None - self.generators = [] - - -class _ListComp(NodeNG): - """class representing a ListComp node""" - _astroid_fields = ('elt', 'generators') - elt = None - generators = None - -if sys.version_info >= (3, 0): - class ListComp(_ListComp, ComprehensionScope): - """class representing a ListComp node""" - def __init__(self): - self.locals = {} -else: - class ListComp(_ListComp): - """class representing a ListComp node""" - -# Function ################################################################### - -def _infer_decorator_callchain(node): - """Detect decorator call chaining and see if the end result is a - static or a classmethod. - """ - if not isinstance(node, Function): - return - if not node.parent: - return - try: - # TODO: We don't handle multiple inference results right now, - # because there's no flow to reason when the return - # is what we are looking for, a static or a class method. - result = next(node.infer_call_result(node.parent)) - except (StopIteration, InferenceError): - return - if isinstance(result, Instance): - result = result._proxied - if isinstance(result, Class): - if result.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - if result.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - - -def _function_type(self): - """ - Function type, possible values are: - method, function, staticmethod, classmethod. - """ - # Can't infer that this node is decorated - # with a subclass of `classmethod` where `type` is first set, - # so do it here. - if self.decorators: - for node in self.decorators.nodes: - if isinstance(node, CallFunc): - # Handle the following case: - # @some_decorator(arg1, arg2) - # def func(...) - # - try: - current = next(node.func.infer()) - except InferenceError: - continue - _type = _infer_decorator_callchain(current) - if _type is not None: - return _type - - try: - for infered in node.infer(): - # Check to see if this returns a static or a class method. - _type = _infer_decorator_callchain(infered) - if _type is not None: - return _type - - if not isinstance(infered, Class): - continue - for ancestor in infered.ancestors(): - if not isinstance(ancestor, Class): - continue - if ancestor.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - except InferenceError: - pass - return self._type - - -class Lambda(LocalsDictNodeNG, FilterStmtsMixin): - _astroid_fields = ('args', 'body',) - name = '' - - # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' - type = 'function' - - def __init__(self): - self.locals = {} - self.args = [] - self.body = [] - - def pytype(self): - if 'method' in self.type: - return '%s.instancemethod' % BUILTINS - return '%s.function' % BUILTINS - - def display_type(self): - if 'method' in self.type: - return 'Method' - return 'Function' - - def callable(self): - return True - - def argnames(self): - """return a list of argument names""" - if self.args.args: # maybe None with builtin functions - names = _rec_get_names(self.args.args) - else: - names = [] - if self.args.vararg: - names.append(self.args.vararg) - if self.args.kwarg: - names.append(self.args.kwarg) - return names - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - return self.body.infer(context) - - def scope_lookup(self, node, name, offset=0): - if node in self.args.defaults or node in self.args.kw_defaults: - frame = self.parent.frame() - # line offset to avoid that def func(f=func) resolve the default - # value to the defined function - offset = -1 - else: - # check this is not used in function decorators - frame = self - return frame._scope_lookup(node, name, offset) - - -class Function(Statement, Lambda): - if PY3K: - _astroid_fields = ('decorators', 'args', 'body', 'returns') - returns = None - else: - _astroid_fields = ('decorators', 'args', 'body') - - special_attributes = set(('__name__', '__doc__', '__dict__')) - is_function = True - # attributes below are set by the builder module or by raw factories - blockstart_tolineno = None - decorators = None - _type = "function" - type = cachedproperty(_function_type) - - def __init__(self, name, doc): - self.locals = {} - self.args = [] - self.body = [] - self.name = name - self.doc = doc - self.extra_decorators = [] - self.instance_attrs = {} - - @cachedproperty - def fromlineno(self): - # lineno is the line number of the first decorator, we want the def - # statement lineno - lineno = self.lineno - if self.decorators is not None: - lineno += sum(node.tolineno - node.lineno + 1 - for node in self.decorators.nodes) - - return lineno - - @cachedproperty - def blockstart_tolineno(self): - return self.args.tolineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "def" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - """ - if name == '__module__': - return [cf(self.root().qname())] - if name in self.instance_attrs: - return self.instance_attrs[name] - return std_special_attributes(self, name, False) - - def is_method(self): - """return true if the function node should be considered as a method""" - # check we are defined in a Class, because this is usually expected - # (e.g. pylint...) when is_method() return True - return self.type != 'function' and isinstance(self.parent.frame(), Class) - - def decoratornames(self): - """return a list of decorator qualified names""" - result = set() - decoratornodes = [] - if self.decorators is not None: - decoratornodes += self.decorators.nodes - decoratornodes += self.extra_decorators - for decnode in decoratornodes: - for infnode in decnode.infer(): - result.add(infnode.qname()) - return result - decoratornames = cached(decoratornames) - - def is_bound(self): - """return true if the function is bound to an Instance or a class""" - return self.type == 'classmethod' - - def is_abstract(self, pass_is_abstract=True): - """Returns True if the method is abstract. - - A method is considered abstract if - - the only statement is 'raise NotImplementedError', or - - the only statement is 'pass' and pass_is_abstract is True, or - - the method is annotated with abc.astractproperty/abc.abstractmethod - """ - if self.decorators: - for node in self.decorators.nodes: - try: - infered = next(node.infer()) - except InferenceError: - continue - if infered and infered.qname() in ('abc.abstractproperty', - 'abc.abstractmethod'): - return True - - for child_node in self.body: - if isinstance(child_node, Raise): - if child_node.raises_not_implemented(): - return True - if pass_is_abstract and isinstance(child_node, Pass): - return True - return False - # empty function is the same as function with a single "pass" statement - if pass_is_abstract: - return True - - def is_generator(self): - """return true if this is a generator function""" - # XXX should be flagged, not computed - return next(self.nodes_of_class((Yield, YieldFrom), - skip_klass=(Function, Lambda)), False) - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - if self.is_generator(): - yield Generator() - return - # This is really a gigantic hack to work around metaclass generators - # that return transient class-generating functions. Pylint's AST structure - # cannot handle a base class object that is only used for calling __new__, - # but does not contribute to the inheritance structure itself. We inject - # a fake class into the hierarchy here for several well-known metaclass - # generators, and filter it out later. - if (self.name == 'with_metaclass' and - len(self.args.args) == 1 and - self.args.vararg is not None): - metaclass = next(caller.args[0].infer(context)) - if isinstance(metaclass, Class): - c = Class('temporary_class', None) - c.hide = True - c.parent = self - bases = [next(b.infer(context)) for b in caller.args[1:]] - c.bases = [base for base in bases if base != YES] - c._metaclass = metaclass - yield c - return - returns = self.nodes_of_class(Return, skip_klass=Function) - for returnnode in returns: - if returnnode.value is None: - yield Const(None) - else: - try: - for infered in returnnode.value.infer(context): - yield infered - except InferenceError: - yield YES - - -def _rec_get_names(args, names=None): - """return a list of all argument names""" - if names is None: - names = [] - for arg in args: - if isinstance(arg, Tuple): - _rec_get_names(arg.elts, names) - else: - names.append(arg.name) - return names - - -# Class ###################################################################### - - -def _is_metaclass(klass, seen=None): - """ Return if the given class can be - used as a metaclass. - """ - if klass.name == 'type': - return True - if seen is None: - seen = set() - for base in klass.bases: - try: - for baseobj in base.infer(): - if baseobj in seen: - continue - else: - seen.add(baseobj) - if isinstance(baseobj, Instance): - # not abstract - return False - if baseobj is YES: - continue - if baseobj is klass: - continue - if not isinstance(baseobj, Class): - continue - if baseobj._type == 'metaclass': - return True - if _is_metaclass(baseobj, seen): - return True - except InferenceError: - continue - return False - - -def _class_type(klass, ancestors=None): - """return a Class node type to differ metaclass, interface and exception - from 'regular' classes - """ - # XXX we have to store ancestors in case we have a ancestor loop - if klass._type is not None: - return klass._type - if _is_metaclass(klass): - klass._type = 'metaclass' - elif klass.name.endswith('Interface'): - klass._type = 'interface' - elif klass.name.endswith('Exception'): - klass._type = 'exception' - else: - if ancestors is None: - ancestors = set() - if klass in ancestors: - # XXX we are in loop ancestors, and have found no type - klass._type = 'class' - return 'class' - ancestors.add(klass) - for base in klass.ancestors(recurs=False): - name = _class_type(base, ancestors) - if name != 'class': - if name == 'metaclass' and not _is_metaclass(klass): - # don't propagate it if the current class - # can't be a metaclass - continue - klass._type = base.type - break - if klass._type is None: - klass._type = 'class' - return klass._type - -def _iface_hdlr(iface_node): - """a handler function used by interfaces to handle suspicious - interface nodes - """ - return True - - -class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): - - # some of the attributes below are set by the builder module or - # by a raw factories - - # a dictionary of class instances attributes - _astroid_fields = ('decorators', 'bases', 'body') # name - - decorators = None - special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', - '__bases__', '__mro__', '__subclasses__')) - blockstart_tolineno = None - - _type = None - _metaclass_hack = False - hide = False - type = property(_class_type, - doc="class'type, possible values are 'class' | " - "'metaclass' | 'interface' | 'exception'") - - def __init__(self, name, doc): - self.instance_attrs = {} - self.locals = {} - self.bases = [] - self.body = [] - self.name = name - self.doc = doc - - def _newstyle_impl(self, context=None): - if context is None: - context = InferenceContext() - if self._newstyle is not None: - return self._newstyle - for base in self.ancestors(recurs=False, context=context): - if base._newstyle_impl(context): - self._newstyle = True - break - klass = self._explicit_metaclass() - # could be any callable, we'd need to infer the result of klass(name, - # bases, dict). punt if it's not a class node. - if klass is not None and isinstance(klass, Class): - self._newstyle = klass._newstyle_impl(context) - if self._newstyle is None: - self._newstyle = False - return self._newstyle - - _newstyle = None - newstyle = property(_newstyle_impl, - doc="boolean indicating if it's a new style class" - "or not") - - @cachedproperty - def blockstart_tolineno(self): - if self.bases: - return self.bases[-1].tolineno - else: - return self.fromlineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "class" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def pytype(self): - if self.newstyle: - return '%s.type' % BUILTINS - return '%s.classobj' % BUILTINS - - def display_type(self): - return 'Class' - - def callable(self): - return True - - def is_subtype_of(self, type_name, context=None): - if self.qname() == type_name: - return True - for anc in self.ancestors(context=context): - if anc.qname() == type_name: - return True - - def infer_call_result(self, caller, context=None): - """infer what a class is returning when called""" - if self.is_subtype_of('%s.type' % (BUILTINS,), context) and len(caller.args) == 3: - name_node = next(caller.args[0].infer(context)) - if (isinstance(name_node, Const) and - isinstance(name_node.value, six.string_types)): - name = name_node.value - else: - yield YES - return - result = Class(name, None) - bases = next(caller.args[1].infer(context)) - if isinstance(bases, (Tuple, List)): - result.bases = bases.itered() - else: - # There is currently no AST node that can represent an 'unknown' - # node (YES is not an AST node), therefore we simply return YES here - # although we know at least the name of the class. - yield YES - return - result.parent = caller.parent - yield result - else: - yield Instance(self) - - def scope_lookup(self, node, name, offset=0): - if any(node == base or base.parent_of(node) - for base in self.bases): - # Handle the case where we have either a name - # in the bases of a class, which exists before - # the actual definition or the case where we have - # a Getattr node, with that name. - # - # name = ... - # class A(name): - # def name(self): ... - # - # import name - # class A(name.Name): - # def name(self): ... - - frame = self.parent.frame() - # line offset to avoid that class A(A) resolve the ancestor to - # the defined class - offset = -1 - else: - frame = self - return frame._scope_lookup(node, name, offset) - - # list of parent class as a list of string (i.e. names as they appear - # in the class definition) XXX bw compat - def basenames(self): - return [bnode.as_string() for bnode in self.bases] - basenames = property(basenames) - - def ancestors(self, recurs=True, context=None): - """return an iterator on the node base classes in a prefixed - depth first order - - :param recurs: - boolean indicating if it should recurse or return direct - ancestors only - """ - # FIXME: should be possible to choose the resolution order - # FIXME: inference make infinite loops possible here - yielded = set([self]) - if context is None: - context = InferenceContext() - if sys.version_info[0] >= 3: - if not self.bases and self.qname() != 'builtins.object': - yield builtin_lookup("object")[1][0] - return - - for stmt in self.bases: - with context.restore_path(): - try: - for baseobj in stmt.infer(context): - if not isinstance(baseobj, Class): - if isinstance(baseobj, Instance): - baseobj = baseobj._proxied - else: - # duh ? - continue - if not baseobj.hide: - if baseobj in yielded: - continue # cf xxx above - yielded.add(baseobj) - yield baseobj - if recurs: - for grandpa in baseobj.ancestors(recurs=True, - context=context): - if grandpa is self: - # This class is the ancestor of itself. - break - if grandpa in yielded: - continue # cf xxx above - yielded.add(grandpa) - yield grandpa - except InferenceError: - # XXX log error ? - continue - - def local_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their locals - """ - for astroid in self.ancestors(context=context): - if name in astroid: - yield astroid - - def instance_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their instance attribute dictionary - """ - for astroid in self.ancestors(context=context): - if name in astroid.instance_attrs: - yield astroid - - def has_base(self, node): - return node in self.bases - - def local_attr(self, name, context=None): - """return the list of assign node associated to name in this class - locals or in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - try: - return self.locals[name] - except KeyError: - # get if from the first parent implementing it if any - for class_node in self.local_attr_ancestors(name, context): - return class_node.locals[name] - raise NotFoundError(name) - local_attr = remove_nodes(local_attr, DelAttr) - - def instance_attr(self, name, context=None): - """return the astroid nodes associated to name in this class instance - attributes dictionary and in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - # Return a copy, so we don't modify self.instance_attrs, - # which could lead to infinite loop. - values = list(self.instance_attrs.get(name, [])) - # get all values from parents - for class_node in self.instance_attr_ancestors(name, context): - values += class_node.instance_attrs[name] - if not values: - raise NotFoundError(name) - return values - instance_attr = remove_nodes(instance_attr, DelAttr) - - def instanciate_class(self): - """return Instance of Class node, else return self""" - return Instance(self) - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - - It may return a YES object if the attribute has not been actually - found but a __getattr__ or __getattribute__ method is defined - """ - values = self.locals.get(name, []) - if name in self.special_attributes: - if name == '__module__': - return [cf(self.root().qname())] + values - # FIXME: do we really need the actual list of ancestors? - # returning [Tuple()] + values don't break any test - # this is ticket http://www.logilab.org/ticket/52785 - # XXX need proper meta class handling + MRO implementation - if name == '__bases__' or (name == '__mro__' and self.newstyle): - node = Tuple() - node.items = self.ancestors(recurs=True, context=context) - return [node] + values - return std_special_attributes(self, name) - # don't modify the list in self.locals! - values = list(values) - for classnode in self.ancestors(recurs=True, context=context): - values += classnode.locals.get(name, []) - if not values: - raise NotFoundError(name) - return values - - def igetattr(self, name, context=None): - """inferred getattr, need special treatment in class to handle - descriptors - """ - # set lookup name since this is necessary to infer on import nodes for - # instance - context = copy_context(context) - context.lookupname = name - try: - for infered in _infer_stmts(self.getattr(name, context), context, - frame=self): - # yield YES object instead of descriptors when necessary - if not isinstance(infered, Const) and isinstance(infered, Instance): - try: - infered._proxied.getattr('__get__', context) - except NotFoundError: - yield infered - else: - yield YES - else: - yield function_to_method(infered, self) - except NotFoundError: - if not name.startswith('__') and self.has_dynamic_getattr(context): - # class handle some dynamic attributes, return a YES object - yield YES - else: - raise InferenceError(name) - - def has_dynamic_getattr(self, context=None): - """return True if the class has a custom __getattr__ or - __getattribute__ method - """ - # need to explicitly handle optparse.Values (setattr is not detected) - if self.name == 'Values' and self.root().name == 'optparse': - return True - try: - self.getattr('__getattr__', context) - return True - except NotFoundError: - #if self.newstyle: XXX cause an infinite recursion error - try: - getattribute = self.getattr('__getattribute__', context)[0] - if getattribute.root().name != BUILTINS: - # class has a custom __getattribute__ defined - return True - except NotFoundError: - pass - return False - - def methods(self): - """return an iterator on all methods defined in the class and - its ancestors - """ - done = {} - for astroid in chain(iter((self,)), self.ancestors()): - for meth in astroid.mymethods(): - if meth.name in done: - continue - done[meth.name] = None - yield meth - - def mymethods(self): - """return an iterator on all methods defined in the class""" - for member in self.values(): - if isinstance(member, Function): - yield member - - def interfaces(self, herited=True, handler_func=_iface_hdlr): - """return an iterator on interfaces implemented by the given - class node - """ - # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... - try: - implements = Instance(self).getattr('__implements__')[0] - except NotFoundError: - return - if not herited and not implements.frame() is self: - return - found = set() - missing = False - for iface in unpack_infer(implements): - if iface is YES: - missing = True - continue - if not iface in found and handler_func(iface): - found.add(iface) - yield iface - if missing: - raise InferenceError() - - _metaclass = None - def _explicit_metaclass(self): - """ Return the explicit defined metaclass - for the current class. - - An explicit defined metaclass is defined - either by passing the ``metaclass`` keyword argument - in the class definition line (Python 3) or (Python 2) by - having a ``__metaclass__`` class attribute, or if there are - no explicit bases but there is a global ``__metaclass__`` variable. - """ - for base in self.bases: - try: - for baseobj in base.infer(): - if isinstance(baseobj, Class) and baseobj.hide: - self._metaclass = baseobj._metaclass - self._metaclass_hack = True - break - except InferenceError: - pass - - if self._metaclass: - # Expects this from Py3k TreeRebuilder - try: - return next(node for node in self._metaclass.infer() - if node is not YES) - except (InferenceError, StopIteration): - return None - if sys.version_info >= (3, ): - return None - - if '__metaclass__' in self.locals: - assignment = self.locals['__metaclass__'][-1] - elif self.bases: - return None - elif '__metaclass__' in self.root().locals: - assignments = [ass for ass in self.root().locals['__metaclass__'] - if ass.lineno < self.lineno] - if not assignments: - return None - assignment = assignments[-1] - else: - return None - - try: - infered = next(assignment.infer()) - except InferenceError: - return - if infered is YES: # don't expose this - return None - return infered - - def metaclass(self): - """ Return the metaclass of this class. - - If this class does not define explicitly a metaclass, - then the first defined metaclass in ancestors will be used - instead. - """ - klass = self._explicit_metaclass() - if klass is None: - for parent in self.ancestors(): - klass = parent.metaclass() - if klass is not None: - break - return klass - - def has_metaclass_hack(self): - return self._metaclass_hack - - def _islots(self): - """ Return an iterator with the inferred slots. """ - if '__slots__' not in self.locals: - return - for slots in self.igetattr('__slots__'): - # check if __slots__ is a valid type - for meth in ITER_METHODS: - try: - slots.getattr(meth) - break - except NotFoundError: - continue - else: - continue - - if isinstance(slots, Const): - # a string. Ignore the following checks, - # but yield the node, only if it has a value - if slots.value: - yield slots - continue - if not hasattr(slots, 'itered'): - # we can't obtain the values, maybe a .deque? - continue - - if isinstance(slots, Dict): - values = [item[0] for item in slots.items] - else: - values = slots.itered() - if values is YES: - continue - - for elt in values: - try: - for infered in elt.infer(): - if infered is YES: - continue - if (not isinstance(infered, Const) or - not isinstance(infered.value, - six.string_types)): - continue - if not infered.value: - continue - yield infered - except InferenceError: - continue - - # Cached, because inferring them all the time is expensive - @cached - def slots(self): - """Get all the slots for this node. - - If the class doesn't define any slot, through `__slots__` - variable, then this function will return a None. - Also, it will return None in the case the slots weren't inferred. - Otherwise, it will return a list of slot names. - """ - if not self.newstyle: - raise NotImplementedError( - "The concept of slots is undefined for old-style classes.") - - slots = self._islots() - try: - first = next(slots) - except StopIteration: - # The class doesn't have a __slots__ definition. - return None - return [first] + list(slots) - - def _inferred_bases(self, recurs=True, context=None): - # TODO(cpopa): really similar with .ancestors, - # but the difference is when one base is inferred, - # only the first object is wanted. That's because - # we aren't interested in superclasses, as in the following - # example: - # - # class SomeSuperClass(object): pass - # class SomeClass(SomeSuperClass): pass - # class Test(SomeClass): pass - # - # Inferring SomeClass from the Test's bases will give - # us both SomeClass and SomeSuperClass, but we are interested - # only in SomeClass. - - if context is None: - context = InferenceContext() - if sys.version_info[0] >= 3: - if not self.bases and self.qname() != 'builtins.object': - yield builtin_lookup("object")[1][0] - return - - for stmt in self.bases: - try: - baseobj = next(stmt.infer(context=context)) - except InferenceError: - # XXX log error ? - continue - if isinstance(baseobj, Instance): - baseobj = baseobj._proxied - if not isinstance(baseobj, Class): - continue - if not baseobj.hide: - yield baseobj - - def mro(self, context=None): - """Get the method resolution order, using C3 linearization. - - It returns the list of ancestors sorted by the mro. - This will raise `NotImplementedError` for old-style classes, since - they don't have the concept of MRO. - """ - if not self.newstyle: - raise NotImplementedError( - "Could not obtain mro for old-style classes.") - - bases = list(self._inferred_bases(context=context)) - unmerged_mro = ([[self]] + - [base.mro() for base in bases if base is not self] + - [bases]) - - _verify_duplicates_mro(unmerged_mro) - return _c3_merge(unmerged_mro) diff --git a/pymode/libs/astroid/test_utils.py b/pymode/libs/astroid/test_utils.py deleted file mode 100644 index 19bd7b96..00000000 --- a/pymode/libs/astroid/test_utils.py +++ /dev/null @@ -1,218 +0,0 @@ -"""Utility functions for test code that uses astroid ASTs as input.""" -import functools -import sys -import textwrap - -from astroid import nodes -from astroid import builder -# The name of the transient function that is used to -# wrap expressions to be extracted when calling -# extract_node. -_TRANSIENT_FUNCTION = '__' - -# The comment used to select a statement to be extracted -# when calling extract_node. -_STATEMENT_SELECTOR = '#@' - - -def _extract_expressions(node): - """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. - - The function walks the AST recursively to search for expressions that - are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an - expression, it completely removes the function call node from the tree, - replacing it by the wrapped expression inside the parent. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :yields: The sequence of wrapped expressions on the modified tree - expression can be found. - """ - if (isinstance(node, nodes.CallFunc) - and isinstance(node.func, nodes.Name) - and node.func.name == _TRANSIENT_FUNCTION): - real_expr = node.args[0] - real_expr.parent = node.parent - # Search for node in all _astng_fields (the fields checked when - # get_children is called) of its parent. Some of those fields may - # be lists or tuples, in which case the elements need to be checked. - # When we find it, replace it by real_expr, so that the AST looks - # like no call to _TRANSIENT_FUNCTION ever took place. - for name in node.parent._astroid_fields: - child = getattr(node.parent, name) - if isinstance(child, (list, tuple)): - for idx, compound_child in enumerate(child): - if compound_child is node: - child[idx] = real_expr - elif child is node: - setattr(node.parent, name, real_expr) - yield real_expr - else: - for child in node.get_children(): - for result in _extract_expressions(child): - yield result - - -def _find_statement_by_line(node, line): - """Extracts the statement on a specific line from an AST. - - If the line number of node matches line, it will be returned; - otherwise its children are iterated and the function is called - recursively. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :param line: The line number of the statement to extract. - :type line: int - :returns: The statement on the line, or None if no statement for the line - can be found. - :rtype: astroid.bases.NodeNG or None - """ - if isinstance(node, (nodes.Class, nodes.Function)): - # This is an inaccuracy in the AST: the nodes that can be - # decorated do not carry explicit information on which line - # the actual definition (class/def), but .fromline seems to - # be close enough. - node_line = node.fromlineno - else: - node_line = node.lineno - - if node_line == line: - return node - - for child in node.get_children(): - result = _find_statement_by_line(child, line) - if result: - return result - - return None - -def extract_node(code, module_name=''): - """Parses some Python code as a module and extracts a designated AST node. - - Statements: - To extract one or more statement nodes, append #@ to the end of the line - - Examples: - >>> def x(): - >>> def y(): - >>> return 1 #@ - - The return statement will be extracted. - - >>> class X(object): - >>> def meth(self): #@ - >>> pass - - The funcion object 'meth' will be extracted. - - Expressions: - To extract arbitrary expressions, surround them with the fake - function call __(...). After parsing, the surrounded expression - will be returned and the whole AST (accessible via the returned - node's parent attribute) will look like the function call was - never there in the first place. - - Examples: - >>> a = __(1) - - The const node will be extracted. - - >>> def x(d=__(foo.bar)): pass - - The node containing the default argument will be extracted. - - >>> def foo(a, b): - >>> return 0 < __(len(a)) < b - - The node containing the function call 'len' will be extracted. - - If no statements or expressions are selected, the last toplevel - statement will be returned. - - If the selected statement is a discard statement, (i.e. an expression - turned into a statement), the wrapped expression is returned instead. - - For convenience, singleton lists are unpacked. - - :param str code: A piece of Python code that is parsed as - a module. Will be passed through textwrap.dedent first. - :param str module_name: The name of the module. - :returns: The designated node from the parse tree, or a list of nodes. - :rtype: astroid.bases.NodeNG, or a list of nodes. - """ - def _extract(node): - if isinstance(node, nodes.Discard): - return node.value - else: - return node - - requested_lines = [] - for idx, line in enumerate(code.splitlines()): - if line.strip().endswith(_STATEMENT_SELECTOR): - requested_lines.append(idx + 1) - - tree = build_module(code, module_name=module_name) - extracted = [] - if requested_lines: - for line in requested_lines: - extracted.append(_find_statement_by_line(tree, line)) - - # Modifies the tree. - extracted.extend(_extract_expressions(tree)) - - if not extracted: - extracted.append(tree.body[-1]) - - extracted = [_extract(node) for node in extracted] - if len(extracted) == 1: - return extracted[0] - else: - return extracted - - -def build_module(code, module_name='', path=None): - """Parses a string module with a builder. - :param code: The code for the module. - :type code: str - :param module_name: The name for the module - :type module_name: str - :param path: The path for the module - :type module_name: str - :returns: The module AST. - :rtype: astroid.bases.NodeNG - """ - code = textwrap.dedent(code) - return builder.AstroidBuilder(None).string_build(code, modname=module_name, path=path) - - -def require_version(minver=None, maxver=None): - """ Compare version of python interpreter to the given one. Skip the test - if older. - """ - def parse(string, default=None): - string = string or default - try: - return tuple(int(v) for v in string.split('.')) - except ValueError: - raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version) - - def check_require_version(f): - current = sys.version_info[:3] - if parse(minver, "0") < current <= parse(maxver, "4"): - return f - else: - str_version = '.'.join(str(v) for v in sys.version_info) - @functools.wraps(f) - def new_f(self, *args, **kwargs): - if minver is not None: - self.skipTest('Needs Python > %s. Current version is %s.' % (minver, str_version)) - elif maxver is not None: - self.skipTest('Needs Python <= %s. Current version is %s.' % (maxver, str_version)) - return new_f - - - return check_require_version - -def get_name_node(start_from, name, index=0): - return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index] diff --git a/pymode/libs/astroid/utils.py b/pymode/libs/astroid/utils.py deleted file mode 100644 index ae72a92c..00000000 --- a/pymode/libs/astroid/utils.py +++ /dev/null @@ -1,239 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains some utilities to navigate in the tree or to -extract information from it -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from astroid.exceptions import AstroidBuildingException -from astroid.builder import parse - - -class ASTWalker(object): - """a walker visiting a tree in preorder, calling on the handler: - - * visit_ on entering a node, where class name is the class of - the node in lower case - - * leave_ on leaving a node, where class name is the class of - the node in lower case - """ - - def __init__(self, handler): - self.handler = handler - self._cache = {} - - def walk(self, node, _done=None): - """walk on the tree from , getting callbacks from handler""" - if _done is None: - _done = set() - if node in _done: - raise AssertionError((id(node), node, node.parent)) - _done.add(node) - self.visit(node) - for child_node in node.get_children(): - self.handler.set_context(node, child_node) - assert child_node is not node - self.walk(child_node, _done) - self.leave(node) - assert node.parent is not node - - def get_callbacks(self, node): - """get callbacks from handler for the visited node""" - klass = node.__class__ - methods = self._cache.get(klass) - if methods is None: - handler = self.handler - kid = klass.__name__.lower() - e_method = getattr(handler, 'visit_%s' % kid, - getattr(handler, 'visit_default', None)) - l_method = getattr(handler, 'leave_%s' % kid, - getattr(handler, 'leave_default', None)) - self._cache[klass] = (e_method, l_method) - else: - e_method, l_method = methods - return e_method, l_method - - def visit(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[0] - if method is not None: - method(node) - - def leave(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[1] - if method is not None: - method(node) - - -class LocalsVisitor(ASTWalker): - """visit a project by traversing the locals dictionary""" - def __init__(self): - ASTWalker.__init__(self, self) - self._visited = {} - - def visit(self, node): - """launch the visit starting from the given node""" - if node in self._visited: - return - self._visited[node] = 1 # FIXME: use set ? - methods = self.get_callbacks(node) - if methods[0] is not None: - methods[0](node) - if 'locals' in node.__dict__: # skip Instance and other proxy - for local_node in node.values(): - self.visit(local_node) - if methods[1] is not None: - return methods[1](node) - - -def _check_children(node): - """a helper function to check children - parent relations""" - for child in node.get_children(): - ok = False - if child is None: - print("Hm, child of %s is None" % node) - continue - if not hasattr(child, 'parent'): - print(" ERROR: %s has child %s %x with no parent" % ( - node, child, id(child))) - elif not child.parent: - print(" ERROR: %s has child %s %x with parent %r" % ( - node, child, id(child), child.parent)) - elif child.parent is not node: - print(" ERROR: %s %x has child %s %x with wrong parent %s" % ( - node, id(node), child, id(child), child.parent)) - else: - ok = True - if not ok: - print("lines;", node.lineno, child.lineno) - print("of module", node.root(), node.root().name) - raise AstroidBuildingException - _check_children(child) - - -class TreeTester(object): - '''A helper class to see _ast tree and compare with astroid tree - - indent: string for tree indent representation - lineno: bool to tell if we should print the line numbers - - >>> tester = TreeTester('print') - >>> print tester.native_tree_repr() - - - . body = [ - . - . . nl = True - . ] - >>> print tester.astroid_tree_repr() - Module() - body = [ - Print() - dest = - values = [ - ] - ] - ''' - - indent = '. ' - lineno = False - - def __init__(self, sourcecode): - self._string = '' - self.sourcecode = sourcecode - self._ast_node = None - self.build_ast() - - def build_ast(self): - """build the _ast tree from the source code""" - self._ast_node = parse(self.sourcecode) - - def native_tree_repr(self, node=None, indent=''): - """get a nice representation of the _ast tree""" - self._string = '' - if node is None: - node = self._ast_node - self._native_repr_tree(node, indent) - return self._string - - - def _native_repr_tree(self, node, indent, _done=None): - """recursive method for the native tree representation""" - from _ast import Load as _Load, Store as _Store, Del as _Del - from _ast import AST as Node - if _done is None: - _done = set() - if node in _done: - self._string += '\nloop in tree: %r (%s)' % ( - node, getattr(node, 'lineno', None)) - return - _done.add(node) - self._string += '\n' + indent + '<%s>' % node.__class__.__name__ - indent += self.indent - if not hasattr(node, '__dict__'): - self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node) - return - node_dict = node.__dict__ - if hasattr(node, '_attributes'): - for a in node._attributes: - attr = node_dict[a] - if attr is None: - continue - if a in ("lineno", "col_offset") and not self.lineno: - continue - self._string += '\n' + indent + a + " = " + repr(attr) - for field in node._fields or (): - attr = node_dict[field] - if attr is None: - continue - if isinstance(attr, list): - if not attr: - continue - self._string += '\n' + indent + field + ' = [' - for elt in attr: - self._native_repr_tree(elt, indent, _done) - self._string += '\n' + indent + ']' - continue - if isinstance(attr, (_Load, _Store, _Del)): - continue - if isinstance(attr, Node): - self._string += '\n' + indent + field + " = " - self._native_repr_tree(attr, indent, _done) - else: - self._string += '\n' + indent + field + " = " + repr(attr) - - - def build_astroid_tree(self): - """build astroid tree from the _ast tree - """ - from astroid.builder import AstroidBuilder - tree = AstroidBuilder().string_build(self.sourcecode) - return tree - - def astroid_tree_repr(self, ids=False): - """build the astroid tree and return a nice tree representation""" - mod = self.build_astroid_tree() - return mod.repr_tree(ids) - - -__all__ = ('LocalsVisitor', 'ASTWalker',) - diff --git a/pymode/libs/easy_install.py b/pymode/libs/easy_install.py deleted file mode 100644 index d87e9840..00000000 --- a/pymode/libs/easy_install.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Run the EasyInstall command""" - -if __name__ == '__main__': - from setuptools.command.easy_install import main - main() diff --git a/pymode/libs/logilab/__init__.py b/pymode/libs/logilab/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/pymode/libs/logilab/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pymode/libs/logilab/common/__init__.py b/pymode/libs/logilab/common/__init__.py deleted file mode 100644 index fc01e4df..00000000 --- a/pymode/libs/logilab/common/__init__.py +++ /dev/null @@ -1,184 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Logilab common library (aka Logilab's extension to the standard library). - -:type STD_BLACKLIST: tuple -:var STD_BLACKLIST: directories ignored by default by the functions in - this package which have to recurse into directories - -:type IGNORED_EXTENSIONS: tuple -:var IGNORED_EXTENSIONS: file extensions that may usually be ignored -""" -__docformat__ = "restructuredtext en" - -import sys -import types -import pkg_resources - -__version__ = pkg_resources.get_distribution('logilab-common').version - -# deprecated, but keep compatibility with pylint < 1.4.4 -__pkginfo__ = types.ModuleType('__pkginfo__') -__pkginfo__.__package__ = __name__ -__pkginfo__.version = __version__ -sys.modules['logilab.common.__pkginfo__'] = __pkginfo__ - -STD_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build') - -IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc', '~', '.swp', '.orig') - -# set this to False if you've mx DateTime installed but you don't want your db -# adapter to use it (should be set before you got a connection) -USE_MX_DATETIME = True - - -class attrdict(dict): - """A dictionary for which keys are also accessible as attributes.""" - def __getattr__(self, attr): - try: - return self[attr] - except KeyError: - raise AttributeError(attr) - -class dictattr(dict): - def __init__(self, proxy): - self.__proxy = proxy - - def __getitem__(self, attr): - try: - return getattr(self.__proxy, attr) - except AttributeError: - raise KeyError(attr) - -class nullobject(object): - def __repr__(self): - return '' - def __bool__(self): - return False - __nonzero__ = __bool__ - -class tempattr(object): - def __init__(self, obj, attr, value): - self.obj = obj - self.attr = attr - self.value = value - - def __enter__(self): - self.oldvalue = getattr(self.obj, self.attr) - setattr(self.obj, self.attr, self.value) - return self.obj - - def __exit__(self, exctype, value, traceback): - setattr(self.obj, self.attr, self.oldvalue) - - - -# flatten ----- -# XXX move in a specific module and use yield instead -# do not mix flatten and translate -# -# def iterable(obj): -# try: iter(obj) -# except: return False -# return True -# -# def is_string_like(obj): -# try: obj +'' -# except (TypeError, ValueError): return False -# return True -# -#def is_scalar(obj): -# return is_string_like(obj) or not iterable(obj) -# -#def flatten(seq): -# for item in seq: -# if is_scalar(item): -# yield item -# else: -# for subitem in flatten(item): -# yield subitem - -def flatten(iterable, tr_func=None, results=None): - """Flatten a list of list with any level. - - If tr_func is not None, it should be a one argument function that'll be called - on each final element. - - :rtype: list - - >>> flatten([1, [2, 3]]) - [1, 2, 3] - """ - if results is None: - results = [] - for val in iterable: - if isinstance(val, (list, tuple)): - flatten(val, tr_func, results) - elif tr_func is None: - results.append(val) - else: - results.append(tr_func(val)) - return results - - -# XXX is function below still used ? - -def make_domains(lists): - """ - Given a list of lists, return a list of domain for each list to produce all - combinations of possibles values. - - :rtype: list - - Example: - - >>> make_domains(['a', 'b'], ['c','d', 'e']) - [['a', 'b', 'a', 'b', 'a', 'b'], ['c', 'c', 'd', 'd', 'e', 'e']] - """ - from six.moves import range - domains = [] - for iterable in lists: - new_domain = iterable[:] - for i in range(len(domains)): - domains[i] = domains[i]*len(iterable) - if domains: - missing = (len(domains[0]) - len(iterable)) / len(iterable) - i = 0 - for j in range(len(iterable)): - value = iterable[j] - for dummy in range(missing): - new_domain.insert(i, value) - i += 1 - i += 1 - domains.append(new_domain) - return domains - - -# private stuff ################################################################ - -def _handle_blacklist(blacklist, dirnames, filenames): - """remove files/directories in the black list - - dirnames/filenames are usually from os.walk - """ - for norecurs in blacklist: - if norecurs in dirnames: - dirnames.remove(norecurs) - elif norecurs in filenames: - filenames.remove(norecurs) - diff --git a/pymode/libs/logilab/common/cache.py b/pymode/libs/logilab/common/cache.py deleted file mode 100644 index 11ed1370..00000000 --- a/pymode/libs/logilab/common/cache.py +++ /dev/null @@ -1,114 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Cache module, with a least recently used algorithm for the management of the -deletion of entries. - - - - -""" -__docformat__ = "restructuredtext en" - -from threading import Lock - -from logilab.common.decorators import locked - -_marker = object() - -class Cache(dict): - """A dictionary like cache. - - inv: - len(self._usage) <= self.size - len(self.data) <= self.size - """ - - def __init__(self, size=100): - """ Warning : Cache.__init__() != dict.__init__(). - Constructor does not take any arguments beside size. - """ - assert size >= 0, 'cache size must be >= 0 (0 meaning no caching)' - self.size = size - self._usage = [] - self._lock = Lock() - super(Cache, self).__init__() - - def _acquire(self): - self._lock.acquire() - - def _release(self): - self._lock.release() - - def _update_usage(self, key): - if not self._usage: - self._usage.append(key) - elif self._usage[-1] != key: - try: - self._usage.remove(key) - except ValueError: - # we are inserting a new key - # check the size of the dictionary - # and remove the oldest item in the cache - if self.size and len(self._usage) >= self.size: - super(Cache, self).__delitem__(self._usage[0]) - del self._usage[0] - self._usage.append(key) - else: - pass # key is already the most recently used key - - def __getitem__(self, key): - value = super(Cache, self).__getitem__(key) - self._update_usage(key) - return value - __getitem__ = locked(_acquire, _release)(__getitem__) - - def __setitem__(self, key, item): - # Just make sure that size > 0 before inserting a new item in the cache - if self.size > 0: - super(Cache, self).__setitem__(key, item) - self._update_usage(key) - __setitem__ = locked(_acquire, _release)(__setitem__) - - def __delitem__(self, key): - super(Cache, self).__delitem__(key) - self._usage.remove(key) - __delitem__ = locked(_acquire, _release)(__delitem__) - - def clear(self): - super(Cache, self).clear() - self._usage = [] - clear = locked(_acquire, _release)(clear) - - def pop(self, key, default=_marker): - if key in self: - self._usage.remove(key) - #if default is _marker: - # return super(Cache, self).pop(key) - return super(Cache, self).pop(key, default) - pop = locked(_acquire, _release)(pop) - - def popitem(self): - raise NotImplementedError() - - def setdefault(self, key, default=None): - raise NotImplementedError() - - def update(self, other): - raise NotImplementedError() - - diff --git a/pymode/libs/logilab/common/changelog.py b/pymode/libs/logilab/common/changelog.py deleted file mode 100644 index 2fff2ed6..00000000 --- a/pymode/libs/logilab/common/changelog.py +++ /dev/null @@ -1,238 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Manipulation of upstream change log files. - -The upstream change log files format handled is simpler than the one -often used such as those generated by the default Emacs changelog mode. - -Sample ChangeLog format:: - - Change log for project Yoo - ========================== - - -- - * add a new functionality - - 2002-02-01 -- 0.1.1 - * fix bug #435454 - * fix bug #434356 - - 2002-01-01 -- 0.1 - * initial release - - -There is 3 entries in this change log, one for each released version and one -for the next version (i.e. the current entry). -Each entry contains a set of messages corresponding to changes done in this -release. -All the non empty lines before the first entry are considered as the change -log title. -""" - -__docformat__ = "restructuredtext en" - -import sys -from stat import S_IWRITE - -from six import string_types - -BULLET = '*' -SUBBULLET = '-' -INDENT = ' ' * 4 - -class NoEntry(Exception): - """raised when we are unable to find an entry""" - -class EntryNotFound(Exception): - """raised when we are unable to find a given entry""" - -class Version(tuple): - """simple class to handle soft version number has a tuple while - correctly printing it as X.Y.Z - """ - def __new__(cls, versionstr): - if isinstance(versionstr, string_types): - versionstr = versionstr.strip(' :') # XXX (syt) duh? - parsed = cls.parse(versionstr) - else: - parsed = versionstr - return tuple.__new__(cls, parsed) - - @classmethod - def parse(cls, versionstr): - versionstr = versionstr.strip(' :') - try: - return [int(i) for i in versionstr.split('.')] - except ValueError as ex: - raise ValueError("invalid literal for version '%s' (%s)"%(versionstr, ex)) - - def __str__(self): - return '.'.join([str(i) for i in self]) - -# upstream change log ######################################################### - -class ChangeLogEntry(object): - """a change log entry, i.e. a set of messages associated to a version and - its release date - """ - version_class = Version - - def __init__(self, date=None, version=None, **kwargs): - self.__dict__.update(kwargs) - if version: - self.version = self.version_class(version) - else: - self.version = None - self.date = date - self.messages = [] - - def add_message(self, msg): - """add a new message""" - self.messages.append(([msg], [])) - - def complete_latest_message(self, msg_suite): - """complete the latest added message - """ - if not self.messages: - raise ValueError('unable to complete last message as there is no previous message)') - if self.messages[-1][1]: # sub messages - self.messages[-1][1][-1].append(msg_suite) - else: # message - self.messages[-1][0].append(msg_suite) - - def add_sub_message(self, sub_msg, key=None): - if not self.messages: - raise ValueError('unable to complete last message as there is no previous message)') - if key is None: - self.messages[-1][1].append([sub_msg]) - else: - raise NotImplementedError("sub message to specific key are not implemented yet") - - def write(self, stream=sys.stdout): - """write the entry to file """ - stream.write('%s -- %s\n' % (self.date or '', self.version or '')) - for msg, sub_msgs in self.messages: - stream.write('%s%s %s\n' % (INDENT, BULLET, msg[0])) - stream.write(''.join(msg[1:])) - if sub_msgs: - stream.write('\n') - for sub_msg in sub_msgs: - stream.write('%s%s %s\n' % (INDENT * 2, SUBBULLET, sub_msg[0])) - stream.write(''.join(sub_msg[1:])) - stream.write('\n') - - stream.write('\n\n') - -class ChangeLog(object): - """object representation of a whole ChangeLog file""" - - entry_class = ChangeLogEntry - - def __init__(self, changelog_file, title=''): - self.file = changelog_file - self.title = title - self.additional_content = '' - self.entries = [] - self.load() - - def __repr__(self): - return '' % (self.file, id(self), - len(self.entries)) - - def add_entry(self, entry): - """add a new entry to the change log""" - self.entries.append(entry) - - def get_entry(self, version='', create=None): - """ return a given changelog entry - if version is omitted, return the current entry - """ - if not self.entries: - if version or not create: - raise NoEntry() - self.entries.append(self.entry_class()) - if not version: - if self.entries[0].version and create is not None: - self.entries.insert(0, self.entry_class()) - return self.entries[0] - version = self.version_class(version) - for entry in self.entries: - if entry.version == version: - return entry - raise EntryNotFound() - - def add(self, msg, create=None): - """add a new message to the latest opened entry""" - entry = self.get_entry(create=create) - entry.add_message(msg) - - def load(self): - """ read a logilab's ChangeLog from file """ - try: - stream = open(self.file) - except IOError: - return - last = None - expect_sub = False - for line in stream.readlines(): - sline = line.strip() - words = sline.split() - # if new entry - if len(words) == 1 and words[0] == '--': - expect_sub = False - last = self.entry_class() - self.add_entry(last) - # if old entry - elif len(words) == 3 and words[1] == '--': - expect_sub = False - last = self.entry_class(words[0], words[2]) - self.add_entry(last) - # if title - elif sline and last is None: - self.title = '%s%s' % (self.title, line) - # if new entry - elif sline and sline[0] == BULLET: - expect_sub = False - last.add_message(sline[1:].strip()) - # if new sub_entry - elif expect_sub and sline and sline[0] == SUBBULLET: - last.add_sub_message(sline[1:].strip()) - # if new line for current entry - elif sline and last.messages: - last.complete_latest_message(line) - else: - expect_sub = True - self.additional_content += line - stream.close() - - def format_title(self): - return '%s\n\n' % self.title.strip() - - def save(self): - """write back change log""" - # filetutils isn't importable in appengine, so import locally - from logilab.common.fileutils import ensure_fs_mode - ensure_fs_mode(self.file, S_IWRITE) - self.write(open(self.file, 'w')) - - def write(self, stream=sys.stdout): - """write changelog to stream""" - stream.write(self.format_title()) - for entry in self.entries: - entry.write(stream) - diff --git a/pymode/libs/logilab/common/clcommands.py b/pymode/libs/logilab/common/clcommands.py deleted file mode 100644 index 4778b99b..00000000 --- a/pymode/libs/logilab/common/clcommands.py +++ /dev/null @@ -1,334 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Helper functions to support command line tools providing more than -one command. - -e.g called as "tool command [options] args..." where and are -command'specific -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import logging -from os.path import basename - -from logilab.common.configuration import Configuration -from logilab.common.logging_ext import init_log, get_threshold -from logilab.common.deprecation import deprecated - - -class BadCommandUsage(Exception): - """Raised when an unknown command is used or when a command is not - correctly used (bad options, too much / missing arguments...). - - Trigger display of command usage. - """ - -class CommandError(Exception): - """Raised when a command can't be processed and we want to display it and - exit, without traceback nor usage displayed. - """ - - -# command line access point #################################################### - -class CommandLine(dict): - """Usage: - - >>> LDI = cli.CommandLine('ldi', doc='Logilab debian installer', - version=version, rcfile=RCFILE) - >>> LDI.register(MyCommandClass) - >>> LDI.register(MyOtherCommandClass) - >>> LDI.run(sys.argv[1:]) - - Arguments: - - * `pgm`, the program name, default to `basename(sys.argv[0])` - - * `doc`, a short description of the command line tool - - * `copyright`, additional doc string that will be appended to the generated - doc - - * `version`, version number of string of the tool. If specified, global - --version option will be available. - - * `rcfile`, path to a configuration file. If specified, global --C/--rc-file - option will be available? self.rcfile = rcfile - - * `logger`, logger to propagate to commands, default to - `logging.getLogger(self.pgm))` - """ - def __init__(self, pgm=None, doc=None, copyright=None, version=None, - rcfile=None, logthreshold=logging.ERROR, - check_duplicated_command=True): - if pgm is None: - pgm = basename(sys.argv[0]) - self.pgm = pgm - self.doc = doc - self.copyright = copyright - self.version = version - self.rcfile = rcfile - self.logger = None - self.logthreshold = logthreshold - self.check_duplicated_command = check_duplicated_command - - def register(self, cls, force=False): - """register the given :class:`Command` subclass""" - assert not self.check_duplicated_command or force or not cls.name in self, \ - 'a command %s is already defined' % cls.name - self[cls.name] = cls - return cls - - def run(self, args): - """main command line access point: - * init logging - * handle global options (-h/--help, --version, -C/--rc-file) - * check command - * run command - - Terminate by :exc:`SystemExit` - """ - init_log(debug=True, # so that we use StreamHandler - logthreshold=self.logthreshold, - logformat='%(levelname)s: %(message)s') - try: - arg = args.pop(0) - except IndexError: - self.usage_and_exit(1) - if arg in ('-h', '--help'): - self.usage_and_exit(0) - if self.version is not None and arg in ('--version'): - print(self.version) - sys.exit(0) - rcfile = self.rcfile - if rcfile is not None and arg in ('-C', '--rc-file'): - try: - rcfile = args.pop(0) - arg = args.pop(0) - except IndexError: - self.usage_and_exit(1) - try: - command = self.get_command(arg) - except KeyError: - print('ERROR: no %s command' % arg) - print() - self.usage_and_exit(1) - try: - sys.exit(command.main_run(args, rcfile)) - except KeyboardInterrupt as exc: - print('Interrupted', end=' ') - if str(exc): - print(': %s' % exc, end=' ') - print() - sys.exit(4) - except BadCommandUsage as err: - print('ERROR:', err) - print() - print(command.help()) - sys.exit(1) - - def create_logger(self, handler, logthreshold=None): - logger = logging.Logger(self.pgm) - logger.handlers = [handler] - if logthreshold is None: - logthreshold = get_threshold(self.logthreshold) - logger.setLevel(logthreshold) - return logger - - def get_command(self, cmd, logger=None): - if logger is None: - logger = self.logger - if logger is None: - logger = self.logger = logging.getLogger(self.pgm) - logger.setLevel(get_threshold(self.logthreshold)) - return self[cmd](logger) - - def usage(self): - """display usage for the main program (i.e. when no command supplied) - and exit - """ - print('usage:', self.pgm, end=' ') - if self.rcfile: - print('[--rc-file=]', end=' ') - print(' [options] ...') - if self.doc: - print('\n%s' % self.doc) - print(''' -Type "%(pgm)s --help" for more information about a specific -command. Available commands are :\n''' % self.__dict__) - max_len = max([len(cmd) for cmd in self]) - padding = ' ' * max_len - for cmdname, cmd in sorted(self.items()): - if not cmd.hidden: - print(' ', (cmdname + padding)[:max_len], cmd.short_description()) - if self.rcfile: - print(''' -Use --rc-file= / -C before the command -to specify a configuration file. Default to %s. -''' % self.rcfile) - print('''%(pgm)s -h/--help - display this usage information and exit''' % self.__dict__) - if self.version: - print('''%(pgm)s -v/--version - display version configuration and exit''' % self.__dict__) - if self.copyright: - print('\n', self.copyright) - - def usage_and_exit(self, status): - self.usage() - sys.exit(status) - - -# base command classes ######################################################### - -class Command(Configuration): - """Base class for command line commands. - - Class attributes: - - * `name`, the name of the command - - * `min_args`, minimum number of arguments, None if unspecified - - * `max_args`, maximum number of arguments, None if unspecified - - * `arguments`, string describing arguments, used in command usage - - * `hidden`, boolean flag telling if the command should be hidden, e.g. does - not appear in help's commands list - - * `options`, options list, as allowed by :mod:configuration - """ - - arguments = '' - name = '' - # hidden from help ? - hidden = False - # max/min args, None meaning unspecified - min_args = None - max_args = None - - @classmethod - def description(cls): - return cls.__doc__.replace(' ', '') - - @classmethod - def short_description(cls): - return cls.description().split('.')[0] - - def __init__(self, logger): - usage = '%%prog %s %s\n\n%s' % (self.name, self.arguments, - self.description()) - Configuration.__init__(self, usage=usage) - self.logger = logger - - def check_args(self, args): - """check command's arguments are provided""" - if self.min_args is not None and len(args) < self.min_args: - raise BadCommandUsage('missing argument') - if self.max_args is not None and len(args) > self.max_args: - raise BadCommandUsage('too many arguments') - - def main_run(self, args, rcfile=None): - """Run the command and return status 0 if everything went fine. - - If :exc:`CommandError` is raised by the underlying command, simply log - the error and return status 2. - - Any other exceptions, including :exc:`BadCommandUsage` will be - propagated. - """ - if rcfile: - self.load_file_configuration(rcfile) - args = self.load_command_line_configuration(args) - try: - self.check_args(args) - self.run(args) - except CommandError as err: - self.logger.error(err) - return 2 - return 0 - - def run(self, args): - """run the command with its specific arguments""" - raise NotImplementedError() - - -class ListCommandsCommand(Command): - """list available commands, useful for bash completion.""" - name = 'listcommands' - arguments = '[command]' - hidden = True - - def run(self, args): - """run the command with its specific arguments""" - if args: - command = args.pop() - cmd = _COMMANDS[command] - for optname, optdict in cmd.options: - print('--help') - print('--' + optname) - else: - commands = sorted(_COMMANDS.keys()) - for command in commands: - cmd = _COMMANDS[command] - if not cmd.hidden: - print(command) - - -# deprecated stuff ############################################################# - -_COMMANDS = CommandLine() - -DEFAULT_COPYRIGHT = '''\ -Copyright (c) 2004-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -http://www.logilab.fr/ -- mailto:contact@logilab.fr''' - -@deprecated('use cls.register(cli)') -def register_commands(commands): - """register existing commands""" - for command_klass in commands: - _COMMANDS.register(command_klass) - -@deprecated('use args.pop(0)') -def main_run(args, doc=None, copyright=None, version=None): - """command line tool: run command specified by argument list (without the - program name). Raise SystemExit with status 0 if everything went fine. - - >>> main_run(sys.argv[1:]) - """ - _COMMANDS.doc = doc - _COMMANDS.copyright = copyright - _COMMANDS.version = version - _COMMANDS.run(args) - -@deprecated('use args.pop(0)') -def pop_arg(args_list, expected_size_after=None, msg="Missing argument"): - """helper function to get and check command line arguments""" - try: - value = args_list.pop(0) - except IndexError: - raise BadCommandUsage(msg) - if expected_size_after is not None and len(args_list) > expected_size_after: - raise BadCommandUsage('too many arguments') - return value - diff --git a/pymode/libs/logilab/common/compat.py b/pymode/libs/logilab/common/compat.py deleted file mode 100644 index f2eb5905..00000000 --- a/pymode/libs/logilab/common/compat.py +++ /dev/null @@ -1,78 +0,0 @@ -# pylint: disable=E0601,W0622,W0611 -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Wrappers around some builtins introduced in python 2.3, 2.4 and -2.5, making them available in for earlier versions of python. - -See another compatibility snippets from other projects: - - :mod:`lib2to3.fixes` - :mod:`coverage.backward` - :mod:`unittest2.compatibility` -""" - - -__docformat__ = "restructuredtext en" - -import os -import sys -import types -from warnings import warn - -# not used here, but imported to preserve API -from six.moves import builtins - -if sys.version_info < (3, 0): - str_to_bytes = str - def str_encode(string, encoding): - if isinstance(string, unicode): - return string.encode(encoding) - return str(string) -else: - def str_to_bytes(string): - return str.encode(string) - # we have to ignore the encoding in py3k to be able to write a string into a - # TextIOWrapper or like object (which expect an unicode string) - def str_encode(string, encoding): - return str(string) - -# See also http://bugs.python.org/issue11776 -if sys.version_info[0] == 3: - def method_type(callable, instance, klass): - # api change. klass is no more considered - return types.MethodType(callable, instance) -else: - # alias types otherwise - method_type = types.MethodType - -# Pythons 2 and 3 differ on where to get StringIO -if sys.version_info < (3, 0): - from cStringIO import StringIO - FileIO = file - BytesIO = StringIO - reload = reload -else: - from io import FileIO, BytesIO, StringIO - from imp import reload - -from logilab.common.deprecation import deprecated - -# Other projects import these from here, keep providing them for -# backwards compat -any = deprecated('use builtin "any"')(any) -all = deprecated('use builtin "all"')(all) diff --git a/pymode/libs/logilab/common/configuration.py b/pymode/libs/logilab/common/configuration.py deleted file mode 100644 index b2924277..00000000 --- a/pymode/libs/logilab/common/configuration.py +++ /dev/null @@ -1,1105 +0,0 @@ -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Classes to handle advanced configuration in simple to complex applications. - -Allows to load the configuration from a file or from command line -options, to generate a sample configuration file or to display -program's usage. Fills the gap between optik/optparse and ConfigParser -by adding data types (which are also available as a standalone optik -extension in the `optik_ext` module). - - -Quick start: simplest usage ---------------------------- - -.. python :: - - >>> import sys - >>> from logilab.common.configuration import Configuration - >>> options = [('dothis', {'type':'yn', 'default': True, 'metavar': ''}), - ... ('value', {'type': 'string', 'metavar': ''}), - ... ('multiple', {'type': 'csv', 'default': ('yop',), - ... 'metavar': '', - ... 'help': 'you can also document the option'}), - ... ('number', {'type': 'int', 'default':2, 'metavar':''}), - ... ] - >>> config = Configuration(options=options, name='My config') - >>> print config['dothis'] - True - >>> print config['value'] - None - >>> print config['multiple'] - ('yop',) - >>> print config['number'] - 2 - >>> print config.help() - Usage: [options] - - Options: - -h, --help show this help message and exit - --dothis= - --value= - --multiple= - you can also document the option [current: none] - --number= - - >>> f = open('myconfig.ini', 'w') - >>> f.write('''[MY CONFIG] - ... number = 3 - ... dothis = no - ... multiple = 1,2,3 - ... ''') - >>> f.close() - >>> config.load_file_configuration('myconfig.ini') - >>> print config['dothis'] - False - >>> print config['value'] - None - >>> print config['multiple'] - ['1', '2', '3'] - >>> print config['number'] - 3 - >>> sys.argv = ['mon prog', '--value', 'bacon', '--multiple', '4,5,6', - ... 'nonoptionargument'] - >>> print config.load_command_line_configuration() - ['nonoptionargument'] - >>> print config['value'] - bacon - >>> config.generate_config() - # class for simple configurations which don't need the - # manager / providers model and prefer delegation to inheritance - # - # configuration values are accessible through a dict like interface - # - [MY CONFIG] - - dothis=no - - value=bacon - - # you can also document the option - multiple=4,5,6 - - number=3 - - Note : starting with Python 2.7 ConfigParser is able to take into - account the order of occurrences of the options into a file (by - using an OrderedDict). If you have two options changing some common - state, like a 'disable-all-stuff' and a 'enable-some-stuff-a', their - order of appearance will be significant : the last specified in the - file wins. For earlier version of python and logilab.common newer - than 0.61 the behaviour is unspecified. - -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -__all__ = ('OptionsManagerMixIn', 'OptionsProviderMixIn', - 'ConfigurationMixIn', 'Configuration', - 'OptionsManager2ConfigurationAdapter') - -import os -import sys -import re -from os.path import exists, expanduser -from copy import copy -from warnings import warn - -from six import string_types -from six.moves import range, configparser as cp, input - -from logilab.common.compat import str_encode as _encode -from logilab.common.deprecation import deprecated -from logilab.common.textutils import normalize_text, unquote -from logilab.common import optik_ext - -OptionError = optik_ext.OptionError - -REQUIRED = [] - -class UnsupportedAction(Exception): - """raised by set_option when it doesn't know what to do for an action""" - - -def _get_encoding(encoding, stream): - encoding = encoding or getattr(stream, 'encoding', None) - if not encoding: - import locale - encoding = locale.getpreferredencoding() - return encoding - - -# validation functions ######################################################## - -# validators will return the validated value or raise optparse.OptionValueError -# XXX add to documentation - -def choice_validator(optdict, name, value): - """validate and return a converted value for option of type 'choice' - """ - if not value in optdict['choices']: - msg = "option %s: invalid value: %r, should be in %s" - raise optik_ext.OptionValueError(msg % (name, value, optdict['choices'])) - return value - -def multiple_choice_validator(optdict, name, value): - """validate and return a converted value for option of type 'choice' - """ - choices = optdict['choices'] - values = optik_ext.check_csv(None, name, value) - for value in values: - if not value in choices: - msg = "option %s: invalid value: %r, should be in %s" - raise optik_ext.OptionValueError(msg % (name, value, choices)) - return values - -def csv_validator(optdict, name, value): - """validate and return a converted value for option of type 'csv' - """ - return optik_ext.check_csv(None, name, value) - -def yn_validator(optdict, name, value): - """validate and return a converted value for option of type 'yn' - """ - return optik_ext.check_yn(None, name, value) - -def named_validator(optdict, name, value): - """validate and return a converted value for option of type 'named' - """ - return optik_ext.check_named(None, name, value) - -def file_validator(optdict, name, value): - """validate and return a filepath for option of type 'file'""" - return optik_ext.check_file(None, name, value) - -def color_validator(optdict, name, value): - """validate and return a valid color for option of type 'color'""" - return optik_ext.check_color(None, name, value) - -def password_validator(optdict, name, value): - """validate and return a string for option of type 'password'""" - return optik_ext.check_password(None, name, value) - -def date_validator(optdict, name, value): - """validate and return a mx DateTime object for option of type 'date'""" - return optik_ext.check_date(None, name, value) - -def time_validator(optdict, name, value): - """validate and return a time object for option of type 'time'""" - return optik_ext.check_time(None, name, value) - -def bytes_validator(optdict, name, value): - """validate and return an integer for option of type 'bytes'""" - return optik_ext.check_bytes(None, name, value) - - -VALIDATORS = {'string': unquote, - 'int': int, - 'float': float, - 'file': file_validator, - 'font': unquote, - 'color': color_validator, - 'regexp': re.compile, - 'csv': csv_validator, - 'yn': yn_validator, - 'bool': yn_validator, - 'named': named_validator, - 'password': password_validator, - 'date': date_validator, - 'time': time_validator, - 'bytes': bytes_validator, - 'choice': choice_validator, - 'multiple_choice': multiple_choice_validator, - } - -def _call_validator(opttype, optdict, option, value): - if opttype not in VALIDATORS: - raise Exception('Unsupported type "%s"' % opttype) - try: - return VALIDATORS[opttype](optdict, option, value) - except TypeError: - try: - return VALIDATORS[opttype](value) - except optik_ext.OptionValueError: - raise - except: - raise optik_ext.OptionValueError('%s value (%r) should be of type %s' % - (option, value, opttype)) - -# user input functions ######################################################## - -# user input functions will ask the user for input on stdin then validate -# the result and return the validated value or raise optparse.OptionValueError -# XXX add to documentation - -def input_password(optdict, question='password:'): - from getpass import getpass - while True: - value = getpass(question) - value2 = getpass('confirm: ') - if value == value2: - return value - print('password mismatch, try again') - -def input_string(optdict, question): - value = input(question).strip() - return value or None - -def _make_input_function(opttype): - def input_validator(optdict, question): - while True: - value = input(question) - if not value.strip(): - return None - try: - return _call_validator(opttype, optdict, None, value) - except optik_ext.OptionValueError as ex: - msg = str(ex).split(':', 1)[-1].strip() - print('bad value: %s' % msg) - return input_validator - -INPUT_FUNCTIONS = { - 'string': input_string, - 'password': input_password, - } - -for opttype in VALIDATORS.keys(): - INPUT_FUNCTIONS.setdefault(opttype, _make_input_function(opttype)) - -# utility functions ############################################################ - -def expand_default(self, option): - """monkey patch OptionParser.expand_default since we have a particular - way to handle defaults to avoid overriding values in the configuration - file - """ - if self.parser is None or not self.default_tag: - return option.help - optname = option._long_opts[0][2:] - try: - provider = self.parser.options_manager._all_options[optname] - except KeyError: - value = None - else: - optdict = provider.get_option_def(optname) - optname = provider.option_attrname(optname, optdict) - value = getattr(provider.config, optname, optdict) - value = format_option_value(optdict, value) - if value is optik_ext.NO_DEFAULT or not value: - value = self.NO_DEFAULT_VALUE - return option.help.replace(self.default_tag, str(value)) - - -def _validate(value, optdict, name=''): - """return a validated value for an option according to its type - - optional argument name is only used for error message formatting - """ - try: - _type = optdict['type'] - except KeyError: - # FIXME - return value - return _call_validator(_type, optdict, name, value) -convert = deprecated('[0.60] convert() was renamed _validate()')(_validate) - -# format and output functions ################################################## - -def comment(string): - """return string as a comment""" - lines = [line.strip() for line in string.splitlines()] - return '# ' + ('%s# ' % os.linesep).join(lines) - -def format_time(value): - if not value: - return '0' - if value != int(value): - return '%.2fs' % value - value = int(value) - nbmin, nbsec = divmod(value, 60) - if nbsec: - return '%ss' % value - nbhour, nbmin_ = divmod(nbmin, 60) - if nbmin_: - return '%smin' % nbmin - nbday, nbhour_ = divmod(nbhour, 24) - if nbhour_: - return '%sh' % nbhour - return '%sd' % nbday - -def format_bytes(value): - if not value: - return '0' - if value != int(value): - return '%.2fB' % value - value = int(value) - prevunit = 'B' - for unit in ('KB', 'MB', 'GB', 'TB'): - next, remain = divmod(value, 1024) - if remain: - return '%s%s' % (value, prevunit) - prevunit = unit - value = next - return '%s%s' % (value, unit) - -def format_option_value(optdict, value): - """return the user input's value from a 'compiled' value""" - if isinstance(value, (list, tuple)): - value = ','.join(value) - elif isinstance(value, dict): - value = ','.join(['%s:%s' % (k, v) for k, v in value.items()]) - elif hasattr(value, 'match'): # optdict.get('type') == 'regexp' - # compiled regexp - value = value.pattern - elif optdict.get('type') == 'yn': - value = value and 'yes' or 'no' - elif isinstance(value, string_types) and value.isspace(): - value = "'%s'" % value - elif optdict.get('type') == 'time' and isinstance(value, (float, int, long)): - value = format_time(value) - elif optdict.get('type') == 'bytes' and hasattr(value, '__int__'): - value = format_bytes(value) - return value - -def ini_format_section(stream, section, options, encoding=None, doc=None): - """format an options section using the INI format""" - encoding = _get_encoding(encoding, stream) - if doc: - print(_encode(comment(doc), encoding), file=stream) - print('[%s]' % section, file=stream) - ini_format(stream, options, encoding) - -def ini_format(stream, options, encoding): - """format options using the INI format""" - for optname, optdict, value in options: - value = format_option_value(optdict, value) - help = optdict.get('help') - if help: - help = normalize_text(help, line_len=79, indent='# ') - print(file=stream) - print(_encode(help, encoding), file=stream) - else: - print(file=stream) - if value is None: - print('#%s=' % optname, file=stream) - else: - value = _encode(value, encoding).strip() - print('%s=%s' % (optname, value), file=stream) - -format_section = ini_format_section - -def rest_format_section(stream, section, options, encoding=None, doc=None): - """format an options section using as ReST formatted output""" - encoding = _get_encoding(encoding, stream) - if section: - print('%s\n%s' % (section, "'"*len(section)), file=stream) - if doc: - print(_encode(normalize_text(doc, line_len=79, indent=''), encoding), file=stream) - print(file=stream) - for optname, optdict, value in options: - help = optdict.get('help') - print(':%s:' % optname, file=stream) - if help: - help = normalize_text(help, line_len=79, indent=' ') - print(_encode(help, encoding), file=stream) - if value: - value = _encode(format_option_value(optdict, value), encoding) - print(file=stream) - print(' Default: ``%s``' % value.replace("`` ", "```` ``"), file=stream) - -# Options Manager ############################################################## - -class OptionsManagerMixIn(object): - """MixIn to handle a configuration from both a configuration file and - command line options - """ - - def __init__(self, usage, config_file=None, version=None, quiet=0): - self.config_file = config_file - self.reset_parsers(usage, version=version) - # list of registered options providers - self.options_providers = [] - # dictionary associating option name to checker - self._all_options = {} - self._short_options = {} - self._nocallback_options = {} - self._mygroups = dict() - # verbosity - self.quiet = quiet - self._maxlevel = 0 - - def reset_parsers(self, usage='', version=None): - # configuration file parser - self.cfgfile_parser = cp.ConfigParser() - # command line parser - self.cmdline_parser = optik_ext.OptionParser(usage=usage, version=version) - self.cmdline_parser.options_manager = self - self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS) - - def register_options_provider(self, provider, own_group=True): - """register an options provider""" - assert provider.priority <= 0, "provider's priority can't be >= 0" - for i in range(len(self.options_providers)): - if provider.priority > self.options_providers[i].priority: - self.options_providers.insert(i, provider) - break - else: - self.options_providers.append(provider) - non_group_spec_options = [option for option in provider.options - if 'group' not in option[1]] - groups = getattr(provider, 'option_groups', ()) - if own_group and non_group_spec_options: - self.add_option_group(provider.name.upper(), provider.__doc__, - non_group_spec_options, provider) - else: - for opt, optdict in non_group_spec_options: - self.add_optik_option(provider, self.cmdline_parser, opt, optdict) - for gname, gdoc in groups: - gname = gname.upper() - goptions = [option for option in provider.options - if option[1].get('group', '').upper() == gname] - self.add_option_group(gname, gdoc, goptions, provider) - - def add_option_group(self, group_name, doc, options, provider): - """add an option group including the listed options - """ - assert options - # add option group to the command line parser - if group_name in self._mygroups: - group = self._mygroups[group_name] - else: - group = optik_ext.OptionGroup(self.cmdline_parser, - title=group_name.capitalize()) - self.cmdline_parser.add_option_group(group) - group.level = provider.level - self._mygroups[group_name] = group - # add section to the config file - if group_name != "DEFAULT": - self.cfgfile_parser.add_section(group_name) - # add provider's specific options - for opt, optdict in options: - self.add_optik_option(provider, group, opt, optdict) - - def add_optik_option(self, provider, optikcontainer, opt, optdict): - if 'inputlevel' in optdict: - warn('[0.50] "inputlevel" in option dictionary for %s is deprecated,' - ' use "level"' % opt, DeprecationWarning) - optdict['level'] = optdict.pop('inputlevel') - args, optdict = self.optik_option(provider, opt, optdict) - option = optikcontainer.add_option(*args, **optdict) - self._all_options[opt] = provider - self._maxlevel = max(self._maxlevel, option.level or 0) - - def optik_option(self, provider, opt, optdict): - """get our personal option definition and return a suitable form for - use with optik/optparse - """ - optdict = copy(optdict) - others = {} - if 'action' in optdict: - self._nocallback_options[provider] = opt - else: - optdict['action'] = 'callback' - optdict['callback'] = self.cb_set_provider_option - # default is handled here and *must not* be given to optik if you - # want the whole machinery to work - if 'default' in optdict: - if ('help' in optdict - and optdict.get('default') is not None - and not optdict['action'] in ('store_true', 'store_false')): - optdict['help'] += ' [current: %default]' - del optdict['default'] - args = ['--' + str(opt)] - if 'short' in optdict: - self._short_options[optdict['short']] = opt - args.append('-' + optdict['short']) - del optdict['short'] - # cleanup option definition dict before giving it to optik - for key in list(optdict.keys()): - if not key in self._optik_option_attrs: - optdict.pop(key) - return args, optdict - - def cb_set_provider_option(self, option, opt, value, parser): - """optik callback for option setting""" - if opt.startswith('--'): - # remove -- on long option - opt = opt[2:] - else: - # short option, get its long equivalent - opt = self._short_options[opt[1:]] - # trick since we can't set action='store_true' on options - if value is None: - value = 1 - self.global_set_option(opt, value) - - def global_set_option(self, opt, value): - """set option on the correct option provider""" - self._all_options[opt].set_option(opt, value) - - def generate_config(self, stream=None, skipsections=(), encoding=None): - """write a configuration file according to the current configuration - into the given stream or stdout - """ - options_by_section = {} - sections = [] - for provider in self.options_providers: - for section, options in provider.options_by_section(): - if section is None: - section = provider.name - if section in skipsections: - continue - options = [(n, d, v) for (n, d, v) in options - if d.get('type') is not None] - if not options: - continue - if not section in sections: - sections.append(section) - alloptions = options_by_section.setdefault(section, []) - alloptions += options - stream = stream or sys.stdout - encoding = _get_encoding(encoding, stream) - printed = False - for section in sections: - if printed: - print('\n', file=stream) - format_section(stream, section.upper(), options_by_section[section], - encoding) - printed = True - - def generate_manpage(self, pkginfo, section=1, stream=None): - """write a man page for the current configuration into the given - stream or stdout - """ - self._monkeypatch_expand_default() - try: - optik_ext.generate_manpage(self.cmdline_parser, pkginfo, - section, stream=stream or sys.stdout, - level=self._maxlevel) - finally: - self._unmonkeypatch_expand_default() - - # initialization methods ################################################## - - def load_provider_defaults(self): - """initialize configuration using default values""" - for provider in self.options_providers: - provider.load_defaults() - - def load_file_configuration(self, config_file=None): - """load the configuration from file""" - self.read_config_file(config_file) - self.load_config_file() - - def read_config_file(self, config_file=None): - """read the configuration file but do not load it (i.e. dispatching - values to each options provider) - """ - helplevel = 1 - while helplevel <= self._maxlevel: - opt = '-'.join(['long'] * helplevel) + '-help' - if opt in self._all_options: - break # already processed - def helpfunc(option, opt, val, p, level=helplevel): - print(self.help(level)) - sys.exit(0) - helpmsg = '%s verbose help.' % ' '.join(['more'] * helplevel) - optdict = {'action' : 'callback', 'callback' : helpfunc, - 'help' : helpmsg} - provider = self.options_providers[0] - self.add_optik_option(provider, self.cmdline_parser, opt, optdict) - provider.options += ( (opt, optdict), ) - helplevel += 1 - if config_file is None: - config_file = self.config_file - if config_file is not None: - config_file = expanduser(config_file) - if config_file and exists(config_file): - parser = self.cfgfile_parser - parser.read([config_file]) - # normalize sections'title - for sect, values in parser._sections.items(): - if not sect.isupper() and values: - parser._sections[sect.upper()] = values - elif not self.quiet: - msg = 'No config file found, using default configuration' - print(msg, file=sys.stderr) - return - - def input_config(self, onlysection=None, inputlevel=0, stream=None): - """interactively get configuration values by asking to the user and generate - a configuration file - """ - if onlysection is not None: - onlysection = onlysection.upper() - for provider in self.options_providers: - for section, option, optdict in provider.all_options(): - if onlysection is not None and section != onlysection: - continue - if not 'type' in optdict: - # ignore action without type (callback, store_true...) - continue - provider.input_option(option, optdict, inputlevel) - # now we can generate the configuration file - if stream is not None: - self.generate_config(stream) - - def load_config_file(self): - """dispatch values previously read from a configuration file to each - options provider) - """ - parser = self.cfgfile_parser - for section in parser.sections(): - for option, value in parser.items(section): - try: - self.global_set_option(option, value) - except (KeyError, OptionError): - # TODO handle here undeclared options appearing in the config file - continue - - def load_configuration(self, **kwargs): - """override configuration according to given parameters - """ - for opt, opt_value in kwargs.items(): - opt = opt.replace('_', '-') - provider = self._all_options[opt] - provider.set_option(opt, opt_value) - - def load_command_line_configuration(self, args=None): - """override configuration according to command line parameters - - return additional arguments - """ - self._monkeypatch_expand_default() - try: - if args is None: - args = sys.argv[1:] - else: - args = list(args) - (options, args) = self.cmdline_parser.parse_args(args=args) - for provider in self._nocallback_options.keys(): - config = provider.config - for attr in config.__dict__.keys(): - value = getattr(options, attr, None) - if value is None: - continue - setattr(config, attr, value) - return args - finally: - self._unmonkeypatch_expand_default() - - - # help methods ############################################################ - - def add_help_section(self, title, description, level=0): - """add a dummy option section for help purpose """ - group = optik_ext.OptionGroup(self.cmdline_parser, - title=title.capitalize(), - description=description) - group.level = level - self._maxlevel = max(self._maxlevel, level) - self.cmdline_parser.add_option_group(group) - - def _monkeypatch_expand_default(self): - # monkey patch optik_ext to deal with our default values - try: - self.__expand_default_backup = optik_ext.HelpFormatter.expand_default - optik_ext.HelpFormatter.expand_default = expand_default - except AttributeError: - # python < 2.4: nothing to be done - pass - def _unmonkeypatch_expand_default(self): - # remove monkey patch - if hasattr(optik_ext.HelpFormatter, 'expand_default'): - # unpatch optik_ext to avoid side effects - optik_ext.HelpFormatter.expand_default = self.__expand_default_backup - - def help(self, level=0): - """return the usage string for available options """ - self.cmdline_parser.formatter.output_level = level - self._monkeypatch_expand_default() - try: - return self.cmdline_parser.format_help() - finally: - self._unmonkeypatch_expand_default() - - -class Method(object): - """used to ease late binding of default method (so you can define options - on the class using default methods on the configuration instance) - """ - def __init__(self, methname): - self.method = methname - self._inst = None - - def bind(self, instance): - """bind the method to its instance""" - if self._inst is None: - self._inst = instance - - def __call__(self, *args, **kwargs): - assert self._inst, 'unbound method' - return getattr(self._inst, self.method)(*args, **kwargs) - -# Options Provider ############################################################# - -class OptionsProviderMixIn(object): - """Mixin to provide options to an OptionsManager""" - - # those attributes should be overridden - priority = -1 - name = 'default' - options = () - level = 0 - - def __init__(self): - self.config = optik_ext.Values() - for option in self.options: - try: - option, optdict = option - except ValueError: - raise Exception('Bad option: %r' % option) - if isinstance(optdict.get('default'), Method): - optdict['default'].bind(self) - elif isinstance(optdict.get('callback'), Method): - optdict['callback'].bind(self) - self.load_defaults() - - def load_defaults(self): - """initialize the provider using default values""" - for opt, optdict in self.options: - action = optdict.get('action') - if action != 'callback': - # callback action have no default - default = self.option_default(opt, optdict) - if default is REQUIRED: - continue - self.set_option(opt, default, action, optdict) - - def option_default(self, opt, optdict=None): - """return the default value for an option""" - if optdict is None: - optdict = self.get_option_def(opt) - default = optdict.get('default') - if callable(default): - default = default() - return default - - def option_attrname(self, opt, optdict=None): - """get the config attribute corresponding to opt - """ - if optdict is None: - optdict = self.get_option_def(opt) - return optdict.get('dest', opt.replace('-', '_')) - option_name = deprecated('[0.60] OptionsProviderMixIn.option_name() was renamed to option_attrname()')(option_attrname) - - def option_value(self, opt): - """get the current value for the given option""" - return getattr(self.config, self.option_attrname(opt), None) - - def set_option(self, opt, value, action=None, optdict=None): - """method called to set an option (registered in the options list) - """ - if optdict is None: - optdict = self.get_option_def(opt) - if value is not None: - value = _validate(value, optdict, opt) - if action is None: - action = optdict.get('action', 'store') - if optdict.get('type') == 'named': # XXX need specific handling - optname = self.option_attrname(opt, optdict) - currentvalue = getattr(self.config, optname, None) - if currentvalue: - currentvalue.update(value) - value = currentvalue - if action == 'store': - setattr(self.config, self.option_attrname(opt, optdict), value) - elif action in ('store_true', 'count'): - setattr(self.config, self.option_attrname(opt, optdict), 0) - elif action == 'store_false': - setattr(self.config, self.option_attrname(opt, optdict), 1) - elif action == 'append': - opt = self.option_attrname(opt, optdict) - _list = getattr(self.config, opt, None) - if _list is None: - if isinstance(value, (list, tuple)): - _list = value - elif value is not None: - _list = [] - _list.append(value) - setattr(self.config, opt, _list) - elif isinstance(_list, tuple): - setattr(self.config, opt, _list + (value,)) - else: - _list.append(value) - elif action == 'callback': - optdict['callback'](None, opt, value, None) - else: - raise UnsupportedAction(action) - - def input_option(self, option, optdict, inputlevel=99): - default = self.option_default(option, optdict) - if default is REQUIRED: - defaultstr = '(required): ' - elif optdict.get('level', 0) > inputlevel: - return - elif optdict['type'] == 'password' or default is None: - defaultstr = ': ' - else: - defaultstr = '(default: %s): ' % format_option_value(optdict, default) - print(':%s:' % option) - print(optdict.get('help') or option) - inputfunc = INPUT_FUNCTIONS[optdict['type']] - value = inputfunc(optdict, defaultstr) - while default is REQUIRED and not value: - print('please specify a value') - value = inputfunc(optdict, '%s: ' % option) - if value is None and default is not None: - value = default - self.set_option(option, value, optdict=optdict) - - def get_option_def(self, opt): - """return the dictionary defining an option given it's name""" - assert self.options - for option in self.options: - if option[0] == opt: - return option[1] - raise OptionError('no such option %s in section %r' - % (opt, self.name), opt) - - - def all_options(self): - """return an iterator on available options for this provider - option are actually described by a 3-uple: - (section, option name, option dictionary) - """ - for section, options in self.options_by_section(): - if section is None: - if self.name is None: - continue - section = self.name.upper() - for option, optiondict, value in options: - yield section, option, optiondict - - def options_by_section(self): - """return an iterator on options grouped by section - - (section, [list of (optname, optdict, optvalue)]) - """ - sections = {} - for optname, optdict in self.options: - sections.setdefault(optdict.get('group'), []).append( - (optname, optdict, self.option_value(optname))) - if None in sections: - yield None, sections.pop(None) - for section, options in sections.items(): - yield section.upper(), options - - def options_and_values(self, options=None): - if options is None: - options = self.options - for optname, optdict in options: - yield (optname, optdict, self.option_value(optname)) - -# configuration ################################################################ - -class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn): - """basic mixin for simple configurations which don't need the - manager / providers model - """ - def __init__(self, *args, **kwargs): - if not args: - kwargs.setdefault('usage', '') - kwargs.setdefault('quiet', 1) - OptionsManagerMixIn.__init__(self, *args, **kwargs) - OptionsProviderMixIn.__init__(self) - if not getattr(self, 'option_groups', None): - self.option_groups = [] - for option, optdict in self.options: - try: - gdef = (optdict['group'].upper(), '') - except KeyError: - continue - if not gdef in self.option_groups: - self.option_groups.append(gdef) - self.register_options_provider(self, own_group=False) - - def register_options(self, options): - """add some options to the configuration""" - options_by_group = {} - for optname, optdict in options: - options_by_group.setdefault(optdict.get('group', self.name.upper()), []).append((optname, optdict)) - for group, options in options_by_group.items(): - self.add_option_group(group, None, options, self) - self.options += tuple(options) - - def load_defaults(self): - OptionsProviderMixIn.load_defaults(self) - - def __iter__(self): - return iter(self.config.__dict__.iteritems()) - - def __getitem__(self, key): - try: - return getattr(self.config, self.option_attrname(key)) - except (optik_ext.OptionValueError, AttributeError): - raise KeyError(key) - - def __setitem__(self, key, value): - self.set_option(key, value) - - def get(self, key, default=None): - try: - return getattr(self.config, self.option_attrname(key)) - except (OptionError, AttributeError): - return default - - -class Configuration(ConfigurationMixIn): - """class for simple configurations which don't need the - manager / providers model and prefer delegation to inheritance - - configuration values are accessible through a dict like interface - """ - - def __init__(self, config_file=None, options=None, name=None, - usage=None, doc=None, version=None): - if options is not None: - self.options = options - if name is not None: - self.name = name - if doc is not None: - self.__doc__ = doc - super(Configuration, self).__init__(config_file=config_file, usage=usage, version=version) - - -class OptionsManager2ConfigurationAdapter(object): - """Adapt an option manager to behave like a - `logilab.common.configuration.Configuration` instance - """ - def __init__(self, provider): - self.config = provider - - def __getattr__(self, key): - return getattr(self.config, key) - - def __getitem__(self, key): - provider = self.config._all_options[key] - try: - return getattr(provider.config, provider.option_attrname(key)) - except AttributeError: - raise KeyError(key) - - def __setitem__(self, key, value): - self.config.global_set_option(self.config.option_attrname(key), value) - - def get(self, key, default=None): - provider = self.config._all_options[key] - try: - return getattr(provider.config, provider.option_attrname(key)) - except AttributeError: - return default - -# other functions ############################################################## - -def read_old_config(newconfig, changes, configfile): - """initialize newconfig from a deprecated configuration file - - possible changes: - * ('renamed', oldname, newname) - * ('moved', option, oldgroup, newgroup) - * ('typechanged', option, oldtype, newvalue) - """ - # build an index of changes - changesindex = {} - for action in changes: - if action[0] == 'moved': - option, oldgroup, newgroup = action[1:] - changesindex.setdefault(option, []).append((action[0], oldgroup, newgroup)) - continue - if action[0] == 'renamed': - oldname, newname = action[1:] - changesindex.setdefault(newname, []).append((action[0], oldname)) - continue - if action[0] == 'typechanged': - option, oldtype, newvalue = action[1:] - changesindex.setdefault(option, []).append((action[0], oldtype, newvalue)) - continue - if action[1] in ('added', 'removed'): - continue # nothing to do here - raise Exception('unknown change %s' % action[0]) - # build a config object able to read the old config - options = [] - for optname, optdef in newconfig.options: - for action in changesindex.pop(optname, ()): - if action[0] == 'moved': - oldgroup, newgroup = action[1:] - optdef = optdef.copy() - optdef['group'] = oldgroup - elif action[0] == 'renamed': - optname = action[1] - elif action[0] == 'typechanged': - oldtype = action[1] - optdef = optdef.copy() - optdef['type'] = oldtype - options.append((optname, optdef)) - if changesindex: - raise Exception('unapplied changes: %s' % changesindex) - oldconfig = Configuration(options=options, name=newconfig.name) - # read the old config - oldconfig.load_file_configuration(configfile) - # apply values reverting changes - changes.reverse() - done = set() - for action in changes: - if action[0] == 'renamed': - oldname, newname = action[1:] - newconfig[newname] = oldconfig[oldname] - done.add(newname) - elif action[0] == 'typechanged': - optname, oldtype, newvalue = action[1:] - newconfig[optname] = newvalue - done.add(optname) - for optname, optdef in newconfig.options: - if optdef.get('type') and not optname in done: - newconfig.set_option(optname, oldconfig[optname], optdict=optdef) - - -def merge_options(options, optgroup=None): - """preprocess a list of options and remove duplicates, returning a new list - (tuple actually) of options. - - Options dictionaries are copied to avoid later side-effect. Also, if - `otpgroup` argument is specified, ensure all options are in the given group. - """ - alloptions = {} - options = list(options) - for i in range(len(options)-1, -1, -1): - optname, optdict = options[i] - if optname in alloptions: - options.pop(i) - alloptions[optname].update(optdict) - else: - optdict = optdict.copy() - options[i] = (optname, optdict) - alloptions[optname] = optdict - if optgroup is not None: - alloptions[optname]['group'] = optgroup - return tuple(options) diff --git a/pymode/libs/logilab/common/daemon.py b/pymode/libs/logilab/common/daemon.py deleted file mode 100644 index 40319a43..00000000 --- a/pymode/libs/logilab/common/daemon.py +++ /dev/null @@ -1,101 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""A daemonize function (for Unices)""" - -__docformat__ = "restructuredtext en" - -import os -import errno -import signal -import sys -import time -import warnings - -from six.moves import range - -def setugid(user): - """Change process user and group ID - - Argument is a numeric user id or a user name""" - try: - from pwd import getpwuid - passwd = getpwuid(int(user)) - except ValueError: - from pwd import getpwnam - passwd = getpwnam(user) - - if hasattr(os, 'initgroups'): # python >= 2.7 - os.initgroups(passwd.pw_name, passwd.pw_gid) - else: - import ctypes - if ctypes.CDLL(None).initgroups(passwd.pw_name, passwd.pw_gid) < 0: - err = ctypes.c_int.in_dll(ctypes.pythonapi,"errno").value - raise OSError(err, os.strerror(err), 'initgroups') - os.setgid(passwd.pw_gid) - os.setuid(passwd.pw_uid) - os.environ['HOME'] = passwd.pw_dir - - -def daemonize(pidfile=None, uid=None, umask=0o77): - """daemonize a Unix process. Set paranoid umask by default. - - Return 1 in the original process, 2 in the first fork, and None for the - second fork (eg daemon process). - """ - # http://www.faqs.org/faqs/unix-faq/programmer/faq/ - # - # fork so the parent can exit - if os.fork(): # launch child and... - return 1 - # disconnect from tty and create a new session - os.setsid() - # fork again so the parent, (the session group leader), can exit. - # as a non-session group leader, we can never regain a controlling - # terminal. - if os.fork(): # launch child again. - return 2 - # move to the root to avoit mount pb - os.chdir('/') - # redirect standard descriptors - null = os.open('/dev/null', os.O_RDWR) - for i in range(3): - try: - os.dup2(null, i) - except OSError as e: - if e.errno != errno.EBADF: - raise - os.close(null) - # filter warnings - warnings.filterwarnings('ignore') - # write pid in a file - if pidfile: - # ensure the directory where the pid-file should be set exists (for - # instance /var/run/cubicweb may be deleted on computer restart) - piddir = os.path.dirname(pidfile) - if not os.path.exists(piddir): - os.makedirs(piddir) - f = file(pidfile, 'w') - f.write(str(os.getpid())) - f.close() - # set umask if specified - if umask is not None: - os.umask(umask) - # change process uid - if uid: - setugid(uid) - return None diff --git a/pymode/libs/logilab/common/date.py b/pymode/libs/logilab/common/date.py deleted file mode 100644 index a093a8a9..00000000 --- a/pymode/libs/logilab/common/date.py +++ /dev/null @@ -1,335 +0,0 @@ -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Date manipulation helper functions.""" -from __future__ import division - -__docformat__ = "restructuredtext en" - -import math -import re -import sys -from locale import getlocale, LC_TIME -from datetime import date, time, datetime, timedelta -from time import strptime as time_strptime -from calendar import monthrange, timegm - -from six.moves import range - -try: - from mx.DateTime import RelativeDateTime, Date, DateTimeType -except ImportError: - endOfMonth = None - DateTimeType = datetime -else: - endOfMonth = RelativeDateTime(months=1, day=-1) - -# NOTE: should we implement a compatibility layer between date representations -# as we have in lgc.db ? - -FRENCH_FIXED_HOLIDAYS = { - 'jour_an': '%s-01-01', - 'fete_travail': '%s-05-01', - 'armistice1945': '%s-05-08', - 'fete_nat': '%s-07-14', - 'assomption': '%s-08-15', - 'toussaint': '%s-11-01', - 'armistice1918': '%s-11-11', - 'noel': '%s-12-25', - } - -FRENCH_MOBILE_HOLIDAYS = { - 'paques2004': '2004-04-12', - 'ascension2004': '2004-05-20', - 'pentecote2004': '2004-05-31', - - 'paques2005': '2005-03-28', - 'ascension2005': '2005-05-05', - 'pentecote2005': '2005-05-16', - - 'paques2006': '2006-04-17', - 'ascension2006': '2006-05-25', - 'pentecote2006': '2006-06-05', - - 'paques2007': '2007-04-09', - 'ascension2007': '2007-05-17', - 'pentecote2007': '2007-05-28', - - 'paques2008': '2008-03-24', - 'ascension2008': '2008-05-01', - 'pentecote2008': '2008-05-12', - - 'paques2009': '2009-04-13', - 'ascension2009': '2009-05-21', - 'pentecote2009': '2009-06-01', - - 'paques2010': '2010-04-05', - 'ascension2010': '2010-05-13', - 'pentecote2010': '2010-05-24', - - 'paques2011': '2011-04-25', - 'ascension2011': '2011-06-02', - 'pentecote2011': '2011-06-13', - - 'paques2012': '2012-04-09', - 'ascension2012': '2012-05-17', - 'pentecote2012': '2012-05-28', - } - -# XXX this implementation cries for multimethod dispatching - -def get_step(dateobj, nbdays=1): - # assume date is either a python datetime or a mx.DateTime object - if isinstance(dateobj, date): - return ONEDAY * nbdays - return nbdays # mx.DateTime is ok with integers - -def datefactory(year, month, day, sampledate): - # assume date is either a python datetime or a mx.DateTime object - if isinstance(sampledate, datetime): - return datetime(year, month, day) - if isinstance(sampledate, date): - return date(year, month, day) - return Date(year, month, day) - -def weekday(dateobj): - # assume date is either a python datetime or a mx.DateTime object - if isinstance(dateobj, date): - return dateobj.weekday() - return dateobj.day_of_week - -def str2date(datestr, sampledate): - # NOTE: datetime.strptime is not an option until we drop py2.4 compat - year, month, day = [int(chunk) for chunk in datestr.split('-')] - return datefactory(year, month, day, sampledate) - -def days_between(start, end): - if isinstance(start, date): - delta = end - start - # datetime.timedelta.days is always an integer (floored) - if delta.seconds: - return delta.days + 1 - return delta.days - else: - return int(math.ceil((end - start).days)) - -def get_national_holidays(begin, end): - """return french national days off between begin and end""" - begin = datefactory(begin.year, begin.month, begin.day, begin) - end = datefactory(end.year, end.month, end.day, end) - holidays = [str2date(datestr, begin) - for datestr in FRENCH_MOBILE_HOLIDAYS.values()] - for year in range(begin.year, end.year+1): - for datestr in FRENCH_FIXED_HOLIDAYS.values(): - date = str2date(datestr % year, begin) - if date not in holidays: - holidays.append(date) - return [day for day in holidays if begin <= day < end] - -def add_days_worked(start, days): - """adds date but try to only take days worked into account""" - step = get_step(start) - weeks, plus = divmod(days, 5) - end = start + ((weeks * 7) + plus) * step - if weekday(end) >= 5: # saturday or sunday - end += (2 * step) - end += len([x for x in get_national_holidays(start, end + step) - if weekday(x) < 5]) * step - if weekday(end) >= 5: # saturday or sunday - end += (2 * step) - return end - -def nb_open_days(start, end): - assert start <= end - step = get_step(start) - days = days_between(start, end) - weeks, plus = divmod(days, 7) - if weekday(start) > weekday(end): - plus -= 2 - elif weekday(end) == 6: - plus -= 1 - open_days = weeks * 5 + plus - nb_week_holidays = len([x for x in get_national_holidays(start, end+step) - if weekday(x) < 5 and x < end]) - open_days -= nb_week_holidays - if open_days < 0: - return 0 - return open_days - -def date_range(begin, end, incday=None, incmonth=None): - """yields each date between begin and end - - :param begin: the start date - :param end: the end date - :param incr: the step to use to iterate over dates. Default is - one day. - :param include: None (means no exclusion) or a function taking a - date as parameter, and returning True if the date - should be included. - - When using mx datetime, you should *NOT* use incmonth argument, use instead - oneDay, oneHour, oneMinute, oneSecond, oneWeek or endOfMonth (to enumerate - months) as `incday` argument - """ - assert not (incday and incmonth) - begin = todate(begin) - end = todate(end) - if incmonth: - while begin < end: - yield begin - begin = next_month(begin, incmonth) - else: - incr = get_step(begin, incday or 1) - while begin < end: - yield begin - begin += incr - -# makes py datetime usable ##################################################### - -ONEDAY = timedelta(days=1) -ONEWEEK = timedelta(days=7) - -try: - strptime = datetime.strptime -except AttributeError: # py < 2.5 - from time import strptime as time_strptime - def strptime(value, format): - return datetime(*time_strptime(value, format)[:6]) - -def strptime_time(value, format='%H:%M'): - return time(*time_strptime(value, format)[3:6]) - -def todate(somedate): - """return a date from a date (leaving unchanged) or a datetime""" - if isinstance(somedate, datetime): - return date(somedate.year, somedate.month, somedate.day) - assert isinstance(somedate, (date, DateTimeType)), repr(somedate) - return somedate - -def totime(somedate): - """return a time from a time (leaving unchanged), date or datetime""" - # XXX mx compat - if not isinstance(somedate, time): - return time(somedate.hour, somedate.minute, somedate.second) - assert isinstance(somedate, (time)), repr(somedate) - return somedate - -def todatetime(somedate): - """return a date from a date (leaving unchanged) or a datetime""" - # take care, datetime is a subclass of date - if isinstance(somedate, datetime): - return somedate - assert isinstance(somedate, (date, DateTimeType)), repr(somedate) - return datetime(somedate.year, somedate.month, somedate.day) - -def datetime2ticks(somedate): - return timegm(somedate.timetuple()) * 1000 - -def ticks2datetime(ticks): - miliseconds, microseconds = divmod(ticks, 1000) - try: - return datetime.fromtimestamp(miliseconds) - except (ValueError, OverflowError): - epoch = datetime.fromtimestamp(0) - nb_days, seconds = divmod(int(miliseconds), 86400) - delta = timedelta(nb_days, seconds=seconds, microseconds=microseconds) - try: - return epoch + delta - except (ValueError, OverflowError): - raise - -def days_in_month(somedate): - return monthrange(somedate.year, somedate.month)[1] - -def days_in_year(somedate): - feb = date(somedate.year, 2, 1) - if days_in_month(feb) == 29: - return 366 - else: - return 365 - -def previous_month(somedate, nbmonth=1): - while nbmonth: - somedate = first_day(somedate) - ONEDAY - nbmonth -= 1 - return somedate - -def next_month(somedate, nbmonth=1): - while nbmonth: - somedate = last_day(somedate) + ONEDAY - nbmonth -= 1 - return somedate - -def first_day(somedate): - return date(somedate.year, somedate.month, 1) - -def last_day(somedate): - return date(somedate.year, somedate.month, days_in_month(somedate)) - -def ustrftime(somedate, fmt='%Y-%m-%d'): - """like strftime, but returns a unicode string instead of an encoded - string which may be problematic with localized date. - """ - if sys.version_info >= (3, 3): - # datetime.date.strftime() supports dates since year 1 in Python >=3.3. - return somedate.strftime(fmt) - else: - try: - if sys.version_info < (3, 0): - encoding = getlocale(LC_TIME)[1] or 'ascii' - return unicode(somedate.strftime(str(fmt)), encoding) - else: - return somedate.strftime(fmt) - except ValueError: - if somedate.year >= 1900: - raise - # datetime is not happy with dates before 1900 - # we try to work around this, assuming a simple - # format string - fields = {'Y': somedate.year, - 'm': somedate.month, - 'd': somedate.day, - } - if isinstance(somedate, datetime): - fields.update({'H': somedate.hour, - 'M': somedate.minute, - 'S': somedate.second}) - fmt = re.sub('%([YmdHMS])', r'%(\1)02d', fmt) - return unicode(fmt) % fields - -def utcdatetime(dt): - if dt.tzinfo is None: - return dt - return (dt.replace(tzinfo=None) - dt.utcoffset()) - -def utctime(dt): - if dt.tzinfo is None: - return dt - return (dt + dt.utcoffset() + dt.dst()).replace(tzinfo=None) - -def datetime_to_seconds(date): - """return the number of seconds since the begining of the day for that date - """ - return date.second+60*date.minute + 3600*date.hour - -def timedelta_to_days(delta): - """return the time delta as a number of seconds""" - return delta.days + delta.seconds / (3600*24) - -def timedelta_to_seconds(delta): - """return the time delta as a fraction of days""" - return delta.days*(3600*24) + delta.seconds diff --git a/pymode/libs/logilab/common/debugger.py b/pymode/libs/logilab/common/debugger.py deleted file mode 100644 index 1f540a18..00000000 --- a/pymode/libs/logilab/common/debugger.py +++ /dev/null @@ -1,214 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Customized version of pdb's default debugger. - -- sets up a history file -- uses ipython if available to colorize lines of code -- overrides list command to search for current block instead - of using 5 lines of context - - - - -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -try: - import readline -except ImportError: - readline = None -import os -import os.path as osp -import sys -from pdb import Pdb -import inspect - -from logilab.common.compat import StringIO - -try: - from IPython import PyColorize -except ImportError: - def colorize(source, *args): - """fallback colorize function""" - return source - def colorize_source(source, *args): - return source -else: - def colorize(source, start_lineno, curlineno): - """colorize and annotate source with linenos - (as in pdb's list command) - """ - parser = PyColorize.Parser() - output = StringIO() - parser.format(source, output) - annotated = [] - for index, line in enumerate(output.getvalue().splitlines()): - lineno = index + start_lineno - if lineno == curlineno: - annotated.append('%4s\t->\t%s' % (lineno, line)) - else: - annotated.append('%4s\t\t%s' % (lineno, line)) - return '\n'.join(annotated) - - def colorize_source(source): - """colorize given source""" - parser = PyColorize.Parser() - output = StringIO() - parser.format(source, output) - return output.getvalue() - - -def getsource(obj): - """Return the text of the source code for an object. - - The argument may be a module, class, method, function, traceback, frame, - or code object. The source code is returned as a single string. An - IOError is raised if the source code cannot be retrieved.""" - lines, lnum = inspect.getsourcelines(obj) - return ''.join(lines), lnum - - -################################################################ -class Debugger(Pdb): - """custom debugger - - - sets up a history file - - uses ipython if available to colorize lines of code - - overrides list command to search for current block instead - of using 5 lines of context - """ - def __init__(self, tcbk=None): - Pdb.__init__(self) - self.reset() - if tcbk: - while tcbk.tb_next is not None: - tcbk = tcbk.tb_next - self._tcbk = tcbk - self._histfile = os.path.expanduser("~/.pdbhist") - - def setup_history_file(self): - """if readline is available, read pdb history file - """ - if readline is not None: - try: - # XXX try..except shouldn't be necessary - # read_history_file() can accept None - readline.read_history_file(self._histfile) - except IOError: - pass - - def start(self): - """starts the interactive mode""" - self.interaction(self._tcbk.tb_frame, self._tcbk) - - def setup(self, frame, tcbk): - """setup hook: set up history file""" - self.setup_history_file() - Pdb.setup(self, frame, tcbk) - - def set_quit(self): - """quit hook: save commands in the history file""" - if readline is not None: - readline.write_history_file(self._histfile) - Pdb.set_quit(self) - - def complete_p(self, text, line, begin_idx, end_idx): - """provide variable names completion for the ``p`` command""" - namespace = dict(self.curframe.f_globals) - namespace.update(self.curframe.f_locals) - if '.' in text: - return self.attr_matches(text, namespace) - return [varname for varname in namespace if varname.startswith(text)] - - - def attr_matches(self, text, namespace): - """implementation coming from rlcompleter.Completer.attr_matches - Compute matches when text contains a dot. - - Assuming the text is of the form NAME.NAME....[NAME], and is - evaluatable in self.namespace, it will be evaluated and its attributes - (as revealed by dir()) are used as possible completions. (For class - instances, class members are also considered.) - - WARNING: this can still invoke arbitrary C code, if an object - with a __getattr__ hook is evaluated. - - """ - import re - m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) - if not m: - return - expr, attr = m.group(1, 3) - object = eval(expr, namespace) - words = dir(object) - if hasattr(object, '__class__'): - words.append('__class__') - words = words + self.get_class_members(object.__class__) - matches = [] - n = len(attr) - for word in words: - if word[:n] == attr and word != "__builtins__": - matches.append("%s.%s" % (expr, word)) - return matches - - def get_class_members(self, klass): - """implementation coming from rlcompleter.get_class_members""" - ret = dir(klass) - if hasattr(klass, '__bases__'): - for base in klass.__bases__: - ret = ret + self.get_class_members(base) - return ret - - ## specific / overridden commands - def do_list(self, arg): - """overrides default list command to display the surrounding block - instead of 5 lines of context - """ - self.lastcmd = 'list' - if not arg: - try: - source, start_lineno = getsource(self.curframe) - print(colorize(''.join(source), start_lineno, - self.curframe.f_lineno)) - except KeyboardInterrupt: - pass - except IOError: - Pdb.do_list(self, arg) - else: - Pdb.do_list(self, arg) - do_l = do_list - - def do_open(self, arg): - """opens source file corresponding to the current stack level""" - filename = self.curframe.f_code.co_filename - lineno = self.curframe.f_lineno - cmd = 'emacsclient --no-wait +%s %s' % (lineno, filename) - os.system(cmd) - - do_o = do_open - -def pm(): - """use our custom debugger""" - dbg = Debugger(sys.last_traceback) - dbg.start() - -def set_trace(): - Debugger().set_trace(sys._getframe().f_back) diff --git a/pymode/libs/logilab/common/decorators.py b/pymode/libs/logilab/common/decorators.py deleted file mode 100644 index beafa202..00000000 --- a/pymode/libs/logilab/common/decorators.py +++ /dev/null @@ -1,281 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -""" A few useful function/method decorators. """ - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import types -from time import clock, time -from inspect import isgeneratorfunction, getargspec - -from logilab.common.compat import method_type - -# XXX rewrite so we can use the decorator syntax when keyarg has to be specified - -class cached_decorator(object): - def __init__(self, cacheattr=None, keyarg=None): - self.cacheattr = cacheattr - self.keyarg = keyarg - def __call__(self, callableobj=None): - assert not isgeneratorfunction(callableobj), \ - 'cannot cache generator function: %s' % callableobj - if len(getargspec(callableobj).args) == 1 or self.keyarg == 0: - cache = _SingleValueCache(callableobj, self.cacheattr) - elif self.keyarg: - cache = _MultiValuesKeyArgCache(callableobj, self.keyarg, self.cacheattr) - else: - cache = _MultiValuesCache(callableobj, self.cacheattr) - return cache.closure() - -class _SingleValueCache(object): - def __init__(self, callableobj, cacheattr=None): - self.callable = callableobj - if cacheattr is None: - self.cacheattr = '_%s_cache_' % callableobj.__name__ - else: - assert cacheattr != callableobj.__name__ - self.cacheattr = cacheattr - - def __call__(__me, self, *args): - try: - return self.__dict__[__me.cacheattr] - except KeyError: - value = __me.callable(self, *args) - setattr(self, __me.cacheattr, value) - return value - - def closure(self): - def wrapped(*args, **kwargs): - return self.__call__(*args, **kwargs) - wrapped.cache_obj = self - try: - wrapped.__doc__ = self.callable.__doc__ - wrapped.__name__ = self.callable.__name__ - except: - pass - return wrapped - - def clear(self, holder): - holder.__dict__.pop(self.cacheattr, None) - - -class _MultiValuesCache(_SingleValueCache): - def _get_cache(self, holder): - try: - _cache = holder.__dict__[self.cacheattr] - except KeyError: - _cache = {} - setattr(holder, self.cacheattr, _cache) - return _cache - - def __call__(__me, self, *args, **kwargs): - _cache = __me._get_cache(self) - try: - return _cache[args] - except KeyError: - _cache[args] = __me.callable(self, *args) - return _cache[args] - -class _MultiValuesKeyArgCache(_MultiValuesCache): - def __init__(self, callableobj, keyarg, cacheattr=None): - super(_MultiValuesKeyArgCache, self).__init__(callableobj, cacheattr) - self.keyarg = keyarg - - def __call__(__me, self, *args, **kwargs): - _cache = __me._get_cache(self) - key = args[__me.keyarg-1] - try: - return _cache[key] - except KeyError: - _cache[key] = __me.callable(self, *args, **kwargs) - return _cache[key] - - -def cached(callableobj=None, keyarg=None, **kwargs): - """Simple decorator to cache result of method call.""" - kwargs['keyarg'] = keyarg - decorator = cached_decorator(**kwargs) - if callableobj is None: - return decorator - else: - return decorator(callableobj) - - -class cachedproperty(object): - """ Provides a cached property equivalent to the stacking of - @cached and @property, but more efficient. - - After first usage, the becomes part of the object's - __dict__. Doing: - - del obj. empties the cache. - - Idea taken from the pyramid_ framework and the mercurial_ project. - - .. _pyramid: http://pypi.python.org/pypi/pyramid - .. _mercurial: http://pypi.python.org/pypi/Mercurial - """ - __slots__ = ('wrapped',) - - def __init__(self, wrapped): - try: - wrapped.__name__ - except AttributeError: - raise TypeError('%s must have a __name__ attribute' % - wrapped) - self.wrapped = wrapped - - @property - def __doc__(self): - doc = getattr(self.wrapped, '__doc__', None) - return ('%s' - % ('\n%s' % doc if doc else '')) - - def __get__(self, inst, objtype=None): - if inst is None: - return self - val = self.wrapped(inst) - setattr(inst, self.wrapped.__name__, val) - return val - - -def get_cache_impl(obj, funcname): - cls = obj.__class__ - member = getattr(cls, funcname) - if isinstance(member, property): - member = member.fget - return member.cache_obj - -def clear_cache(obj, funcname): - """Clear a cache handled by the :func:`cached` decorator. If 'x' class has - @cached on its method `foo`, type - - >>> clear_cache(x, 'foo') - - to purge this method's cache on the instance. - """ - get_cache_impl(obj, funcname).clear(obj) - -def copy_cache(obj, funcname, cacheobj): - """Copy cache for from cacheobj to obj.""" - cacheattr = get_cache_impl(obj, funcname).cacheattr - try: - setattr(obj, cacheattr, cacheobj.__dict__[cacheattr]) - except KeyError: - pass - - -class wproperty(object): - """Simple descriptor expecting to take a modifier function as first argument - and looking for a _ to retrieve the attribute. - """ - def __init__(self, setfunc): - self.setfunc = setfunc - self.attrname = '_%s' % setfunc.__name__ - - def __set__(self, obj, value): - self.setfunc(obj, value) - - def __get__(self, obj, cls): - assert obj is not None - return getattr(obj, self.attrname) - - -class classproperty(object): - """this is a simple property-like class but for class attributes. - """ - def __init__(self, get): - self.get = get - def __get__(self, inst, cls): - return self.get(cls) - - -class iclassmethod(object): - '''Descriptor for method which should be available as class method if called - on the class or instance method if called on an instance. - ''' - def __init__(self, func): - self.func = func - def __get__(self, instance, objtype): - if instance is None: - return method_type(self.func, objtype, objtype.__class__) - return method_type(self.func, instance, objtype) - def __set__(self, instance, value): - raise AttributeError("can't set attribute") - - -def timed(f): - def wrap(*args, **kwargs): - t = time() - c = clock() - res = f(*args, **kwargs) - print('%s clock: %.9f / time: %.9f' % (f.__name__, - clock() - c, time() - t)) - return res - return wrap - - -def locked(acquire, release): - """Decorator taking two methods to acquire/release a lock as argument, - returning a decorator function which will call the inner method after - having called acquire(self) et will call release(self) afterwards. - """ - def decorator(f): - def wrapper(self, *args, **kwargs): - acquire(self) - try: - return f(self, *args, **kwargs) - finally: - release(self) - return wrapper - return decorator - - -def monkeypatch(klass, methodname=None): - """Decorator extending class with the decorated callable. This is basically - a syntactic sugar vs class assignment. - - >>> class A: - ... pass - >>> @monkeypatch(A) - ... def meth(self): - ... return 12 - ... - >>> a = A() - >>> a.meth() - 12 - >>> @monkeypatch(A, 'foo') - ... def meth(self): - ... return 12 - ... - >>> a.foo() - 12 - """ - def decorator(func): - try: - name = methodname or func.__name__ - except AttributeError: - raise AttributeError('%s has no __name__ attribute: ' - 'you should provide an explicit `methodname`' - % func) - setattr(klass, name, func) - return func - return decorator diff --git a/pymode/libs/logilab/common/deprecation.py b/pymode/libs/logilab/common/deprecation.py deleted file mode 100644 index 1c81b638..00000000 --- a/pymode/libs/logilab/common/deprecation.py +++ /dev/null @@ -1,189 +0,0 @@ -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Deprecation utilities.""" - -__docformat__ = "restructuredtext en" - -import sys -from warnings import warn - -from logilab.common.changelog import Version - - -class DeprecationWrapper(object): - """proxy to print a warning on access to any attribute of the wrapped object - """ - def __init__(self, proxied, msg=None): - self._proxied = proxied - self._msg = msg - - def __getattr__(self, attr): - warn(self._msg, DeprecationWarning, stacklevel=2) - return getattr(self._proxied, attr) - - def __setattr__(self, attr, value): - if attr in ('_proxied', '_msg'): - self.__dict__[attr] = value - else: - warn(self._msg, DeprecationWarning, stacklevel=2) - setattr(self._proxied, attr, value) - - -class DeprecationManager(object): - """Manage the deprecation message handling. Messages are dropped for - versions more recent than the 'compatible' version. Example:: - - deprecator = deprecation.DeprecationManager("module_name") - deprecator.compatibility('1.3') - - deprecator.warn('1.2', "message.") - - @deprecator.deprecated('1.2', 'Message') - def any_func(): - pass - - class AnyClass(object): - __metaclass__ = deprecator.class_deprecated('1.2') - """ - def __init__(self, module_name=None): - """ - """ - self.module_name = module_name - self.compatible_version = None - - def compatibility(self, compatible_version): - """Set the compatible version. - """ - self.compatible_version = Version(compatible_version) - - def deprecated(self, version=None, reason=None, stacklevel=2, name=None, doc=None): - """Display a deprecation message only if the version is older than the - compatible version. - """ - def decorator(func): - message = reason or 'The function "%s" is deprecated' - if '%s' in message: - message %= func.__name__ - def wrapped(*args, **kwargs): - self.warn(version, message, stacklevel+1) - return func(*args, **kwargs) - return wrapped - return decorator - - def class_deprecated(self, version=None): - class metaclass(type): - """metaclass to print a warning on instantiation of a deprecated class""" - - def __call__(cls, *args, **kwargs): - msg = getattr(cls, "__deprecation_warning__", - "%(cls)s is deprecated") % {'cls': cls.__name__} - self.warn(version, msg, stacklevel=3) - return type.__call__(cls, *args, **kwargs) - return metaclass - - def moved(self, version, modpath, objname): - """use to tell that a callable has been moved to a new module. - - It returns a callable wrapper, so that when its called a warning is printed - telling where the object can be found, import is done (and not before) and - the actual object is called. - - NOTE: the usage is somewhat limited on classes since it will fail if the - wrapper is use in a class ancestors list, use the `class_moved` function - instead (which has no lazy import feature though). - """ - def callnew(*args, **kwargs): - from logilab.common.modutils import load_module_from_name - message = "object %s has been moved to module %s" % (objname, modpath) - self.warn(version, message) - m = load_module_from_name(modpath) - return getattr(m, objname)(*args, **kwargs) - return callnew - - def class_renamed(self, version, old_name, new_class, message=None): - clsdict = {} - if message is None: - message = '%s is deprecated, use %s' % (old_name, new_class.__name__) - clsdict['__deprecation_warning__'] = message - try: - # new-style class - return self.class_deprecated(version)(old_name, (new_class,), clsdict) - except (NameError, TypeError): - # old-style class - warn = self.warn - class DeprecatedClass(new_class): - """FIXME: There might be a better way to handle old/new-style class - """ - def __init__(self, *args, **kwargs): - warn(version, message, stacklevel=3) - new_class.__init__(self, *args, **kwargs) - return DeprecatedClass - - def class_moved(self, version, new_class, old_name=None, message=None): - """nice wrapper around class_renamed when a class has been moved into - another module - """ - if old_name is None: - old_name = new_class.__name__ - if message is None: - message = 'class %s is now available as %s.%s' % ( - old_name, new_class.__module__, new_class.__name__) - return self.class_renamed(version, old_name, new_class, message) - - def warn(self, version=None, reason="", stacklevel=2): - """Display a deprecation message only if the version is older than the - compatible version. - """ - if (self.compatible_version is None - or version is None - or Version(version) < self.compatible_version): - if self.module_name and version: - reason = '[%s %s] %s' % (self.module_name, version, reason) - elif self.module_name: - reason = '[%s] %s' % (self.module_name, reason) - elif version: - reason = '[%s] %s' % (version, reason) - warn(reason, DeprecationWarning, stacklevel=stacklevel) - -_defaultdeprecator = DeprecationManager() - -def deprecated(reason=None, stacklevel=2, name=None, doc=None): - return _defaultdeprecator.deprecated(None, reason, stacklevel, name, doc) - -class_deprecated = _defaultdeprecator.class_deprecated() - -def moved(modpath, objname): - return _defaultdeprecator.moved(None, modpath, objname) -moved.__doc__ = _defaultdeprecator.moved.__doc__ - -def class_renamed(old_name, new_class, message=None): - """automatically creates a class which fires a DeprecationWarning - when instantiated. - - >>> Set = class_renamed('Set', set, 'Set is now replaced by set') - >>> s = Set() - sample.py:57: DeprecationWarning: Set is now replaced by set - s = Set() - >>> - """ - return _defaultdeprecator.class_renamed(None, old_name, new_class, message) - -def class_moved(new_class, old_name=None, message=None): - return _defaultdeprecator.class_moved(None, new_class, old_name, message) -class_moved.__doc__ = _defaultdeprecator.class_moved.__doc__ - diff --git a/pymode/libs/logilab/common/fileutils.py b/pymode/libs/logilab/common/fileutils.py deleted file mode 100644 index b30cf5f8..00000000 --- a/pymode/libs/logilab/common/fileutils.py +++ /dev/null @@ -1,404 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""File and file-path manipulation utilities. - -:group path manipulation: first_level_directory, relative_path, is_binary,\ -get_by_ext, remove_dead_links -:group file manipulation: norm_read, norm_open, lines, stream_lines, lines,\ -write_open_mode, ensure_fs_mode, export -:sort: path manipulation, file manipulation -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import shutil -import mimetypes -from os.path import isabs, isdir, islink, split, exists, normpath, join -from os.path import abspath -from os import sep, mkdir, remove, listdir, stat, chmod, walk -from stat import ST_MODE, S_IWRITE - -from logilab.common import STD_BLACKLIST as BASE_BLACKLIST, IGNORED_EXTENSIONS -from logilab.common.shellutils import find -from logilab.common.deprecation import deprecated -from logilab.common.compat import FileIO - -def first_level_directory(path): - """Return the first level directory of a path. - - >>> first_level_directory('home/syt/work') - 'home' - >>> first_level_directory('/home/syt/work') - '/' - >>> first_level_directory('work') - 'work' - >>> - - :type path: str - :param path: the path for which we want the first level directory - - :rtype: str - :return: the first level directory appearing in `path` - """ - head, tail = split(path) - while head and tail: - head, tail = split(head) - if tail: - return tail - # path was absolute, head is the fs root - return head - -def abspath_listdir(path): - """Lists path's content using absolute paths. - - >>> os.listdir('/home') - ['adim', 'alf', 'arthur', 'auc'] - >>> abspath_listdir('/home') - ['/home/adim', '/home/alf', '/home/arthur', '/home/auc'] - """ - path = abspath(path) - return [join(path, filename) for filename in listdir(path)] - - -def is_binary(filename): - """Return true if filename may be a binary file, according to it's - extension. - - :type filename: str - :param filename: the name of the file - - :rtype: bool - :return: - true if the file is a binary file (actually if it's mime type - isn't beginning by text/) - """ - try: - return not mimetypes.guess_type(filename)[0].startswith('text') - except AttributeError: - return 1 - - -def write_open_mode(filename): - """Return the write mode that should used to open file. - - :type filename: str - :param filename: the name of the file - - :rtype: str - :return: the mode that should be use to open the file ('w' or 'wb') - """ - if is_binary(filename): - return 'wb' - return 'w' - - -def ensure_fs_mode(filepath, desired_mode=S_IWRITE): - """Check that the given file has the given mode(s) set, else try to - set it. - - :type filepath: str - :param filepath: path of the file - - :type desired_mode: int - :param desired_mode: - ORed flags describing the desired mode. Use constants from the - `stat` module for file permission's modes - """ - mode = stat(filepath)[ST_MODE] - if not mode & desired_mode: - chmod(filepath, mode | desired_mode) - - -# XXX (syt) unused? kill? -class ProtectedFile(FileIO): - """A special file-object class that automatically does a 'chmod +w' when - needed. - - XXX: for now, the way it is done allows 'normal file-objects' to be - created during the ProtectedFile object lifetime. - One way to circumvent this would be to chmod / unchmod on each - write operation. - - One other way would be to : - - - catch the IOError in the __init__ - - - if IOError, then create a StringIO object - - - each write operation writes in this StringIO object - - - on close()/del(), write/append the StringIO content to the file and - do the chmod only once - """ - def __init__(self, filepath, mode): - self.original_mode = stat(filepath)[ST_MODE] - self.mode_changed = False - if mode in ('w', 'a', 'wb', 'ab'): - if not self.original_mode & S_IWRITE: - chmod(filepath, self.original_mode | S_IWRITE) - self.mode_changed = True - FileIO.__init__(self, filepath, mode) - - def _restore_mode(self): - """restores the original mode if needed""" - if self.mode_changed: - chmod(self.name, self.original_mode) - # Don't re-chmod in case of several restore - self.mode_changed = False - - def close(self): - """restore mode before closing""" - self._restore_mode() - FileIO.close(self) - - def __del__(self): - if not self.closed: - self.close() - - -class UnresolvableError(Exception): - """Exception raised by relative path when it's unable to compute relative - path between two paths. - """ - -def relative_path(from_file, to_file): - """Try to get a relative path from `from_file` to `to_file` - (path will be absolute if to_file is an absolute file). This function - is useful to create link in `from_file` to `to_file`. This typical use - case is used in this function description. - - If both files are relative, they're expected to be relative to the same - directory. - - >>> relative_path( from_file='toto/index.html', to_file='index.html') - '../index.html' - >>> relative_path( from_file='index.html', to_file='toto/index.html') - 'toto/index.html' - >>> relative_path( from_file='tutu/index.html', to_file='toto/index.html') - '../toto/index.html' - >>> relative_path( from_file='toto/index.html', to_file='/index.html') - '/index.html' - >>> relative_path( from_file='/toto/index.html', to_file='/index.html') - '../index.html' - >>> relative_path( from_file='/toto/index.html', to_file='/toto/summary.html') - 'summary.html' - >>> relative_path( from_file='index.html', to_file='index.html') - '' - >>> relative_path( from_file='/index.html', to_file='toto/index.html') - Traceback (most recent call last): - File "", line 1, in ? - File "", line 37, in relative_path - UnresolvableError - >>> relative_path( from_file='/index.html', to_file='/index.html') - '' - >>> - - :type from_file: str - :param from_file: source file (where links will be inserted) - - :type to_file: str - :param to_file: target file (on which links point) - - :raise UnresolvableError: if it has been unable to guess a correct path - - :rtype: str - :return: the relative path of `to_file` from `from_file` - """ - from_file = normpath(from_file) - to_file = normpath(to_file) - if from_file == to_file: - return '' - if isabs(to_file): - if not isabs(from_file): - return to_file - elif isabs(from_file): - raise UnresolvableError() - from_parts = from_file.split(sep) - to_parts = to_file.split(sep) - idem = 1 - result = [] - while len(from_parts) > 1: - dirname = from_parts.pop(0) - if idem and len(to_parts) > 1 and dirname == to_parts[0]: - to_parts.pop(0) - else: - idem = 0 - result.append('..') - result += to_parts - return sep.join(result) - - -def norm_read(path): - """Return the content of the file with normalized line feeds. - - :type path: str - :param path: path to the file to read - - :rtype: str - :return: the content of the file with normalized line feeds - """ - return open(path, 'U').read() -norm_read = deprecated("use \"open(path, 'U').read()\"")(norm_read) - -def norm_open(path): - """Return a stream for a file with content with normalized line feeds. - - :type path: str - :param path: path to the file to open - - :rtype: file or StringIO - :return: the opened file with normalized line feeds - """ - return open(path, 'U') -norm_open = deprecated("use \"open(path, 'U')\"")(norm_open) - -def lines(path, comments=None): - """Return a list of non empty lines in the file located at `path`. - - :type path: str - :param path: path to the file - - :type comments: str or None - :param comments: - optional string which can be used to comment a line in the file - (i.e. lines starting with this string won't be returned) - - :rtype: list - :return: - a list of stripped line in the file, without empty and commented - lines - - :warning: at some point this function will probably return an iterator - """ - stream = open(path, 'U') - result = stream_lines(stream, comments) - stream.close() - return result - - -def stream_lines(stream, comments=None): - """Return a list of non empty lines in the given `stream`. - - :type stream: object implementing 'xreadlines' or 'readlines' - :param stream: file like object - - :type comments: str or None - :param comments: - optional string which can be used to comment a line in the file - (i.e. lines starting with this string won't be returned) - - :rtype: list - :return: - a list of stripped line in the file, without empty and commented - lines - - :warning: at some point this function will probably return an iterator - """ - try: - readlines = stream.xreadlines - except AttributeError: - readlines = stream.readlines - result = [] - for line in readlines(): - line = line.strip() - if line and (comments is None or not line.startswith(comments)): - result.append(line) - return result - - -def export(from_dir, to_dir, - blacklist=BASE_BLACKLIST, ignore_ext=IGNORED_EXTENSIONS, - verbose=0): - """Make a mirror of `from_dir` in `to_dir`, omitting directories and - files listed in the black list or ending with one of the given - extensions. - - :type from_dir: str - :param from_dir: directory to export - - :type to_dir: str - :param to_dir: destination directory - - :type blacklist: list or tuple - :param blacklist: - list of files or directories to ignore, default to the content of - `BASE_BLACKLIST` - - :type ignore_ext: list or tuple - :param ignore_ext: - list of extensions to ignore, default to the content of - `IGNORED_EXTENSIONS` - - :type verbose: bool - :param verbose: - flag indicating whether information about exported files should be - printed to stderr, default to False - """ - try: - mkdir(to_dir) - except OSError: - pass # FIXME we should use "exists" if the point is about existing dir - # else (permission problems?) shouldn't return / raise ? - for directory, dirnames, filenames in walk(from_dir): - for norecurs in blacklist: - try: - dirnames.remove(norecurs) - except ValueError: - continue - for dirname in dirnames: - src = join(directory, dirname) - dest = to_dir + src[len(from_dir):] - if isdir(src): - if not exists(dest): - mkdir(dest) - for filename in filenames: - # don't include binary files - # endswith does not accept tuple in 2.4 - if any([filename.endswith(ext) for ext in ignore_ext]): - continue - src = join(directory, filename) - dest = to_dir + src[len(from_dir):] - if verbose: - print(src, '->', dest, file=sys.stderr) - if exists(dest): - remove(dest) - shutil.copy2(src, dest) - - -def remove_dead_links(directory, verbose=0): - """Recursively traverse directory and remove all dead links. - - :type directory: str - :param directory: directory to cleanup - - :type verbose: bool - :param verbose: - flag indicating whether information about deleted links should be - printed to stderr, default to False - """ - for dirpath, dirname, filenames in walk(directory): - for filename in dirnames + filenames: - src = join(dirpath, filename) - if islink(src) and not exists(src): - if verbose: - print('remove dead link', src) - remove(src) - diff --git a/pymode/libs/logilab/common/graph.py b/pymode/libs/logilab/common/graph.py deleted file mode 100644 index cef1c984..00000000 --- a/pymode/libs/logilab/common/graph.py +++ /dev/null @@ -1,282 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Graph manipulation utilities. - -(dot generation adapted from pypy/translator/tool/make_dot.py) -""" - -__docformat__ = "restructuredtext en" - -__metaclass__ = type - -import os.path as osp -import os -import sys -import tempfile -import codecs -import errno - -def escape(value): - """Make usable in a dot file.""" - lines = [line.replace('"', '\\"') for line in value.split('\n')] - data = '\\l'.join(lines) - return '\\n' + data - -def target_info_from_filename(filename): - """Transforms /some/path/foo.png into ('/some/path', 'foo.png', 'png').""" - basename = osp.basename(filename) - storedir = osp.dirname(osp.abspath(filename)) - target = filename.split('.')[-1] - return storedir, basename, target - - -class DotBackend: - """Dot File backend.""" - def __init__(self, graphname, rankdir=None, size=None, ratio=None, - charset='utf-8', renderer='dot', additionnal_param={}): - self.graphname = graphname - self.renderer = renderer - self.lines = [] - self._source = None - self.emit("digraph %s {" % normalize_node_id(graphname)) - if rankdir: - self.emit('rankdir=%s' % rankdir) - if ratio: - self.emit('ratio=%s' % ratio) - if size: - self.emit('size="%s"' % size) - if charset: - assert charset.lower() in ('utf-8', 'iso-8859-1', 'latin1'), \ - 'unsupported charset %s' % charset - self.emit('charset="%s"' % charset) - for param in sorted(additionnal_param.items()): - self.emit('='.join(param)) - - def get_source(self): - """returns self._source""" - if self._source is None: - self.emit("}\n") - self._source = '\n'.join(self.lines) - del self.lines - return self._source - - source = property(get_source) - - def generate(self, outputfile=None, dotfile=None, mapfile=None): - """Generates a graph file. - - :param outputfile: filename and path [defaults to graphname.png] - :param dotfile: filename and path [defaults to graphname.dot] - - :rtype: str - :return: a path to the generated file - """ - import subprocess # introduced in py 2.4 - name = self.graphname - if not dotfile: - # if 'outputfile' is a dot file use it as 'dotfile' - if outputfile and outputfile.endswith(".dot"): - dotfile = outputfile - else: - dotfile = '%s.dot' % name - if outputfile is not None: - storedir, basename, target = target_info_from_filename(outputfile) - if target != "dot": - pdot, dot_sourcepath = tempfile.mkstemp(".dot", name) - os.close(pdot) - else: - dot_sourcepath = osp.join(storedir, dotfile) - else: - target = 'png' - pdot, dot_sourcepath = tempfile.mkstemp(".dot", name) - ppng, outputfile = tempfile.mkstemp(".png", name) - os.close(pdot) - os.close(ppng) - pdot = codecs.open(dot_sourcepath, 'w', encoding='utf8') - pdot.write(self.source) - pdot.close() - if target != 'dot': - if sys.platform == 'win32': - use_shell = True - else: - use_shell = False - try: - if mapfile: - subprocess.call([self.renderer, '-Tcmapx', '-o', mapfile, '-T', target, dot_sourcepath, '-o', outputfile], - shell=use_shell) - else: - subprocess.call([self.renderer, '-T', target, - dot_sourcepath, '-o', outputfile], - shell=use_shell) - except OSError as e: - if e.errno == errno.ENOENT: - e.strerror = 'File not found: {0}'.format(self.renderer) - raise - os.unlink(dot_sourcepath) - return outputfile - - def emit(self, line): - """Adds to final output.""" - self.lines.append(line) - - def emit_edge(self, name1, name2, **props): - """emit an edge from to . - edge properties: see http://www.graphviz.org/doc/info/attrs.html - """ - attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] - n_from, n_to = normalize_node_id(name1), normalize_node_id(name2) - self.emit('%s -> %s [%s];' % (n_from, n_to, ', '.join(sorted(attrs))) ) - - def emit_node(self, name, **props): - """emit a node with given properties. - node properties: see http://www.graphviz.org/doc/info/attrs.html - """ - attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] - self.emit('%s [%s];' % (normalize_node_id(name), ', '.join(sorted(attrs)))) - -def normalize_node_id(nid): - """Returns a suitable DOT node id for `nid`.""" - return '"%s"' % nid - -class GraphGenerator: - def __init__(self, backend): - # the backend is responsible to output the graph in a particular format - self.backend = backend - - # XXX doesn't like space in outpufile / mapfile - def generate(self, visitor, propshdlr, outputfile=None, mapfile=None): - # the visitor - # the property handler is used to get node and edge properties - # according to the graph and to the backend - self.propshdlr = propshdlr - for nodeid, node in visitor.nodes(): - props = propshdlr.node_properties(node) - self.backend.emit_node(nodeid, **props) - for subjnode, objnode, edge in visitor.edges(): - props = propshdlr.edge_properties(edge, subjnode, objnode) - self.backend.emit_edge(subjnode, objnode, **props) - return self.backend.generate(outputfile=outputfile, mapfile=mapfile) - - -class UnorderableGraph(Exception): - pass - -def ordered_nodes(graph): - """takes a dependency graph dict as arguments and return an ordered tuple of - nodes starting with nodes without dependencies and up to the outermost node. - - If there is some cycle in the graph, :exc:`UnorderableGraph` will be raised. - - Also the given graph dict will be emptied. - """ - # check graph consistency - cycles = get_cycles(graph) - if cycles: - cycles = '\n'.join([' -> '.join(cycle) for cycle in cycles]) - raise UnorderableGraph('cycles in graph: %s' % cycles) - vertices = set(graph) - to_vertices = set() - for edges in graph.values(): - to_vertices |= set(edges) - missing_vertices = to_vertices - vertices - if missing_vertices: - raise UnorderableGraph('missing vertices: %s' % ', '.join(missing_vertices)) - # order vertices - order = [] - order_set = set() - old_len = None - while graph: - if old_len == len(graph): - raise UnorderableGraph('unknown problem with %s' % graph) - old_len = len(graph) - deps_ok = [] - for node, node_deps in graph.items(): - for dep in node_deps: - if dep not in order_set: - break - else: - deps_ok.append(node) - order.append(deps_ok) - order_set |= set(deps_ok) - for node in deps_ok: - del graph[node] - result = [] - for grp in reversed(order): - result.extend(sorted(grp)) - return tuple(result) - - -def get_cycles(graph_dict, vertices=None): - '''given a dictionary representing an ordered graph (i.e. key are vertices - and values is a list of destination vertices representing edges), return a - list of detected cycles - ''' - if not graph_dict: - return () - result = [] - if vertices is None: - vertices = graph_dict.keys() - for vertice in vertices: - _get_cycles(graph_dict, [], set(), result, vertice) - return result - -def _get_cycles(graph_dict, path, visited, result, vertice): - """recursive function doing the real work for get_cycles""" - if vertice in path: - cycle = [vertice] - for node in path[::-1]: - if node == vertice: - break - cycle.insert(0, node) - # make a canonical representation - start_from = min(cycle) - index = cycle.index(start_from) - cycle = cycle[index:] + cycle[0:index] - # append it to result if not already in - if not cycle in result: - result.append(cycle) - return - path.append(vertice) - try: - for node in graph_dict[vertice]: - # don't check already visited nodes again - if node not in visited: - _get_cycles(graph_dict, path, visited, result, node) - visited.add(node) - except KeyError: - pass - path.pop() - -def has_path(graph_dict, fromnode, tonode, path=None): - """generic function taking a simple graph definition as a dictionary, with - node has key associated to a list of nodes directly reachable from it. - - Return None if no path exists to go from `fromnode` to `tonode`, else the - first path found (as a list including the destination node at last) - """ - if path is None: - path = [] - elif fromnode in path: - return None - path.append(fromnode) - for destnode in graph_dict[fromnode]: - if destnode == tonode or has_path(graph_dict, destnode, tonode, path): - return path[1:] + [tonode] - path.pop() - return None - diff --git a/pymode/libs/logilab/common/interface.py b/pymode/libs/logilab/common/interface.py deleted file mode 100644 index 3ea4ab7e..00000000 --- a/pymode/libs/logilab/common/interface.py +++ /dev/null @@ -1,71 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Bases class for interfaces to provide 'light' interface handling. - - TODO: - _ implements a check method which check that an object implements the - interface - _ Attribute objects - - This module requires at least python 2.2 -""" -__docformat__ = "restructuredtext en" - - -class Interface(object): - """Base class for interfaces.""" - def is_implemented_by(cls, instance): - return implements(instance, cls) - is_implemented_by = classmethod(is_implemented_by) - - -def implements(obj, interface): - """Return true if the give object (maybe an instance or class) implements - the interface. - """ - kimplements = getattr(obj, '__implements__', ()) - if not isinstance(kimplements, (list, tuple)): - kimplements = (kimplements,) - for implementedinterface in kimplements: - if issubclass(implementedinterface, interface): - return True - return False - - -def extend(klass, interface, _recurs=False): - """Add interface to klass'__implements__ if not already implemented in. - - If klass is subclassed, ensure subclasses __implements__ it as well. - - NOTE: klass should be e new class. - """ - if not implements(klass, interface): - try: - kimplements = klass.__implements__ - kimplementsklass = type(kimplements) - kimplements = list(kimplements) - except AttributeError: - kimplementsklass = tuple - kimplements = [] - kimplements.append(interface) - klass.__implements__ = kimplementsklass(kimplements) - for subklass in klass.__subclasses__(): - extend(subklass, interface, _recurs=True) - elif _recurs: - for subklass in klass.__subclasses__(): - extend(subklass, interface, _recurs=True) diff --git a/pymode/libs/logilab/common/logging_ext.py b/pymode/libs/logilab/common/logging_ext.py deleted file mode 100644 index 3b6a580a..00000000 --- a/pymode/libs/logilab/common/logging_ext.py +++ /dev/null @@ -1,195 +0,0 @@ -# -*- coding: utf-8 -*- -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Extends the logging module from the standard library.""" - -__docformat__ = "restructuredtext en" - -import os -import sys -import logging - -from six import string_types - -from logilab.common.textutils import colorize_ansi - - -def set_log_methods(cls, logger): - """bind standard logger's methods as methods on the class""" - cls.__logger = logger - for attr in ('debug', 'info', 'warning', 'error', 'critical', 'exception'): - setattr(cls, attr, getattr(logger, attr)) - - -def xxx_cyan(record): - if 'XXX' in record.message: - return 'cyan' - -class ColorFormatter(logging.Formatter): - """ - A color Formatter for the logging standard module. - - By default, colorize CRITICAL and ERROR in red, WARNING in orange, INFO in - green and DEBUG in yellow. - - self.colors is customizable via the 'color' constructor argument (dictionary). - - self.colorfilters is a list of functions that get the LogRecord - and return a color name or None. - """ - - def __init__(self, fmt=None, datefmt=None, colors=None): - logging.Formatter.__init__(self, fmt, datefmt) - self.colorfilters = [] - self.colors = {'CRITICAL': 'red', - 'ERROR': 'red', - 'WARNING': 'magenta', - 'INFO': 'green', - 'DEBUG': 'yellow', - } - if colors is not None: - assert isinstance(colors, dict) - self.colors.update(colors) - - def format(self, record): - msg = logging.Formatter.format(self, record) - if record.levelname in self.colors: - color = self.colors[record.levelname] - return colorize_ansi(msg, color) - else: - for cf in self.colorfilters: - color = cf(record) - if color: - return colorize_ansi(msg, color) - return msg - -def set_color_formatter(logger=None, **kw): - """ - Install a color formatter on the 'logger'. If not given, it will - defaults to the default logger. - - Any additional keyword will be passed as-is to the ColorFormatter - constructor. - """ - if logger is None: - logger = logging.getLogger() - if not logger.handlers: - logging.basicConfig() - format_msg = logger.handlers[0].formatter._fmt - fmt = ColorFormatter(format_msg, **kw) - fmt.colorfilters.append(xxx_cyan) - logger.handlers[0].setFormatter(fmt) - - -LOG_FORMAT = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s' -LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' - -def get_handler(debug=False, syslog=False, logfile=None, rotation_parameters=None): - """get an apropriate handler according to given parameters""" - if os.environ.get('APYCOT_ROOT'): - handler = logging.StreamHandler(sys.stdout) - if debug: - handler = logging.StreamHandler() - elif logfile is None: - if syslog: - from logging import handlers - handler = handlers.SysLogHandler() - else: - handler = logging.StreamHandler() - else: - try: - if rotation_parameters is None: - if os.name == 'posix' and sys.version_info >= (2, 6): - from logging.handlers import WatchedFileHandler - handler = WatchedFileHandler(logfile) - else: - handler = logging.FileHandler(logfile) - else: - from logging.handlers import TimedRotatingFileHandler - handler = TimedRotatingFileHandler( - logfile, **rotation_parameters) - except IOError: - handler = logging.StreamHandler() - return handler - -def get_threshold(debug=False, logthreshold=None): - if logthreshold is None: - if debug: - logthreshold = logging.DEBUG - else: - logthreshold = logging.ERROR - elif isinstance(logthreshold, string_types): - logthreshold = getattr(logging, THRESHOLD_MAP.get(logthreshold, - logthreshold)) - return logthreshold - -def _colorable_terminal(): - isatty = hasattr(sys.__stdout__, 'isatty') and sys.__stdout__.isatty() - if not isatty: - return False - if os.name == 'nt': - try: - from colorama import init as init_win32_colors - except ImportError: - return False - init_win32_colors() - return True - -def get_formatter(logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT): - if _colorable_terminal(): - fmt = ColorFormatter(logformat, logdateformat) - def col_fact(record): - if 'XXX' in record.message: - return 'cyan' - if 'kick' in record.message: - return 'red' - fmt.colorfilters.append(col_fact) - else: - fmt = logging.Formatter(logformat, logdateformat) - return fmt - -def init_log(debug=False, syslog=False, logthreshold=None, logfile=None, - logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT, fmt=None, - rotation_parameters=None, handler=None): - """init the log service""" - logger = logging.getLogger() - if handler is None: - handler = get_handler(debug, syslog, logfile, rotation_parameters) - # only addHandler and removeHandler method while I would like a setHandler - # method, so do it this way :$ - logger.handlers = [handler] - logthreshold = get_threshold(debug, logthreshold) - logger.setLevel(logthreshold) - if fmt is None: - if debug: - fmt = get_formatter(logformat=logformat, logdateformat=logdateformat) - else: - fmt = logging.Formatter(logformat, logdateformat) - handler.setFormatter(fmt) - return handler - -# map logilab.common.logger thresholds to logging thresholds -THRESHOLD_MAP = {'LOG_DEBUG': 'DEBUG', - 'LOG_INFO': 'INFO', - 'LOG_NOTICE': 'INFO', - 'LOG_WARN': 'WARNING', - 'LOG_WARNING': 'WARNING', - 'LOG_ERR': 'ERROR', - 'LOG_ERROR': 'ERROR', - 'LOG_CRIT': 'CRITICAL', - } diff --git a/pymode/libs/logilab/common/modutils.py b/pymode/libs/logilab/common/modutils.py deleted file mode 100644 index dd725d24..00000000 --- a/pymode/libs/logilab/common/modutils.py +++ /dev/null @@ -1,713 +0,0 @@ -# -*- coding: utf-8 -*- -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Python modules manipulation utility functions. - -:type PY_SOURCE_EXTS: tuple(str) -:var PY_SOURCE_EXTS: list of possible python source file extension - -:type STD_LIB_DIR: str -:var STD_LIB_DIR: directory where standard modules are located - -:type BUILTIN_MODULES: dict -:var BUILTIN_MODULES: dictionary with builtin module names as key -""" - -__docformat__ = "restructuredtext en" - -import sys -import os -from os.path import splitext, join, abspath, isdir, dirname, exists, basename -from imp import find_module, load_module, C_BUILTIN, PY_COMPILED, PKG_DIRECTORY -from distutils.sysconfig import get_config_var, get_python_lib, get_python_version -from distutils.errors import DistutilsPlatformError - -from six.moves import range - -try: - import zipimport -except ImportError: - zipimport = None - -ZIPFILE = object() - -from logilab.common import STD_BLACKLIST, _handle_blacklist - -# Notes about STD_LIB_DIR -# Consider arch-specific installation for STD_LIB_DIR definition -# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on -# -# :see: `Problems with /usr/lib64 builds `_ -# :see: `FHS `_ -if sys.platform.startswith('win'): - PY_SOURCE_EXTS = ('py', 'pyw') - PY_COMPILED_EXTS = ('dll', 'pyd') -else: - PY_SOURCE_EXTS = ('py',) - PY_COMPILED_EXTS = ('so',) - -try: - STD_LIB_DIR = get_python_lib(standard_lib=True) -# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to -# non-valid path, see https://bugs.pypy.org/issue1164 -except DistutilsPlatformError: - STD_LIB_DIR = '//' - -EXT_LIB_DIR = get_python_lib() - -BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - -class LazyObject(object): - def __init__(self, module, obj): - self.module = module - self.obj = obj - self._imported = None - - def _getobj(self): - if self._imported is None: - self._imported = getattr(load_module_from_name(self.module), - self.obj) - return self._imported - - def __getattribute__(self, attr): - try: - return super(LazyObject, self).__getattribute__(attr) - except AttributeError as ex: - return getattr(self._getobj(), attr) - - def __call__(self, *args, **kwargs): - return self._getobj()(*args, **kwargs) - - -def load_module_from_name(dotted_name, path=None, use_sys=True): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=True): - """Load a python module from its splitted name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package splitted on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = find_module(part, path) - module = load_module(curname, mp_file, mp_filename, mp_desc) - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, '__file__', '') - prevmodule = module - if not _file and _is_namespace(curname): - continue - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):]) ) - path = [dirname( _file )] - return module - - -def load_module_from_file(filepath, path=None, use_sys=True, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def _check_init(path, mod_path): - """check there are some __init__.py all along the way""" - modpath = [] - for part in mod_path: - modpath.append(part) - path = join(path, part) - if not _is_namespace('.'.join(modpath)) and not _has_init(path): - return False - return True - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding splitted module's name - (i.e name of a module or package splitted on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package splitted in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding splitted module's name - """ - base = splitext(abspath(filename))[0] - if extrapath is not None: - for path_ in extrapath: - path = abspath(path_) - if path and base[:len(path)] == path: - submodpath = [pkg for pkg in base[len(path):].split(os.sep) - if pkg] - if _check_init(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - for path in sys.path: - path = abspath(path) - if path and base.startswith(path): - modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] - if _check_init(path, modpath[:-1]): - return modpath - raise ImportError('Unable to find module for %s in %s' % ( - filename, ', \n'.join(sys.path))) - - - -def file_from_modpath(modpath, path=None, context_file=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file, giving priority to source file over precompiled - file if it exists - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.') - (this means explicit relative imports that start with dots have - empty strings in this list!) - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the path to the module's file or None if it's an integrated - builtin module such as 'sys' - """ - if context_file is not None: - context = dirname(context_file) - else: - context = context_file - if modpath[0] == 'xml': - # handle _xmlplus - try: - return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context) - except ImportError: - return _file_from_modpath(modpath, path, context) - elif modpath == ['os', 'path']: - # FIXME: currently ignoring search_path... - return os.path.__file__ - return _file_from_modpath(modpath, path, context) - - - -def get_module_part(dotted_name, context_file=None): - """given a dotted name return the module part of the name : - - >>> get_module_part('logilab.common.modutils.get_module_part') - 'logilab.common.modutils' - - :type dotted_name: str - :param dotted_name: full name of the identifier we are interested in - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the module part of the name or None if we have not been able at - all to import the given name - - XXX: deprecated, since it doesn't handle package precedence over module - (see #10066) - """ - # os.path trick - if dotted_name.startswith('os.path'): - return 'os.path' - parts = dotted_name.split('.') - if context_file is not None: - # first check for builtin module which won't be considered latter - # in that case (path != None) - if parts[0] in BUILTIN_MODULES: - if len(parts) > 2: - raise ImportError(dotted_name) - return parts[0] - # don't use += or insert, we want a new list to be created ! - path = None - starti = 0 - if parts[0] == '': - assert context_file is not None, \ - 'explicit relative import, but no context_file?' - path = [] # prevent resolving the import non-relatively - starti = 1 - while parts[starti] == '': # for all further dots: change context - starti += 1 - context_file = dirname(context_file) - for i in range(starti, len(parts)): - try: - file_from_modpath(parts[starti:i+1], - path=path, context_file=context_file) - except ImportError: - if not i >= max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_modules(package, src_directory, blacklist=STD_BLACKLIST): - """given a package directory return a list of all available python - modules in the package and its subpackages - - :type package: str - :param package: the python name for the package - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to - the value of `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all available python modules in the package and its - subpackages - """ - modules = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not '__init__.py' in filenames: - dirnames[:] = () - continue - if directory != src_directory: - dir_package = directory[len(src_directory):].replace(os.sep, '.') - modules.append(package + dir_package) - for filename in filenames: - if _is_python_file(filename) and filename != '__init__.py': - src = join(directory, filename) - module = package + src[len(src_directory):-3] - modules.append(module.replace(os.sep, '.')) - return modules - - - -def get_module_files(src_directory, blacklist=STD_BLACKLIST): - """given a package directory return a list of all available python - module's files in the package and its subpackages - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all available python module's files in the package and - its subpackages - """ - files = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not '__init__.py' in filenames: - dirnames[:] = () - continue - for filename in filenames: - if _is_python_file(filename): - src = join(directory, filename) - files.append(src) - return files - - -def get_source_file(filename, include_no_ext=False): - """given a python module's file name return the matching source file - name (the filename will be returned identically if it's a already an - absolute path to a python source file...) - - :type filename: str - :param filename: python module's file name - - - :raise NoSourceFile: if no source file exists on the file system - - :rtype: str - :return: the absolute path of the source file if it exists - """ - base, orig_ext = splitext(abspath(filename)) - for ext in PY_SOURCE_EXTS: - source_path = '%s.%s' % (base, ext) - if exists(source_path): - return source_path - if include_no_ext and not orig_ext and exists(base): - return base - raise NoSourceFile(filename) - - -def cleanup_sys_modules(directories): - """remove submodules of `directories` from `sys.modules`""" - cleaned = [] - for modname, module in list(sys.modules.items()): - modfile = getattr(module, '__file__', None) - if modfile: - for directory in directories: - if modfile.startswith(directory): - cleaned.append(modname) - del sys.modules[modname] - break - return cleaned - - -def is_python_source(filename): - """ - rtype: bool - return: True if the filename is a python source file - """ - return splitext(filename)[1][1:] in PY_SOURCE_EXTS - - -def is_standard_module(modname, std_path=(STD_LIB_DIR,)): - """try to guess if a module is a standard python module (by default, - see `std_path` parameter's description) - - :type modname: str - :param modname: name of the module we are interested in - - :type std_path: list(str) or tuple(str) - :param std_path: list of path considered as standard - - - :rtype: bool - :return: - true if the module: - - is located on the path listed in one of the directory in `std_path` - - is a built-in module - - Note: this function is known to return wrong values when inside virtualenv. - See https://www.logilab.org/ticket/294756. - """ - modname = modname.split('.')[0] - try: - filename = file_from_modpath([modname]) - except ImportError as ex: - # import failed, i'm probably not so wrong by supposing it's - # not standard... - return False - # modules which are not living in a file are considered standard - # (sys and __builtin__ for instance) - if filename is None: - # we assume there are no namespaces in stdlib - return not _is_namespace(modname) - filename = abspath(filename) - if filename.startswith(EXT_LIB_DIR): - return False - for path in std_path: - if filename.startswith(abspath(path)): - return True - return False - - - -def is_relative(modname, from_file): - """return true if the given module name is relative to the given - file name - - :type modname: str - :param modname: name of the module we are interested in - - :type from_file: str - :param from_file: - path of the module from which modname has been imported - - :rtype: bool - :return: - true if the module has been imported relatively to `from_file` - """ - if not isdir(from_file): - from_file = dirname(from_file) - if from_file in sys.path: - return False - try: - find_module(modname.split('.')[0], [from_file]) - return True - except ImportError: - return False - - -# internal only functions ##################################################### - -def _file_from_modpath(modpath, path=None, context=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file - - this function is used internally, see `file_from_modpath`'s - documentation for more information - """ - assert len(modpath) > 0 - if context is not None: - try: - mtype, mp_filename = _module_file(modpath, [context]) - except ImportError: - mtype, mp_filename = _module_file(modpath, path) - else: - mtype, mp_filename = _module_file(modpath, path) - if mtype == PY_COMPILED: - try: - return get_source_file(mp_filename) - except NoSourceFile: - return mp_filename - elif mtype == C_BUILTIN: - # integrated builtin module - return None - elif mtype == PKG_DIRECTORY: - mp_filename = _has_init(mp_filename) - return mp_filename - -def _search_zip(modpath, pic): - for filepath, importer in pic.items(): - if importer is not None: - if importer.find_module(modpath[0]): - if not importer.find_module('/'.join(modpath)): - raise ImportError('No module named %s in %s/%s' % ( - '.'.join(modpath[1:]), filepath, modpath)) - return ZIPFILE, abspath(filepath) + '/' + '/'.join(modpath), filepath - raise ImportError('No module named %s' % '.'.join(modpath)) - -try: - import pkg_resources -except ImportError: - pkg_resources = None - - -def _is_namespace(modname): - return (pkg_resources is not None - and modname in pkg_resources._namespace_packages) - - -def _module_file(modpath, path=None): - """get a module type / file path - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.'), with leading empty strings for explicit relative import - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - - :rtype: tuple(int, str) - :return: the module type flag and the file path for a module - """ - # egg support compat - try: - pic = sys.path_importer_cache - _path = (path is None and sys.path or path) - for __path in _path: - if not __path in pic: - try: - pic[__path] = zipimport.zipimporter(__path) - except zipimport.ZipImportError: - pic[__path] = None - checkeggs = True - except AttributeError: - checkeggs = False - # pkg_resources support (aka setuptools namespace packages) - if (_is_namespace(modpath[0]) and modpath[0] in sys.modules): - # setuptools has added into sys.modules a module object with proper - # __path__, get back information from there - module = sys.modules[modpath.pop(0)] - path = module.__path__ - if not modpath: - return C_BUILTIN, None - imported = [] - while modpath: - modname = modpath[0] - # take care to changes in find_module implementation wrt builtin modules - # - # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) - # >>> imp.find_module('posix') - # (None, 'posix', ('', '', 6)) - # - # Python 3.3.1 (default, Apr 26 2013, 12:08:46) - # >>> imp.find_module('posix') - # (None, None, ('', '', 6)) - try: - _, mp_filename, mp_desc = find_module(modname, path) - except ImportError: - if checkeggs: - return _search_zip(modpath, pic)[:2] - raise - else: - if checkeggs and mp_filename: - fullabspath = [abspath(x) for x in _path] - try: - pathindex = fullabspath.index(dirname(abspath(mp_filename))) - emtype, emp_filename, zippath = _search_zip(modpath, pic) - if pathindex > _path.index(zippath): - # an egg takes priority - return emtype, emp_filename - except ValueError: - # XXX not in _path - pass - except ImportError: - pass - checkeggs = False - imported.append(modpath.pop(0)) - mtype = mp_desc[2] - if modpath: - if mtype != PKG_DIRECTORY: - raise ImportError('No module %s in %s' % ('.'.join(modpath), - '.'.join(imported))) - # XXX guess if package is using pkgutil.extend_path by looking for - # those keywords in the first four Kbytes - try: - with open(join(mp_filename, '__init__.py')) as stream: - data = stream.read(4096) - except IOError: - path = [mp_filename] - else: - if 'pkgutil' in data and 'extend_path' in data: - # extend_path is called, search sys.path for module/packages - # of this name see pkgutil.extend_path documentation - path = [join(p, *imported) for p in sys.path - if isdir(join(p, *imported))] - else: - path = [mp_filename] - return mtype, mp_filename - -def _is_python_file(filename): - """return true if the given filename should be considered as a python file - - .pyc and .pyo are ignored - """ - for ext in ('.py', '.so', '.pyd', '.pyw'): - if filename.endswith(ext): - return True - return False - - -def _has_init(directory): - """if the given directory has a valid __init__ file, return its path, - else return None - """ - mod_or_pack = join(directory, '__init__') - for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'): - if exists(mod_or_pack + '.' + ext): - return mod_or_pack + '.' + ext - return None diff --git a/pymode/libs/logilab/common/optik_ext.py b/pymode/libs/logilab/common/optik_ext.py deleted file mode 100644 index 1fd2a7f8..00000000 --- a/pymode/libs/logilab/common/optik_ext.py +++ /dev/null @@ -1,392 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Add an abstraction level to transparently import optik classes from optparse -(python >= 2.3) or the optik package. - -It also defines three new types for optik/optparse command line parser : - - * regexp - argument of this type will be converted using re.compile - * csv - argument of this type will be converted using split(',') - * yn - argument of this type will be true if 'y' or 'yes', false if 'n' or 'no' - * named - argument of this type are in the form = or : - * password - argument of this type wont be converted but this is used by other tools - such as interactive prompt for configuration to double check value and - use an invisible field - * multiple_choice - same as default "choice" type but multiple choices allowed - * file - argument of this type wont be converted but checked that the given file exists - * color - argument of this type wont be converted but checked its either a - named color or a color specified using hexadecimal notation (preceded by a #) - * time - argument of this type will be converted to a float value in seconds - according to time units (ms, s, min, h, d) - * bytes - argument of this type will be converted to a float value in bytes - according to byte units (b, kb, mb, gb, tb) -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import re -import sys -import time -from copy import copy -from os.path import exists - -# python >= 2.3 -from optparse import OptionParser as BaseParser, Option as BaseOption, \ - OptionGroup, OptionContainer, OptionValueError, OptionError, \ - Values, HelpFormatter, NO_DEFAULT, SUPPRESS_HELP - -try: - from mx import DateTime - HAS_MX_DATETIME = True -except ImportError: - HAS_MX_DATETIME = False - -from logilab.common.textutils import splitstrip, TIME_UNITS, BYTE_UNITS, \ - apply_units - - -def check_regexp(option, opt, value): - """check a regexp value by trying to compile it - return the compiled regexp - """ - if hasattr(value, 'pattern'): - return value - try: - return re.compile(value) - except ValueError: - raise OptionValueError( - "option %s: invalid regexp value: %r" % (opt, value)) - -def check_csv(option, opt, value): - """check a csv value by trying to split it - return the list of separated values - """ - if isinstance(value, (list, tuple)): - return value - try: - return splitstrip(value) - except ValueError: - raise OptionValueError( - "option %s: invalid csv value: %r" % (opt, value)) - -def check_yn(option, opt, value): - """check a yn value - return true for yes and false for no - """ - if isinstance(value, int): - return bool(value) - if value in ('y', 'yes'): - return True - if value in ('n', 'no'): - return False - msg = "option %s: invalid yn value %r, should be in (y, yes, n, no)" - raise OptionValueError(msg % (opt, value)) - -def check_named(option, opt, value): - """check a named value - return a dictionary containing (name, value) associations - """ - if isinstance(value, dict): - return value - values = [] - for value in check_csv(option, opt, value): - if value.find('=') != -1: - values.append(value.split('=', 1)) - elif value.find(':') != -1: - values.append(value.split(':', 1)) - if values: - return dict(values) - msg = "option %s: invalid named value %r, should be = or \ -:" - raise OptionValueError(msg % (opt, value)) - -def check_password(option, opt, value): - """check a password value (can't be empty) - """ - # no actual checking, monkey patch if you want more - return value - -def check_file(option, opt, value): - """check a file value - return the filepath - """ - if exists(value): - return value - msg = "option %s: file %r does not exist" - raise OptionValueError(msg % (opt, value)) - -# XXX use python datetime -def check_date(option, opt, value): - """check a file value - return the filepath - """ - try: - return DateTime.strptime(value, "%Y/%m/%d") - except DateTime.Error : - raise OptionValueError( - "expected format of %s is yyyy/mm/dd" % opt) - -def check_color(option, opt, value): - """check a color value and returns it - /!\ does *not* check color labels (like 'red', 'green'), only - checks hexadecimal forms - """ - # Case (1) : color label, we trust the end-user - if re.match('[a-z0-9 ]+$', value, re.I): - return value - # Case (2) : only accepts hexadecimal forms - if re.match('#[a-f0-9]{6}', value, re.I): - return value - # Else : not a color label neither a valid hexadecimal form => error - msg = "option %s: invalid color : %r, should be either hexadecimal \ - value or predefined color" - raise OptionValueError(msg % (opt, value)) - -def check_time(option, opt, value): - if isinstance(value, (int, long, float)): - return value - return apply_units(value, TIME_UNITS) - -def check_bytes(option, opt, value): - if hasattr(value, '__int__'): - return value - return apply_units(value, BYTE_UNITS) - - -class Option(BaseOption): - """override optik.Option to add some new option types - """ - TYPES = BaseOption.TYPES + ('regexp', 'csv', 'yn', 'named', 'password', - 'multiple_choice', 'file', 'color', - 'time', 'bytes') - ATTRS = BaseOption.ATTRS + ['hide', 'level'] - TYPE_CHECKER = copy(BaseOption.TYPE_CHECKER) - TYPE_CHECKER['regexp'] = check_regexp - TYPE_CHECKER['csv'] = check_csv - TYPE_CHECKER['yn'] = check_yn - TYPE_CHECKER['named'] = check_named - TYPE_CHECKER['multiple_choice'] = check_csv - TYPE_CHECKER['file'] = check_file - TYPE_CHECKER['color'] = check_color - TYPE_CHECKER['password'] = check_password - TYPE_CHECKER['time'] = check_time - TYPE_CHECKER['bytes'] = check_bytes - if HAS_MX_DATETIME: - TYPES += ('date',) - TYPE_CHECKER['date'] = check_date - - def __init__(self, *opts, **attrs): - BaseOption.__init__(self, *opts, **attrs) - if hasattr(self, "hide") and self.hide: - self.help = SUPPRESS_HELP - - def _check_choice(self): - """FIXME: need to override this due to optik misdesign""" - if self.type in ("choice", "multiple_choice"): - if self.choices is None: - raise OptionError( - "must supply a list of choices for type 'choice'", self) - elif not isinstance(self.choices, (tuple, list)): - raise OptionError( - "choices must be a list of strings ('%s' supplied)" - % str(type(self.choices)).split("'")[1], self) - elif self.choices is not None: - raise OptionError( - "must not supply choices for type %r" % self.type, self) - BaseOption.CHECK_METHODS[2] = _check_choice - - - def process(self, opt, value, values, parser): - # First, convert the value(s) to the right type. Howl if any - # value(s) are bogus. - value = self.convert_value(opt, value) - if self.type == 'named': - existant = getattr(values, self.dest) - if existant: - existant.update(value) - value = existant - # And then take whatever action is expected of us. - # This is a separate method to make life easier for - # subclasses to add new actions. - return self.take_action( - self.action, self.dest, opt, value, values, parser) - - -class OptionParser(BaseParser): - """override optik.OptionParser to use our Option class - """ - def __init__(self, option_class=Option, *args, **kwargs): - BaseParser.__init__(self, option_class=Option, *args, **kwargs) - - def format_option_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - outputlevel = getattr(formatter, 'output_level', 0) - formatter.store_option_strings(self) - result = [] - result.append(formatter.format_heading("Options")) - formatter.indent() - if self.option_list: - result.append(OptionContainer.format_option_help(self, formatter)) - result.append("\n") - for group in self.option_groups: - if group.level <= outputlevel and ( - group.description or level_options(group, outputlevel)): - result.append(group.format_help(formatter)) - result.append("\n") - formatter.dedent() - # Drop the last "\n", or the header if no options or option groups: - return "".join(result[:-1]) - - -OptionGroup.level = 0 - -def level_options(group, outputlevel): - return [option for option in group.option_list - if (getattr(option, 'level', 0) or 0) <= outputlevel - and not option.help is SUPPRESS_HELP] - -def format_option_help(self, formatter): - result = [] - outputlevel = getattr(formatter, 'output_level', 0) or 0 - for option in level_options(self, outputlevel): - result.append(formatter.format_option(option)) - return "".join(result) -OptionContainer.format_option_help = format_option_help - - -class ManHelpFormatter(HelpFormatter): - """Format help using man pages ROFF format""" - - def __init__ (self, - indent_increment=0, - max_help_position=24, - width=79, - short_first=0): - HelpFormatter.__init__ ( - self, indent_increment, max_help_position, width, short_first) - - def format_heading(self, heading): - return '.SH %s\n' % heading.upper() - - def format_description(self, description): - return description - - def format_option(self, option): - try: - optstring = option.option_strings - except AttributeError: - optstring = self.format_option_strings(option) - if option.help: - help_text = self.expand_default(option) - help = ' '.join([l.strip() for l in help_text.splitlines()]) - else: - help = '' - return '''.IP "%s" -%s -''' % (optstring, help) - - def format_head(self, optparser, pkginfo, section=1): - long_desc = "" - try: - pgm = optparser._get_prog_name() - except AttributeError: - # py >= 2.4.X (dunno which X exactly, at least 2) - pgm = optparser.get_prog_name() - short_desc = self.format_short_description(pgm, pkginfo.description) - if hasattr(pkginfo, "long_desc"): - long_desc = self.format_long_description(pgm, pkginfo.long_desc) - return '%s\n%s\n%s\n%s' % (self.format_title(pgm, section), - short_desc, self.format_synopsis(pgm), - long_desc) - - def format_title(self, pgm, section): - date = '-'.join([str(num) for num in time.localtime()[:3]]) - return '.TH %s %s "%s" %s' % (pgm, section, date, pgm) - - def format_short_description(self, pgm, short_desc): - return '''.SH NAME -.B %s -\- %s -''' % (pgm, short_desc.strip()) - - def format_synopsis(self, pgm): - return '''.SH SYNOPSIS -.B %s -[ -.I OPTIONS -] [ -.I -] -''' % pgm - - def format_long_description(self, pgm, long_desc): - long_desc = '\n'.join([line.lstrip() - for line in long_desc.splitlines()]) - long_desc = long_desc.replace('\n.\n', '\n\n') - if long_desc.lower().startswith(pgm): - long_desc = long_desc[len(pgm):] - return '''.SH DESCRIPTION -.B %s -%s -''' % (pgm, long_desc.strip()) - - def format_tail(self, pkginfo): - tail = '''.SH SEE ALSO -/usr/share/doc/pythonX.Y-%s/ - -.SH BUGS -Please report bugs on the project\'s mailing list: -%s - -.SH AUTHOR -%s <%s> -''' % (getattr(pkginfo, 'debian_name', pkginfo.modname), - pkginfo.mailinglist, pkginfo.author, pkginfo.author_email) - - if hasattr(pkginfo, "copyright"): - tail += ''' -.SH COPYRIGHT -%s -''' % pkginfo.copyright - - return tail - -def generate_manpage(optparser, pkginfo, section=1, stream=sys.stdout, level=0): - """generate a man page from an optik parser""" - formatter = ManHelpFormatter() - formatter.output_level = level - formatter.parser = optparser - print(formatter.format_head(optparser, pkginfo, section), file=stream) - print(optparser.format_option_help(formatter), file=stream) - print(formatter.format_tail(pkginfo), file=stream) - - -__all__ = ('OptionParser', 'Option', 'OptionGroup', 'OptionValueError', - 'Values') diff --git a/pymode/libs/logilab/common/optparser.py b/pymode/libs/logilab/common/optparser.py deleted file mode 100644 index aa17750e..00000000 --- a/pymode/libs/logilab/common/optparser.py +++ /dev/null @@ -1,92 +0,0 @@ -# -*- coding: utf-8 -*- -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Extend OptionParser with commands. - -Example: - ->>> parser = OptionParser() ->>> parser.usage = '%prog COMMAND [options] ...' ->>> parser.add_command('build', 'mymod.build') ->>> parser.add_command('clean', run_clean, add_opt_clean) ->>> run, options, args = parser.parse_command(sys.argv[1:]) ->>> return run(options, args[1:]) - -With mymod.build that defines two functions run and add_options -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from warnings import warn -warn('lgc.optparser module is deprecated, use lgc.clcommands instead', DeprecationWarning, - stacklevel=2) - -import sys -import optparse - -class OptionParser(optparse.OptionParser): - - def __init__(self, *args, **kwargs): - optparse.OptionParser.__init__(self, *args, **kwargs) - self._commands = {} - self.min_args, self.max_args = 0, 1 - - def add_command(self, name, mod_or_funcs, help=''): - """name of the command, name of module or tuple of functions - (run, add_options) - """ - assert isinstance(mod_or_funcs, str) or isinstance(mod_or_funcs, tuple), \ - "mod_or_funcs has to be a module name or a tuple of functions" - self._commands[name] = (mod_or_funcs, help) - - def print_main_help(self): - optparse.OptionParser.print_help(self) - print('\ncommands:') - for cmdname, (_, help) in self._commands.items(): - print('% 10s - %s' % (cmdname, help)) - - def parse_command(self, args): - if len(args) == 0: - self.print_main_help() - sys.exit(1) - cmd = args[0] - args = args[1:] - if cmd not in self._commands: - if cmd in ('-h', '--help'): - self.print_main_help() - sys.exit(0) - elif self.version is not None and cmd == "--version": - self.print_version() - sys.exit(0) - self.error('unknown command') - self.prog = '%s %s' % (self.prog, cmd) - mod_or_f, help = self._commands[cmd] - # optparse inserts self.description between usage and options help - self.description = help - if isinstance(mod_or_f, str): - exec('from %s import run, add_options' % mod_or_f) - else: - run, add_options = mod_or_f - add_options(self) - (options, args) = self.parse_args(args) - if not (self.min_args <= len(args) <= self.max_args): - self.error('incorrect number of arguments') - return run, options, args - - diff --git a/pymode/libs/logilab/common/proc.py b/pymode/libs/logilab/common/proc.py deleted file mode 100644 index c27356c6..00000000 --- a/pymode/libs/logilab/common/proc.py +++ /dev/null @@ -1,277 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""module providing: -* process information (linux specific: rely on /proc) -* a class for resource control (memory / time / cpu time) - -This module doesn't work on windows platforms (only tested on linux) - -:organization: Logilab - - - -""" -__docformat__ = "restructuredtext en" - -import os -import stat -from resource import getrlimit, setrlimit, RLIMIT_CPU, RLIMIT_AS -from signal import signal, SIGXCPU, SIGKILL, SIGUSR2, SIGUSR1 -from threading import Timer, currentThread, Thread, Event -from time import time - -from logilab.common.tree import Node - -class NoSuchProcess(Exception): pass - -def proc_exists(pid): - """check the a pid is registered in /proc - raise NoSuchProcess exception if not - """ - if not os.path.exists('/proc/%s' % pid): - raise NoSuchProcess() - -PPID = 3 -UTIME = 13 -STIME = 14 -CUTIME = 15 -CSTIME = 16 -VSIZE = 22 - -class ProcInfo(Node): - """provide access to process information found in /proc""" - - def __init__(self, pid): - self.pid = int(pid) - Node.__init__(self, self.pid) - proc_exists(self.pid) - self.file = '/proc/%s/stat' % self.pid - self.ppid = int(self.status()[PPID]) - - def memory_usage(self): - """return the memory usage of the process in Ko""" - try : - return int(self.status()[VSIZE]) - except IOError: - return 0 - - def lineage_memory_usage(self): - return self.memory_usage() + sum([child.lineage_memory_usage() - for child in self.children]) - - def time(self, children=0): - """return the number of jiffies that this process has been scheduled - in user and kernel mode""" - status = self.status() - time = int(status[UTIME]) + int(status[STIME]) - if children: - time += int(status[CUTIME]) + int(status[CSTIME]) - return time - - def status(self): - """return the list of fields found in /proc//stat""" - return open(self.file).read().split() - - def name(self): - """return the process name found in /proc//stat - """ - return self.status()[1].strip('()') - - def age(self): - """return the age of the process - """ - return os.stat(self.file)[stat.ST_MTIME] - -class ProcInfoLoader: - """manage process information""" - - def __init__(self): - self._loaded = {} - - def list_pids(self): - """return a list of existent process ids""" - for subdir in os.listdir('/proc'): - if subdir.isdigit(): - yield int(subdir) - - def load(self, pid): - """get a ProcInfo object for a given pid""" - pid = int(pid) - try: - return self._loaded[pid] - except KeyError: - procinfo = ProcInfo(pid) - procinfo.manager = self - self._loaded[pid] = procinfo - return procinfo - - - def load_all(self): - """load all processes information""" - for pid in self.list_pids(): - try: - procinfo = self.load(pid) - if procinfo.parent is None and procinfo.ppid: - pprocinfo = self.load(procinfo.ppid) - pprocinfo.append(procinfo) - except NoSuchProcess: - pass - - -try: - class ResourceError(BaseException): - """Error raise when resource limit is reached""" - limit = "Unknown Resource Limit" -except NameError: - class ResourceError(Exception): - """Error raise when resource limit is reached""" - limit = "Unknown Resource Limit" - - -class XCPUError(ResourceError): - """Error raised when CPU Time limit is reached""" - limit = "CPU Time" - -class LineageMemoryError(ResourceError): - """Error raised when the total amount of memory used by a process and - it's child is reached""" - limit = "Lineage total Memory" - -class TimeoutError(ResourceError): - """Error raised when the process is running for to much time""" - limit = "Real Time" - -# Can't use subclass because the StandardError MemoryError raised -RESOURCE_LIMIT_EXCEPTION = (ResourceError, MemoryError) - - -class MemorySentinel(Thread): - """A class checking a process don't use too much memory in a separated - daemonic thread - """ - def __init__(self, interval, memory_limit, gpid=os.getpid()): - Thread.__init__(self, target=self._run, name="Test.Sentinel") - self.memory_limit = memory_limit - self._stop = Event() - self.interval = interval - self.setDaemon(True) - self.gpid = gpid - - def stop(self): - """stop ap""" - self._stop.set() - - def _run(self): - pil = ProcInfoLoader() - while not self._stop.isSet(): - if self.memory_limit <= pil.load(self.gpid).lineage_memory_usage(): - os.killpg(self.gpid, SIGUSR1) - self._stop.wait(self.interval) - - -class ResourceController: - - def __init__(self, max_cpu_time=None, max_time=None, max_memory=None, - max_reprieve=60): - if SIGXCPU == -1: - raise RuntimeError("Unsupported platform") - self.max_time = max_time - self.max_memory = max_memory - self.max_cpu_time = max_cpu_time - self._reprieve = max_reprieve - self._timer = None - self._msentinel = None - self._old_max_memory = None - self._old_usr1_hdlr = None - self._old_max_cpu_time = None - self._old_usr2_hdlr = None - self._old_sigxcpu_hdlr = None - self._limit_set = 0 - self._abort_try = 0 - self._start_time = None - self._elapse_time = 0 - - def _hangle_sig_timeout(self, sig, frame): - raise TimeoutError() - - def _hangle_sig_memory(self, sig, frame): - if self._abort_try < self._reprieve: - self._abort_try += 1 - raise LineageMemoryError("Memory limit reached") - else: - os.killpg(os.getpid(), SIGKILL) - - def _handle_sigxcpu(self, sig, frame): - if self._abort_try < self._reprieve: - self._abort_try += 1 - raise XCPUError("Soft CPU time limit reached") - else: - os.killpg(os.getpid(), SIGKILL) - - def _time_out(self): - if self._abort_try < self._reprieve: - self._abort_try += 1 - os.killpg(os.getpid(), SIGUSR2) - if self._limit_set > 0: - self._timer = Timer(1, self._time_out) - self._timer.start() - else: - os.killpg(os.getpid(), SIGKILL) - - def setup_limit(self): - """set up the process limit""" - assert currentThread().getName() == 'MainThread' - os.setpgrp() - if self._limit_set <= 0: - if self.max_time is not None: - self._old_usr2_hdlr = signal(SIGUSR2, self._hangle_sig_timeout) - self._timer = Timer(max(1, int(self.max_time) - self._elapse_time), - self._time_out) - self._start_time = int(time()) - self._timer.start() - if self.max_cpu_time is not None: - self._old_max_cpu_time = getrlimit(RLIMIT_CPU) - cpu_limit = (int(self.max_cpu_time), self._old_max_cpu_time[1]) - self._old_sigxcpu_hdlr = signal(SIGXCPU, self._handle_sigxcpu) - setrlimit(RLIMIT_CPU, cpu_limit) - if self.max_memory is not None: - self._msentinel = MemorySentinel(1, int(self.max_memory) ) - self._old_max_memory = getrlimit(RLIMIT_AS) - self._old_usr1_hdlr = signal(SIGUSR1, self._hangle_sig_memory) - as_limit = (int(self.max_memory), self._old_max_memory[1]) - setrlimit(RLIMIT_AS, as_limit) - self._msentinel.start() - self._limit_set += 1 - - def clean_limit(self): - """reinstall the old process limit""" - if self._limit_set > 0: - if self.max_time is not None: - self._timer.cancel() - self._elapse_time += int(time())-self._start_time - self._timer = None - signal(SIGUSR2, self._old_usr2_hdlr) - if self.max_cpu_time is not None: - setrlimit(RLIMIT_CPU, self._old_max_cpu_time) - signal(SIGXCPU, self._old_sigxcpu_hdlr) - if self.max_memory is not None: - self._msentinel.stop() - self._msentinel = None - setrlimit(RLIMIT_AS, self._old_max_memory) - signal(SIGUSR1, self._old_usr1_hdlr) - self._limit_set -= 1 diff --git a/pymode/libs/logilab/common/pytest.py b/pymode/libs/logilab/common/pytest.py deleted file mode 100644 index 3d8aca34..00000000 --- a/pymode/libs/logilab/common/pytest.py +++ /dev/null @@ -1,1202 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""pytest is a tool that eases test running and debugging. - -To be able to use pytest, you should either write tests using -the logilab.common.testlib's framework or the unittest module of the -Python's standard library. - -You can customize pytest's behaviour by defining a ``pytestconf.py`` file -somewhere in your test directory. In this file, you can add options or -change the way tests are run. - -To add command line options, you must define a ``update_parser`` function in -your ``pytestconf.py`` file. The function must accept a single parameter -that will be the OptionParser's instance to customize. - -If you wish to customize the tester, you'll have to define a class named -``CustomPyTester``. This class should extend the default `PyTester` class -defined in the pytest module. Take a look at the `PyTester` and `DjangoTester` -classes for more information about what can be done. - -For instance, if you wish to add a custom -l option to specify a loglevel, you -could define the following ``pytestconf.py`` file :: - - import logging - from logilab.common.pytest import PyTester - - def update_parser(parser): - parser.add_option('-l', '--loglevel', dest='loglevel', action='store', - choices=('debug', 'info', 'warning', 'error', 'critical'), - default='critical', help="the default log level possible choices are " - "('debug', 'info', 'warning', 'error', 'critical')") - return parser - - - class CustomPyTester(PyTester): - def __init__(self, cvg, options): - super(CustomPyTester, self).__init__(cvg, options) - loglevel = options.loglevel.upper() - logger = logging.getLogger('erudi') - logger.setLevel(logging.getLevelName(loglevel)) - - -In your TestCase class you can then get the value of a specific option with -the ``optval`` method:: - - class MyTestCase(TestCase): - def test_foo(self): - loglevel = self.optval('loglevel') - # ... - - -You can also tag your tag your test for fine filtering - -With those tag:: - - from logilab.common.testlib import tag, TestCase - - class Exemple(TestCase): - - @tag('rouge', 'carre') - def toto(self): - pass - - @tag('carre', 'vert') - def tata(self): - pass - - @tag('rouge') - def titi(test): - pass - -you can filter the function with a simple python expression - - * ``toto`` and ``titi`` match ``rouge`` - * ``toto``, ``tata`` and ``titi``, match ``rouge or carre`` - * ``tata`` and ``titi`` match``rouge ^ carre`` - * ``titi`` match ``rouge and not carre`` -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -PYTEST_DOC = """%prog [OPTIONS] [testfile [testpattern]] - -examples: - -pytest path/to/mytests.py -pytest path/to/mytests.py TheseTests -pytest path/to/mytests.py TheseTests.test_thisone -pytest path/to/mytests.py -m '(not long and database) or regr' - -pytest one (will run both test_thisone and test_thatone) -pytest path/to/mytests.py -s not (will skip test_notthisone) -""" - -ENABLE_DBC = False -FILE_RESTART = ".pytest.restart" - -import os, sys, re -import os.path as osp -from time import time, clock -import warnings -import types -from inspect import isgeneratorfunction, isclass -from contextlib import contextmanager -from random import shuffle - -from logilab.common.fileutils import abspath_listdir -from logilab.common import textutils -from logilab.common import testlib, STD_BLACKLIST -# use the same unittest module as testlib -from logilab.common.testlib import unittest, start_interactive_mode -from logilab.common.deprecation import deprecated -import doctest - -import unittest as unittest_legacy -if not getattr(unittest_legacy, "__package__", None): - try: - import unittest2.suite as unittest_suite - except ImportError: - sys.exit("You have to install python-unittest2 to use this module") -else: - import unittest.suite as unittest_suite - -try: - import django - from logilab.common.modutils import modpath_from_file, load_module_from_modpath - DJANGO_FOUND = True -except ImportError: - DJANGO_FOUND = False - -CONF_FILE = 'pytestconf.py' - -## coverage pausing tools - -@contextmanager -def replace_trace(trace=None): - """A context manager that temporary replaces the trace function""" - oldtrace = sys.gettrace() - sys.settrace(trace) - try: - yield - finally: - # specific hack to work around a bug in pycoverage, see - # https://bitbucket.org/ned/coveragepy/issue/123 - if (oldtrace is not None and not callable(oldtrace) and - hasattr(oldtrace, 'pytrace')): - oldtrace = oldtrace.pytrace - sys.settrace(oldtrace) - - -def pause_trace(): - """A context manager that temporary pauses any tracing""" - return replace_trace() - -class TraceController(object): - ctx_stack = [] - - @classmethod - @deprecated('[lgc 0.63.1] Use the pause_trace() context manager') - def pause_tracing(cls): - cls.ctx_stack.append(pause_trace()) - cls.ctx_stack[-1].__enter__() - - @classmethod - @deprecated('[lgc 0.63.1] Use the pause_trace() context manager') - def resume_tracing(cls): - cls.ctx_stack.pop().__exit__(None, None, None) - - -pause_tracing = TraceController.pause_tracing -resume_tracing = TraceController.resume_tracing - - -def nocoverage(func): - """Function decorator that pauses tracing functions""" - if hasattr(func, 'uncovered'): - return func - func.uncovered = True - - def not_covered(*args, **kwargs): - with pause_trace(): - return func(*args, **kwargs) - not_covered.uncovered = True - return not_covered - -## end of coverage pausing tools - - -TESTFILE_RE = re.compile("^((unit)?test.*|smoketest)\.py$") -def this_is_a_testfile(filename): - """returns True if `filename` seems to be a test file""" - return TESTFILE_RE.match(osp.basename(filename)) - -TESTDIR_RE = re.compile("^(unit)?tests?$") -def this_is_a_testdir(dirpath): - """returns True if `filename` seems to be a test directory""" - return TESTDIR_RE.match(osp.basename(dirpath)) - - -def load_pytest_conf(path, parser): - """loads a ``pytestconf.py`` file and update default parser - and / or tester. - """ - namespace = {} - exec(open(path, 'rb').read(), namespace) - if 'update_parser' in namespace: - namespace['update_parser'](parser) - return namespace.get('CustomPyTester', PyTester) - - -def project_root(parser, projdir=os.getcwd()): - """try to find project's root and add it to sys.path""" - previousdir = curdir = osp.abspath(projdir) - testercls = PyTester - conf_file_path = osp.join(curdir, CONF_FILE) - if osp.isfile(conf_file_path): - testercls = load_pytest_conf(conf_file_path, parser) - while this_is_a_testdir(curdir) or \ - osp.isfile(osp.join(curdir, '__init__.py')): - newdir = osp.normpath(osp.join(curdir, os.pardir)) - if newdir == curdir: - break - previousdir = curdir - curdir = newdir - conf_file_path = osp.join(curdir, CONF_FILE) - if osp.isfile(conf_file_path): - testercls = load_pytest_conf(conf_file_path, parser) - return previousdir, testercls - - -class GlobalTestReport(object): - """this class holds global test statistics""" - def __init__(self): - self.ran = 0 - self.skipped = 0 - self.failures = 0 - self.errors = 0 - self.ttime = 0 - self.ctime = 0 - self.modulescount = 0 - self.errmodules = [] - - def feed(self, filename, testresult, ttime, ctime): - """integrates new test information into internal statistics""" - ran = testresult.testsRun - self.ran += ran - self.skipped += len(getattr(testresult, 'skipped', ())) - self.failures += len(testresult.failures) - self.errors += len(testresult.errors) - self.ttime += ttime - self.ctime += ctime - self.modulescount += 1 - if not testresult.wasSuccessful(): - problems = len(testresult.failures) + len(testresult.errors) - self.errmodules.append((filename[:-3], problems, ran)) - - def failed_to_test_module(self, filename): - """called when the test module could not be imported by unittest - """ - self.errors += 1 - self.modulescount += 1 - self.ran += 1 - self.errmodules.append((filename[:-3], 1, 1)) - - def skip_module(self, filename): - self.modulescount += 1 - self.ran += 1 - self.errmodules.append((filename[:-3], 0, 0)) - - def __str__(self): - """this is just presentation stuff""" - line1 = ['Ran %s test cases in %.2fs (%.2fs CPU)' - % (self.ran, self.ttime, self.ctime)] - if self.errors: - line1.append('%s errors' % self.errors) - if self.failures: - line1.append('%s failures' % self.failures) - if self.skipped: - line1.append('%s skipped' % self.skipped) - modulesok = self.modulescount - len(self.errmodules) - if self.errors or self.failures: - line2 = '%s modules OK (%s failed)' % (modulesok, - len(self.errmodules)) - descr = ', '.join(['%s [%s/%s]' % info for info in self.errmodules]) - line3 = '\nfailures: %s' % descr - elif modulesok: - line2 = 'All %s modules OK' % modulesok - line3 = '' - else: - return '' - return '%s\n%s%s' % (', '.join(line1), line2, line3) - - - -def remove_local_modules_from_sys(testdir): - """remove all modules from cache that come from `testdir` - - This is used to avoid strange side-effects when using the - testall() mode of pytest. - For instance, if we run pytest on this tree:: - - A/test/test_utils.py - B/test/test_utils.py - - we **have** to clean sys.modules to make sure the correct test_utils - module is ran in B - """ - for modname, mod in list(sys.modules.items()): - if mod is None: - continue - if not hasattr(mod, '__file__'): - # this is the case of some built-in modules like sys, imp, marshal - continue - modfile = mod.__file__ - # if modfile is not an absolute path, it was probably loaded locally - # during the tests - if not osp.isabs(modfile) or modfile.startswith(testdir): - del sys.modules[modname] - - - -class PyTester(object): - """encapsulates testrun logic""" - - def __init__(self, cvg, options): - self.report = GlobalTestReport() - self.cvg = cvg - self.options = options - self.firstwrite = True - self._errcode = None - - def show_report(self): - """prints the report and returns appropriate exitcode""" - # everything has been ran, print report - print("*" * 79) - print(self.report) - - def get_errcode(self): - # errcode set explicitly - if self._errcode is not None: - return self._errcode - return self.report.failures + self.report.errors - - def set_errcode(self, errcode): - self._errcode = errcode - errcode = property(get_errcode, set_errcode) - - def testall(self, exitfirst=False): - """walks through current working directory, finds something - which can be considered as a testdir and runs every test there - """ - here = os.getcwd() - for dirname, dirs, _ in os.walk(here): - for skipped in STD_BLACKLIST: - if skipped in dirs: - dirs.remove(skipped) - basename = osp.basename(dirname) - if this_is_a_testdir(basename): - print("going into", dirname) - # we found a testdir, let's explore it ! - if not self.testonedir(dirname, exitfirst): - break - dirs[:] = [] - if self.report.ran == 0: - print("no test dir found testing here:", here) - # if no test was found during the visit, consider - # the local directory as a test directory even if - # it doesn't have a traditional test directory name - self.testonedir(here) - - def testonedir(self, testdir, exitfirst=False): - """finds each testfile in the `testdir` and runs it - - return true when all tests has been executed, false if exitfirst and - some test has failed. - """ - files = abspath_listdir(testdir) - shuffle(files) - for filename in files: - if this_is_a_testfile(filename): - if self.options.exitfirst and not self.options.restart: - # overwrite restart file - try: - restartfile = open(FILE_RESTART, "w") - restartfile.close() - except Exception: - print("Error while overwriting succeeded test file :", - osp.join(os.getcwd(), FILE_RESTART), - file=sys.__stderr__) - raise - # run test and collect information - prog = self.testfile(filename, batchmode=True) - if exitfirst and (prog is None or not prog.result.wasSuccessful()): - return False - self.firstwrite = True - # clean local modules - remove_local_modules_from_sys(testdir) - return True - - def testfile(self, filename, batchmode=False): - """runs every test in `filename` - - :param filename: an absolute path pointing to a unittest file - """ - here = os.getcwd() - dirname = osp.dirname(filename) - if dirname: - os.chdir(dirname) - # overwrite restart file if it has not been done already - if self.options.exitfirst and not self.options.restart and self.firstwrite: - try: - restartfile = open(FILE_RESTART, "w") - restartfile.close() - except Exception: - print("Error while overwriting succeeded test file :", - osp.join(os.getcwd(), FILE_RESTART), file=sys.__stderr__) - raise - modname = osp.basename(filename)[:-3] - print((' %s ' % osp.basename(filename)).center(70, '='), - file=sys.__stderr__) - try: - tstart, cstart = time(), clock() - try: - testprog = SkipAwareTestProgram(modname, batchmode=batchmode, cvg=self.cvg, - options=self.options, outstream=sys.stderr) - except KeyboardInterrupt: - raise - except SystemExit as exc: - self.errcode = exc.code - raise - except testlib.SkipTest: - print("Module skipped:", filename) - self.report.skip_module(filename) - return None - except Exception: - self.report.failed_to_test_module(filename) - print('unhandled exception occurred while testing', modname, - file=sys.stderr) - import traceback - traceback.print_exc(file=sys.stderr) - return None - - tend, cend = time(), clock() - ttime, ctime = (tend - tstart), (cend - cstart) - self.report.feed(filename, testprog.result, ttime, ctime) - return testprog - finally: - if dirname: - os.chdir(here) - - - -class DjangoTester(PyTester): - - def load_django_settings(self, dirname): - """try to find project's setting and load it""" - curdir = osp.abspath(dirname) - previousdir = curdir - while not osp.isfile(osp.join(curdir, 'settings.py')) and \ - osp.isfile(osp.join(curdir, '__init__.py')): - newdir = osp.normpath(osp.join(curdir, os.pardir)) - if newdir == curdir: - raise AssertionError('could not find settings.py') - previousdir = curdir - curdir = newdir - # late django initialization - settings = load_module_from_modpath(modpath_from_file(osp.join(curdir, 'settings.py'))) - from django.core.management import setup_environ - setup_environ(settings) - settings.DEBUG = False - self.settings = settings - # add settings dir to pythonpath since it's the project's root - if curdir not in sys.path: - sys.path.insert(1, curdir) - - def before_testfile(self): - # Those imports must be done **after** setup_environ was called - from django.test.utils import setup_test_environment - from django.test.utils import create_test_db - setup_test_environment() - create_test_db(verbosity=0) - self.dbname = self.settings.TEST_DATABASE_NAME - - def after_testfile(self): - # Those imports must be done **after** setup_environ was called - from django.test.utils import teardown_test_environment - from django.test.utils import destroy_test_db - teardown_test_environment() - print('destroying', self.dbname) - destroy_test_db(self.dbname, verbosity=0) - - def testall(self, exitfirst=False): - """walks through current working directory, finds something - which can be considered as a testdir and runs every test there - """ - for dirname, dirs, files in os.walk(os.getcwd()): - for skipped in ('CVS', '.svn', '.hg'): - if skipped in dirs: - dirs.remove(skipped) - if 'tests.py' in files: - if not self.testonedir(dirname, exitfirst): - break - dirs[:] = [] - else: - basename = osp.basename(dirname) - if basename in ('test', 'tests'): - print("going into", dirname) - # we found a testdir, let's explore it ! - if not self.testonedir(dirname, exitfirst): - break - dirs[:] = [] - - def testonedir(self, testdir, exitfirst=False): - """finds each testfile in the `testdir` and runs it - - return true when all tests has been executed, false if exitfirst and - some test has failed. - """ - # special django behaviour : if tests are splitted in several files, - # remove the main tests.py file and tests each test file separately - testfiles = [fpath for fpath in abspath_listdir(testdir) - if this_is_a_testfile(fpath)] - if len(testfiles) > 1: - try: - testfiles.remove(osp.join(testdir, 'tests.py')) - except ValueError: - pass - for filename in testfiles: - # run test and collect information - prog = self.testfile(filename, batchmode=True) - if exitfirst and (prog is None or not prog.result.wasSuccessful()): - return False - # clean local modules - remove_local_modules_from_sys(testdir) - return True - - def testfile(self, filename, batchmode=False): - """runs every test in `filename` - - :param filename: an absolute path pointing to a unittest file - """ - here = os.getcwd() - dirname = osp.dirname(filename) - if dirname: - os.chdir(dirname) - self.load_django_settings(dirname) - modname = osp.basename(filename)[:-3] - print((' %s ' % osp.basename(filename)).center(70, '='), - file=sys.stderr) - try: - try: - tstart, cstart = time(), clock() - self.before_testfile() - testprog = SkipAwareTestProgram(modname, batchmode=batchmode, cvg=self.cvg) - tend, cend = time(), clock() - ttime, ctime = (tend - tstart), (cend - cstart) - self.report.feed(filename, testprog.result, ttime, ctime) - return testprog - except SystemExit: - raise - except Exception as exc: - import traceback - traceback.print_exc() - self.report.failed_to_test_module(filename) - print('unhandled exception occurred while testing', modname) - print('error: %s' % exc) - return None - finally: - self.after_testfile() - if dirname: - os.chdir(here) - - -def make_parser(): - """creates the OptionParser instance - """ - from optparse import OptionParser - parser = OptionParser(usage=PYTEST_DOC) - - parser.newargs = [] - def rebuild_cmdline(option, opt, value, parser): - """carry the option to unittest_main""" - parser.newargs.append(opt) - - def rebuild_and_store(option, opt, value, parser): - """carry the option to unittest_main and store - the value on current parser - """ - parser.newargs.append(opt) - setattr(parser.values, option.dest, True) - - def capture_and_rebuild(option, opt, value, parser): - warnings.simplefilter('ignore', DeprecationWarning) - rebuild_cmdline(option, opt, value, parser) - - # pytest options - parser.add_option('-t', dest='testdir', default=None, - help="directory where the tests will be found") - parser.add_option('-d', dest='dbc', default=False, - action="store_true", help="enable design-by-contract") - # unittest_main options provided and passed through pytest - parser.add_option('-v', '--verbose', callback=rebuild_cmdline, - action="callback", help="Verbose output") - parser.add_option('-i', '--pdb', callback=rebuild_and_store, - dest="pdb", action="callback", - help="Enable test failure inspection") - parser.add_option('-x', '--exitfirst', callback=rebuild_and_store, - dest="exitfirst", default=False, - action="callback", help="Exit on first failure " - "(only make sense when pytest run one test file)") - parser.add_option('-R', '--restart', callback=rebuild_and_store, - dest="restart", default=False, - action="callback", - help="Restart tests from where it failed (implies exitfirst) " - "(only make sense if tests previously ran with exitfirst only)") - parser.add_option('--color', callback=rebuild_cmdline, - action="callback", - help="colorize tracebacks") - parser.add_option('-s', '--skip', - # XXX: I wish I could use the callback action but it - # doesn't seem to be able to get the value - # associated to the option - action="store", dest="skipped", default=None, - help="test names matching this name will be skipped " - "to skip several patterns, use commas") - parser.add_option('-q', '--quiet', callback=rebuild_cmdline, - action="callback", help="Minimal output") - parser.add_option('-P', '--profile', default=None, dest='profile', - help="Profile execution and store data in the given file") - parser.add_option('-m', '--match', default=None, dest='tags_pattern', - help="only execute test whose tag match the current pattern") - - if DJANGO_FOUND: - parser.add_option('-J', '--django', dest='django', default=False, - action="store_true", - help='use pytest for django test cases') - return parser - - -def parseargs(parser): - """Parse the command line and return (options processed), (options to pass to - unittest_main()), (explicitfile or None). - """ - # parse the command line - options, args = parser.parse_args() - filenames = [arg for arg in args if arg.endswith('.py')] - if filenames: - if len(filenames) > 1: - parser.error("only one filename is acceptable") - explicitfile = filenames[0] - args.remove(explicitfile) - else: - explicitfile = None - # someone wants DBC - testlib.ENABLE_DBC = options.dbc - newargs = parser.newargs - if options.skipped: - newargs.extend(['--skip', options.skipped]) - # restart implies exitfirst - if options.restart: - options.exitfirst = True - # append additional args to the new sys.argv and let unittest_main - # do the rest - newargs += args - return options, explicitfile - - - -def run(): - parser = make_parser() - rootdir, testercls = project_root(parser) - options, explicitfile = parseargs(parser) - # mock a new command line - sys.argv[1:] = parser.newargs - cvg = None - if not '' in sys.path: - sys.path.insert(0, '') - if DJANGO_FOUND and options.django: - tester = DjangoTester(cvg, options) - else: - tester = testercls(cvg, options) - if explicitfile: - cmd, args = tester.testfile, (explicitfile,) - elif options.testdir: - cmd, args = tester.testonedir, (options.testdir, options.exitfirst) - else: - cmd, args = tester.testall, (options.exitfirst,) - try: - try: - if options.profile: - import hotshot - prof = hotshot.Profile(options.profile) - prof.runcall(cmd, *args) - prof.close() - print('profile data saved in', options.profile) - else: - cmd(*args) - except SystemExit: - raise - except: - import traceback - traceback.print_exc() - finally: - tester.show_report() - sys.exit(tester.errcode) - -class SkipAwareTestProgram(unittest.TestProgram): - # XXX: don't try to stay close to unittest.py, use optparse - USAGE = """\ -Usage: %(progName)s [options] [test] [...] - -Options: - -h, --help Show this message - -v, --verbose Verbose output - -i, --pdb Enable test failure inspection - -x, --exitfirst Exit on first failure - -s, --skip skip test matching this pattern (no regexp for now) - -q, --quiet Minimal output - --color colorize tracebacks - - -m, --match Run only test whose tag match this pattern - - -P, --profile FILE: Run the tests using cProfile and saving results - in FILE - -Examples: - %(progName)s - run default set of tests - %(progName)s MyTestSuite - run suite 'MyTestSuite' - %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething - %(progName)s MyTestCase - run all 'test*' test methods - in MyTestCase -""" - def __init__(self, module='__main__', defaultTest=None, batchmode=False, - cvg=None, options=None, outstream=sys.stderr): - self.batchmode = batchmode - self.cvg = cvg - self.options = options - self.outstream = outstream - super(SkipAwareTestProgram, self).__init__( - module=module, defaultTest=defaultTest, - testLoader=NonStrictTestLoader()) - - def parseArgs(self, argv): - self.pdbmode = False - self.exitfirst = False - self.skipped_patterns = [] - self.test_pattern = None - self.tags_pattern = None - self.colorize = False - self.profile_name = None - import getopt - try: - options, args = getopt.getopt(argv[1:], 'hHvixrqcp:s:m:P:', - ['help', 'verbose', 'quiet', 'pdb', - 'exitfirst', 'restart', - 'skip=', 'color', 'match=', 'profile=']) - for opt, value in options: - if opt in ('-h', '-H', '--help'): - self.usageExit() - if opt in ('-i', '--pdb'): - self.pdbmode = True - if opt in ('-x', '--exitfirst'): - self.exitfirst = True - if opt in ('-r', '--restart'): - self.restart = True - self.exitfirst = True - if opt in ('-q', '--quiet'): - self.verbosity = 0 - if opt in ('-v', '--verbose'): - self.verbosity = 2 - if opt in ('-s', '--skip'): - self.skipped_patterns = [pat.strip() for pat in - value.split(', ')] - if opt == '--color': - self.colorize = True - if opt in ('-m', '--match'): - #self.tags_pattern = value - self.options["tag_pattern"] = value - if opt in ('-P', '--profile'): - self.profile_name = value - self.testLoader.skipped_patterns = self.skipped_patterns - if len(args) == 0 and self.defaultTest is None: - suitefunc = getattr(self.module, 'suite', None) - if isinstance(suitefunc, (types.FunctionType, - types.MethodType)): - self.test = self.module.suite() - else: - self.test = self.testLoader.loadTestsFromModule(self.module) - return - if len(args) > 0: - self.test_pattern = args[0] - self.testNames = args - else: - self.testNames = (self.defaultTest, ) - self.createTests() - except getopt.error as msg: - self.usageExit(msg) - - def runTests(self): - if self.profile_name: - import cProfile - cProfile.runctx('self._runTests()', globals(), locals(), self.profile_name ) - else: - return self._runTests() - - def _runTests(self): - self.testRunner = SkipAwareTextTestRunner(verbosity=self.verbosity, - stream=self.outstream, - exitfirst=self.exitfirst, - pdbmode=self.pdbmode, - cvg=self.cvg, - test_pattern=self.test_pattern, - skipped_patterns=self.skipped_patterns, - colorize=self.colorize, - batchmode=self.batchmode, - options=self.options) - - def removeSucceededTests(obj, succTests): - """ Recursive function that removes succTests from - a TestSuite or TestCase - """ - if isinstance(obj, unittest.TestSuite): - removeSucceededTests(obj._tests, succTests) - if isinstance(obj, list): - for el in obj[:]: - if isinstance(el, unittest.TestSuite): - removeSucceededTests(el, succTests) - elif isinstance(el, unittest.TestCase): - descr = '.'.join((el.__class__.__module__, - el.__class__.__name__, - el._testMethodName)) - if descr in succTests: - obj.remove(el) - # take care, self.options may be None - if getattr(self.options, 'restart', False): - # retrieve succeeded tests from FILE_RESTART - try: - restartfile = open(FILE_RESTART, 'r') - try: - succeededtests = list(elem.rstrip('\n\r') for elem in - restartfile.readlines()) - removeSucceededTests(self.test, succeededtests) - finally: - restartfile.close() - except Exception as ex: - raise Exception("Error while reading succeeded tests into %s: %s" - % (osp.join(os.getcwd(), FILE_RESTART), ex)) - - result = self.testRunner.run(self.test) - # help garbage collection: we want TestSuite, which hold refs to every - # executed TestCase, to be gc'ed - del self.test - if getattr(result, "debuggers", None) and \ - getattr(self, "pdbmode", None): - start_interactive_mode(result) - if not getattr(self, "batchmode", None): - sys.exit(not result.wasSuccessful()) - self.result = result - - -class SkipAwareTextTestRunner(unittest.TextTestRunner): - - def __init__(self, stream=sys.stderr, verbosity=1, - exitfirst=False, pdbmode=False, cvg=None, test_pattern=None, - skipped_patterns=(), colorize=False, batchmode=False, - options=None): - super(SkipAwareTextTestRunner, self).__init__(stream=stream, - verbosity=verbosity) - self.exitfirst = exitfirst - self.pdbmode = pdbmode - self.cvg = cvg - self.test_pattern = test_pattern - self.skipped_patterns = skipped_patterns - self.colorize = colorize - self.batchmode = batchmode - self.options = options - - def _this_is_skipped(self, testedname): - return any([(pat in testedname) for pat in self.skipped_patterns]) - - def _runcondition(self, test, skipgenerator=True): - if isinstance(test, testlib.InnerTest): - testname = test.name - else: - if isinstance(test, testlib.TestCase): - meth = test._get_test_method() - testname = '%s.%s' % (test.__name__, meth.__name__) - elif isinstance(test, types.FunctionType): - func = test - testname = func.__name__ - elif isinstance(test, types.MethodType): - cls = test.__self__.__class__ - testname = '%s.%s' % (cls.__name__, test.__name__) - else: - return True # Not sure when this happens - if isgeneratorfunction(test) and skipgenerator: - return self.does_match_tags(test) # Let inner tests decide at run time - if self._this_is_skipped(testname): - return False # this was explicitly skipped - if self.test_pattern is not None: - try: - classpattern, testpattern = self.test_pattern.split('.') - klass, name = testname.split('.') - if classpattern not in klass or testpattern not in name: - return False - except ValueError: - if self.test_pattern not in testname: - return False - - return self.does_match_tags(test) - - def does_match_tags(self, test): - if self.options is not None: - tags_pattern = getattr(self.options, 'tags_pattern', None) - if tags_pattern is not None: - tags = getattr(test, 'tags', testlib.Tags()) - if tags.inherit and isinstance(test, types.MethodType): - tags = tags | getattr(test.__self__.__class__, 'tags', testlib.Tags()) - return tags.match(tags_pattern) - return True # no pattern - - def _makeResult(self): - return testlib.SkipAwareTestResult(self.stream, self.descriptions, - self.verbosity, self.exitfirst, - self.pdbmode, self.cvg, self.colorize) - - def run(self, test): - "Run the given test case or test suite." - result = self._makeResult() - startTime = time() - test(result, runcondition=self._runcondition, options=self.options) - stopTime = time() - timeTaken = stopTime - startTime - result.printErrors() - if not self.batchmode: - self.stream.writeln(result.separator2) - run = result.testsRun - self.stream.writeln("Ran %d test%s in %.3fs" % - (run, run != 1 and "s" or "", timeTaken)) - self.stream.writeln() - if not result.wasSuccessful(): - if self.colorize: - self.stream.write(textutils.colorize_ansi("FAILED", color='red')) - else: - self.stream.write("FAILED") - else: - if self.colorize: - self.stream.write(textutils.colorize_ansi("OK", color='green')) - else: - self.stream.write("OK") - failed, errored, skipped = map(len, (result.failures, - result.errors, - result.skipped)) - - det_results = [] - for name, value in (("failures", result.failures), - ("errors",result.errors), - ("skipped", result.skipped)): - if value: - det_results.append("%s=%i" % (name, len(value))) - if det_results: - self.stream.write(" (") - self.stream.write(', '.join(det_results)) - self.stream.write(")") - self.stream.writeln("") - return result - -class NonStrictTestLoader(unittest.TestLoader): - """ - Overrides default testloader to be able to omit classname when - specifying tests to run on command line. - - For example, if the file test_foo.py contains :: - - class FooTC(TestCase): - def test_foo1(self): # ... - def test_foo2(self): # ... - def test_bar1(self): # ... - - class BarTC(TestCase): - def test_bar2(self): # ... - - 'python test_foo.py' will run the 3 tests in FooTC - 'python test_foo.py FooTC' will run the 3 tests in FooTC - 'python test_foo.py test_foo' will run test_foo1 and test_foo2 - 'python test_foo.py test_foo1' will run test_foo1 - 'python test_foo.py test_bar' will run FooTC.test_bar1 and BarTC.test_bar2 - """ - - def __init__(self): - self.skipped_patterns = () - - # some magic here to accept empty list by extending - # and to provide callable capability - def loadTestsFromNames(self, names, module=None): - suites = [] - for name in names: - suites.extend(self.loadTestsFromName(name, module)) - return self.suiteClass(suites) - - def _collect_tests(self, module): - tests = {} - for obj in vars(module).values(): - if isclass(obj) and issubclass(obj, unittest.TestCase): - classname = obj.__name__ - if classname[0] == '_' or self._this_is_skipped(classname): - continue - methodnames = [] - # obj is a TestCase class - for attrname in dir(obj): - if attrname.startswith(self.testMethodPrefix): - attr = getattr(obj, attrname) - if callable(attr): - methodnames.append(attrname) - # keep track of class (obj) for convenience - tests[classname] = (obj, methodnames) - return tests - - def loadTestsFromSuite(self, module, suitename): - try: - suite = getattr(module, suitename)() - except AttributeError: - return [] - assert hasattr(suite, '_tests'), \ - "%s.%s is not a valid TestSuite" % (module.__name__, suitename) - # python2.3 does not implement __iter__ on suites, we need to return - # _tests explicitly - return suite._tests - - def loadTestsFromName(self, name, module=None): - parts = name.split('.') - if module is None or len(parts) > 2: - # let the base class do its job here - return [super(NonStrictTestLoader, self).loadTestsFromName(name)] - tests = self._collect_tests(module) - collected = [] - if len(parts) == 1: - pattern = parts[0] - if callable(getattr(module, pattern, None) - ) and pattern not in tests: - # consider it as a suite - return self.loadTestsFromSuite(module, pattern) - if pattern in tests: - # case python unittest_foo.py MyTestTC - klass, methodnames = tests[pattern] - for methodname in methodnames: - collected = [klass(methodname) - for methodname in methodnames] - else: - # case python unittest_foo.py something - for klass, methodnames in tests.values(): - # skip methodname if matched by skipped_patterns - for skip_pattern in self.skipped_patterns: - methodnames = [methodname - for methodname in methodnames - if skip_pattern not in methodname] - collected += [klass(methodname) - for methodname in methodnames - if pattern in methodname] - elif len(parts) == 2: - # case "MyClass.test_1" - classname, pattern = parts - klass, methodnames = tests.get(classname, (None, [])) - for methodname in methodnames: - collected = [klass(methodname) for methodname in methodnames - if pattern in methodname] - return collected - - def _this_is_skipped(self, testedname): - return any([(pat in testedname) for pat in self.skipped_patterns]) - - def getTestCaseNames(self, testCaseClass): - """Return a sorted sequence of method names found within testCaseClass - """ - is_skipped = self._this_is_skipped - classname = testCaseClass.__name__ - if classname[0] == '_' or is_skipped(classname): - return [] - testnames = super(NonStrictTestLoader, self).getTestCaseNames( - testCaseClass) - return [testname for testname in testnames if not is_skipped(testname)] - - -# The 2 functions below are modified versions of the TestSuite.run method -# that is provided with unittest2 for python 2.6, in unittest2/suite.py -# It is used to monkeypatch the original implementation to support -# extra runcondition and options arguments (see in testlib.py) - -def _ts_run(self, result, runcondition=None, options=None): - self._wrapped_run(result, runcondition=runcondition, options=options) - self._tearDownPreviousClass(None, result) - self._handleModuleTearDown(result) - return result - -def _ts_wrapped_run(self, result, debug=False, runcondition=None, options=None): - for test in self: - if result.shouldStop: - break - if unittest_suite._isnotsuite(test): - self._tearDownPreviousClass(test, result) - self._handleModuleFixture(test, result) - self._handleClassSetUp(test, result) - result._previousTestClass = test.__class__ - if (getattr(test.__class__, '_classSetupFailed', False) or - getattr(result, '_moduleSetUpFailed', False)): - continue - - # --- modifications to deal with _wrapped_run --- - # original code is: - # - # if not debug: - # test(result) - # else: - # test.debug() - if hasattr(test, '_wrapped_run'): - try: - test._wrapped_run(result, debug, runcondition=runcondition, options=options) - except TypeError: - test._wrapped_run(result, debug) - elif not debug: - try: - test(result, runcondition, options) - except TypeError: - test(result) - else: - test.debug() - # --- end of modifications to deal with _wrapped_run --- - return result - -if sys.version_info >= (2, 7): - # The function below implements a modified version of the - # TestSuite.run method that is provided with python 2.7, in - # unittest/suite.py - def _ts_run(self, result, debug=False, runcondition=None, options=None): - topLevel = False - if getattr(result, '_testRunEntered', False) is False: - result._testRunEntered = topLevel = True - - self._wrapped_run(result, debug, runcondition, options) - - if topLevel: - self._tearDownPreviousClass(None, result) - self._handleModuleTearDown(result) - result._testRunEntered = False - return result - - -def enable_dbc(*args): - """ - Without arguments, return True if contracts can be enabled and should be - enabled (see option -d), return False otherwise. - - With arguments, return False if contracts can't or shouldn't be enabled, - otherwise weave ContractAspect with items passed as arguments. - """ - if not ENABLE_DBC: - return False - try: - from logilab.aspects.weaver import weaver - from logilab.aspects.lib.contracts import ContractAspect - except ImportError: - sys.stderr.write( - 'Warning: logilab.aspects is not available. Contracts disabled.') - return False - for arg in args: - weaver.weave_module(arg, ContractAspect) - return True - - -# monkeypatch unittest and doctest (ouch !) -unittest._TextTestResult = testlib.SkipAwareTestResult -unittest.TextTestRunner = SkipAwareTextTestRunner -unittest.TestLoader = NonStrictTestLoader -unittest.TestProgram = SkipAwareTestProgram - -if sys.version_info >= (2, 4): - doctest.DocTestCase.__bases__ = (testlib.TestCase,) - # XXX check python2.6 compatibility - #doctest.DocTestCase._cleanups = [] - #doctest.DocTestCase._out = [] -else: - unittest.FunctionTestCase.__bases__ = (testlib.TestCase,) -unittest.TestSuite.run = _ts_run -unittest.TestSuite._wrapped_run = _ts_wrapped_run diff --git a/pymode/libs/logilab/common/registry.py b/pymode/libs/logilab/common/registry.py deleted file mode 100644 index 86a85f94..00000000 --- a/pymode/libs/logilab/common/registry.py +++ /dev/null @@ -1,1125 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of Logilab-common. -# -# Logilab-common is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# Logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with Logilab-common. If not, see . -"""This module provides bases for predicates dispatching (the pattern in use -here is similar to what's refered as multi-dispatch or predicate-dispatch in the -literature, though a bit different since the idea is to select across different -implementation 'e.g. classes), not to dispatch a message to a function or -method. It contains the following classes: - -* :class:`RegistryStore`, the top level object which loads implementation - objects and stores them into registries. You'll usually use it to access - registries and their contained objects; - -* :class:`Registry`, the base class which contains objects semantically grouped - (for instance, sharing a same API, hence the 'implementation' name). You'll - use it to select the proper implementation according to a context. Notice you - may use registries on their own without using the store. - -.. Note:: - - implementation objects are usually designed to be accessed through the - registry and not by direct instantiation, besides to use it as base classe. - -The selection procedure is delegated to a selector, which is responsible for -scoring the object according to some context. At the end of the selection, if an -implementation has been found, an instance of this class is returned. A selector -is built from one or more predicates combined together using AND, OR, NOT -operators (actually `&`, `|` and `~`). You'll thus find some base classes to -build predicates: - -* :class:`Predicate`, the abstract base predicate class - -* :class:`AndPredicate`, :class:`OrPredicate`, :class:`NotPredicate`, which you - shouldn't have to use directly. You'll use `&`, `|` and '~' operators between - predicates directly - -* :func:`objectify_predicate` - -You'll eventually find one concrete predicate: :class:`yes` - -.. autoclass:: RegistryStore -.. autoclass:: Registry - -Predicates ----------- -.. autoclass:: Predicate -.. autofunc:: objectify_predicate -.. autoclass:: yes - -Debugging ---------- -.. autoclass:: traced_selection - -Exceptions ----------- -.. autoclass:: RegistryException -.. autoclass:: RegistryNotFound -.. autoclass:: ObjectNotFound -.. autoclass:: NoSelectableObject -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import sys -import types -import weakref -import traceback as tb -from os import listdir, stat -from os.path import join, isdir, exists -from logging import getLogger -from warnings import warn - -from six import string_types, add_metaclass - -from logilab.common.modutils import modpath_from_file -from logilab.common.logging_ext import set_log_methods -from logilab.common.decorators import classproperty - - -class RegistryException(Exception): - """Base class for registry exception.""" - -class RegistryNotFound(RegistryException): - """Raised when an unknown registry is requested. - - This is usually a programming/typo error. - """ - -class ObjectNotFound(RegistryException): - """Raised when an unregistered object is requested. - - This may be a programming/typo or a misconfiguration error. - """ - -class NoSelectableObject(RegistryException): - """Raised when no object is selectable for a given context.""" - def __init__(self, args, kwargs, objects): - self.args = args - self.kwargs = kwargs - self.objects = objects - - def __str__(self): - return ('args: %s, kwargs: %s\ncandidates: %s' - % (self.args, self.kwargs.keys(), self.objects)) - -class SelectAmbiguity(RegistryException): - """Raised when several objects compete at selection time with an equal - score. - - """ - - -def _modname_from_path(path, extrapath=None): - modpath = modpath_from_file(path, extrapath) - # omit '__init__' from package's name to avoid loading that module - # once for each name when it is imported by some other object - # module. This supposes import in modules are done as:: - # - # from package import something - # - # not:: - # - # from package.__init__ import something - # - # which seems quite correct. - if modpath[-1] == '__init__': - modpath.pop() - return '.'.join(modpath) - - -def _toload_info(path, extrapath, _toload=None): - """Return a dictionary of : and an ordered list of - (file, module name) to load - """ - if _toload is None: - assert isinstance(path, list) - _toload = {}, [] - for fileordir in path: - if isdir(fileordir) and exists(join(fileordir, '__init__.py')): - subfiles = [join(fileordir, fname) for fname in listdir(fileordir)] - _toload_info(subfiles, extrapath, _toload) - elif fileordir[-3:] == '.py': - modname = _modname_from_path(fileordir, extrapath) - _toload[0][modname] = fileordir - _toload[1].append((fileordir, modname)) - return _toload - - -class RegistrableObject(object): - """This is the base class for registrable objects which are selected - according to a context. - - :attr:`__registry__` - name of the registry for this object (string like 'views', - 'templates'...). You may want to define `__registries__` directly if your - object should be registered in several registries. - - :attr:`__regid__` - object's identifier in the registry (string like 'main', - 'primary', 'folder_box') - - :attr:`__select__` - class'selector - - Moreover, the `__abstract__` attribute may be set to True to indicate that a - class is abstract and should not be registered. - - You don't have to inherit from this class to put it in a registry (having - `__regid__` and `__select__` is enough), though this is needed for classes - that should be automatically registered. - """ - - __registry__ = None - __regid__ = None - __select__ = None - __abstract__ = True # see doc snipppets below (in Registry class) - - @classproperty - def __registries__(cls): - if cls.__registry__ is None: - return () - return (cls.__registry__,) - - -class RegistrableInstance(RegistrableObject): - """Inherit this class if you want instances of the classes to be - automatically registered. - """ - - def __new__(cls, *args, **kwargs): - """Add a __module__ attribute telling the module where the instance was - created, for automatic registration. - """ - obj = super(RegistrableInstance, cls).__new__(cls) - # XXX subclass must no override __new__ - filepath = tb.extract_stack(limit=2)[0][0] - obj.__module__ = _modname_from_path(filepath) - return obj - - -class Registry(dict): - """The registry store a set of implementations associated to identifier: - - * to each identifier are associated a list of implementations - - * to select an implementation of a given identifier, you should use one of the - :meth:`select` or :meth:`select_or_none` method - - * to select a list of implementations for a context, you should use the - :meth:`possible_objects` method - - * dictionary like access to an identifier will return the bare list of - implementations for this identifier. - - To be usable in a registry, the only requirement is to have a `__select__` - attribute. - - At the end of the registration process, the :meth:`__registered__` - method is called on each registered object which have them, given the - registry in which it's registered as argument. - - Registration methods: - - .. automethod: register - .. automethod: unregister - - Selection methods: - - .. automethod: select - .. automethod: select_or_none - .. automethod: possible_objects - .. automethod: object_by_id - """ - def __init__(self, debugmode): - super(Registry, self).__init__() - self.debugmode = debugmode - - def __getitem__(self, name): - """return the registry (list of implementation objects) associated to - this name - """ - try: - return super(Registry, self).__getitem__(name) - except KeyError: - exc = ObjectNotFound(name) - exc.__traceback__ = sys.exc_info()[-1] - raise exc - - @classmethod - def objid(cls, obj): - """returns a unique identifier for an object stored in the registry""" - return '%s.%s' % (obj.__module__, cls.objname(obj)) - - @classmethod - def objname(cls, obj): - """returns a readable name for an object stored in the registry""" - return getattr(obj, '__name__', id(obj)) - - def initialization_completed(self): - """call method __registered__() on registered objects when the callback - is defined""" - for objects in self.values(): - for objectcls in objects: - registered = getattr(objectcls, '__registered__', None) - if registered: - registered(self) - if self.debugmode: - wrap_predicates(_lltrace) - - def register(self, obj, oid=None, clear=False): - """base method to add an object in the registry""" - assert not '__abstract__' in obj.__dict__, obj - assert obj.__select__, obj - oid = oid or obj.__regid__ - assert oid, ('no explicit name supplied to register object %s, ' - 'which has no __regid__ set' % obj) - if clear: - objects = self[oid] = [] - else: - objects = self.setdefault(oid, []) - assert not obj in objects, 'object %s is already registered' % obj - objects.append(obj) - - def register_and_replace(self, obj, replaced): - """remove and register """ - # XXXFIXME this is a duplication of unregister() - # remove register_and_replace in favor of unregister + register - # or simplify by calling unregister then register here - if not isinstance(replaced, string_types): - replaced = self.objid(replaced) - # prevent from misspelling - assert obj is not replaced, 'replacing an object by itself: %s' % obj - registered_objs = self.get(obj.__regid__, ()) - for index, registered in enumerate(registered_objs): - if self.objid(registered) == replaced: - del registered_objs[index] - break - else: - self.warning('trying to replace %s that is not registered with %s', - replaced, obj) - self.register(obj) - - def unregister(self, obj): - """remove object from this registry""" - objid = self.objid(obj) - oid = obj.__regid__ - for registered in self.get(oid, ()): - # use self.objid() to compare objects because vreg will probably - # have its own version of the object, loaded through execfile - if self.objid(registered) == objid: - self[oid].remove(registered) - break - else: - self.warning('can\'t remove %s, no id %s in the registry', - objid, oid) - - def all_objects(self): - """return a list containing all objects in this registry. - """ - result = [] - for objs in self.values(): - result += objs - return result - - # dynamic selection methods ################################################ - - def object_by_id(self, oid, *args, **kwargs): - """return object with the `oid` identifier. Only one object is expected - to be found. - - raise :exc:`ObjectNotFound` if there are no object with id `oid` in this - registry - - raise :exc:`AssertionError` if there is more than one object there - """ - objects = self[oid] - assert len(objects) == 1, objects - return objects[0](*args, **kwargs) - - def select(self, __oid, *args, **kwargs): - """return the most specific object among those with the given oid - according to the given context. - - raise :exc:`ObjectNotFound` if there are no object with id `oid` in this - registry - - raise :exc:`NoSelectableObject` if no object can be selected - """ - obj = self._select_best(self[__oid], *args, **kwargs) - if obj is None: - raise NoSelectableObject(args, kwargs, self[__oid] ) - return obj - - def select_or_none(self, __oid, *args, **kwargs): - """return the most specific object among those with the given oid - according to the given context, or None if no object applies. - """ - try: - return self._select_best(self[__oid], *args, **kwargs) - except ObjectNotFound: - return None - - def possible_objects(self, *args, **kwargs): - """return an iterator on possible objects in this registry for the given - context - """ - for objects in self.values(): - obj = self._select_best(objects, *args, **kwargs) - if obj is None: - continue - yield obj - - def _select_best(self, objects, *args, **kwargs): - """return an instance of the most specific object according - to parameters - - return None if not object apply (don't raise `NoSelectableObject` since - it's costly when searching objects using `possible_objects` - (e.g. searching for hooks). - """ - score, winners = 0, None - for obj in objects: - objectscore = obj.__select__(obj, *args, **kwargs) - if objectscore > score: - score, winners = objectscore, [obj] - elif objectscore > 0 and objectscore == score: - winners.append(obj) - if winners is None: - return None - if len(winners) > 1: - # log in production environement / test, error while debugging - msg = 'select ambiguity: %s\n(args: %s, kwargs: %s)' - if self.debugmode: - # raise bare exception in debug mode - raise SelectAmbiguity(msg % (winners, args, kwargs.keys())) - self.error(msg, winners, args, kwargs.keys()) - # return the result of calling the object - return self.selected(winners[0], args, kwargs) - - def selected(self, winner, args, kwargs): - """override here if for instance you don't want "instanciation" - """ - return winner(*args, **kwargs) - - # these are overridden by set_log_methods below - # only defining here to prevent pylint from complaining - info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None - - -def obj_registries(cls, registryname=None): - """return a tuple of registry names (see __registries__)""" - if registryname: - return (registryname,) - return cls.__registries__ - - -class RegistryStore(dict): - """This class is responsible for loading objects and storing them - in their registry which is created on the fly as needed. - - It handles dynamic registration of objects and provides a - convenient api to access them. To be recognized as an object that - should be stored into one of the store's registry - (:class:`Registry`), an object must provide the following - attributes, used control how they interact with the registry: - - :attr:`__registries__` - list of registry names (string like 'views', 'templates'...) into which - the object should be registered - - :attr:`__regid__` - object identifier in the registry (string like 'main', - 'primary', 'folder_box') - - :attr:`__select__` - the object predicate selectors - - Moreover, the :attr:`__abstract__` attribute may be set to `True` - to indicate that an object is abstract and should not be registered - (such inherited attributes not considered). - - .. Note:: - - When using the store to load objects dynamically, you *always* have - to use **super()** to get the methods and attributes of the - superclasses, and not use the class identifier. If not, you'll get into - trouble at reload time. - - For example, instead of writing:: - - class Thing(Parent): - __regid__ = 'athing' - __select__ = yes() - - def f(self, arg1): - Parent.f(self, arg1) - - You must write:: - - class Thing(Parent): - __regid__ = 'athing' - __select__ = yes() - - def f(self, arg1): - super(Thing, self).f(arg1) - - Controlling object registration - ------------------------------- - - Dynamic loading is triggered by calling the - :meth:`register_objects` method, given a list of directories to - inspect for python modules. - - .. automethod: register_objects - - For each module, by default, all compatible objects are registered - automatically. However if some objects come as replacement of - other objects, or have to be included only if some condition is - met, you'll have to define a `registration_callback(vreg)` - function in the module and explicitly register **all objects** in - this module, using the api defined below. - - - .. automethod:: RegistryStore.register_all - .. automethod:: RegistryStore.register_and_replace - .. automethod:: RegistryStore.register - .. automethod:: RegistryStore.unregister - - .. Note:: - Once the function `registration_callback(vreg)` is implemented in a - module, all the objects from this module have to be explicitly - registered as it disables the automatic object registration. - - - Examples: - - .. sourcecode:: python - - def registration_callback(store): - # register everything in the module except BabarClass - store.register_all(globals().values(), __name__, (BabarClass,)) - - # conditionally register BabarClass - if 'babar_relation' in store.schema: - store.register(BabarClass) - - In this example, we register all application object classes defined in the module - except `BabarClass`. This class is then registered only if the 'babar_relation' - relation type is defined in the instance schema. - - .. sourcecode:: python - - def registration_callback(store): - store.register(Elephant) - # replace Babar by Celeste - store.register_and_replace(Celeste, Babar) - - In this example, we explicitly register classes one by one: - - * the `Elephant` class - * the `Celeste` to replace `Babar` - - If at some point we register a new appobject class in this module, it won't be - registered at all without modification to the `registration_callback` - implementation. The first example will register it though, thanks to the call - to the `register_all` method. - - Controlling registry instantiation - ---------------------------------- - - The `REGISTRY_FACTORY` class dictionary allows to specify which class should - be instantiated for a given registry name. The class associated to `None` - key will be the class used when there is no specific class for a name. - """ - - def __init__(self, debugmode=False): - super(RegistryStore, self).__init__() - self.debugmode = debugmode - - def reset(self): - """clear all registries managed by this store""" - # don't use self.clear, we want to keep existing subdictionaries - for subdict in self.values(): - subdict.clear() - self._lastmodifs = {} - - def __getitem__(self, name): - """return the registry (dictionary of class objects) associated to - this name - """ - try: - return super(RegistryStore, self).__getitem__(name) - except KeyError: - exc = RegistryNotFound(name) - exc.__traceback__ = sys.exc_info()[-1] - raise exc - - # methods for explicit (un)registration ################################### - - # default class, when no specific class set - REGISTRY_FACTORY = {None: Registry} - - def registry_class(self, regid): - """return existing registry named regid or use factory to create one and - return it""" - try: - return self.REGISTRY_FACTORY[regid] - except KeyError: - return self.REGISTRY_FACTORY[None] - - def setdefault(self, regid): - try: - return self[regid] - except RegistryNotFound: - self[regid] = self.registry_class(regid)(self.debugmode) - return self[regid] - - def register_all(self, objects, modname, butclasses=()): - """register registrable objects into `objects`. - - Registrable objects are properly configured subclasses of - :class:`RegistrableObject`. Objects which are not defined in the module - `modname` or which are in `butclasses` won't be registered. - - Typical usage is: - - .. sourcecode:: python - - store.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,)) - - So you get partially automatic registration, keeping manual registration - for some object (to use - :meth:`~logilab.common.registry.RegistryStore.register_and_replace` for - instance). - """ - assert isinstance(modname, string_types), \ - 'modname expected to be a module name (ie string), got %r' % modname - for obj in objects: - if self.is_registrable(obj) and obj.__module__ == modname and not obj in butclasses: - if isinstance(obj, type): - self._load_ancestors_then_object(modname, obj, butclasses) - else: - self.register(obj) - - def register(self, obj, registryname=None, oid=None, clear=False): - """register `obj` implementation into `registryname` or - `obj.__registries__` if not specified, with identifier `oid` or - `obj.__regid__` if not specified. - - If `clear` is true, all objects with the same identifier will be - previously unregistered. - """ - assert not obj.__dict__.get('__abstract__'), obj - for registryname in obj_registries(obj, registryname): - registry = self.setdefault(registryname) - registry.register(obj, oid=oid, clear=clear) - self.debug("register %s in %s['%s']", - registry.objname(obj), registryname, oid or obj.__regid__) - self._loadedmods.setdefault(obj.__module__, {})[registry.objid(obj)] = obj - - def unregister(self, obj, registryname=None): - """unregister `obj` object from the registry `registryname` or - `obj.__registries__` if not specified. - """ - for registryname in obj_registries(obj, registryname): - registry = self[registryname] - registry.unregister(obj) - self.debug("unregister %s from %s['%s']", - registry.objname(obj), registryname, obj.__regid__) - - def register_and_replace(self, obj, replaced, registryname=None): - """register `obj` object into `registryname` or - `obj.__registries__` if not specified. If found, the `replaced` object - will be unregistered first (else a warning will be issued as it is - generally unexpected). - """ - for registryname in obj_registries(obj, registryname): - registry = self[registryname] - registry.register_and_replace(obj, replaced) - self.debug("register %s in %s['%s'] instead of %s", - registry.objname(obj), registryname, obj.__regid__, - registry.objname(replaced)) - - # initialization methods ################################################### - - def init_registration(self, path, extrapath=None): - """reset registry and walk down path to return list of (path, name) - file modules to be loaded""" - # XXX make this private by renaming it to _init_registration ? - self.reset() - # compute list of all modules that have to be loaded - self._toloadmods, filemods = _toload_info(path, extrapath) - # XXX is _loadedmods still necessary ? It seems like it's useful - # to avoid loading same module twice, especially with the - # _load_ancestors_then_object logic but this needs to be checked - self._loadedmods = {} - return filemods - - def register_objects(self, path, extrapath=None): - """register all objects found walking down """ - # load views from each directory in the instance's path - # XXX inline init_registration ? - filemods = self.init_registration(path, extrapath) - for filepath, modname in filemods: - self.load_file(filepath, modname) - self.initialization_completed() - - def initialization_completed(self): - """call initialization_completed() on all known registries""" - for reg in self.values(): - reg.initialization_completed() - - def _mdate(self, filepath): - """ return the modification date of a file path """ - try: - return stat(filepath)[-2] - except OSError: - # this typically happens on emacs backup files (.#foo.py) - self.warning('Unable to load %s. It is likely to be a backup file', - filepath) - return None - - def is_reload_needed(self, path): - """return True if something module changed and the registry should be - reloaded - """ - lastmodifs = self._lastmodifs - for fileordir in path: - if isdir(fileordir) and exists(join(fileordir, '__init__.py')): - if self.is_reload_needed([join(fileordir, fname) - for fname in listdir(fileordir)]): - return True - elif fileordir[-3:] == '.py': - mdate = self._mdate(fileordir) - if mdate is None: - continue # backup file, see _mdate implementation - elif "flymake" in fileordir: - # flymake + pylint in use, don't consider these they will corrupt the registry - continue - if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate: - self.info('File %s changed since last visit', fileordir) - return True - return False - - def load_file(self, filepath, modname): - """ load registrable objects (if any) from a python file """ - from logilab.common.modutils import load_module_from_name - if modname in self._loadedmods: - return - self._loadedmods[modname] = {} - mdate = self._mdate(filepath) - if mdate is None: - return # backup file, see _mdate implementation - elif "flymake" in filepath: - # flymake + pylint in use, don't consider these they will corrupt the registry - return - # set update time before module loading, else we get some reloading - # weirdness in case of syntax error or other error while importing the - # module - self._lastmodifs[filepath] = mdate - # load the module - module = load_module_from_name(modname) - self.load_module(module) - - def load_module(self, module): - """Automatically handle module objects registration. - - Instances are registered as soon as they are hashable and have the - following attributes: - - * __regid__ (a string) - * __select__ (a callable) - * __registries__ (a tuple/list of string) - - For classes this is a bit more complicated : - - - first ensure parent classes are already registered - - - class with __abstract__ == True in their local dictionary are skipped - - - object class needs to have registries and identifier properly set to a - non empty string to be registered. - """ - self.info('loading %s from %s', module.__name__, module.__file__) - if hasattr(module, 'registration_callback'): - module.registration_callback(self) - else: - self.register_all(vars(module).values(), module.__name__) - - def _load_ancestors_then_object(self, modname, objectcls, butclasses=()): - """handle class registration according to rules defined in - :meth:`load_module` - """ - # backward compat, we used to allow whatever else than classes - if not isinstance(objectcls, type): - if self.is_registrable(objectcls) and objectcls.__module__ == modname: - self.register(objectcls) - return - # imported classes - objmodname = objectcls.__module__ - if objmodname != modname: - # The module of the object is not the same as the currently - # worked on module, or this is actually an instance, which - # has no module at all - if objmodname in self._toloadmods: - # if this is still scheduled for loading, let's proceed immediately, - # but using the object module - self.load_file(self._toloadmods[objmodname], objmodname) - return - # ensure object hasn't been already processed - clsid = '%s.%s' % (modname, objectcls.__name__) - if clsid in self._loadedmods[modname]: - return - self._loadedmods[modname][clsid] = objectcls - # ensure ancestors are registered - for parent in objectcls.__bases__: - self._load_ancestors_then_object(modname, parent, butclasses) - # ensure object is registrable - if objectcls in butclasses or not self.is_registrable(objectcls): - return - # backward compat - reg = self.setdefault(obj_registries(objectcls)[0]) - if reg.objname(objectcls)[0] == '_': - warn("[lgc 0.59] object whose name start with '_' won't be " - "skipped anymore at some point, use __abstract__ = True " - "instead (%s)" % objectcls, DeprecationWarning) - return - # register, finally - self.register(objectcls) - - @classmethod - def is_registrable(cls, obj): - """ensure `obj` should be registered - - as arbitrary stuff may be registered, do a lot of check and warn about - weird cases (think to dumb proxy objects) - """ - if isinstance(obj, type): - if not issubclass(obj, RegistrableObject): - # ducktyping backward compat - if not (getattr(obj, '__registries__', None) - and getattr(obj, '__regid__', None) - and getattr(obj, '__select__', None)): - return False - elif issubclass(obj, RegistrableInstance): - return False - elif not isinstance(obj, RegistrableInstance): - return False - if not obj.__regid__: - return False # no regid - registries = obj.__registries__ - if not registries: - return False # no registries - selector = obj.__select__ - if not selector: - return False # no selector - if obj.__dict__.get('__abstract__', False): - return False - # then detect potential problems that should be warned - if not isinstance(registries, (tuple, list)): - cls.warning('%s has __registries__ which is not a list or tuple', obj) - return False - if not callable(selector): - cls.warning('%s has not callable __select__', obj) - return False - return True - - # these are overridden by set_log_methods below - # only defining here to prevent pylint from complaining - info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None - - -# init logging -set_log_methods(RegistryStore, getLogger('registry.store')) -set_log_methods(Registry, getLogger('registry')) - - -# helpers for debugging selectors -TRACED_OIDS = None - -def _trace_selector(cls, selector, args, ret): - vobj = args[0] - if TRACED_OIDS == 'all' or vobj.__regid__ in TRACED_OIDS: - print('%s -> %s for %s(%s)' % (cls, ret, vobj, vobj.__regid__)) - -def _lltrace(selector): - """use this decorator on your predicates so they become traceable with - :class:`traced_selection` - """ - def traced(cls, *args, **kwargs): - ret = selector(cls, *args, **kwargs) - if TRACED_OIDS is not None: - _trace_selector(cls, selector, args, ret) - return ret - traced.__name__ = selector.__name__ - traced.__doc__ = selector.__doc__ - return traced - -class traced_selection(object): # pylint: disable=C0103 - """ - Typical usage is : - - .. sourcecode:: python - - >>> from logilab.common.registry import traced_selection - >>> with traced_selection(): - ... # some code in which you want to debug selectors - ... # for all objects - - This will yield lines like this in the logs:: - - selector one_line_rset returned 0 for - - You can also give to :class:`traced_selection` the identifiers of objects on - which you want to debug selection ('oid1' and 'oid2' in the example above). - - .. sourcecode:: python - - >>> with traced_selection( ('regid1', 'regid2') ): - ... # some code in which you want to debug selectors - ... # for objects with __regid__ 'regid1' and 'regid2' - - A potentially useful point to set up such a tracing function is - the `logilab.common.registry.Registry.select` method body. - """ - - def __init__(self, traced='all'): - self.traced = traced - - def __enter__(self): - global TRACED_OIDS - TRACED_OIDS = self.traced - - def __exit__(self, exctype, exc, traceback): - global TRACED_OIDS - TRACED_OIDS = None - return traceback is None - -# selector base classes and operations ######################################## - -def objectify_predicate(selector_func): - """Most of the time, a simple score function is enough to build a selector. - The :func:`objectify_predicate` decorator turn it into a proper selector - class:: - - @objectify_predicate - def one(cls, req, rset=None, **kwargs): - return 1 - - class MyView(View): - __select__ = View.__select__ & one() - - """ - return type(selector_func.__name__, (Predicate,), - {'__doc__': selector_func.__doc__, - '__call__': lambda self, *a, **kw: selector_func(*a, **kw)}) - - -_PREDICATES = {} - -def wrap_predicates(decorator): - for predicate in _PREDICATES.values(): - if not '_decorators' in predicate.__dict__: - predicate._decorators = set() - if decorator in predicate._decorators: - continue - predicate._decorators.add(decorator) - predicate.__call__ = decorator(predicate.__call__) - -class PredicateMetaClass(type): - def __new__(mcs, *args, **kwargs): - # use __new__ so subclasses doesn't have to call Predicate.__init__ - inst = type.__new__(mcs, *args, **kwargs) - proxy = weakref.proxy(inst, lambda p: _PREDICATES.pop(id(p))) - _PREDICATES[id(proxy)] = proxy - return inst - - -@add_metaclass(PredicateMetaClass) -class Predicate(object): - """base class for selector classes providing implementation - for operators ``&``, ``|`` and ``~`` - - This class is only here to give access to binary operators, the selector - logic itself should be implemented in the :meth:`__call__` method. Notice it - should usually accept any arbitrary arguments (the context), though that may - vary depending on your usage of the registry. - - a selector is called to help choosing the correct object for a - particular context by returning a score (`int`) telling how well - the implementation given as first argument fit to the given context. - - 0 score means that the class doesn't apply. - """ - - @property - def func_name(self): - # backward compatibility - return self.__class__.__name__ - - def search_selector(self, selector): - """search for the given selector, selector instance or tuple of - selectors in the selectors tree. Return None if not found. - """ - if self is selector: - return self - if (isinstance(selector, type) or isinstance(selector, tuple)) and \ - isinstance(self, selector): - return self - return None - - def __str__(self): - return self.__class__.__name__ - - def __and__(self, other): - return AndPredicate(self, other) - def __rand__(self, other): - return AndPredicate(other, self) - def __iand__(self, other): - return AndPredicate(self, other) - def __or__(self, other): - return OrPredicate(self, other) - def __ror__(self, other): - return OrPredicate(other, self) - def __ior__(self, other): - return OrPredicate(self, other) - - def __invert__(self): - return NotPredicate(self) - - # XXX (function | function) or (function & function) not managed yet - - def __call__(self, cls, *args, **kwargs): - return NotImplementedError("selector %s must implement its logic " - "in its __call__ method" % self.__class__) - - def __repr__(self): - return u'' % (self.__class__.__name__, id(self)) - - -class MultiPredicate(Predicate): - """base class for compound selector classes""" - - def __init__(self, *selectors): - self.selectors = self.merge_selectors(selectors) - - def __str__(self): - return '%s(%s)' % (self.__class__.__name__, - ','.join(str(s) for s in self.selectors)) - - @classmethod - def merge_selectors(cls, selectors): - """deal with selector instanciation when necessary and merge - multi-selectors if possible: - - AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4)) - ==> AndPredicate(sel1, sel2, sel3, sel4) - """ - merged_selectors = [] - for selector in selectors: - # XXX do we really want magic-transformations below? - # if so, wanna warn about them? - if isinstance(selector, types.FunctionType): - selector = objectify_predicate(selector)() - if isinstance(selector, type) and issubclass(selector, Predicate): - selector = selector() - assert isinstance(selector, Predicate), selector - if isinstance(selector, cls): - merged_selectors += selector.selectors - else: - merged_selectors.append(selector) - return merged_selectors - - def search_selector(self, selector): - """search for the given selector or selector instance (or tuple of - selectors) in the selectors tree. Return None if not found - """ - for childselector in self.selectors: - if childselector is selector: - return childselector - found = childselector.search_selector(selector) - if found is not None: - return found - # if not found in children, maybe we are looking for self? - return super(MultiPredicate, self).search_selector(selector) - - -class AndPredicate(MultiPredicate): - """and-chained selectors""" - def __call__(self, cls, *args, **kwargs): - score = 0 - for selector in self.selectors: - partscore = selector(cls, *args, **kwargs) - if not partscore: - return 0 - score += partscore - return score - - -class OrPredicate(MultiPredicate): - """or-chained selectors""" - def __call__(self, cls, *args, **kwargs): - for selector in self.selectors: - partscore = selector(cls, *args, **kwargs) - if partscore: - return partscore - return 0 - -class NotPredicate(Predicate): - """negation selector""" - def __init__(self, selector): - self.selector = selector - - def __call__(self, cls, *args, **kwargs): - score = self.selector(cls, *args, **kwargs) - return int(not score) - - def __str__(self): - return 'NOT(%s)' % self.selector - - -class yes(Predicate): # pylint: disable=C0103 - """Return the score given as parameter, with a default score of 0.5 so any - other selector take precedence. - - Usually used for objects which can be selected whatever the context, or - also sometimes to add arbitrary points to a score. - - Take care, `yes(0)` could be named 'no'... - """ - def __init__(self, score=0.5): - self.score = score - - def __call__(self, *args, **kwargs): - return self.score - - -# deprecated stuff ############################################################# - -from logilab.common.deprecation import deprecated - -@deprecated('[lgc 0.59] use Registry.objid class method instead') -def classid(cls): - return '%s.%s' % (cls.__module__, cls.__name__) - -@deprecated('[lgc 0.59] use obj_registries function instead') -def class_registries(cls, registryname): - return obj_registries(cls, registryname) - diff --git a/pymode/libs/logilab/common/shellutils.py b/pymode/libs/logilab/common/shellutils.py deleted file mode 100644 index 4e689560..00000000 --- a/pymode/libs/logilab/common/shellutils.py +++ /dev/null @@ -1,462 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""shell/term utilities, useful to write some python scripts instead of shell -scripts. -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import os -import glob -import shutil -import stat -import sys -import tempfile -import time -import fnmatch -import errno -import string -import random -import subprocess -from os.path import exists, isdir, islink, basename, join - -from six import string_types -from six.moves import range, input as raw_input - -from logilab.common import STD_BLACKLIST, _handle_blacklist -from logilab.common.compat import str_to_bytes -from logilab.common.deprecation import deprecated - -try: - from logilab.common.proc import ProcInfo, NoSuchProcess -except ImportError: - # windows platform - class NoSuchProcess(Exception): pass - - def ProcInfo(pid): - raise NoSuchProcess() - - -class tempdir(object): - - def __enter__(self): - self.path = tempfile.mkdtemp() - return self.path - - def __exit__(self, exctype, value, traceback): - # rmtree in all cases - shutil.rmtree(self.path) - return traceback is None - - -class pushd(object): - def __init__(self, directory): - self.directory = directory - - def __enter__(self): - self.cwd = os.getcwd() - os.chdir(self.directory) - return self.directory - - def __exit__(self, exctype, value, traceback): - os.chdir(self.cwd) - - -def chown(path, login=None, group=None): - """Same as `os.chown` function but accepting user login or group name as - argument. If login or group is omitted, it's left unchanged. - - Note: you must own the file to chown it (or be root). Otherwise OSError is raised. - """ - if login is None: - uid = -1 - else: - try: - uid = int(login) - except ValueError: - import pwd # Platforms: Unix - uid = pwd.getpwnam(login).pw_uid - if group is None: - gid = -1 - else: - try: - gid = int(group) - except ValueError: - import grp - gid = grp.getgrnam(group).gr_gid - os.chown(path, uid, gid) - -def mv(source, destination, _action=shutil.move): - """A shell-like mv, supporting wildcards. - """ - sources = glob.glob(source) - if len(sources) > 1: - assert isdir(destination) - for filename in sources: - _action(filename, join(destination, basename(filename))) - else: - try: - source = sources[0] - except IndexError: - raise OSError('No file matching %s' % source) - if isdir(destination) and exists(destination): - destination = join(destination, basename(source)) - try: - _action(source, destination) - except OSError as ex: - raise OSError('Unable to move %r to %r (%s)' % ( - source, destination, ex)) - -def rm(*files): - """A shell-like rm, supporting wildcards. - """ - for wfile in files: - for filename in glob.glob(wfile): - if islink(filename): - os.remove(filename) - elif isdir(filename): - shutil.rmtree(filename) - else: - os.remove(filename) - -def cp(source, destination): - """A shell-like cp, supporting wildcards. - """ - mv(source, destination, _action=shutil.copy) - -def find(directory, exts, exclude=False, blacklist=STD_BLACKLIST): - """Recursively find files ending with the given extensions from the directory. - - :type directory: str - :param directory: - directory where the search should start - - :type exts: basestring or list or tuple - :param exts: - extensions or lists or extensions to search - - :type exclude: boolean - :param exts: - if this argument is True, returning files NOT ending with the given - extensions - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all matching files - """ - if isinstance(exts, string_types): - exts = (exts,) - if exclude: - def match(filename, exts): - for ext in exts: - if filename.endswith(ext): - return False - return True - else: - def match(filename, exts): - for ext in exts: - if filename.endswith(ext): - return True - return False - files = [] - for dirpath, dirnames, filenames in os.walk(directory): - _handle_blacklist(blacklist, dirnames, filenames) - # don't append files if the directory is blacklisted - dirname = basename(dirpath) - if dirname in blacklist: - continue - files.extend([join(dirpath, f) for f in filenames if match(f, exts)]) - return files - - -def globfind(directory, pattern, blacklist=STD_BLACKLIST): - """Recursively finds files matching glob `pattern` under `directory`. - - This is an alternative to `logilab.common.shellutils.find`. - - :type directory: str - :param directory: - directory where the search should start - - :type pattern: basestring - :param pattern: - the glob pattern (e.g *.py, foo*.py, etc.) - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: iterator - :return: - iterator over the list of all matching files - """ - for curdir, dirnames, filenames in os.walk(directory): - _handle_blacklist(blacklist, dirnames, filenames) - for fname in fnmatch.filter(filenames, pattern): - yield join(curdir, fname) - -def unzip(archive, destdir): - import zipfile - if not exists(destdir): - os.mkdir(destdir) - zfobj = zipfile.ZipFile(archive) - for name in zfobj.namelist(): - if name.endswith('/'): - os.mkdir(join(destdir, name)) - else: - outfile = open(join(destdir, name), 'wb') - outfile.write(zfobj.read(name)) - outfile.close() - - -class Execute: - """This is a deadlock safe version of popen2 (no stdin), that returns - an object with errorlevel, out and err. - """ - - def __init__(self, command): - cmd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - self.out, self.err = cmd.communicate() - self.status = os.WEXITSTATUS(cmd.returncode) - -Execute = deprecated('Use subprocess.Popen instead')(Execute) - - -def acquire_lock(lock_file, max_try=10, delay=10, max_delay=3600): - """Acquire a lock represented by a file on the file system - - If the process written in lock file doesn't exist anymore, we remove the - lock file immediately - If age of the lock_file is greater than max_delay, then we raise a UserWarning - """ - count = abs(max_try) - while count: - try: - fd = os.open(lock_file, os.O_EXCL | os.O_RDWR | os.O_CREAT) - os.write(fd, str_to_bytes(str(os.getpid())) ) - os.close(fd) - return True - except OSError as e: - if e.errno == errno.EEXIST: - try: - fd = open(lock_file, "r") - pid = int(fd.readline()) - pi = ProcInfo(pid) - age = (time.time() - os.stat(lock_file)[stat.ST_MTIME]) - if age / max_delay > 1 : - raise UserWarning("Command '%s' (pid %s) has locked the " - "file '%s' for %s minutes" - % (pi.name(), pid, lock_file, age/60)) - except UserWarning: - raise - except NoSuchProcess: - os.remove(lock_file) - except Exception: - # The try block is not essential. can be skipped. - # Note: ProcInfo object is only available for linux - # process information are not accessible... - # or lock_file is no more present... - pass - else: - raise - count -= 1 - time.sleep(delay) - else: - raise Exception('Unable to acquire %s' % lock_file) - -def release_lock(lock_file): - """Release a lock represented by a file on the file system.""" - os.remove(lock_file) - - -class ProgressBar(object): - """A simple text progression bar.""" - - def __init__(self, nbops, size=20, stream=sys.stdout, title=''): - if title: - self._fstr = '\r%s [%%-%ss]' % (title, int(size)) - else: - self._fstr = '\r[%%-%ss]' % int(size) - self._stream = stream - self._total = nbops - self._size = size - self._current = 0 - self._progress = 0 - self._current_text = None - self._last_text_write_size = 0 - - def _get_text(self): - return self._current_text - - def _set_text(self, text=None): - if text != self._current_text: - self._current_text = text - self.refresh() - - def _del_text(self): - self.text = None - - text = property(_get_text, _set_text, _del_text) - - def update(self, offset=1, exact=False): - """Move FORWARD to new cursor position (cursor will never go backward). - - :offset: fraction of ``size`` - - :exact: - - - False: offset relative to current cursor position if True - - True: offset as an asbsolute position - - """ - if exact: - self._current = offset - else: - self._current += offset - - progress = int((float(self._current)/float(self._total))*self._size) - if progress > self._progress: - self._progress = progress - self.refresh() - - def refresh(self): - """Refresh the progression bar display.""" - self._stream.write(self._fstr % ('=' * min(self._progress, self._size)) ) - if self._last_text_write_size or self._current_text: - template = ' %%-%is' % (self._last_text_write_size) - text = self._current_text - if text is None: - text = '' - self._stream.write(template % text) - self._last_text_write_size = len(text.rstrip()) - self._stream.flush() - - def finish(self): - self._stream.write('\n') - self._stream.flush() - - -class DummyProgressBar(object): - __slot__ = ('text',) - - def refresh(self): - pass - def update(self): - pass - def finish(self): - pass - - -_MARKER = object() -class progress(object): - - def __init__(self, nbops=_MARKER, size=_MARKER, stream=_MARKER, title=_MARKER, enabled=True): - self.nbops = nbops - self.size = size - self.stream = stream - self.title = title - self.enabled = enabled - - def __enter__(self): - if self.enabled: - kwargs = {} - for attr in ('nbops', 'size', 'stream', 'title'): - value = getattr(self, attr) - if value is not _MARKER: - kwargs[attr] = value - self.pb = ProgressBar(**kwargs) - else: - self.pb = DummyProgressBar() - return self.pb - - def __exit__(self, exc_type, exc_val, exc_tb): - self.pb.finish() - -class RawInput(object): - - def __init__(self, input=None, printer=None): - self._input = input or raw_input - self._print = printer - - def ask(self, question, options, default): - assert default in options - choices = [] - for option in options: - if option == default: - label = option[0].upper() - else: - label = option[0].lower() - if len(option) > 1: - label += '(%s)' % option[1:].lower() - choices.append((option, label)) - prompt = "%s [%s]: " % (question, - '/'.join([opt[1] for opt in choices])) - tries = 3 - while tries > 0: - answer = self._input(prompt).strip().lower() - if not answer: - return default - possible = [option for option, label in choices - if option.lower().startswith(answer)] - if len(possible) == 1: - return possible[0] - elif len(possible) == 0: - msg = '%s is not an option.' % answer - else: - msg = ('%s is an ambiguous answer, do you mean %s ?' % ( - answer, ' or '.join(possible))) - if self._print: - self._print(msg) - else: - print(msg) - tries -= 1 - raise Exception('unable to get a sensible answer') - - def confirm(self, question, default_is_yes=True): - default = default_is_yes and 'y' or 'n' - answer = self.ask(question, ('y', 'n'), default) - return answer == 'y' - -ASK = RawInput() - - -def getlogin(): - """avoid using os.getlogin() because of strange tty / stdin problems - (man 3 getlogin) - Another solution would be to use $LOGNAME, $USER or $USERNAME - """ - if sys.platform != 'win32': - import pwd # Platforms: Unix - return pwd.getpwuid(os.getuid())[0] - else: - return os.environ['USERNAME'] - -def generate_password(length=8, vocab=string.ascii_letters + string.digits): - """dumb password generation function""" - pwd = '' - for i in range(length): - pwd += random.choice(vocab) - return pwd diff --git a/pymode/libs/logilab/common/sphinx_ext.py b/pymode/libs/logilab/common/sphinx_ext.py deleted file mode 100644 index a24608ce..00000000 --- a/pymode/libs/logilab/common/sphinx_ext.py +++ /dev/null @@ -1,87 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -from logilab.common.decorators import monkeypatch - -from sphinx.ext import autodoc - -class DocstringOnlyModuleDocumenter(autodoc.ModuleDocumenter): - objtype = 'docstring' - def format_signature(self): - pass - def add_directive_header(self, sig): - pass - def document_members(self, all_members=False): - pass - - def resolve_name(self, modname, parents, path, base): - if modname is not None: - return modname, parents + [base] - return (path or '') + base, [] - - -#autodoc.add_documenter(DocstringOnlyModuleDocumenter) - -def setup(app): - app.add_autodocumenter(DocstringOnlyModuleDocumenter) - - - -from sphinx.ext.autodoc import (ViewList, Options, AutodocReporter, nodes, - assemble_option_dict, nested_parse_with_titles) - -@monkeypatch(autodoc.AutoDirective) -def run(self): - self.filename_set = set() # a set of dependent filenames - self.reporter = self.state.document.reporter - self.env = self.state.document.settings.env - self.warnings = [] - self.result = ViewList() - - # find out what documenter to call - objtype = self.name[4:] - doc_class = self._registry[objtype] - # process the options with the selected documenter's option_spec - self.genopt = Options(assemble_option_dict( - self.options.items(), doc_class.option_spec)) - # generate the output - documenter = doc_class(self, self.arguments[0]) - documenter.generate(more_content=self.content) - if not self.result: - return self.warnings - - # record all filenames as dependencies -- this will at least - # partially make automatic invalidation possible - for fn in self.filename_set: - self.env.note_dependency(fn) - - # use a custom reporter that correctly assigns lines to source - # filename/description and lineno - old_reporter = self.state.memo.reporter - self.state.memo.reporter = AutodocReporter(self.result, - self.state.memo.reporter) - if self.name in ('automodule', 'autodocstring'): - node = nodes.section() - # necessary so that the child nodes get the right source/line set - node.document = self.state.document - nested_parse_with_titles(self.state, self.result, node) - else: - node = nodes.paragraph() - node.document = self.state.document - self.state.nested_parse(self.result, 0, node) - self.state.memo.reporter = old_reporter - return self.warnings + node.children diff --git a/pymode/libs/logilab/common/sphinxutils.py b/pymode/libs/logilab/common/sphinxutils.py deleted file mode 100644 index ab6e8a18..00000000 --- a/pymode/libs/logilab/common/sphinxutils.py +++ /dev/null @@ -1,122 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Sphinx utils - -ModuleGenerator: Generate a file that lists all the modules of a list of -packages in order to pull all the docstring. -This should not be used in a makefile to systematically generate sphinx -documentation! - -Typical usage: - ->>> from logilab.common.sphinxutils import ModuleGenerator ->>> mgen = ModuleGenerator('logilab common', '/home/adim/src/logilab/common') ->>> mgen.generate('api_logilab_common.rst', exclude_dirs=('test',)) -""" - -import os, sys -import os.path as osp -import inspect - -from logilab.common import STD_BLACKLIST -from logilab.common.shellutils import globfind -from logilab.common.modutils import load_module_from_file, modpath_from_file - -def module_members(module): - members = [] - for name, value in inspect.getmembers(module): - if getattr(value, '__module__', None) == module.__name__: - members.append( (name, value) ) - return sorted(members) - - -def class_members(klass): - return sorted([name for name in vars(klass) - if name not in ('__doc__', '__module__', - '__dict__', '__weakref__')]) - -class ModuleGenerator: - file_header = """.. -*- coding: utf-8 -*-\n\n%s\n""" - module_def = """ -:mod:`%s` -=======%s - -.. automodule:: %s - :members: %s -""" - class_def = """ - -.. autoclass:: %s - :members: %s - -""" - - def __init__(self, project_title, code_dir): - self.title = project_title - self.code_dir = osp.abspath(code_dir) - - def generate(self, dest_file, exclude_dirs=STD_BLACKLIST): - """make the module file""" - self.fn = open(dest_file, 'w') - num = len(self.title) + 6 - title = "=" * num + "\n %s API\n" % self.title + "=" * num - self.fn.write(self.file_header % title) - self.gen_modules(exclude_dirs=exclude_dirs) - self.fn.close() - - def gen_modules(self, exclude_dirs): - """generate all modules""" - for module in self.find_modules(exclude_dirs): - modname = module.__name__ - classes = [] - modmembers = [] - for objname, obj in module_members(module): - if inspect.isclass(obj): - classmembers = class_members(obj) - classes.append( (objname, classmembers) ) - else: - modmembers.append(objname) - self.fn.write(self.module_def % (modname, '=' * len(modname), - modname, - ', '.join(modmembers))) - for klass, members in classes: - self.fn.write(self.class_def % (klass, ', '.join(members))) - - def find_modules(self, exclude_dirs): - basepath = osp.dirname(self.code_dir) - basedir = osp.basename(basepath) + osp.sep - if basedir not in sys.path: - sys.path.insert(1, basedir) - for filepath in globfind(self.code_dir, '*.py', exclude_dirs): - if osp.basename(filepath) in ('setup.py', '__pkginfo__.py'): - continue - try: - module = load_module_from_file(filepath) - except: # module might be broken or magic - dotted_path = modpath_from_file(filepath) - module = type('.'.join(dotted_path), (), {}) # mock it - yield module - - -if __name__ == '__main__': - # example : - title, code_dir, outfile = sys.argv[1:] - generator = ModuleGenerator(title, code_dir) - # XXX modnames = ['logilab'] - generator.generate(outfile, ('test', 'tests', 'examples', - 'data', 'doc', '.hg', 'migration')) diff --git a/pymode/libs/logilab/common/table.py b/pymode/libs/logilab/common/table.py deleted file mode 100644 index 2f3df694..00000000 --- a/pymode/libs/logilab/common/table.py +++ /dev/null @@ -1,929 +0,0 @@ -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Table management module.""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from six.moves import range - -class Table(object): - """Table defines a data table with column and row names. - inv: - len(self.data) <= len(self.row_names) - forall(self.data, lambda x: len(x) <= len(self.col_names)) - """ - - def __init__(self, default_value=0, col_names=None, row_names=None): - self.col_names = [] - self.row_names = [] - self.data = [] - self.default_value = default_value - if col_names: - self.create_columns(col_names) - if row_names: - self.create_rows(row_names) - - def _next_row_name(self): - return 'row%s' % (len(self.row_names)+1) - - def __iter__(self): - return iter(self.data) - - def __eq__(self, other): - if other is None: - return False - else: - return list(self) == list(other) - - __hash__ = object.__hash__ - - def __ne__(self, other): - return not self == other - - def __len__(self): - return len(self.row_names) - - ## Rows / Columns creation ################################################# - def create_rows(self, row_names): - """Appends row_names to the list of existing rows - """ - self.row_names.extend(row_names) - for row_name in row_names: - self.data.append([self.default_value]*len(self.col_names)) - - def create_columns(self, col_names): - """Appends col_names to the list of existing columns - """ - for col_name in col_names: - self.create_column(col_name) - - def create_row(self, row_name=None): - """Creates a rowname to the row_names list - """ - row_name = row_name or self._next_row_name() - self.row_names.append(row_name) - self.data.append([self.default_value]*len(self.col_names)) - - - def create_column(self, col_name): - """Creates a colname to the col_names list - """ - self.col_names.append(col_name) - for row in self.data: - row.append(self.default_value) - - ## Sort by column ########################################################## - def sort_by_column_id(self, col_id, method = 'asc'): - """Sorts the table (in-place) according to data stored in col_id - """ - try: - col_index = self.col_names.index(col_id) - self.sort_by_column_index(col_index, method) - except ValueError: - raise KeyError("Col (%s) not found in table" % (col_id)) - - - def sort_by_column_index(self, col_index, method = 'asc'): - """Sorts the table 'in-place' according to data stored in col_index - - method should be in ('asc', 'desc') - """ - sort_list = sorted([(row[col_index], row, row_name) - for row, row_name in zip(self.data, self.row_names)]) - # Sorting sort_list will sort according to col_index - # If we want reverse sort, then reverse list - if method.lower() == 'desc': - sort_list.reverse() - - # Rebuild data / row names - self.data = [] - self.row_names = [] - for val, row, row_name in sort_list: - self.data.append(row) - self.row_names.append(row_name) - - def groupby(self, colname, *others): - """builds indexes of data - :returns: nested dictionaries pointing to actual rows - """ - groups = {} - colnames = (colname,) + others - col_indexes = [self.col_names.index(col_id) for col_id in colnames] - for row in self.data: - ptr = groups - for col_index in col_indexes[:-1]: - ptr = ptr.setdefault(row[col_index], {}) - ptr = ptr.setdefault(row[col_indexes[-1]], - Table(default_value=self.default_value, - col_names=self.col_names)) - ptr.append_row(tuple(row)) - return groups - - def select(self, colname, value): - grouped = self.groupby(colname) - try: - return grouped[value] - except KeyError: - return [] - - def remove(self, colname, value): - col_index = self.col_names.index(colname) - for row in self.data[:]: - if row[col_index] == value: - self.data.remove(row) - - - ## The 'setter' part ####################################################### - def set_cell(self, row_index, col_index, data): - """sets value of cell 'row_indew', 'col_index' to data - """ - self.data[row_index][col_index] = data - - - def set_cell_by_ids(self, row_id, col_id, data): - """sets value of cell mapped by row_id and col_id to data - Raises a KeyError if row_id or col_id are not found in the table - """ - try: - row_index = self.row_names.index(row_id) - except ValueError: - raise KeyError("Row (%s) not found in table" % (row_id)) - else: - try: - col_index = self.col_names.index(col_id) - self.data[row_index][col_index] = data - except ValueError: - raise KeyError("Column (%s) not found in table" % (col_id)) - - - def set_row(self, row_index, row_data): - """sets the 'row_index' row - pre: - type(row_data) == types.ListType - len(row_data) == len(self.col_names) - """ - self.data[row_index] = row_data - - - def set_row_by_id(self, row_id, row_data): - """sets the 'row_id' column - pre: - type(row_data) == types.ListType - len(row_data) == len(self.row_names) - Raises a KeyError if row_id is not found - """ - try: - row_index = self.row_names.index(row_id) - self.set_row(row_index, row_data) - except ValueError: - raise KeyError('Row (%s) not found in table' % (row_id)) - - - def append_row(self, row_data, row_name=None): - """Appends a row to the table - pre: - type(row_data) == types.ListType - len(row_data) == len(self.col_names) - """ - row_name = row_name or self._next_row_name() - self.row_names.append(row_name) - self.data.append(row_data) - return len(self.data) - 1 - - def insert_row(self, index, row_data, row_name=None): - """Appends row_data before 'index' in the table. To make 'insert' - behave like 'list.insert', inserting in an out of range index will - insert row_data to the end of the list - pre: - type(row_data) == types.ListType - len(row_data) == len(self.col_names) - """ - row_name = row_name or self._next_row_name() - self.row_names.insert(index, row_name) - self.data.insert(index, row_data) - - - def delete_row(self, index): - """Deletes the 'index' row in the table, and returns it. - Raises an IndexError if index is out of range - """ - self.row_names.pop(index) - return self.data.pop(index) - - - def delete_row_by_id(self, row_id): - """Deletes the 'row_id' row in the table. - Raises a KeyError if row_id was not found. - """ - try: - row_index = self.row_names.index(row_id) - self.delete_row(row_index) - except ValueError: - raise KeyError('Row (%s) not found in table' % (row_id)) - - - def set_column(self, col_index, col_data): - """sets the 'col_index' column - pre: - type(col_data) == types.ListType - len(col_data) == len(self.row_names) - """ - - for row_index, cell_data in enumerate(col_data): - self.data[row_index][col_index] = cell_data - - - def set_column_by_id(self, col_id, col_data): - """sets the 'col_id' column - pre: - type(col_data) == types.ListType - len(col_data) == len(self.col_names) - Raises a KeyError if col_id is not found - """ - try: - col_index = self.col_names.index(col_id) - self.set_column(col_index, col_data) - except ValueError: - raise KeyError('Column (%s) not found in table' % (col_id)) - - - def append_column(self, col_data, col_name): - """Appends the 'col_index' column - pre: - type(col_data) == types.ListType - len(col_data) == len(self.row_names) - """ - self.col_names.append(col_name) - for row_index, cell_data in enumerate(col_data): - self.data[row_index].append(cell_data) - - - def insert_column(self, index, col_data, col_name): - """Appends col_data before 'index' in the table. To make 'insert' - behave like 'list.insert', inserting in an out of range index will - insert col_data to the end of the list - pre: - type(col_data) == types.ListType - len(col_data) == len(self.row_names) - """ - self.col_names.insert(index, col_name) - for row_index, cell_data in enumerate(col_data): - self.data[row_index].insert(index, cell_data) - - - def delete_column(self, index): - """Deletes the 'index' column in the table, and returns it. - Raises an IndexError if index is out of range - """ - self.col_names.pop(index) - return [row.pop(index) for row in self.data] - - - def delete_column_by_id(self, col_id): - """Deletes the 'col_id' col in the table. - Raises a KeyError if col_id was not found. - """ - try: - col_index = self.col_names.index(col_id) - self.delete_column(col_index) - except ValueError: - raise KeyError('Column (%s) not found in table' % (col_id)) - - - ## The 'getter' part ####################################################### - - def get_shape(self): - """Returns a tuple which represents the table's shape - """ - return len(self.row_names), len(self.col_names) - shape = property(get_shape) - - def __getitem__(self, indices): - """provided for convenience""" - rows, multirows = None, False - cols, multicols = None, False - if isinstance(indices, tuple): - rows = indices[0] - if len(indices) > 1: - cols = indices[1] - else: - rows = indices - # define row slice - if isinstance(rows, str): - try: - rows = self.row_names.index(rows) - except ValueError: - raise KeyError("Row (%s) not found in table" % (rows)) - if isinstance(rows, int): - rows = slice(rows, rows+1) - multirows = False - else: - rows = slice(None) - multirows = True - # define col slice - if isinstance(cols, str): - try: - cols = self.col_names.index(cols) - except ValueError: - raise KeyError("Column (%s) not found in table" % (cols)) - if isinstance(cols, int): - cols = slice(cols, cols+1) - multicols = False - else: - cols = slice(None) - multicols = True - # get sub-table - tab = Table() - tab.default_value = self.default_value - tab.create_rows(self.row_names[rows]) - tab.create_columns(self.col_names[cols]) - for idx, row in enumerate(self.data[rows]): - tab.set_row(idx, row[cols]) - if multirows : - if multicols: - return tab - else: - return [item[0] for item in tab.data] - else: - if multicols: - return tab.data[0] - else: - return tab.data[0][0] - - def get_cell_by_ids(self, row_id, col_id): - """Returns the element at [row_id][col_id] - """ - try: - row_index = self.row_names.index(row_id) - except ValueError: - raise KeyError("Row (%s) not found in table" % (row_id)) - else: - try: - col_index = self.col_names.index(col_id) - except ValueError: - raise KeyError("Column (%s) not found in table" % (col_id)) - return self.data[row_index][col_index] - - def get_row_by_id(self, row_id): - """Returns the 'row_id' row - """ - try: - row_index = self.row_names.index(row_id) - except ValueError: - raise KeyError("Row (%s) not found in table" % (row_id)) - return self.data[row_index] - - def get_column_by_id(self, col_id, distinct=False): - """Returns the 'col_id' col - """ - try: - col_index = self.col_names.index(col_id) - except ValueError: - raise KeyError("Column (%s) not found in table" % (col_id)) - return self.get_column(col_index, distinct) - - def get_columns(self): - """Returns all the columns in the table - """ - return [self[:, index] for index in range(len(self.col_names))] - - def get_column(self, col_index, distinct=False): - """get a column by index""" - col = [row[col_index] for row in self.data] - if distinct: - col = list(set(col)) - return col - - def apply_stylesheet(self, stylesheet): - """Applies the stylesheet to this table - """ - for instruction in stylesheet.instructions: - eval(instruction) - - - def transpose(self): - """Keeps the self object intact, and returns the transposed (rotated) - table. - """ - transposed = Table() - transposed.create_rows(self.col_names) - transposed.create_columns(self.row_names) - for col_index, column in enumerate(self.get_columns()): - transposed.set_row(col_index, column) - return transposed - - - def pprint(self): - """returns a string representing the table in a pretty - printed 'text' format. - """ - # The maximum row name (to know the start_index of the first col) - max_row_name = 0 - for row_name in self.row_names: - if len(row_name) > max_row_name: - max_row_name = len(row_name) - col_start = max_row_name + 5 - - lines = [] - # Build the 'first' line <=> the col_names one - # The first cell <=> an empty one - col_names_line = [' '*col_start] - for col_name in self.col_names: - col_names_line.append(col_name + ' '*5) - lines.append('|' + '|'.join(col_names_line) + '|') - max_line_length = len(lines[0]) - - # Build the table - for row_index, row in enumerate(self.data): - line = [] - # First, build the row_name's cell - row_name = self.row_names[row_index] - line.append(row_name + ' '*(col_start-len(row_name))) - - # Then, build all the table's cell for this line. - for col_index, cell in enumerate(row): - col_name_length = len(self.col_names[col_index]) + 5 - data = str(cell) - line.append(data + ' '*(col_name_length - len(data))) - lines.append('|' + '|'.join(line) + '|') - if len(lines[-1]) > max_line_length: - max_line_length = len(lines[-1]) - - # Wrap the table with '-' to make a frame - lines.insert(0, '-'*max_line_length) - lines.append('-'*max_line_length) - return '\n'.join(lines) - - - def __repr__(self): - return repr(self.data) - - def as_text(self): - data = [] - # We must convert cells into strings before joining them - for row in self.data: - data.append([str(cell) for cell in row]) - lines = ['\t'.join(row) for row in data] - return '\n'.join(lines) - - - -class TableStyle: - """Defines a table's style - """ - - def __init__(self, table): - - self._table = table - self.size = dict([(col_name, '1*') for col_name in table.col_names]) - # __row_column__ is a special key to define the first column which - # actually has no name (<=> left most column <=> row names column) - self.size['__row_column__'] = '1*' - self.alignment = dict([(col_name, 'right') - for col_name in table.col_names]) - self.alignment['__row_column__'] = 'right' - - # We shouldn't have to create an entry for - # the 1st col (the row_column one) - self.units = dict([(col_name, '') for col_name in table.col_names]) - self.units['__row_column__'] = '' - - # XXX FIXME : params order should be reversed for all set() methods - def set_size(self, value, col_id): - """sets the size of the specified col_id to value - """ - self.size[col_id] = value - - def set_size_by_index(self, value, col_index): - """Allows to set the size according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - self.size[col_id] = value - - - def set_alignment(self, value, col_id): - """sets the alignment of the specified col_id to value - """ - self.alignment[col_id] = value - - - def set_alignment_by_index(self, value, col_index): - """Allows to set the alignment according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - self.alignment[col_id] = value - - - def set_unit(self, value, col_id): - """sets the unit of the specified col_id to value - """ - self.units[col_id] = value - - - def set_unit_by_index(self, value, col_index): - """Allows to set the unit according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - (Note that in the 'unit' case, you shouldn't have to set a unit - for the 1st column (the __row__column__ one)) - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - self.units[col_id] = value - - - def get_size(self, col_id): - """Returns the size of the specified col_id - """ - return self.size[col_id] - - - def get_size_by_index(self, col_index): - """Allows to get the size according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - return self.size[col_id] - - - def get_alignment(self, col_id): - """Returns the alignment of the specified col_id - """ - return self.alignment[col_id] - - - def get_alignment_by_index(self, col_index): - """Allors to get the alignment according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - return self.alignment[col_id] - - - def get_unit(self, col_id): - """Returns the unit of the specified col_id - """ - return self.units[col_id] - - - def get_unit_by_index(self, col_index): - """Allors to get the unit according to the column index rather than - using the column's id. - BE CAREFUL : the '0' column is the '__row_column__' one ! - """ - if col_index == 0: - col_id = '__row_column__' - else: - col_id = self._table.col_names[col_index-1] - - return self.units[col_id] - - -import re -CELL_PROG = re.compile("([0-9]+)_([0-9]+)") - -class TableStyleSheet: - """A simple Table stylesheet - Rules are expressions where cells are defined by the row_index - and col_index separated by an underscore ('_'). - For example, suppose you want to say that the (2,5) cell must be - the sum of its two preceding cells in the row, you would create - the following rule : - 2_5 = 2_3 + 2_4 - You can also use all the math.* operations you want. For example: - 2_5 = sqrt(2_3**2 + 2_4**2) - """ - - def __init__(self, rules = None): - rules = rules or [] - self.rules = [] - self.instructions = [] - for rule in rules: - self.add_rule(rule) - - - def add_rule(self, rule): - """Adds a rule to the stylesheet rules - """ - try: - source_code = ['from math import *'] - source_code.append(CELL_PROG.sub(r'self.data[\1][\2]', rule)) - self.instructions.append(compile('\n'.join(source_code), - 'table.py', 'exec')) - self.rules.append(rule) - except SyntaxError: - print("Bad Stylesheet Rule : %s [skipped]" % rule) - - - def add_rowsum_rule(self, dest_cell, row_index, start_col, end_col): - """Creates and adds a rule to sum over the row at row_index from - start_col to end_col. - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_col >= 0 - end_col > start_col - """ - cell_list = ['%d_%d'%(row_index, index) for index in range(start_col, - end_col + 1)] - rule = '%d_%d=' % dest_cell + '+'.join(cell_list) - self.add_rule(rule) - - - def add_rowavg_rule(self, dest_cell, row_index, start_col, end_col): - """Creates and adds a rule to make the row average (from start_col - to end_col) - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_col >= 0 - end_col > start_col - """ - cell_list = ['%d_%d'%(row_index, index) for index in range(start_col, - end_col + 1)] - num = (end_col - start_col + 1) - rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num - self.add_rule(rule) - - - def add_colsum_rule(self, dest_cell, col_index, start_row, end_row): - """Creates and adds a rule to sum over the col at col_index from - start_row to end_row. - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_row >= 0 - end_row > start_row - """ - cell_list = ['%d_%d'%(index, col_index) for index in range(start_row, - end_row + 1)] - rule = '%d_%d=' % dest_cell + '+'.join(cell_list) - self.add_rule(rule) - - - def add_colavg_rule(self, dest_cell, col_index, start_row, end_row): - """Creates and adds a rule to make the col average (from start_row - to end_row) - dest_cell is a tuple of two elements (x,y) of the destination cell - No check is done for indexes ranges. - pre: - start_row >= 0 - end_row > start_row - """ - cell_list = ['%d_%d'%(index, col_index) for index in range(start_row, - end_row + 1)] - num = (end_row - start_row + 1) - rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num - self.add_rule(rule) - - - -class TableCellRenderer: - """Defines a simple text renderer - """ - - def __init__(self, **properties): - """keywords should be properties with an associated boolean as value. - For example : - renderer = TableCellRenderer(units = True, alignment = False) - An unspecified property will have a 'False' value by default. - Possible properties are : - alignment, unit - """ - self.properties = properties - - - def render_cell(self, cell_coord, table, table_style): - """Renders the cell at 'cell_coord' in the table, using table_style - """ - row_index, col_index = cell_coord - cell_value = table.data[row_index][col_index] - final_content = self._make_cell_content(cell_value, - table_style, col_index +1) - return self._render_cell_content(final_content, - table_style, col_index + 1) - - - def render_row_cell(self, row_name, table, table_style): - """Renders the cell for 'row_id' row - """ - cell_value = row_name - return self._render_cell_content(cell_value, table_style, 0) - - - def render_col_cell(self, col_name, table, table_style): - """Renders the cell for 'col_id' row - """ - cell_value = col_name - col_index = table.col_names.index(col_name) - return self._render_cell_content(cell_value, table_style, col_index +1) - - - - def _render_cell_content(self, content, table_style, col_index): - """Makes the appropriate rendering for this cell content. - Rendering properties will be searched using the - *table_style.get_xxx_by_index(col_index)' methods - - **This method should be overridden in the derived renderer classes.** - """ - return content - - - def _make_cell_content(self, cell_content, table_style, col_index): - """Makes the cell content (adds decoration data, like units for - example) - """ - final_content = cell_content - if 'skip_zero' in self.properties: - replacement_char = self.properties['skip_zero'] - else: - replacement_char = 0 - if replacement_char and final_content == 0: - return replacement_char - - try: - units_on = self.properties['units'] - if units_on: - final_content = self._add_unit( - cell_content, table_style, col_index) - except KeyError: - pass - - return final_content - - - def _add_unit(self, cell_content, table_style, col_index): - """Adds unit to the cell_content if needed - """ - unit = table_style.get_unit_by_index(col_index) - return str(cell_content) + " " + unit - - - -class DocbookRenderer(TableCellRenderer): - """Defines how to render a cell for a docboook table - """ - - def define_col_header(self, col_index, table_style): - """Computes the colspec element according to the style - """ - size = table_style.get_size_by_index(col_index) - return '\n' % \ - (col_index, size) - - - def _render_cell_content(self, cell_content, table_style, col_index): - """Makes the appropriate rendering for this cell content. - Rendering properties will be searched using the - table_style.get_xxx_by_index(col_index)' methods. - """ - try: - align_on = self.properties['alignment'] - alignment = table_style.get_alignment_by_index(col_index) - if align_on: - return "%s\n" % \ - (alignment, cell_content) - except KeyError: - # KeyError <=> Default alignment - return "%s\n" % cell_content - - -class TableWriter: - """A class to write tables - """ - - def __init__(self, stream, table, style, **properties): - self._stream = stream - self.style = style or TableStyle(table) - self._table = table - self.properties = properties - self.renderer = None - - - def set_style(self, style): - """sets the table's associated style - """ - self.style = style - - - def set_renderer(self, renderer): - """sets the way to render cell - """ - self.renderer = renderer - - - def update_properties(self, **properties): - """Updates writer's properties (for cell rendering) - """ - self.properties.update(properties) - - - def write_table(self, title = ""): - """Writes the table - """ - raise NotImplementedError("write_table must be implemented !") - - - -class DocbookTableWriter(TableWriter): - """Defines an implementation of TableWriter to write a table in Docbook - """ - - def _write_headers(self): - """Writes col headers - """ - # Define col_headers (colstpec elements) - for col_index in range(len(self._table.col_names)+1): - self._stream.write(self.renderer.define_col_header(col_index, - self.style)) - - self._stream.write("\n\n") - # XXX FIXME : write an empty entry <=> the first (__row_column) column - self._stream.write('\n') - for col_name in self._table.col_names: - self._stream.write(self.renderer.render_col_cell( - col_name, self._table, - self.style)) - - self._stream.write("\n\n") - - - def _write_body(self): - """Writes the table body - """ - self._stream.write('\n') - - for row_index, row in enumerate(self._table.data): - self._stream.write('\n') - row_name = self._table.row_names[row_index] - # Write the first entry (row_name) - self._stream.write(self.renderer.render_row_cell(row_name, - self._table, - self.style)) - - for col_index, cell in enumerate(row): - self._stream.write(self.renderer.render_cell( - (row_index, col_index), - self._table, self.style)) - - self._stream.write('\n') - - self._stream.write('\n') - - - def write_table(self, title = ""): - """Writes the table - """ - self._stream.write('\nCodestin Search App\n'%(title)) - self._stream.write( - '\n'% - (len(self._table.col_names)+1)) - self._write_headers() - self._write_body() - - self._stream.write('\n
\n') - - diff --git a/pymode/libs/logilab/common/tasksqueue.py b/pymode/libs/logilab/common/tasksqueue.py deleted file mode 100644 index ed74cf5a..00000000 --- a/pymode/libs/logilab/common/tasksqueue.py +++ /dev/null @@ -1,101 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Prioritized tasks queue""" - -__docformat__ = "restructuredtext en" - -from bisect import insort_left - -from six.moves import queue - -LOW = 0 -MEDIUM = 10 -HIGH = 100 - -PRIORITY = { - 'LOW': LOW, - 'MEDIUM': MEDIUM, - 'HIGH': HIGH, - } -REVERSE_PRIORITY = dict((values, key) for key, values in PRIORITY.items()) - - - -class PrioritizedTasksQueue(queue.Queue): - - def _init(self, maxsize): - """Initialize the queue representation""" - self.maxsize = maxsize - # ordered list of task, from the lowest to the highest priority - self.queue = [] - - def _put(self, item): - """Put a new item in the queue""" - for i, task in enumerate(self.queue): - # equivalent task - if task == item: - # if new task has a higher priority, remove the one already - # queued so the new priority will be considered - if task < item: - item.merge(task) - del self.queue[i] - break - # else keep it so current order is kept - task.merge(item) - return - insort_left(self.queue, item) - - def _get(self): - """Get an item from the queue""" - return self.queue.pop() - - def __iter__(self): - return iter(self.queue) - - def remove(self, tid): - """remove a specific task from the queue""" - # XXX acquire lock - for i, task in enumerate(self): - if task.id == tid: - self.queue.pop(i) - return - raise ValueError('not task of id %s in queue' % tid) - -class Task(object): - def __init__(self, tid, priority=LOW): - # task id - self.id = tid - # task priority - self.priority = priority - - def __repr__(self): - return '' % (self.id, id(self)) - - def __cmp__(self, other): - return cmp(self.priority, other.priority) - - def __lt__(self, other): - return self.priority < other.priority - - def __eq__(self, other): - return self.id == other.id - - __hash__ = object.__hash__ - - def merge(self, other): - pass diff --git a/pymode/libs/logilab/common/testlib.py b/pymode/libs/logilab/common/testlib.py deleted file mode 100644 index a6b4b1e1..00000000 --- a/pymode/libs/logilab/common/testlib.py +++ /dev/null @@ -1,1338 +0,0 @@ -# -*- coding: utf-8 -*- -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Run tests. - -This will find all modules whose name match a given prefix in the test -directory, and run them. Various command line options provide -additional facilities. - -Command line options: - - -v verbose -- run tests in verbose mode with output to stdout - -q quiet -- don't print anything except if a test fails - -t testdir -- directory where the tests will be found - -x exclude -- add a test to exclude - -p profile -- profiled execution - -d dbc -- enable design-by-contract - -m match -- only run test matching the tag pattern which follow - -If no non-option arguments are present, prefixes used are 'test', -'regrtest', 'smoketest' and 'unittest'. - -""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" -# modified copy of some functions from test/regrtest.py from PyXml -# disable camel case warning -# pylint: disable=C0103 - -import sys -import os, os.path as osp -import re -import traceback -import inspect -import difflib -import tempfile -import math -import warnings -from shutil import rmtree -from operator import itemgetter -from itertools import dropwhile -from inspect import isgeneratorfunction - -from six import string_types -from six.moves import builtins, range, configparser, input - -from logilab.common.deprecation import deprecated - -import unittest as unittest_legacy -if not getattr(unittest_legacy, "__package__", None): - try: - import unittest2 as unittest - from unittest2 import SkipTest - except ImportError: - raise ImportError("You have to install python-unittest2 to use %s" % __name__) -else: - import unittest - from unittest import SkipTest - -from functools import wraps - -from logilab.common.debugger import Debugger, colorize_source -from logilab.common.decorators import cached, classproperty -from logilab.common import textutils - - -__all__ = ['main', 'unittest_main', 'find_tests', 'run_test', 'spawn'] - -DEFAULT_PREFIXES = ('test', 'regrtest', 'smoketest', 'unittest', - 'func', 'validation') - -is_generator = deprecated('[lgc 0.63] use inspect.isgeneratorfunction')(isgeneratorfunction) - -# used by unittest to count the number of relevant levels in the traceback -__unittest = 1 - - -def with_tempdir(callable): - """A decorator ensuring no temporary file left when the function return - Work only for temporary file created with the tempfile module""" - if isgeneratorfunction(callable): - def proxy(*args, **kwargs): - old_tmpdir = tempfile.gettempdir() - new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-") - tempfile.tempdir = new_tmpdir - try: - for x in callable(*args, **kwargs): - yield x - finally: - try: - rmtree(new_tmpdir, ignore_errors=True) - finally: - tempfile.tempdir = old_tmpdir - return proxy - - @wraps(callable) - def proxy(*args, **kargs): - - old_tmpdir = tempfile.gettempdir() - new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-") - tempfile.tempdir = new_tmpdir - try: - return callable(*args, **kargs) - finally: - try: - rmtree(new_tmpdir, ignore_errors=True) - finally: - tempfile.tempdir = old_tmpdir - return proxy - -def in_tempdir(callable): - """A decorator moving the enclosed function inside the tempfile.tempfdir - """ - @wraps(callable) - def proxy(*args, **kargs): - - old_cwd = os.getcwd() - os.chdir(tempfile.tempdir) - try: - return callable(*args, **kargs) - finally: - os.chdir(old_cwd) - return proxy - -def within_tempdir(callable): - """A decorator run the enclosed function inside a tmpdir removed after execution - """ - proxy = with_tempdir(in_tempdir(callable)) - proxy.__name__ = callable.__name__ - return proxy - -def find_tests(testdir, - prefixes=DEFAULT_PREFIXES, suffix=".py", - excludes=(), - remove_suffix=True): - """ - Return a list of all applicable test modules. - """ - tests = [] - for name in os.listdir(testdir): - if not suffix or name.endswith(suffix): - for prefix in prefixes: - if name.startswith(prefix): - if remove_suffix and name.endswith(suffix): - name = name[:-len(suffix)] - if name not in excludes: - tests.append(name) - tests.sort() - return tests - - -## PostMortem Debug facilities ##### -def start_interactive_mode(result): - """starts an interactive shell so that the user can inspect errors - """ - debuggers = result.debuggers - descrs = result.error_descrs + result.fail_descrs - if len(debuggers) == 1: - # don't ask for test name if there's only one failure - debuggers[0].start() - else: - while True: - testindex = 0 - print("Choose a test to debug:") - # order debuggers in the same way than errors were printed - print("\n".join(['\t%s : %s' % (i, descr) for i, (_, descr) - in enumerate(descrs)])) - print("Type 'exit' (or ^D) to quit") - print() - try: - todebug = input('Enter a test name: ') - if todebug.strip().lower() == 'exit': - print() - break - else: - try: - testindex = int(todebug) - debugger = debuggers[descrs[testindex][0]] - except (ValueError, IndexError): - print("ERROR: invalid test number %r" % (todebug, )) - else: - debugger.start() - except (EOFError, KeyboardInterrupt): - print() - break - - -# test utils ################################################################## - -class SkipAwareTestResult(unittest._TextTestResult): - - def __init__(self, stream, descriptions, verbosity, - exitfirst=False, pdbmode=False, cvg=None, colorize=False): - super(SkipAwareTestResult, self).__init__(stream, - descriptions, verbosity) - self.skipped = [] - self.debuggers = [] - self.fail_descrs = [] - self.error_descrs = [] - self.exitfirst = exitfirst - self.pdbmode = pdbmode - self.cvg = cvg - self.colorize = colorize - self.pdbclass = Debugger - self.verbose = verbosity > 1 - - def descrs_for(self, flavour): - return getattr(self, '%s_descrs' % flavour.lower()) - - def _create_pdb(self, test_descr, flavour): - self.descrs_for(flavour).append( (len(self.debuggers), test_descr) ) - if self.pdbmode: - self.debuggers.append(self.pdbclass(sys.exc_info()[2])) - - def _iter_valid_frames(self, frames): - """only consider non-testlib frames when formatting traceback""" - lgc_testlib = osp.abspath(__file__) - std_testlib = osp.abspath(unittest.__file__) - invalid = lambda fi: osp.abspath(fi[1]) in (lgc_testlib, std_testlib) - for frameinfo in dropwhile(invalid, frames): - yield frameinfo - - def _exc_info_to_string(self, err, test): - """Converts a sys.exc_info()-style tuple of values into a string. - - This method is overridden here because we want to colorize - lines if --color is passed, and display local variables if - --verbose is passed - """ - exctype, exc, tb = err - output = ['Traceback (most recent call last)'] - frames = inspect.getinnerframes(tb) - colorize = self.colorize - frames = enumerate(self._iter_valid_frames(frames)) - for index, (frame, filename, lineno, funcname, ctx, ctxindex) in frames: - filename = osp.abspath(filename) - if ctx is None: # pyc files or C extensions for instance - source = '' - else: - source = ''.join(ctx) - if colorize: - filename = textutils.colorize_ansi(filename, 'magenta') - source = colorize_source(source) - output.append(' File "%s", line %s, in %s' % (filename, lineno, funcname)) - output.append(' %s' % source.strip()) - if self.verbose: - output.append('%r == %r' % (dir(frame), test.__module__)) - output.append('') - output.append(' ' + ' local variables '.center(66, '-')) - for varname, value in sorted(frame.f_locals.items()): - output.append(' %s: %r' % (varname, value)) - if varname == 'self': # special handy processing for self - for varname, value in sorted(vars(value).items()): - output.append(' self.%s: %r' % (varname, value)) - output.append(' ' + '-' * 66) - output.append('') - output.append(''.join(traceback.format_exception_only(exctype, exc))) - return '\n'.join(output) - - def addError(self, test, err): - """err -> (exc_type, exc, tcbk)""" - exc_type, exc, _ = err - if isinstance(exc, SkipTest): - assert exc_type == SkipTest - self.addSkip(test, exc) - else: - if self.exitfirst: - self.shouldStop = True - descr = self.getDescription(test) - super(SkipAwareTestResult, self).addError(test, err) - self._create_pdb(descr, 'error') - - def addFailure(self, test, err): - if self.exitfirst: - self.shouldStop = True - descr = self.getDescription(test) - super(SkipAwareTestResult, self).addFailure(test, err) - self._create_pdb(descr, 'fail') - - def addSkip(self, test, reason): - self.skipped.append((test, reason)) - if self.showAll: - self.stream.writeln("SKIPPED") - elif self.dots: - self.stream.write('S') - - def printErrors(self): - super(SkipAwareTestResult, self).printErrors() - self.printSkippedList() - - def printSkippedList(self): - # format (test, err) compatible with unittest2 - for test, err in self.skipped: - descr = self.getDescription(test) - self.stream.writeln(self.separator1) - self.stream.writeln("%s: %s" % ('SKIPPED', descr)) - self.stream.writeln("\t%s" % err) - - def printErrorList(self, flavour, errors): - for (_, descr), (test, err) in zip(self.descrs_for(flavour), errors): - self.stream.writeln(self.separator1) - self.stream.writeln("%s: %s" % (flavour, descr)) - self.stream.writeln(self.separator2) - self.stream.writeln(err) - self.stream.writeln('no stdout'.center(len(self.separator2))) - self.stream.writeln('no stderr'.center(len(self.separator2))) - -# Add deprecation warnings about new api used by module level fixtures in unittest2 -# http://www.voidspace.org.uk/python/articles/unittest2.shtml#setupmodule-and-teardownmodule -class _DebugResult(object): # simplify import statement among unittest flavors.. - "Used by the TestSuite to hold previous class when running in debug." - _previousTestClass = None - _moduleSetUpFailed = False - shouldStop = False - -# backward compatibility: TestSuite might be imported from lgc.testlib -TestSuite = unittest.TestSuite - -class keywords(dict): - """Keyword args (**kwargs) support for generative tests.""" - -class starargs(tuple): - """Variable arguments (*args) for generative tests.""" - def __new__(cls, *args): - return tuple.__new__(cls, args) - -unittest_main = unittest.main - - -class InnerTestSkipped(SkipTest): - """raised when a test is skipped""" - pass - -def parse_generative_args(params): - args = [] - varargs = () - kwargs = {} - flags = 0 # 2 <=> starargs, 4 <=> kwargs - for param in params: - if isinstance(param, starargs): - varargs = param - if flags: - raise TypeError('found starargs after keywords !') - flags |= 2 - args += list(varargs) - elif isinstance(param, keywords): - kwargs = param - if flags & 4: - raise TypeError('got multiple keywords parameters') - flags |= 4 - elif flags & 2 or flags & 4: - raise TypeError('found parameters after kwargs or args') - else: - args.append(param) - - return args, kwargs - - -class InnerTest(tuple): - def __new__(cls, name, *data): - instance = tuple.__new__(cls, data) - instance.name = name - return instance - -class Tags(set): - """A set of tag able validate an expression""" - - def __init__(self, *tags, **kwargs): - self.inherit = kwargs.pop('inherit', True) - if kwargs: - raise TypeError("%s are an invalid keyword argument for this function" % kwargs.keys()) - - if len(tags) == 1 and not isinstance(tags[0], string_types): - tags = tags[0] - super(Tags, self).__init__(tags, **kwargs) - - def __getitem__(self, key): - return key in self - - def match(self, exp): - return eval(exp, {}, self) - - def __or__(self, other): - return Tags(*super(Tags, self).__or__(other)) - - -# duplicate definition from unittest2 of the _deprecate decorator -def _deprecate(original_func): - def deprecated_func(*args, **kwargs): - warnings.warn( - ('Please use %s instead.' % original_func.__name__), - DeprecationWarning, 2) - return original_func(*args, **kwargs) - return deprecated_func - -class TestCase(unittest.TestCase): - """A unittest.TestCase extension with some additional methods.""" - maxDiff = None - pdbclass = Debugger - tags = Tags() - - def __init__(self, methodName='runTest'): - super(TestCase, self).__init__(methodName) - self.__exc_info = sys.exc_info - self.__testMethodName = self._testMethodName - self._current_test_descr = None - self._options_ = None - - @classproperty - @cached - def datadir(cls): # pylint: disable=E0213 - """helper attribute holding the standard test's data directory - - NOTE: this is a logilab's standard - """ - mod = sys.modules[cls.__module__] - return osp.join(osp.dirname(osp.abspath(mod.__file__)), 'data') - # cache it (use a class method to cache on class since TestCase is - # instantiated for each test run) - - @classmethod - def datapath(cls, *fname): - """joins the object's datadir and `fname`""" - return osp.join(cls.datadir, *fname) - - def set_description(self, descr): - """sets the current test's description. - This can be useful for generative tests because it allows to specify - a description per yield - """ - self._current_test_descr = descr - - # override default's unittest.py feature - def shortDescription(self): - """override default unittest shortDescription to handle correctly - generative tests - """ - if self._current_test_descr is not None: - return self._current_test_descr - return super(TestCase, self).shortDescription() - - def quiet_run(self, result, func, *args, **kwargs): - try: - func(*args, **kwargs) - except (KeyboardInterrupt, SystemExit): - raise - except unittest.SkipTest as e: - if hasattr(result, 'addSkip'): - result.addSkip(self, str(e)) - else: - warnings.warn("TestResult has no addSkip method, skips not reported", - RuntimeWarning, 2) - result.addSuccess(self) - return False - except: - result.addError(self, self.__exc_info()) - return False - return True - - def _get_test_method(self): - """return the test method""" - return getattr(self, self._testMethodName) - - def optval(self, option, default=None): - """return the option value or default if the option is not define""" - return getattr(self._options_, option, default) - - def __call__(self, result=None, runcondition=None, options=None): - """rewrite TestCase.__call__ to support generative tests - This is mostly a copy/paste from unittest.py (i.e same - variable names, same logic, except for the generative tests part) - """ - from logilab.common.pytest import FILE_RESTART - if result is None: - result = self.defaultTestResult() - result.pdbclass = self.pdbclass - self._options_ = options - # if result.cvg: - # result.cvg.start() - testMethod = self._get_test_method() - if (getattr(self.__class__, "__unittest_skip__", False) or - getattr(testMethod, "__unittest_skip__", False)): - # If the class or method was skipped. - try: - skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') - or getattr(testMethod, '__unittest_skip_why__', '')) - self._addSkip(result, skip_why) - finally: - result.stopTest(self) - return - if runcondition and not runcondition(testMethod): - return # test is skipped - result.startTest(self) - try: - if not self.quiet_run(result, self.setUp): - return - generative = isgeneratorfunction(testMethod) - # generative tests - if generative: - self._proceed_generative(result, testMethod, - runcondition) - else: - status = self._proceed(result, testMethod) - success = (status == 0) - if not self.quiet_run(result, self.tearDown): - return - if not generative and success: - if hasattr(options, "exitfirst") and options.exitfirst: - # add this test to restart file - try: - restartfile = open(FILE_RESTART, 'a') - try: - descr = '.'.join((self.__class__.__module__, - self.__class__.__name__, - self._testMethodName)) - restartfile.write(descr+os.linesep) - finally: - restartfile.close() - except Exception: - print("Error while saving succeeded test into", - osp.join(os.getcwd(), FILE_RESTART), - file=sys.__stderr__) - raise - result.addSuccess(self) - finally: - # if result.cvg: - # result.cvg.stop() - result.stopTest(self) - - def _proceed_generative(self, result, testfunc, runcondition=None): - # cancel startTest()'s increment - result.testsRun -= 1 - success = True - try: - for params in testfunc(): - if runcondition and not runcondition(testfunc, - skipgenerator=False): - if not (isinstance(params, InnerTest) - and runcondition(params)): - continue - if not isinstance(params, (tuple, list)): - params = (params, ) - func = params[0] - args, kwargs = parse_generative_args(params[1:]) - # increment test counter manually - result.testsRun += 1 - status = self._proceed(result, func, args, kwargs) - if status == 0: - result.addSuccess(self) - success = True - else: - success = False - # XXX Don't stop anymore if an error occured - #if status == 2: - # result.shouldStop = True - if result.shouldStop: # either on error or on exitfirst + error - break - except: - # if an error occurs between two yield - result.addError(self, self.__exc_info()) - success = False - return success - - def _proceed(self, result, testfunc, args=(), kwargs=None): - """proceed the actual test - returns 0 on success, 1 on failure, 2 on error - - Note: addSuccess can't be called here because we have to wait - for tearDown to be successfully executed to declare the test as - successful - """ - kwargs = kwargs or {} - try: - testfunc(*args, **kwargs) - except self.failureException: - result.addFailure(self, self.__exc_info()) - return 1 - except KeyboardInterrupt: - raise - except InnerTestSkipped as e: - result.addSkip(self, e) - return 1 - except SkipTest as e: - result.addSkip(self, e) - return 0 - except: - result.addError(self, self.__exc_info()) - return 2 - return 0 - - def defaultTestResult(self): - """return a new instance of the defaultTestResult""" - return SkipAwareTestResult() - - skip = _deprecate(unittest.TestCase.skipTest) - assertEquals = _deprecate(unittest.TestCase.assertEqual) - assertNotEquals = _deprecate(unittest.TestCase.assertNotEqual) - assertAlmostEquals = _deprecate(unittest.TestCase.assertAlmostEqual) - assertNotAlmostEquals = _deprecate(unittest.TestCase.assertNotAlmostEqual) - - def innerSkip(self, msg=None): - """mark a generative test as skipped for the reason""" - msg = msg or 'test was skipped' - raise InnerTestSkipped(msg) - - @deprecated('Please use assertDictEqual instead.') - def assertDictEquals(self, dict1, dict2, msg=None, context=None): - """compares two dicts - - If the two dict differ, the first difference is shown in the error - message - :param dict1: a Python Dictionary - :param dict2: a Python Dictionary - :param msg: custom message (String) in case of failure - """ - dict1 = dict(dict1) - msgs = [] - for key, value in dict2.items(): - try: - if dict1[key] != value: - msgs.append('%r != %r for key %r' % (dict1[key], value, - key)) - del dict1[key] - except KeyError: - msgs.append('missing %r key' % key) - if dict1: - msgs.append('dict2 is lacking %r' % dict1) - if msg: - self.failureException(msg) - elif msgs: - if context is not None: - base = '%s\n' % context - else: - base = '' - self.fail(base + '\n'.join(msgs)) - - @deprecated('Please use assertCountEqual instead.') - def assertUnorderedIterableEquals(self, got, expected, msg=None): - """compares two iterable and shows difference between both - - :param got: the unordered Iterable that we found - :param expected: the expected unordered Iterable - :param msg: custom message (String) in case of failure - """ - got, expected = list(got), list(expected) - self.assertSetEqual(set(got), set(expected), msg) - if len(got) != len(expected): - if msg is None: - msg = ['Iterable have the same elements but not the same number', - '\t\ti\t'] - got_count = {} - expected_count = {} - for element in got: - got_count[element] = got_count.get(element, 0) + 1 - for element in expected: - expected_count[element] = expected_count.get(element, 0) + 1 - # we know that got_count.key() == expected_count.key() - # because of assertSetEqual - for element, count in got_count.iteritems(): - other_count = expected_count[element] - if other_count != count: - msg.append('\t%s\t%s\t%s' % (element, other_count, count)) - - self.fail(msg) - - assertUnorderedIterableEqual = assertUnorderedIterableEquals - assertUnordIterEquals = assertUnordIterEqual = assertUnorderedIterableEqual - - @deprecated('Please use assertSetEqual instead.') - def assertSetEquals(self,got,expected, msg=None): - """compares two sets and shows difference between both - - Don't use it for iterables other than sets. - - :param got: the Set that we found - :param expected: the second Set to be compared to the first one - :param msg: custom message (String) in case of failure - """ - - if not(isinstance(got, set) and isinstance(expected, set)): - warnings.warn("the assertSetEquals function if now intended for set only."\ - "use assertUnorderedIterableEquals instead.", - DeprecationWarning, 2) - return self.assertUnorderedIterableEquals(got, expected, msg) - - items={} - items['missing'] = expected - got - items['unexpected'] = got - expected - if any(items.itervalues()): - if msg is None: - msg = '\n'.join('%s:\n\t%s' % (key, "\n\t".join(str(value) for value in values)) - for key, values in items.iteritems() if values) - self.fail(msg) - - @deprecated('Please use assertListEqual instead.') - def assertListEquals(self, list_1, list_2, msg=None): - """compares two lists - - If the two list differ, the first difference is shown in the error - message - - :param list_1: a Python List - :param list_2: a second Python List - :param msg: custom message (String) in case of failure - """ - _l1 = list_1[:] - for i, value in enumerate(list_2): - try: - if _l1[0] != value: - from pprint import pprint - pprint(list_1) - pprint(list_2) - self.fail('%r != %r for index %d' % (_l1[0], value, i)) - del _l1[0] - except IndexError: - if msg is None: - msg = 'list_1 has only %d elements, not %s '\ - '(at least %r missing)'% (i, len(list_2), value) - self.fail(msg) - if _l1: - if msg is None: - msg = 'list_2 is lacking %r' % _l1 - self.fail(msg) - - @deprecated('Non-standard. Please use assertMultiLineEqual instead.') - def assertLinesEquals(self, string1, string2, msg=None, striplines=False): - """compare two strings and assert that the text lines of the strings - are equal. - - :param string1: a String - :param string2: a String - :param msg: custom message (String) in case of failure - :param striplines: Boolean to trigger line stripping before comparing - """ - lines1 = string1.splitlines() - lines2 = string2.splitlines() - if striplines: - lines1 = [l.strip() for l in lines1] - lines2 = [l.strip() for l in lines2] - self.assertListEqual(lines1, lines2, msg) - assertLineEqual = assertLinesEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertXMLWellFormed(self, stream, msg=None, context=2): - """asserts the XML stream is well-formed (no DTD conformance check) - - :param context: number of context lines in standard message - (show all data if negative). - Only available with element tree - """ - try: - from xml.etree.ElementTree import parse - self._assertETXMLWellFormed(stream, parse, msg) - except ImportError: - from xml.sax import make_parser, SAXParseException - parser = make_parser() - try: - parser.parse(stream) - except SAXParseException as ex: - if msg is None: - stream.seek(0) - for _ in range(ex.getLineNumber()): - line = stream.readline() - pointer = ('' * (ex.getLineNumber() - 1)) + '^' - msg = 'XML stream not well formed: %s\n%s%s' % (ex, line, pointer) - self.fail(msg) - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertXMLStringWellFormed(self, xml_string, msg=None, context=2): - """asserts the XML string is well-formed (no DTD conformance check) - - :param context: number of context lines in standard message - (show all data if negative). - Only available with element tree - """ - try: - from xml.etree.ElementTree import fromstring - except ImportError: - from elementtree.ElementTree import fromstring - self._assertETXMLWellFormed(xml_string, fromstring, msg) - - def _assertETXMLWellFormed(self, data, parse, msg=None, context=2): - """internal function used by /assertXML(String)?WellFormed/ functions - - :param data: xml_data - :param parse: appropriate parser function for this data - :param msg: error message - :param context: number of context lines in standard message - (show all data if negative). - Only available with element tree - """ - from xml.parsers.expat import ExpatError - try: - from xml.etree.ElementTree import ParseError - except ImportError: - # compatibility for 1: - if len(tup)<=1: - self.fail( "tuple %s has no attributes (%s expected)"%(tup, - dict(element.attrib))) - self.assertDictEqual(element.attrib, tup[1]) - # check children - if len(element) or len(tup)>2: - if len(tup)<=2: - self.fail( "tuple %s has no children (%i expected)"%(tup, - len(element))) - if len(element) != len(tup[2]): - self.fail( "tuple %s has %i children%s (%i expected)"%(tup, - len(tup[2]), - ('', 's')[len(tup[2])>1], len(element))) - for index in range(len(tup[2])): - self.assertXMLEqualsTuple(element[index], tup[2][index]) - #check text - if element.text or len(tup)>3: - if len(tup)<=3: - self.fail( "tuple %s has no text value (%r expected)"%(tup, - element.text)) - self.assertTextEquals(element.text, tup[3]) - #check tail - if element.tail or len(tup)>4: - if len(tup)<=4: - self.fail( "tuple %s has no tail value (%r expected)"%(tup, - element.tail)) - self.assertTextEquals(element.tail, tup[4]) - - def _difftext(self, lines1, lines2, junk=None, msg_prefix='Texts differ'): - junk = junk or (' ', '\t') - # result is a generator - result = difflib.ndiff(lines1, lines2, charjunk=lambda x: x in junk) - read = [] - for line in result: - read.append(line) - # lines that don't start with a ' ' are diff ones - if not line.startswith(' '): - self.fail('\n'.join(['%s\n'%msg_prefix]+read + list(result))) - - @deprecated('Non-standard. Please use assertMultiLineEqual instead.') - def assertTextEquals(self, text1, text2, junk=None, - msg_prefix='Text differ', striplines=False): - """compare two multiline strings (using difflib and splitlines()) - - :param text1: a Python BaseString - :param text2: a second Python Basestring - :param junk: List of Caracters - :param msg_prefix: String (message prefix) - :param striplines: Boolean to trigger line stripping before comparing - """ - msg = [] - if not isinstance(text1, string_types): - msg.append('text1 is not a string (%s)'%(type(text1))) - if not isinstance(text2, string_types): - msg.append('text2 is not a string (%s)'%(type(text2))) - if msg: - self.fail('\n'.join(msg)) - lines1 = text1.strip().splitlines(True) - lines2 = text2.strip().splitlines(True) - if striplines: - lines1 = [line.strip() for line in lines1] - lines2 = [line.strip() for line in lines2] - self._difftext(lines1, lines2, junk, msg_prefix) - assertTextEqual = assertTextEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertStreamEquals(self, stream1, stream2, junk=None, - msg_prefix='Stream differ'): - """compare two streams (using difflib and readlines())""" - # if stream2 is stream2, readlines() on stream1 will also read lines - # in stream2, so they'll appear different, although they're not - if stream1 is stream2: - return - # make sure we compare from the beginning of the stream - stream1.seek(0) - stream2.seek(0) - # compare - self._difftext(stream1.readlines(), stream2.readlines(), junk, - msg_prefix) - - assertStreamEqual = assertStreamEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertFileEquals(self, fname1, fname2, junk=(' ', '\t')): - """compares two files using difflib""" - self.assertStreamEqual(open(fname1), open(fname2), junk, - msg_prefix='Files differs\n-:%s\n+:%s\n'%(fname1, fname2)) - - assertFileEqual = assertFileEquals - - @deprecated('Non-standard: please copy test method to your TestCase class') - def assertDirEquals(self, path_a, path_b): - """compares two files using difflib""" - assert osp.exists(path_a), "%s doesn't exists" % path_a - assert osp.exists(path_b), "%s doesn't exists" % path_b - - all_a = [ (ipath[len(path_a):].lstrip('/'), idirs, ifiles) - for ipath, idirs, ifiles in os.walk(path_a)] - all_a.sort(key=itemgetter(0)) - - all_b = [ (ipath[len(path_b):].lstrip('/'), idirs, ifiles) - for ipath, idirs, ifiles in os.walk(path_b)] - all_b.sort(key=itemgetter(0)) - - iter_a, iter_b = iter(all_a), iter(all_b) - partial_iter = True - ipath_a, idirs_a, ifiles_a = data_a = None, None, None - while True: - try: - ipath_a, idirs_a, ifiles_a = datas_a = next(iter_a) - partial_iter = False - ipath_b, idirs_b, ifiles_b = datas_b = next(iter_b) - partial_iter = True - - - self.assertTrue(ipath_a == ipath_b, - "unexpected %s in %s while looking %s from %s" % - (ipath_a, path_a, ipath_b, path_b)) - - - errors = {} - sdirs_a = set(idirs_a) - sdirs_b = set(idirs_b) - errors["unexpected directories"] = sdirs_a - sdirs_b - errors["missing directories"] = sdirs_b - sdirs_a - - sfiles_a = set(ifiles_a) - sfiles_b = set(ifiles_b) - errors["unexpected files"] = sfiles_a - sfiles_b - errors["missing files"] = sfiles_b - sfiles_a - - - msgs = [ "%s: %s"% (name, items) - for name, items in errors.items() if items] - - if msgs: - msgs.insert(0, "%s and %s differ :" % ( - osp.join(path_a, ipath_a), - osp.join(path_b, ipath_b), - )) - self.fail("\n".join(msgs)) - - for files in (ifiles_a, ifiles_b): - files.sort() - - for index, path in enumerate(ifiles_a): - self.assertFileEquals(osp.join(path_a, ipath_a, path), - osp.join(path_b, ipath_b, ifiles_b[index])) - - except StopIteration: - break - - assertDirEqual = assertDirEquals - - def assertIsInstance(self, obj, klass, msg=None, strict=False): - """check if an object is an instance of a class - - :param obj: the Python Object to be checked - :param klass: the target class - :param msg: a String for a custom message - :param strict: if True, check that the class of is ; - else check with 'isinstance' - """ - if strict: - warnings.warn('[API] Non-standard. Strict parameter has vanished', - DeprecationWarning, stacklevel=2) - if msg is None: - if strict: - msg = '%r is not of class %s but of %s' - else: - msg = '%r is not an instance of %s but of %s' - msg = msg % (obj, klass, type(obj)) - if strict: - self.assertTrue(obj.__class__ is klass, msg) - else: - self.assertTrue(isinstance(obj, klass), msg) - - @deprecated('Please use assertIsNone instead.') - def assertNone(self, obj, msg=None): - """assert obj is None - - :param obj: Python Object to be tested - """ - if msg is None: - msg = "reference to %r when None expected"%(obj,) - self.assertTrue( obj is None, msg ) - - @deprecated('Please use assertIsNotNone instead.') - def assertNotNone(self, obj, msg=None): - """assert obj is not None""" - if msg is None: - msg = "unexpected reference to None" - self.assertTrue( obj is not None, msg ) - - @deprecated('Non-standard. Please use assertAlmostEqual instead.') - def assertFloatAlmostEquals(self, obj, other, prec=1e-5, - relative=False, msg=None): - """compares if two floats have a distance smaller than expected - precision. - - :param obj: a Float - :param other: another Float to be comparted to - :param prec: a Float describing the precision - :param relative: boolean switching to relative/absolute precision - :param msg: a String for a custom message - """ - if msg is None: - msg = "%r != %r" % (obj, other) - if relative: - prec = prec*math.fabs(obj) - self.assertTrue(math.fabs(obj - other) < prec, msg) - - def failUnlessRaises(self, excClass, callableObj=None, *args, **kwargs): - """override default failUnlessRaises method to return the raised - exception instance. - - Fail unless an exception of class excClass is thrown - by callableObj when invoked with arguments args and keyword - arguments kwargs. If a different type of exception is - thrown, it will not be caught, and the test case will be - deemed to have suffered an error, exactly as for an - unexpected exception. - - CAUTION! There are subtle differences between Logilab and unittest2 - - exc is not returned in standard version - - context capabilities in standard version - - try/except/else construction (minor) - - :param excClass: the Exception to be raised - :param callableObj: a callable Object which should raise - :param args: a List of arguments for - :param kwargs: a List of keyword arguments for - """ - # XXX cube vcslib : test_branches_from_app - if callableObj is None: - _assert = super(TestCase, self).assertRaises - return _assert(excClass, callableObj, *args, **kwargs) - try: - callableObj(*args, **kwargs) - except excClass as exc: - class ProxyException: - def __init__(self, obj): - self._obj = obj - def __getattr__(self, attr): - warn_msg = ("This exception was retrieved with the old testlib way " - "`exc = self.assertRaises(Exc, callable)`, please use " - "the context manager instead'") - warnings.warn(warn_msg, DeprecationWarning, 2) - return self._obj.__getattribute__(attr) - return ProxyException(exc) - else: - if hasattr(excClass, '__name__'): - excName = excClass.__name__ - else: - excName = str(excClass) - raise self.failureException("%s not raised" % excName) - - assertRaises = failUnlessRaises - - if sys.version_info >= (3,2): - assertItemsEqual = unittest.TestCase.assertCountEqual - else: - assertCountEqual = unittest.TestCase.assertItemsEqual - if sys.version_info < (2,7): - def assertIsNotNone(self, value, *args, **kwargs): - self.assertNotEqual(None, value, *args, **kwargs) - -TestCase.assertItemsEqual = deprecated('assertItemsEqual is deprecated, use assertCountEqual')( - TestCase.assertItemsEqual) - -import doctest - -class SkippedSuite(unittest.TestSuite): - def test(self): - """just there to trigger test execution""" - self.skipped_test('doctest module has no DocTestSuite class') - - -class DocTestFinder(doctest.DocTestFinder): - - def __init__(self, *args, **kwargs): - self.skipped = kwargs.pop('skipped', ()) - doctest.DocTestFinder.__init__(self, *args, **kwargs) - - def _get_test(self, obj, name, module, globs, source_lines): - """override default _get_test method to be able to skip tests - according to skipped attribute's value - """ - if getattr(obj, '__name__', '') in self.skipped: - return None - return doctest.DocTestFinder._get_test(self, obj, name, module, - globs, source_lines) - - -class DocTest(TestCase): - """trigger module doctest - I don't know how to make unittest.main consider the DocTestSuite instance - without this hack - """ - skipped = () - def __call__(self, result=None, runcondition=None, options=None):\ - # pylint: disable=W0613 - try: - finder = DocTestFinder(skipped=self.skipped) - suite = doctest.DocTestSuite(self.module, test_finder=finder) - # XXX iirk - doctest.DocTestCase._TestCase__exc_info = sys.exc_info - except AttributeError: - suite = SkippedSuite() - # doctest may gork the builtins dictionnary - # This happen to the "_" entry used by gettext - old_builtins = builtins.__dict__.copy() - try: - return suite.run(result) - finally: - builtins.__dict__.clear() - builtins.__dict__.update(old_builtins) - run = __call__ - - def test(self): - """just there to trigger test execution""" - -MAILBOX = None - -class MockSMTP: - """fake smtplib.SMTP""" - - def __init__(self, host, port): - self.host = host - self.port = port - global MAILBOX - self.reveived = MAILBOX = [] - - def set_debuglevel(self, debuglevel): - """ignore debug level""" - - def sendmail(self, fromaddr, toaddres, body): - """push sent mail in the mailbox""" - self.reveived.append((fromaddr, toaddres, body)) - - def quit(self): - """ignore quit""" - - -class MockConfigParser(configparser.ConfigParser): - """fake ConfigParser.ConfigParser""" - - def __init__(self, options): - configparser.ConfigParser.__init__(self) - for section, pairs in options.iteritems(): - self.add_section(section) - for key, value in pairs.iteritems(): - self.set(section, key, value) - def write(self, _): - raise NotImplementedError() - - -class MockConnection: - """fake DB-API 2.0 connexion AND cursor (i.e. cursor() return self)""" - - def __init__(self, results): - self.received = [] - self.states = [] - self.results = results - - def cursor(self): - """Mock cursor method""" - return self - def execute(self, query, args=None): - """Mock execute method""" - self.received.append( (query, args) ) - def fetchone(self): - """Mock fetchone method""" - return self.results[0] - def fetchall(self): - """Mock fetchall method""" - return self.results - def commit(self): - """Mock commiy method""" - self.states.append( ('commit', len(self.received)) ) - def rollback(self): - """Mock rollback method""" - self.states.append( ('rollback', len(self.received)) ) - def close(self): - """Mock close method""" - pass - - -def mock_object(**params): - """creates an object using params to set attributes - >>> option = mock_object(verbose=False, index=range(5)) - >>> option.verbose - False - >>> option.index - [0, 1, 2, 3, 4] - """ - return type('Mock', (), params)() - - -def create_files(paths, chroot): - """Creates directories and files found in . - - :param paths: list of relative paths to files or directories - :param chroot: the root directory in which paths will be created - - >>> from os.path import isdir, isfile - >>> isdir('/tmp/a') - False - >>> create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], '/tmp') - >>> isdir('/tmp/a') - True - >>> isdir('/tmp/a/b/c') - True - >>> isfile('/tmp/a/b/c/d/e.py') - True - >>> isfile('/tmp/a/b/foo.py') - True - """ - dirs, files = set(), set() - for path in paths: - path = osp.join(chroot, path) - filename = osp.basename(path) - # path is a directory path - if filename == '': - dirs.add(path) - # path is a filename path - else: - dirs.add(osp.dirname(path)) - files.add(path) - for dirpath in dirs: - if not osp.isdir(dirpath): - os.makedirs(dirpath) - for filepath in files: - open(filepath, 'w').close() - - -class AttrObject: # XXX cf mock_object - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - -def tag(*args, **kwargs): - """descriptor adding tag to a function""" - def desc(func): - assert not hasattr(func, 'tags') - func.tags = Tags(*args, **kwargs) - return func - return desc - -def require_version(version): - """ Compare version of python interpreter to the given one. Skip the test - if older. - """ - def check_require_version(f): - version_elements = version.split('.') - try: - compare = tuple([int(v) for v in version_elements]) - except ValueError: - raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version) - current = sys.version_info[:3] - if current < compare: - def new_f(self, *args, **kwargs): - self.skipTest('Need at least %s version of python. Current version is %s.' % (version, '.'.join([str(element) for element in current]))) - new_f.__name__ = f.__name__ - return new_f - else: - return f - return check_require_version - -def require_module(module): - """ Check if the given module is loaded. Skip the test if not. - """ - def check_require_module(f): - try: - __import__(module) - return f - except ImportError: - def new_f(self, *args, **kwargs): - self.skipTest('%s can not be imported.' % module) - new_f.__name__ = f.__name__ - return new_f - return check_require_module - diff --git a/pymode/libs/logilab/common/textutils.py b/pymode/libs/logilab/common/textutils.py deleted file mode 100644 index 9046f975..00000000 --- a/pymode/libs/logilab/common/textutils.py +++ /dev/null @@ -1,537 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Some text manipulation utility functions. - - -:group text formatting: normalize_text, normalize_paragraph, pretty_match,\ -unquote, colorize_ansi -:group text manipulation: searchall, splitstrip -:sort: text formatting, text manipulation - -:type ANSI_STYLES: dict(str) -:var ANSI_STYLES: dictionary mapping style identifier to ANSI terminal code - -:type ANSI_COLORS: dict(str) -:var ANSI_COLORS: dictionary mapping color identifier to ANSI terminal code - -:type ANSI_PREFIX: str -:var ANSI_PREFIX: - ANSI terminal code notifying the start of an ANSI escape sequence - -:type ANSI_END: str -:var ANSI_END: - ANSI terminal code notifying the end of an ANSI escape sequence - -:type ANSI_RESET: str -:var ANSI_RESET: - ANSI terminal code resetting format defined by a previous ANSI escape sequence -""" -__docformat__ = "restructuredtext en" - -import sys -import re -import os.path as osp -from warnings import warn -from unicodedata import normalize as _uninormalize -try: - from os import linesep -except ImportError: - linesep = '\n' # gae - -from logilab.common.deprecation import deprecated - -MANUAL_UNICODE_MAP = { - u'\xa1': u'!', # INVERTED EXCLAMATION MARK - u'\u0142': u'l', # LATIN SMALL LETTER L WITH STROKE - u'\u2044': u'/', # FRACTION SLASH - u'\xc6': u'AE', # LATIN CAPITAL LETTER AE - u'\xa9': u'(c)', # COPYRIGHT SIGN - u'\xab': u'"', # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK - u'\xe6': u'ae', # LATIN SMALL LETTER AE - u'\xae': u'(r)', # REGISTERED SIGN - u'\u0153': u'oe', # LATIN SMALL LIGATURE OE - u'\u0152': u'OE', # LATIN CAPITAL LIGATURE OE - u'\xd8': u'O', # LATIN CAPITAL LETTER O WITH STROKE - u'\xf8': u'o', # LATIN SMALL LETTER O WITH STROKE - u'\xbb': u'"', # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - u'\xdf': u'ss', # LATIN SMALL LETTER SHARP S - } - -def unormalize(ustring, ignorenonascii=None, substitute=None): - """replace diacritical characters with their corresponding ascii characters - - Convert the unicode string to its long normalized form (unicode character - will be transform into several characters) and keep the first one only. - The normal form KD (NFKD) will apply the compatibility decomposition, i.e. - replace all compatibility characters with their equivalents. - - :type substitute: str - :param substitute: replacement character to use if decomposition fails - - :see: Another project about ASCII transliterations of Unicode text - http://pypi.python.org/pypi/Unidecode - """ - # backward compatibility, ignorenonascii was a boolean - if ignorenonascii is not None: - warn("ignorenonascii is deprecated, use substitute named parameter instead", - DeprecationWarning, stacklevel=2) - if ignorenonascii: - substitute = '' - res = [] - for letter in ustring[:]: - try: - replacement = MANUAL_UNICODE_MAP[letter] - except KeyError: - replacement = _uninormalize('NFKD', letter)[0] - if ord(replacement) >= 2 ** 7: - if substitute is None: - raise ValueError("can't deal with non-ascii based characters") - replacement = substitute - res.append(replacement) - return u''.join(res) - -def unquote(string): - """remove optional quotes (simple or double) from the string - - :type string: str or unicode - :param string: an optionally quoted string - - :rtype: str or unicode - :return: the unquoted string (or the input string if it wasn't quoted) - """ - if not string: - return string - if string[0] in '"\'': - string = string[1:] - if string[-1] in '"\'': - string = string[:-1] - return string - - -_BLANKLINES_RGX = re.compile('\r?\n\r?\n') -_NORM_SPACES_RGX = re.compile('\s+') - -def normalize_text(text, line_len=80, indent='', rest=False): - """normalize a text to display it with a maximum line size and - optionally arbitrary indentation. Line jumps are normalized but blank - lines are kept. The indentation string may be used to insert a - comment (#) or a quoting (>) mark for instance. - - :type text: str or unicode - :param text: the input text to normalize - - :type line_len: int - :param line_len: expected maximum line's length, default to 80 - - :type indent: str or unicode - :param indent: optional string to use as indentation - - :rtype: str or unicode - :return: - the input text normalized to fit on lines with a maximized size - inferior to `line_len`, and optionally prefixed by an - indentation string - """ - if rest: - normp = normalize_rest_paragraph - else: - normp = normalize_paragraph - result = [] - for text in _BLANKLINES_RGX.split(text): - result.append(normp(text, line_len, indent)) - return ('%s%s%s' % (linesep, indent, linesep)).join(result) - - -def normalize_paragraph(text, line_len=80, indent=''): - """normalize a text to display it with a maximum line size and - optionally arbitrary indentation. Line jumps are normalized. The - indentation string may be used top insert a comment mark for - instance. - - :type text: str or unicode - :param text: the input text to normalize - - :type line_len: int - :param line_len: expected maximum line's length, default to 80 - - :type indent: str or unicode - :param indent: optional string to use as indentation - - :rtype: str or unicode - :return: - the input text normalized to fit on lines with a maximized size - inferior to `line_len`, and optionally prefixed by an - indentation string - """ - text = _NORM_SPACES_RGX.sub(' ', text) - line_len = line_len - len(indent) - lines = [] - while text: - aline, text = splittext(text.strip(), line_len) - lines.append(indent + aline) - return linesep.join(lines) - -def normalize_rest_paragraph(text, line_len=80, indent=''): - """normalize a ReST text to display it with a maximum line size and - optionally arbitrary indentation. Line jumps are normalized. The - indentation string may be used top insert a comment mark for - instance. - - :type text: str or unicode - :param text: the input text to normalize - - :type line_len: int - :param line_len: expected maximum line's length, default to 80 - - :type indent: str or unicode - :param indent: optional string to use as indentation - - :rtype: str or unicode - :return: - the input text normalized to fit on lines with a maximized size - inferior to `line_len`, and optionally prefixed by an - indentation string - """ - toreport = '' - lines = [] - line_len = line_len - len(indent) - for line in text.splitlines(): - line = toreport + _NORM_SPACES_RGX.sub(' ', line.strip()) - toreport = '' - while len(line) > line_len: - # too long line, need split - line, toreport = splittext(line, line_len) - lines.append(indent + line) - if toreport: - line = toreport + ' ' - toreport = '' - else: - line = '' - if line: - lines.append(indent + line.strip()) - return linesep.join(lines) - - -def splittext(text, line_len): - """split the given text on space according to the given max line size - - return a 2-uple: - * a line <= line_len if possible - * the rest of the text which has to be reported on another line - """ - if len(text) <= line_len: - return text, '' - pos = min(len(text)-1, line_len) - while pos > 0 and text[pos] != ' ': - pos -= 1 - if pos == 0: - pos = min(len(text), line_len) - while len(text) > pos and text[pos] != ' ': - pos += 1 - return text[:pos], text[pos+1:].strip() - - -def splitstrip(string, sep=','): - """return a list of stripped string by splitting the string given as - argument on `sep` (',' by default). Empty string are discarded. - - >>> splitstrip('a, b, c , 4,,') - ['a', 'b', 'c', '4'] - >>> splitstrip('a') - ['a'] - >>> - - :type string: str or unicode - :param string: a csv line - - :type sep: str or unicode - :param sep: field separator, default to the comma (',') - - :rtype: str or unicode - :return: the unquoted string (or the input string if it wasn't quoted) - """ - return [word.strip() for word in string.split(sep) if word.strip()] - -get_csv = deprecated('get_csv is deprecated, use splitstrip')(splitstrip) - - -def split_url_or_path(url_or_path): - """return the latest component of a string containing either an url of the - form :// or a local file system path - """ - if '://' in url_or_path: - return url_or_path.rstrip('/').rsplit('/', 1) - return osp.split(url_or_path.rstrip(osp.sep)) - - -def text_to_dict(text): - """parse multilines text containing simple 'key=value' lines and return a - dict of {'key': 'value'}. When the same key is encountered multiple time, - value is turned into a list containing all values. - - >>> d = text_to_dict('''multiple=1 - ... multiple= 2 - ... single =3 - ... ''') - >>> d['single'] - '3' - >>> d['multiple'] - ['1', '2'] - - """ - res = {} - if not text: - return res - for line in text.splitlines(): - line = line.strip() - if line and not line.startswith('#'): - key, value = [w.strip() for w in line.split('=', 1)] - if key in res: - try: - res[key].append(value) - except AttributeError: - res[key] = [res[key], value] - else: - res[key] = value - return res - - -_BLANK_URE = r'(\s|,)+' -_BLANK_RE = re.compile(_BLANK_URE) -__VALUE_URE = r'-?(([0-9]+\.[0-9]*)|((0x?)?[0-9]+))' -__UNITS_URE = r'[a-zA-Z]+' -_VALUE_RE = re.compile(r'(?P%s)(?P%s)?'%(__VALUE_URE, __UNITS_URE)) -_VALIDATION_RE = re.compile(r'^((%s)(%s))*(%s)?$' % (__VALUE_URE, __UNITS_URE, - __VALUE_URE)) - -BYTE_UNITS = { - "b": 1, - "kb": 1024, - "mb": 1024 ** 2, - "gb": 1024 ** 3, - "tb": 1024 ** 4, -} - -TIME_UNITS = { - "ms": 0.0001, - "s": 1, - "min": 60, - "h": 60 * 60, - "d": 60 * 60 *24, -} - -def apply_units(string, units, inter=None, final=float, blank_reg=_BLANK_RE, - value_reg=_VALUE_RE): - """Parse the string applying the units defined in units - (e.g.: "1.5m",{'m',60} -> 80). - - :type string: str or unicode - :param string: the string to parse - - :type units: dict (or any object with __getitem__ using basestring key) - :param units: a dict mapping a unit string repr to its value - - :type inter: type - :param inter: used to parse every intermediate value (need __sum__) - - :type blank_reg: regexp - :param blank_reg: should match every blank char to ignore. - - :type value_reg: regexp with "value" and optional "unit" group - :param value_reg: match a value and it's unit into the - """ - if inter is None: - inter = final - fstring = _BLANK_RE.sub('', string) - if not (fstring and _VALIDATION_RE.match(fstring)): - raise ValueError("Invalid unit string: %r." % string) - values = [] - for match in value_reg.finditer(fstring): - dic = match.groupdict() - lit, unit = dic["value"], dic.get("unit") - value = inter(lit) - if unit is not None: - try: - value *= units[unit.lower()] - except KeyError: - raise KeyError('invalid unit %s. valid units are %s' % - (unit, units.keys())) - values.append(value) - return final(sum(values)) - - -_LINE_RGX = re.compile('\r\n|\r+|\n') - -def pretty_match(match, string, underline_char='^'): - """return a string with the match location underlined: - - >>> import re - >>> print(pretty_match(re.search('mange', 'il mange du bacon'), 'il mange du bacon')) - il mange du bacon - ^^^^^ - >>> - - :type match: _sre.SRE_match - :param match: object returned by re.match, re.search or re.finditer - - :type string: str or unicode - :param string: - the string on which the regular expression has been applied to - obtain the `match` object - - :type underline_char: str or unicode - :param underline_char: - character to use to underline the matched section, default to the - carret '^' - - :rtype: str or unicode - :return: - the original string with an inserted line to underline the match - location - """ - start = match.start() - end = match.end() - string = _LINE_RGX.sub(linesep, string) - start_line_pos = string.rfind(linesep, 0, start) - if start_line_pos == -1: - start_line_pos = 0 - result = [] - else: - result = [string[:start_line_pos]] - start_line_pos += len(linesep) - offset = start - start_line_pos - underline = ' ' * offset + underline_char * (end - start) - end_line_pos = string.find(linesep, end) - if end_line_pos == -1: - string = string[start_line_pos:] - result.append(string) - result.append(underline) - else: - end = string[end_line_pos + len(linesep):] - string = string[start_line_pos:end_line_pos] - result.append(string) - result.append(underline) - result.append(end) - return linesep.join(result).rstrip() - - -# Ansi colorization ########################################################### - -ANSI_PREFIX = '\033[' -ANSI_END = 'm' -ANSI_RESET = '\033[0m' -ANSI_STYLES = { - 'reset': "0", - 'bold': "1", - 'italic': "3", - 'underline': "4", - 'blink': "5", - 'inverse': "7", - 'strike': "9", -} -ANSI_COLORS = { - 'reset': "0", - 'black': "30", - 'red': "31", - 'green': "32", - 'yellow': "33", - 'blue': "34", - 'magenta': "35", - 'cyan': "36", - 'white': "37", -} - -def _get_ansi_code(color=None, style=None): - """return ansi escape code corresponding to color and style - - :type color: str or None - :param color: - the color name (see `ANSI_COLORS` for available values) - or the color number when 256 colors are available - - :type style: str or None - :param style: - style string (see `ANSI_COLORS` for available values). To get - several style effects at the same time, use a coma as separator. - - :raise KeyError: if an unexistent color or style identifier is given - - :rtype: str - :return: the built escape code - """ - ansi_code = [] - if style: - style_attrs = splitstrip(style) - for effect in style_attrs: - ansi_code.append(ANSI_STYLES[effect]) - if color: - if color.isdigit(): - ansi_code.extend(['38', '5']) - ansi_code.append(color) - else: - ansi_code.append(ANSI_COLORS[color]) - if ansi_code: - return ANSI_PREFIX + ';'.join(ansi_code) + ANSI_END - return '' - -def colorize_ansi(msg, color=None, style=None): - """colorize message by wrapping it with ansi escape codes - - :type msg: str or unicode - :param msg: the message string to colorize - - :type color: str or None - :param color: - the color identifier (see `ANSI_COLORS` for available values) - - :type style: str or None - :param style: - style string (see `ANSI_COLORS` for available values). To get - several style effects at the same time, use a coma as separator. - - :raise KeyError: if an unexistent color or style identifier is given - - :rtype: str or unicode - :return: the ansi escaped string - """ - # If both color and style are not defined, then leave the text as is - if color is None and style is None: - return msg - escape_code = _get_ansi_code(color, style) - # If invalid (or unknown) color, don't wrap msg with ansi codes - if escape_code: - return '%s%s%s' % (escape_code, msg, ANSI_RESET) - return msg - -DIFF_STYLE = {'separator': 'cyan', 'remove': 'red', 'add': 'green'} - -def diff_colorize_ansi(lines, out=sys.stdout, style=DIFF_STYLE): - for line in lines: - if line[:4] in ('--- ', '+++ '): - out.write(colorize_ansi(line, style['separator'])) - elif line[0] == '-': - out.write(colorize_ansi(line, style['remove'])) - elif line[0] == '+': - out.write(colorize_ansi(line, style['add'])) - elif line[:4] == '--- ': - out.write(colorize_ansi(line, style['separator'])) - elif line[:4] == '+++ ': - out.write(colorize_ansi(line, style['separator'])) - else: - out.write(line) - diff --git a/pymode/libs/logilab/common/tree.py b/pymode/libs/logilab/common/tree.py deleted file mode 100644 index 885eb0fa..00000000 --- a/pymode/libs/logilab/common/tree.py +++ /dev/null @@ -1,369 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Base class to represent a tree structure. - - - - -""" -__docformat__ = "restructuredtext en" - -import sys - -from logilab.common import flatten -from logilab.common.visitor import VisitedMixIn, FilteredIterator, no_filter - -## Exceptions ################################################################# - -class NodeNotFound(Exception): - """raised when a node has not been found""" - -EX_SIBLING_NOT_FOUND = "No such sibling as '%s'" -EX_CHILD_NOT_FOUND = "No such child as '%s'" -EX_NODE_NOT_FOUND = "No such node as '%s'" - - -# Base node ################################################################### - -class Node(object): - """a basic tree node, characterized by an id""" - - def __init__(self, nid=None) : - self.id = nid - # navigation - self.parent = None - self.children = [] - - def __iter__(self): - return iter(self.children) - - def __str__(self, indent=0): - s = ['%s%s %s' % (' '*indent, self.__class__.__name__, self.id)] - indent += 2 - for child in self.children: - try: - s.append(child.__str__(indent)) - except TypeError: - s.append(child.__str__()) - return '\n'.join(s) - - def is_leaf(self): - return not self.children - - def append(self, child): - """add a node to children""" - self.children.append(child) - child.parent = self - - def remove(self, child): - """remove a child node""" - self.children.remove(child) - child.parent = None - - def insert(self, index, child): - """insert a child node""" - self.children.insert(index, child) - child.parent = self - - def replace(self, old_child, new_child): - """replace a child node with another""" - i = self.children.index(old_child) - self.children.pop(i) - self.children.insert(i, new_child) - new_child.parent = self - - def get_sibling(self, nid): - """return the sibling node that has given id""" - try: - return self.parent.get_child_by_id(nid) - except NodeNotFound : - raise NodeNotFound(EX_SIBLING_NOT_FOUND % nid) - - def next_sibling(self): - """ - return the next sibling for this node if any - """ - parent = self.parent - if parent is None: - # root node has no sibling - return None - index = parent.children.index(self) - try: - return parent.children[index+1] - except IndexError: - return None - - def previous_sibling(self): - """ - return the previous sibling for this node if any - """ - parent = self.parent - if parent is None: - # root node has no sibling - return None - index = parent.children.index(self) - if index > 0: - return parent.children[index-1] - return None - - def get_node_by_id(self, nid): - """ - return node in whole hierarchy that has given id - """ - root = self.root() - try: - return root.get_child_by_id(nid, 1) - except NodeNotFound : - raise NodeNotFound(EX_NODE_NOT_FOUND % nid) - - def get_child_by_id(self, nid, recurse=None): - """ - return child of given id - """ - if self.id == nid: - return self - for c in self.children : - if recurse: - try: - return c.get_child_by_id(nid, 1) - except NodeNotFound : - continue - if c.id == nid : - return c - raise NodeNotFound(EX_CHILD_NOT_FOUND % nid) - - def get_child_by_path(self, path): - """ - return child of given path (path is a list of ids) - """ - if len(path) > 0 and path[0] == self.id: - if len(path) == 1 : - return self - else : - for c in self.children : - try: - return c.get_child_by_path(path[1:]) - except NodeNotFound : - pass - raise NodeNotFound(EX_CHILD_NOT_FOUND % path) - - def depth(self): - """ - return depth of this node in the tree - """ - if self.parent is not None: - return 1 + self.parent.depth() - else : - return 0 - - def depth_down(self): - """ - return depth of the tree from this node - """ - if self.children: - return 1 + max([c.depth_down() for c in self.children]) - return 1 - - def width(self): - """ - return the width of the tree from this node - """ - return len(self.leaves()) - - def root(self): - """ - return the root node of the tree - """ - if self.parent is not None: - return self.parent.root() - return self - - def leaves(self): - """ - return a list with all the leaves nodes descendant from this node - """ - leaves = [] - if self.children: - for child in self.children: - leaves += child.leaves() - return leaves - else: - return [self] - - def flatten(self, _list=None): - """ - return a list with all the nodes descendant from this node - """ - if _list is None: - _list = [] - _list.append(self) - for c in self.children: - c.flatten(_list) - return _list - - def lineage(self): - """ - return list of parents up to root node - """ - lst = [self] - if self.parent is not None: - lst.extend(self.parent.lineage()) - return lst - -class VNode(Node, VisitedMixIn): - """a visitable node - """ - pass - - -class BinaryNode(VNode): - """a binary node (i.e. only two children - """ - def __init__(self, lhs=None, rhs=None) : - VNode.__init__(self) - if lhs is not None or rhs is not None: - assert lhs and rhs - self.append(lhs) - self.append(rhs) - - def remove(self, child): - """remove the child and replace this node with the other child - """ - self.children.remove(child) - self.parent.replace(self, self.children[0]) - - def get_parts(self): - """ - return the left hand side and the right hand side of this node - """ - return self.children[0], self.children[1] - - - -if sys.version_info[0:2] >= (2, 2): - list_class = list -else: - from UserList import UserList - list_class = UserList - -class ListNode(VNode, list_class): - """Used to manipulate Nodes as Lists - """ - def __init__(self): - list_class.__init__(self) - VNode.__init__(self) - self.children = self - - def __str__(self, indent=0): - return '%s%s %s' % (indent*' ', self.__class__.__name__, - ', '.join([str(v) for v in self])) - - def append(self, child): - """add a node to children""" - list_class.append(self, child) - child.parent = self - - def insert(self, index, child): - """add a node to children""" - list_class.insert(self, index, child) - child.parent = self - - def remove(self, child): - """add a node to children""" - list_class.remove(self, child) - child.parent = None - - def pop(self, index): - """add a node to children""" - child = list_class.pop(self, index) - child.parent = None - - def __iter__(self): - return list_class.__iter__(self) - -# construct list from tree #################################################### - -def post_order_list(node, filter_func=no_filter): - """ - create a list with tree nodes for which the function returned true - in a post order fashion - """ - l, stack = [], [] - poped, index = 0, 0 - while node: - if filter_func(node): - if node.children and not poped: - stack.append((node, index)) - index = 0 - node = node.children[0] - else: - l.append(node) - index += 1 - try: - node = stack[-1][0].children[index] - except IndexError: - node = None - else: - node = None - poped = 0 - if node is None and stack: - node, index = stack.pop() - poped = 1 - return l - -def pre_order_list(node, filter_func=no_filter): - """ - create a list with tree nodes for which the function returned true - in a pre order fashion - """ - l, stack = [], [] - poped, index = 0, 0 - while node: - if filter_func(node): - if not poped: - l.append(node) - if node.children and not poped: - stack.append((node, index)) - index = 0 - node = node.children[0] - else: - index += 1 - try: - node = stack[-1][0].children[index] - except IndexError: - node = None - else: - node = None - poped = 0 - if node is None and len(stack) > 1: - node, index = stack.pop() - poped = 1 - return l - -class PostfixedDepthFirstIterator(FilteredIterator): - """a postfixed depth first iterator, designed to be used with visitors - """ - def __init__(self, node, filter_func=None): - FilteredIterator.__init__(self, node, post_order_list, filter_func) - -class PrefixedDepthFirstIterator(FilteredIterator): - """a prefixed depth first iterator, designed to be used with visitors - """ - def __init__(self, node, filter_func=None): - FilteredIterator.__init__(self, node, pre_order_list, filter_func) - diff --git a/pymode/libs/logilab/common/umessage.py b/pymode/libs/logilab/common/umessage.py deleted file mode 100644 index a5e47995..00000000 --- a/pymode/libs/logilab/common/umessage.py +++ /dev/null @@ -1,194 +0,0 @@ -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Unicode email support (extends email from stdlib)""" - -__docformat__ = "restructuredtext en" - -import email -from encodings import search_function -import sys -if sys.version_info >= (2, 5): - from email.utils import parseaddr, parsedate - from email.header import decode_header -else: - from email.Utils import parseaddr, parsedate - from email.Header import decode_header - -from datetime import datetime - -try: - from mx.DateTime import DateTime -except ImportError: - DateTime = datetime - -import logilab.common as lgc - - -def decode_QP(string): - parts = [] - for decoded, charset in decode_header(string): - if not charset : - charset = 'iso-8859-15' - parts.append(decoded.decode(charset, 'replace')) - - if sys.version_info < (3, 3): - # decoding was non-RFC compliant wrt to whitespace handling - # see http://bugs.python.org/issue1079 - return u' '.join(parts) - return u''.join(parts) - -def message_from_file(fd): - try: - return UMessage(email.message_from_file(fd)) - except email.Errors.MessageParseError: - return '' - -def message_from_string(string): - try: - return UMessage(email.message_from_string(string)) - except email.Errors.MessageParseError: - return '' - -class UMessage: - """Encapsulates an email.Message instance and returns only unicode objects. - """ - - def __init__(self, message): - self.message = message - - # email.Message interface ################################################# - - def get(self, header, default=None): - value = self.message.get(header, default) - if value: - return decode_QP(value) - return value - - def __getitem__(self, header): - return self.get(header) - - def get_all(self, header, default=()): - return [decode_QP(val) for val in self.message.get_all(header, default) - if val is not None] - - def is_multipart(self): - return self.message.is_multipart() - - def get_boundary(self): - return self.message.get_boundary() - - def walk(self): - for part in self.message.walk(): - yield UMessage(part) - - if sys.version_info < (3, 0): - - def get_payload(self, index=None, decode=False): - message = self.message - if index is None: - payload = message.get_payload(index, decode) - if isinstance(payload, list): - return [UMessage(msg) for msg in payload] - if message.get_content_maintype() != 'text': - return payload - - charset = message.get_content_charset() or 'iso-8859-1' - if search_function(charset) is None: - charset = 'iso-8859-1' - return unicode(payload or '', charset, "replace") - else: - payload = UMessage(message.get_payload(index, decode)) - return payload - - def get_content_maintype(self): - return unicode(self.message.get_content_maintype()) - - def get_content_type(self): - return unicode(self.message.get_content_type()) - - def get_filename(self, failobj=None): - value = self.message.get_filename(failobj) - if value is failobj: - return value - try: - return unicode(value) - except UnicodeDecodeError: - return u'error decoding filename' - - else: - - def get_payload(self, index=None, decode=False): - message = self.message - if index is None: - payload = message.get_payload(index, decode) - if isinstance(payload, list): - return [UMessage(msg) for msg in payload] - return payload - else: - payload = UMessage(message.get_payload(index, decode)) - return payload - - def get_content_maintype(self): - return self.message.get_content_maintype() - - def get_content_type(self): - return self.message.get_content_type() - - def get_filename(self, failobj=None): - return self.message.get_filename(failobj) - - # other convenience methods ############################################### - - def headers(self): - """return an unicode string containing all the message's headers""" - values = [] - for header in self.message.keys(): - values.append(u'%s: %s' % (header, self.get(header))) - return '\n'.join(values) - - def multi_addrs(self, header): - """return a list of 2-uple (name, address) for the given address (which - is expected to be an header containing address such as from, to, cc...) - """ - persons = [] - for person in self.get_all(header, ()): - name, mail = parseaddr(person) - persons.append((name, mail)) - return persons - - def date(self, alternative_source=False, return_str=False): - """return a datetime object for the email's date or None if no date is - set or if it can't be parsed - """ - value = self.get('date') - if value is None and alternative_source: - unix_from = self.message.get_unixfrom() - if unix_from is not None: - try: - value = unix_from.split(" ", 2)[2] - except IndexError: - pass - if value is not None: - datetuple = parsedate(value) - if datetuple: - if lgc.USE_MX_DATETIME: - return DateTime(*datetuple[:6]) - return datetime(*datetuple[:6]) - elif not return_str: - return None - return value diff --git a/pymode/libs/logilab/common/ureports/__init__.py b/pymode/libs/logilab/common/ureports/__init__.py deleted file mode 100644 index d76ebe52..00000000 --- a/pymode/libs/logilab/common/ureports/__init__.py +++ /dev/null @@ -1,172 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Universal report objects and some formatting drivers. - -A way to create simple reports using python objects, primarily designed to be -formatted as text and html. -""" -__docformat__ = "restructuredtext en" - -import sys - -from logilab.common.compat import StringIO -from logilab.common.textutils import linesep - - -def get_nodes(node, klass): - """return an iterator on all children node of the given klass""" - for child in node.children: - if isinstance(child, klass): - yield child - # recurse (FIXME: recursion controled by an option) - for grandchild in get_nodes(child, klass): - yield grandchild - -def layout_title(layout): - """try to return the layout's title as string, return None if not found - """ - for child in layout.children: - if isinstance(child, Title): - return u' '.join([node.data for node in get_nodes(child, Text)]) - -def build_summary(layout, level=1): - """make a summary for the report, including X level""" - assert level > 0 - level -= 1 - summary = List(klass=u'summary') - for child in layout.children: - if not isinstance(child, Section): - continue - label = layout_title(child) - if not label and not child.id: - continue - if not child.id: - child.id = label.replace(' ', '-') - node = Link(u'#'+child.id, label=label or child.id) - # FIXME: Three following lines produce not very compliant - # docbook: there are some useless . They might be - # replaced by the three commented lines but this then produces - # a bug in html display... - if level and [n for n in child.children if isinstance(n, Section)]: - node = Paragraph([node, build_summary(child, level)]) - summary.append(node) -# summary.append(node) -# if level and [n for n in child.children if isinstance(n, Section)]: -# summary.append(build_summary(child, level)) - return summary - - -class BaseWriter(object): - """base class for ureport writers""" - - def format(self, layout, stream=None, encoding=None): - """format and write the given layout into the stream object - - unicode policy: unicode strings may be found in the layout; - try to call stream.write with it, but give it back encoded using - the given encoding if it fails - """ - if stream is None: - stream = sys.stdout - if not encoding: - encoding = getattr(stream, 'encoding', 'UTF-8') - self.encoding = encoding or 'UTF-8' - self.__compute_funcs = [] - self.out = stream - self.begin_format(layout) - layout.accept(self) - self.end_format(layout) - - def format_children(self, layout): - """recurse on the layout children and call their accept method - (see the Visitor pattern) - """ - for child in getattr(layout, 'children', ()): - child.accept(self) - - def writeln(self, string=u''): - """write a line in the output buffer""" - self.write(string + linesep) - - def write(self, string): - """write a string in the output buffer""" - try: - self.out.write(string) - except UnicodeEncodeError: - self.out.write(string.encode(self.encoding)) - - def begin_format(self, layout): - """begin to format a layout""" - self.section = 0 - - def end_format(self, layout): - """finished to format a layout""" - - def get_table_content(self, table): - """trick to get table content without actually writing it - - return an aligned list of lists containing table cells values as string - """ - result = [[]] - cols = table.cols - for cell in self.compute_content(table): - if cols == 0: - result.append([]) - cols = table.cols - cols -= 1 - result[-1].append(cell) - # fill missing cells - while len(result[-1]) < cols: - result[-1].append(u'') - return result - - def compute_content(self, layout): - """trick to compute the formatting of children layout before actually - writing it - - return an iterator on strings (one for each child element) - """ - # use cells ! - def write(data): - try: - stream.write(data) - except UnicodeEncodeError: - stream.write(data.encode(self.encoding)) - def writeln(data=u''): - try: - stream.write(data+linesep) - except UnicodeEncodeError: - stream.write(data.encode(self.encoding)+linesep) - self.write = write - self.writeln = writeln - self.__compute_funcs.append((write, writeln)) - for child in layout.children: - stream = StringIO() - child.accept(self) - yield stream.getvalue() - self.__compute_funcs.pop() - try: - self.write, self.writeln = self.__compute_funcs[-1] - except IndexError: - del self.write - del self.writeln - - -from logilab.common.ureports.nodes import * -from logilab.common.ureports.text_writer import TextWriter -from logilab.common.ureports.html_writer import HTMLWriter diff --git a/pymode/libs/logilab/common/ureports/docbook_writer.py b/pymode/libs/logilab/common/ureports/docbook_writer.py deleted file mode 100644 index 857068c8..00000000 --- a/pymode/libs/logilab/common/ureports/docbook_writer.py +++ /dev/null @@ -1,140 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""HTML formatting drivers for ureports""" -__docformat__ = "restructuredtext en" - -from six.moves import range - -from logilab.common.ureports import HTMLWriter - -class DocbookWriter(HTMLWriter): - """format layouts as HTML""" - - def begin_format(self, layout): - """begin to format a layout""" - super(HTMLWriter, self).begin_format(layout) - if self.snippet is None: - self.writeln('') - self.writeln(""" - -""") - - def end_format(self, layout): - """finished to format a layout""" - if self.snippet is None: - self.writeln('') - - def visit_section(self, layout): - """display a section (using (level 0) or
)""" - if self.section == 0: - tag = "chapter" - else: - tag = "section" - self.section += 1 - self.writeln(self._indent('<%s%s>' % (tag, self.handle_attrs(layout)))) - self.format_children(layout) - self.writeln(self._indent(''% tag)) - self.section -= 1 - - def visit_title(self, layout): - """display a title using Codestin Search App') - - def visit_table(self, layout): - """display a table as html""" - self.writeln(self._indent(' Codestin Search App' \ - % (self.handle_attrs(layout), layout.title))) - self.writeln(self._indent(' '% layout.cols)) - for i in range(layout.cols): - self.writeln(self._indent(' ' % i)) - - table_content = self.get_table_content(layout) - # write headers - if layout.cheaders: - self.writeln(self._indent(' ')) - self._write_row(table_content[0]) - self.writeln(self._indent(' ')) - table_content = table_content[1:] - elif layout.rcheaders: - self.writeln(self._indent(' ')) - self._write_row(table_content[-1]) - self.writeln(self._indent(' ')) - table_content = table_content[:-1] - # write body - self.writeln(self._indent(' ')) - for i in range(len(table_content)): - row = table_content[i] - self.writeln(self._indent(' ')) - for j in range(len(row)): - cell = row[j] or ' ' - self.writeln(self._indent(' %s' % cell)) - self.writeln(self._indent(' ')) - self.writeln(self._indent(' ')) - self.writeln(self._indent(' ')) - self.writeln(self._indent(' ')) - - def _write_row(self, row): - """write content of row (using )""" - self.writeln(' ') - for j in range(len(row)): - cell = row[j] or ' ' - self.writeln(' %s' % cell) - self.writeln(self._indent(' ')) - - def visit_list(self, layout): - """display a list (using )""" - self.writeln(self._indent(' ' % self.handle_attrs(layout))) - for row in list(self.compute_content(layout)): - self.writeln(' %s' % row) - self.writeln(self._indent(' ')) - - def visit_paragraph(self, layout): - """display links (using )""" - self.write(self._indent(' ')) - self.format_children(layout) - self.writeln('') - - def visit_span(self, layout): - """display links (using

)""" - #TODO: translate in docbook - self.write('' % self.handle_attrs(layout)) - self.format_children(layout) - self.write('') - - def visit_link(self, layout): - """display links (using )""" - self.write('%s' % (layout.url, - self.handle_attrs(layout), - layout.label)) - - def visit_verbatimtext(self, layout): - """display verbatim text (using )""" - self.writeln(self._indent(' ')) - self.write(layout.data.replace('&', '&').replace('<', '<')) - self.writeln(self._indent(' ')) - - def visit_text(self, layout): - """add some text""" - self.write(layout.data.replace('&', '&').replace('<', '<')) - - def _indent(self, string): - """correctly indent string according to section""" - return ' ' * 2*(self.section) + string diff --git a/pymode/libs/logilab/common/ureports/html_writer.py b/pymode/libs/logilab/common/ureports/html_writer.py deleted file mode 100644 index eba34ea4..00000000 --- a/pymode/libs/logilab/common/ureports/html_writer.py +++ /dev/null @@ -1,133 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""HTML formatting drivers for ureports""" -__docformat__ = "restructuredtext en" - -from cgi import escape - -from six.moves import range - -from logilab.common.ureports import BaseWriter - - -class HTMLWriter(BaseWriter): - """format layouts as HTML""" - - def __init__(self, snippet=None): - super(HTMLWriter, self).__init__() - self.snippet = snippet - - def handle_attrs(self, layout): - """get an attribute string from layout member attributes""" - attrs = u'' - klass = getattr(layout, 'klass', None) - if klass: - attrs += u' class="%s"' % klass - nid = getattr(layout, 'id', None) - if nid: - attrs += u' id="%s"' % nid - return attrs - - def begin_format(self, layout): - """begin to format a layout""" - super(HTMLWriter, self).begin_format(layout) - if self.snippet is None: - self.writeln(u'') - self.writeln(u'') - - def end_format(self, layout): - """finished to format a layout""" - if self.snippet is None: - self.writeln(u'') - self.writeln(u'') - - - def visit_section(self, layout): - """display a section as html, using div + h[section level]""" - self.section += 1 - self.writeln(u'' % self.handle_attrs(layout)) - self.format_children(layout) - self.writeln(u'') - self.section -= 1 - - def visit_title(self, layout): - """display a title using """ - self.write(u'' % (self.section, self.handle_attrs(layout))) - self.format_children(layout) - self.writeln(u'' % self.section) - - def visit_table(self, layout): - """display a table as html""" - self.writeln(u'' % self.handle_attrs(layout)) - table_content = self.get_table_content(layout) - for i in range(len(table_content)): - row = table_content[i] - if i == 0 and layout.rheaders: - self.writeln(u'') - elif i+1 == len(table_content) and layout.rrheaders: - self.writeln(u'') - else: - self.writeln(u'' % (i%2 and 'even' or 'odd')) - for j in range(len(row)): - cell = row[j] or u' ' - if (layout.rheaders and i == 0) or \ - (layout.cheaders and j == 0) or \ - (layout.rrheaders and i+1 == len(table_content)) or \ - (layout.rcheaders and j+1 == len(row)): - self.writeln(u'%s' % cell) - else: - self.writeln(u'%s' % cell) - self.writeln(u'') - self.writeln(u'') - - def visit_list(self, layout): - """display a list as html""" - self.writeln(u'' % self.handle_attrs(layout)) - for row in list(self.compute_content(layout)): - self.writeln(u'

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

    )""" - self.write(u'

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

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

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

    )"""
    -        self.write(u'
    ')
    -        self.write(layout.data.replace(u'&', u'&').replace(u'<', u'<'))
    -        self.write(u'
    ') - - def visit_text(self, layout): - """add some text""" - data = layout.data - if layout.escaped: - data = data.replace(u'&', u'&').replace(u'<', u'<') - self.write(data) diff --git a/pymode/libs/logilab/common/ureports/nodes.py b/pymode/libs/logilab/common/ureports/nodes.py deleted file mode 100644 index a9585b30..00000000 --- a/pymode/libs/logilab/common/ureports/nodes.py +++ /dev/null @@ -1,203 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Micro reports objects. - -A micro report is a tree of layout and content objects. -""" -__docformat__ = "restructuredtext en" - -from logilab.common.tree import VNode - -from six import string_types - -class BaseComponent(VNode): - """base report component - - attributes - * id : the component's optional id - * klass : the component's optional klass - """ - def __init__(self, id=None, klass=None): - VNode.__init__(self, id) - self.klass = klass - -class BaseLayout(BaseComponent): - """base container node - - attributes - * BaseComponent attributes - * children : components in this table (i.e. the table's cells) - """ - def __init__(self, children=(), **kwargs): - super(BaseLayout, self).__init__(**kwargs) - for child in children: - if isinstance(child, BaseComponent): - self.append(child) - else: - self.add_text(child) - - def append(self, child): - """overridden to detect problems easily""" - assert child not in self.parents() - VNode.append(self, child) - - def parents(self): - """return the ancestor nodes""" - assert self.parent is not self - if self.parent is None: - return [] - return [self.parent] + self.parent.parents() - - def add_text(self, text): - """shortcut to add text data""" - self.children.append(Text(text)) - - -# non container nodes ######################################################### - -class Text(BaseComponent): - """a text portion - - attributes : - * BaseComponent attributes - * data : the text value as an encoded or unicode string - """ - def __init__(self, data, escaped=True, **kwargs): - super(Text, self).__init__(**kwargs) - #if isinstance(data, unicode): - # data = data.encode('ascii') - assert isinstance(data, string_types), data.__class__ - self.escaped = escaped - self.data = data - -class VerbatimText(Text): - """a verbatim text, display the raw data - - attributes : - * BaseComponent attributes - * data : the text value as an encoded or unicode string - """ - -class Link(BaseComponent): - """a labelled link - - attributes : - * BaseComponent attributes - * url : the link's target (REQUIRED) - * label : the link's label as a string (use the url by default) - """ - def __init__(self, url, label=None, **kwargs): - super(Link, self).__init__(**kwargs) - assert url - self.url = url - self.label = label or url - - -class Image(BaseComponent): - """an embedded or a single image - - attributes : - * BaseComponent attributes - * filename : the image's filename (REQUIRED) - * stream : the stream object containing the image data (REQUIRED) - * title : the image's optional title - """ - def __init__(self, filename, stream, title=None, **kwargs): - super(Image, self).__init__(**kwargs) - assert filename - assert stream - self.filename = filename - self.stream = stream - self.title = title - - -# container nodes ############################################################# - -class Section(BaseLayout): - """a section - - attributes : - * BaseLayout attributes - - a title may also be given to the constructor, it'll be added - as a first element - a description may also be given to the constructor, it'll be added - as a first paragraph - """ - def __init__(self, title=None, description=None, **kwargs): - super(Section, self).__init__(**kwargs) - if description: - self.insert(0, Paragraph([Text(description)])) - if title: - self.insert(0, Title(children=(title,))) - -class Title(BaseLayout): - """a title - - attributes : - * BaseLayout attributes - - A title must not contains a section nor a paragraph! - """ - -class Span(BaseLayout): - """a title - - attributes : - * BaseLayout attributes - - A span should only contains Text and Link nodes (in-line elements) - """ - -class Paragraph(BaseLayout): - """a simple text paragraph - - attributes : - * BaseLayout attributes - - A paragraph must not contains a section ! - """ - -class Table(BaseLayout): - """some tabular data - - attributes : - * BaseLayout attributes - * cols : the number of columns of the table (REQUIRED) - * rheaders : the first row's elements are table's header - * cheaders : the first col's elements are table's header - * title : the table's optional title - """ - def __init__(self, cols, title=None, - rheaders=0, cheaders=0, rrheaders=0, rcheaders=0, - **kwargs): - super(Table, self).__init__(**kwargs) - assert isinstance(cols, int) - self.cols = cols - self.title = title - self.rheaders = rheaders - self.cheaders = cheaders - self.rrheaders = rrheaders - self.rcheaders = rcheaders - -class List(BaseLayout): - """some list data - - attributes : - * BaseLayout attributes - """ diff --git a/pymode/libs/logilab/common/ureports/text_writer.py b/pymode/libs/logilab/common/ureports/text_writer.py deleted file mode 100644 index c87613c9..00000000 --- a/pymode/libs/logilab/common/ureports/text_writer.py +++ /dev/null @@ -1,145 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Text formatting drivers for ureports""" - -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from six.moves import range - -from logilab.common.textutils import linesep -from logilab.common.ureports import BaseWriter - - -TITLE_UNDERLINES = [u'', u'=', u'-', u'`', u'.', u'~', u'^'] -BULLETS = [u'*', u'-'] - -class TextWriter(BaseWriter): - """format layouts as text - (ReStructured inspiration but not totally handled yet) - """ - def begin_format(self, layout): - super(TextWriter, self).begin_format(layout) - self.list_level = 0 - self.pending_urls = [] - - def visit_section(self, layout): - """display a section as text - """ - self.section += 1 - self.writeln() - self.format_children(layout) - if self.pending_urls: - self.writeln() - for label, url in self.pending_urls: - self.writeln(u'.. _`%s`: %s' % (label, url)) - self.pending_urls = [] - self.section -= 1 - self.writeln() - - def visit_title(self, layout): - title = u''.join(list(self.compute_content(layout))) - self.writeln(title) - try: - self.writeln(TITLE_UNDERLINES[self.section] * len(title)) - except IndexError: - print("FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT") - - def visit_paragraph(self, layout): - """enter a paragraph""" - self.format_children(layout) - self.writeln() - - def visit_span(self, layout): - """enter a span""" - self.format_children(layout) - - def visit_table(self, layout): - """display a table as text""" - table_content = self.get_table_content(layout) - # get columns width - cols_width = [0]*len(table_content[0]) - for row in table_content: - for index in range(len(row)): - col = row[index] - cols_width[index] = max(cols_width[index], len(col)) - if layout.klass == 'field': - self.field_table(layout, table_content, cols_width) - else: - self.default_table(layout, table_content, cols_width) - self.writeln() - - def default_table(self, layout, table_content, cols_width): - """format a table""" - cols_width = [size+1 for size in cols_width] - format_strings = u' '.join([u'%%-%ss'] * len(cols_width)) - format_strings = format_strings % tuple(cols_width) - format_strings = format_strings.split(' ') - table_linesep = u'\n+' + u'+'.join([u'-'*w for w in cols_width]) + u'+\n' - headsep = u'\n+' + u'+'.join([u'='*w for w in cols_width]) + u'+\n' - # FIXME: layout.cheaders - self.write(table_linesep) - for i in range(len(table_content)): - self.write(u'|') - line = table_content[i] - for j in range(len(line)): - self.write(format_strings[j] % line[j]) - self.write(u'|') - if i == 0 and layout.rheaders: - self.write(headsep) - else: - self.write(table_linesep) - - def field_table(self, layout, table_content, cols_width): - """special case for field table""" - assert layout.cols == 2 - format_string = u'%s%%-%ss: %%s' % (linesep, cols_width[0]) - for field, value in table_content: - self.write(format_string % (field, value)) - - - def visit_list(self, layout): - """display a list layout as text""" - bullet = BULLETS[self.list_level % len(BULLETS)] - indent = ' ' * self.list_level - self.list_level += 1 - for child in layout.children: - self.write(u'%s%s%s ' % (linesep, indent, bullet)) - child.accept(self) - self.list_level -= 1 - - def visit_link(self, layout): - """add a hyperlink""" - if layout.label != layout.url: - self.write(u'`%s`_' % layout.label) - self.pending_urls.append( (layout.label, layout.url) ) - else: - self.write(layout.url) - - def visit_verbatimtext(self, layout): - """display a verbatim layout as text (so difficult ;) - """ - self.writeln(u'::\n') - for line in layout.data.splitlines(): - self.writeln(u' ' + line) - self.writeln() - - def visit_text(self, layout): - """add some text""" - self.write(u'%s' % layout.data) diff --git a/pymode/libs/logilab/common/urllib2ext.py b/pymode/libs/logilab/common/urllib2ext.py deleted file mode 100644 index 339aec06..00000000 --- a/pymode/libs/logilab/common/urllib2ext.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import print_function - -import logging -import urllib2 - -import kerberos as krb - -class GssapiAuthError(Exception): - """raised on error during authentication process""" - -import re -RGX = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I) - -def get_negociate_value(headers): - for authreq in headers.getheaders('www-authenticate'): - match = RGX.search(authreq) - if match: - return match.group(1) - -class HTTPGssapiAuthHandler(urllib2.BaseHandler): - """Negotiate HTTP authentication using context from GSSAPI""" - - handler_order = 400 # before Digest Auth - - def __init__(self): - self._reset() - - def _reset(self): - self._retried = 0 - self._context = None - - def clean_context(self): - if self._context is not None: - krb.authGSSClientClean(self._context) - - def http_error_401(self, req, fp, code, msg, headers): - try: - if self._retried > 5: - raise urllib2.HTTPError(req.get_full_url(), 401, - "negotiate auth failed", headers, None) - self._retried += 1 - logging.debug('gssapi handler, try %s' % self._retried) - negotiate = get_negociate_value(headers) - if negotiate is None: - logging.debug('no negociate found in a www-authenticate header') - return None - logging.debug('HTTPGssapiAuthHandler: negotiate 1 is %r' % negotiate) - result, self._context = krb.authGSSClientInit("HTTP@%s" % req.get_host()) - if result < 1: - raise GssapiAuthError("HTTPGssapiAuthHandler: init failed with %d" % result) - result = krb.authGSSClientStep(self._context, negotiate) - if result < 0: - raise GssapiAuthError("HTTPGssapiAuthHandler: step 1 failed with %d" % result) - client_response = krb.authGSSClientResponse(self._context) - logging.debug('HTTPGssapiAuthHandler: client response is %s...' % client_response[:10]) - req.add_unredirected_header('Authorization', "Negotiate %s" % client_response) - server_response = self.parent.open(req) - negotiate = get_negociate_value(server_response.info()) - if negotiate is None: - logging.warning('HTTPGssapiAuthHandler: failed to authenticate server') - else: - logging.debug('HTTPGssapiAuthHandler negotiate 2: %s' % negotiate) - result = krb.authGSSClientStep(self._context, negotiate) - if result < 1: - raise GssapiAuthError("HTTPGssapiAuthHandler: step 2 failed with %d" % result) - return server_response - except GssapiAuthError as exc: - logging.error(repr(exc)) - finally: - self.clean_context() - self._reset() - -if __name__ == '__main__': - import sys - # debug - import httplib - httplib.HTTPConnection.debuglevel = 1 - httplib.HTTPSConnection.debuglevel = 1 - # debug - import logging - logging.basicConfig(level=logging.DEBUG) - # handle cookies - import cookielib - cj = cookielib.CookieJar() - ch = urllib2.HTTPCookieProcessor(cj) - # test with url sys.argv[1] - h = HTTPGssapiAuthHandler() - response = urllib2.build_opener(h, ch).open(sys.argv[1]) - print('\nresponse: %s\n--------------\n' % response.code, response.info()) diff --git a/pymode/libs/logilab/common/vcgutils.py b/pymode/libs/logilab/common/vcgutils.py deleted file mode 100644 index 9cd2acda..00000000 --- a/pymode/libs/logilab/common/vcgutils.py +++ /dev/null @@ -1,216 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""Functions to generate files readable with Georg Sander's vcg -(Visualization of Compiler Graphs). - -You can download vcg at http://rw4.cs.uni-sb.de/~sander/html/gshome.html -Note that vcg exists as a debian package. - -See vcg's documentation for explanation about the different values that -maybe used for the functions parameters. - - - - -""" -__docformat__ = "restructuredtext en" - -import string - -ATTRS_VAL = { - 'algos': ('dfs', 'tree', 'minbackward', - 'left_to_right', 'right_to_left', - 'top_to_bottom', 'bottom_to_top', - 'maxdepth', 'maxdepthslow', 'mindepth', 'mindepthslow', - 'mindegree', 'minindegree', 'minoutdegree', - 'maxdegree', 'maxindegree', 'maxoutdegree'), - 'booleans': ('yes', 'no'), - 'colors': ('black', 'white', 'blue', 'red', 'green', 'yellow', - 'magenta', 'lightgrey', - 'cyan', 'darkgrey', 'darkblue', 'darkred', 'darkgreen', - 'darkyellow', 'darkmagenta', 'darkcyan', 'gold', - 'lightblue', 'lightred', 'lightgreen', 'lightyellow', - 'lightmagenta', 'lightcyan', 'lilac', 'turquoise', - 'aquamarine', 'khaki', 'purple', 'yellowgreen', 'pink', - 'orange', 'orchid'), - 'shapes': ('box', 'ellipse', 'rhomb', 'triangle'), - 'textmodes': ('center', 'left_justify', 'right_justify'), - 'arrowstyles': ('solid', 'line', 'none'), - 'linestyles': ('continuous', 'dashed', 'dotted', 'invisible'), - } - -# meaning of possible values: -# O -> string -# 1 -> int -# list -> value in list -GRAPH_ATTRS = { - 'title': 0, - 'label': 0, - 'color': ATTRS_VAL['colors'], - 'textcolor': ATTRS_VAL['colors'], - 'bordercolor': ATTRS_VAL['colors'], - 'width': 1, - 'height': 1, - 'borderwidth': 1, - 'textmode': ATTRS_VAL['textmodes'], - 'shape': ATTRS_VAL['shapes'], - 'shrink': 1, - 'stretch': 1, - 'orientation': ATTRS_VAL['algos'], - 'vertical_order': 1, - 'horizontal_order': 1, - 'xspace': 1, - 'yspace': 1, - 'layoutalgorithm': ATTRS_VAL['algos'], - 'late_edge_labels': ATTRS_VAL['booleans'], - 'display_edge_labels': ATTRS_VAL['booleans'], - 'dirty_edge_labels': ATTRS_VAL['booleans'], - 'finetuning': ATTRS_VAL['booleans'], - 'manhattan_edges': ATTRS_VAL['booleans'], - 'smanhattan_edges': ATTRS_VAL['booleans'], - 'port_sharing': ATTRS_VAL['booleans'], - 'edges': ATTRS_VAL['booleans'], - 'nodes': ATTRS_VAL['booleans'], - 'splines': ATTRS_VAL['booleans'], - } -NODE_ATTRS = { - 'title': 0, - 'label': 0, - 'color': ATTRS_VAL['colors'], - 'textcolor': ATTRS_VAL['colors'], - 'bordercolor': ATTRS_VAL['colors'], - 'width': 1, - 'height': 1, - 'borderwidth': 1, - 'textmode': ATTRS_VAL['textmodes'], - 'shape': ATTRS_VAL['shapes'], - 'shrink': 1, - 'stretch': 1, - 'vertical_order': 1, - 'horizontal_order': 1, - } -EDGE_ATTRS = { - 'sourcename': 0, - 'targetname': 0, - 'label': 0, - 'linestyle': ATTRS_VAL['linestyles'], - 'class': 1, - 'thickness': 0, - 'color': ATTRS_VAL['colors'], - 'textcolor': ATTRS_VAL['colors'], - 'arrowcolor': ATTRS_VAL['colors'], - 'backarrowcolor': ATTRS_VAL['colors'], - 'arrowsize': 1, - 'backarrowsize': 1, - 'arrowstyle': ATTRS_VAL['arrowstyles'], - 'backarrowstyle': ATTRS_VAL['arrowstyles'], - 'textmode': ATTRS_VAL['textmodes'], - 'priority': 1, - 'anchor': 1, - 'horizontal_order': 1, - } - - -# Misc utilities ############################################################### - -def latin_to_vcg(st): - """Convert latin characters using vcg escape sequence. - """ - for char in st: - if char not in string.ascii_letters: - try: - num = ord(char) - if num >= 192: - st = st.replace(char, r'\fi%d'%ord(char)) - except: - pass - return st - - -class VCGPrinter: - """A vcg graph writer. - """ - - def __init__(self, output_stream): - self._stream = output_stream - self._indent = '' - - def open_graph(self, **args): - """open a vcg graph - """ - self._stream.write('%sgraph:{\n'%self._indent) - self._inc_indent() - self._write_attributes(GRAPH_ATTRS, **args) - - def close_graph(self): - """close a vcg graph - """ - self._dec_indent() - self._stream.write('%s}\n'%self._indent) - - - def node(self, title, **args): - """draw a node - """ - self._stream.write('%snode: {title:"%s"' % (self._indent, title)) - self._write_attributes(NODE_ATTRS, **args) - self._stream.write('}\n') - - - def edge(self, from_node, to_node, edge_type='', **args): - """draw an edge from a node to another. - """ - self._stream.write( - '%s%sedge: {sourcename:"%s" targetname:"%s"' % ( - self._indent, edge_type, from_node, to_node)) - self._write_attributes(EDGE_ATTRS, **args) - self._stream.write('}\n') - - - # private ################################################################## - - def _write_attributes(self, attributes_dict, **args): - """write graph, node or edge attributes - """ - for key, value in args.items(): - try: - _type = attributes_dict[key] - except KeyError: - raise Exception('''no such attribute %s -possible attributes are %s''' % (key, attributes_dict.keys())) - - if not _type: - self._stream.write('%s%s:"%s"\n' % (self._indent, key, value)) - elif _type == 1: - self._stream.write('%s%s:%s\n' % (self._indent, key, - int(value))) - elif value in _type: - self._stream.write('%s%s:%s\n' % (self._indent, key, value)) - else: - raise Exception('''value %s isn\'t correct for attribute %s -correct values are %s''' % (value, key, _type)) - - def _inc_indent(self): - """increment indentation - """ - self._indent = ' %s' % self._indent - - def _dec_indent(self): - """decrement indentation - """ - self._indent = self._indent[:-2] diff --git a/pymode/libs/logilab/common/visitor.py b/pymode/libs/logilab/common/visitor.py deleted file mode 100644 index ed2b70f9..00000000 --- a/pymode/libs/logilab/common/visitor.py +++ /dev/null @@ -1,109 +0,0 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""A generic visitor abstract implementation. - - - - -""" -__docformat__ = "restructuredtext en" - -def no_filter(_): - return 1 - -# Iterators ################################################################### -class FilteredIterator(object): - - def __init__(self, node, list_func, filter_func=None): - self._next = [(node, 0)] - if filter_func is None: - filter_func = no_filter - self._list = list_func(node, filter_func) - - def __next__(self): - try: - return self._list.pop(0) - except : - return None - - next = __next__ - -# Base Visitor ################################################################ -class Visitor(object): - - def __init__(self, iterator_class, filter_func=None): - self._iter_class = iterator_class - self.filter = filter_func - - def visit(self, node, *args, **kargs): - """ - launch the visit on a given node - - call 'open_visit' before the beginning of the visit, with extra args - given - when all nodes have been visited, call the 'close_visit' method - """ - self.open_visit(node, *args, **kargs) - return self.close_visit(self._visit(node)) - - def _visit(self, node): - iterator = self._get_iterator(node) - n = next(iterator) - while n: - result = n.accept(self) - n = next(iterator) - return result - - def _get_iterator(self, node): - return self._iter_class(node, self.filter) - - def open_visit(self, *args, **kargs): - """ - method called at the beginning of the visit - """ - pass - - def close_visit(self, result): - """ - method called at the end of the visit - """ - return result - -# standard visited mixin ###################################################### -class VisitedMixIn(object): - """ - Visited interface allow node visitors to use the node - """ - def get_visit_name(self): - """ - return the visit name for the mixed class. When calling 'accept', the - method <'visit_' + name returned by this method> will be called on the - visitor - """ - try: - return self.TYPE.replace('-', '_') - except: - return self.__class__.__name__.lower() - - def accept(self, visitor, *args, **kwargs): - func = getattr(visitor, 'visit_%s' % self.get_visit_name()) - return func(self, *args, **kwargs) - - def leave(self, visitor, *args, **kwargs): - func = getattr(visitor, 'leave_%s' % self.get_visit_name()) - return func(self, *args, **kwargs) diff --git a/pymode/libs/logilab/common/xmlutils.py b/pymode/libs/logilab/common/xmlutils.py deleted file mode 100644 index d383b9d5..00000000 --- a/pymode/libs/logilab/common/xmlutils.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of logilab-common. -# -# logilab-common is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) any -# later version. -# -# logilab-common is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with logilab-common. If not, see . -"""XML utilities. - -This module contains useful functions for parsing and using XML data. For the -moment, there is only one function that can parse the data inside a processing -instruction and return a Python dictionary. - - - - -""" -__docformat__ = "restructuredtext en" - -import re - -RE_DOUBLE_QUOTE = re.compile('([\w\-\.]+)="([^"]+)"') -RE_SIMPLE_QUOTE = re.compile("([\w\-\.]+)='([^']+)'") - -def parse_pi_data(pi_data): - """ - Utility function that parses the data contained in an XML - processing instruction and returns a dictionary of keywords and their - associated values (most of the time, the processing instructions contain - data like ``keyword="value"``, if a keyword is not associated to a value, - for example ``keyword``, it will be associated to ``None``). - - :param pi_data: data contained in an XML processing instruction. - :type pi_data: unicode - - :returns: Dictionary of the keywords (Unicode strings) associated to - their values (Unicode strings) as they were defined in the - data. - :rtype: dict - """ - results = {} - for elt in pi_data.split(): - if RE_DOUBLE_QUOTE.match(elt): - kwd, val = RE_DOUBLE_QUOTE.match(elt).groups() - elif RE_SIMPLE_QUOTE.match(elt): - kwd, val = RE_SIMPLE_QUOTE.match(elt).groups() - else: - kwd, val = elt, None - results[kwd] = val - return results diff --git a/pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth b/pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth deleted file mode 100644 index d268b884..00000000 --- a/pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('logilab',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and sys.modules.setdefault('logilab', types.ModuleType('logilab'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst b/pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst deleted file mode 100644 index 6b483af3..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,153 +0,0 @@ -Logilab's common library -======================== - -What's this ? -------------- - -This package contains some modules used by different Logilab projects. - -It is released under the GNU Lesser General Public License. - -There is no documentation available yet but the source code should be clean and -well documented. - -Designed to ease: - -* handling command line options and configuration files -* writing interactive command line tools -* manipulation of files and character strings -* manipulation of common structures such as graph, tree, and pattern such as visitor -* generating text and HTML reports -* more... - - -Installation ------------- - -Extract the tarball, jump into the created directory and run :: - - python setup.py install - -For installation options, see :: - - python setup.py install --help - - -Provided modules ----------------- - -Here is a brief description of the available modules. - -Modules providing high-level features -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `cache`, a cache implementation with a least recently used algorithm. - -* `changelog`, a tiny library to manipulate our simplified ChangeLog file format. - -* `clcommands`, high-level classes to define command line programs handling - different subcommands. It is based on `configuration` to get easy command line - / configuration file handling. - -* `configuration`, some classes to handle unified configuration from both - command line (using optparse) and configuration file (using ConfigParser). - -* `proc`, interface to Linux /proc. - -* `umessage`, unicode email support. - -* `ureports`, micro-reports, a way to create simple reports using python objects - without care of the final formatting. ReST and html formatters are provided. - - -Modules providing low-level functions and structures -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `compat`, provides a transparent compatibility layer between different python - versions. - -* `date`, a set of date manipulation functions. - -* `daemon`, a daemon function and mix-in class to properly start an Unix daemon - process. - -* `decorators`, function decorators such as cached, timed... - -* `deprecation`, decorator, metaclass & all to mark functions / classes as - deprecated or moved - -* `fileutils`, some file / file path manipulation utilities. - -* `graph`, graph manipulations functions such as cycle detection, bases for dot - file generation. - -* `modutils`, python module manipulation functions. - -* `shellutils`, some powerful shell like functions to replace shell scripts with - python scripts. - -* `tasksqueue`, a prioritized tasks queue implementation. - -* `textutils`, some text manipulation functions (ansi colorization, line wrapping, - rest support...). - -* `tree`, base class to represent tree structure, and some others to make it - works with the visitor implementation (see below). - -* `visitor`, a generic visitor pattern implementation. - - -Modules extending some standard modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `debugger`, `pdb` customization. - -* `logging_ext`, extensions to `logging` module such as a colorized formatter - and an easier initialization function. - -* `optik_ext`, defines some new option types (regexp, csv, color, date, etc.) - for `optik` / `optparse` - - -Modules extending some external modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `sphinx_ext`, Sphinx_ plugin defining a `autodocstring` directive. - -* `vcgutils` , utilities functions to generate file readable with Georg Sander's - vcg tool (Visualization of Compiler Graphs). - - -To be deprecated modules -~~~~~~~~~~~~~~~~~~~~~~~~ - -Those `logilab.common` modules will much probably be deprecated in future -versions: - -* `testlib`: use `unittest2`_ instead -* `pytest`: use `discover`_ instead -* `interface`: use `zope.interface`_ if you really want this -* `table`, `xmlutils`: is that used? -* `sphinxutils`: we won't go that way imo (i == syt) - - -Comments, support, bug reports ------------------------------- - -Project page https://www.logilab.org/project/logilab-common - -Use the python-projects@lists.logilab.org mailing list. - -You can subscribe to this mailing list at -https://lists.logilab.org/mailman/listinfo/python-projects - -Archives are available at -https://lists.logilab.org/pipermail/python-projects/ - - -.. _Sphinx: http://sphinx.pocoo.org/ -.. _`unittest2`: http://pypi.python.org/pypi/unittest2 -.. _`discover`: http://pypi.python.org/pypi/discover -.. _`zope.interface`: http://pypi.python.org/pypi/zope.interface - - diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/METADATA b/pymode/libs/logilab_common-1.0.2.dist-info/METADATA deleted file mode 100644 index 9a00a498..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/METADATA +++ /dev/null @@ -1,169 +0,0 @@ -Metadata-Version: 2.0 -Name: logilab-common -Version: 1.0.2 -Summary: collection of low-level Python packages and modules used by Logilab projects -Home-page: http://www.logilab.org/project/logilab-common -Author: Logilab -Author-email: contact@logilab.fr -License: LGPL -Platform: UNKNOWN -Classifier: Topic :: Utilities -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Requires-Dist: setuptools -Requires-Dist: six (>=1.4.0) - -Logilab's common library -======================== - -What's this ? -------------- - -This package contains some modules used by different Logilab projects. - -It is released under the GNU Lesser General Public License. - -There is no documentation available yet but the source code should be clean and -well documented. - -Designed to ease: - -* handling command line options and configuration files -* writing interactive command line tools -* manipulation of files and character strings -* manipulation of common structures such as graph, tree, and pattern such as visitor -* generating text and HTML reports -* more... - - -Installation ------------- - -Extract the tarball, jump into the created directory and run :: - - python setup.py install - -For installation options, see :: - - python setup.py install --help - - -Provided modules ----------------- - -Here is a brief description of the available modules. - -Modules providing high-level features -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `cache`, a cache implementation with a least recently used algorithm. - -* `changelog`, a tiny library to manipulate our simplified ChangeLog file format. - -* `clcommands`, high-level classes to define command line programs handling - different subcommands. It is based on `configuration` to get easy command line - / configuration file handling. - -* `configuration`, some classes to handle unified configuration from both - command line (using optparse) and configuration file (using ConfigParser). - -* `proc`, interface to Linux /proc. - -* `umessage`, unicode email support. - -* `ureports`, micro-reports, a way to create simple reports using python objects - without care of the final formatting. ReST and html formatters are provided. - - -Modules providing low-level functions and structures -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `compat`, provides a transparent compatibility layer between different python - versions. - -* `date`, a set of date manipulation functions. - -* `daemon`, a daemon function and mix-in class to properly start an Unix daemon - process. - -* `decorators`, function decorators such as cached, timed... - -* `deprecation`, decorator, metaclass & all to mark functions / classes as - deprecated or moved - -* `fileutils`, some file / file path manipulation utilities. - -* `graph`, graph manipulations functions such as cycle detection, bases for dot - file generation. - -* `modutils`, python module manipulation functions. - -* `shellutils`, some powerful shell like functions to replace shell scripts with - python scripts. - -* `tasksqueue`, a prioritized tasks queue implementation. - -* `textutils`, some text manipulation functions (ansi colorization, line wrapping, - rest support...). - -* `tree`, base class to represent tree structure, and some others to make it - works with the visitor implementation (see below). - -* `visitor`, a generic visitor pattern implementation. - - -Modules extending some standard modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `debugger`, `pdb` customization. - -* `logging_ext`, extensions to `logging` module such as a colorized formatter - and an easier initialization function. - -* `optik_ext`, defines some new option types (regexp, csv, color, date, etc.) - for `optik` / `optparse` - - -Modules extending some external modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `sphinx_ext`, Sphinx_ plugin defining a `autodocstring` directive. - -* `vcgutils` , utilities functions to generate file readable with Georg Sander's - vcg tool (Visualization of Compiler Graphs). - - -To be deprecated modules -~~~~~~~~~~~~~~~~~~~~~~~~ - -Those `logilab.common` modules will much probably be deprecated in future -versions: - -* `testlib`: use `unittest2`_ instead -* `pytest`: use `discover`_ instead -* `interface`: use `zope.interface`_ if you really want this -* `table`, `xmlutils`: is that used? -* `sphinxutils`: we won't go that way imo (i == syt) - - -Comments, support, bug reports ------------------------------- - -Project page https://www.logilab.org/project/logilab-common - -Use the python-projects@lists.logilab.org mailing list. - -You can subscribe to this mailing list at -https://lists.logilab.org/mailman/listinfo/python-projects - -Archives are available at -https://lists.logilab.org/pipermail/python-projects/ - - -.. _Sphinx: http://sphinx.pocoo.org/ -.. _`unittest2`: http://pypi.python.org/pypi/unittest2 -.. _`discover`: http://pypi.python.org/pypi/discover -.. _`zope.interface`: http://pypi.python.org/pypi/zope.interface - - diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/RECORD b/pymode/libs/logilab_common-1.0.2.dist-info/RECORD deleted file mode 100644 index e6e4730a..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/RECORD +++ /dev/null @@ -1,87 +0,0 @@ -logilab_common-1.0.2-py2.7-nspkg.pth,sha256=ZY-Jf8tK2WQu_mjLvZuFpvpX9uwdpX3yDS1AuRncCZA,308 -logilab/common/__init__.py,sha256=UiR9rv7f7WsAnIHsxa3UApVCJGTzXbZoC-c4EQJpcvg,5390 -logilab/common/cache.py,sha256=wmY87WSoyERDhAlfIKKUipYavlZPpm3sGAQMpzbDHTM,3621 -logilab/common/changelog.py,sha256=Ea_4j22rWJJ33VSCj4Lz0pBGP0wP7LMP2Zo4DR7iZIo,8075 -logilab/common/clcommands.py,sha256=abMNAsB6ADT7Ns5MsxNtAMOlTQGJLCMO9MUkNYdsVG8,11237 -logilab/common/compat.py,sha256=rMGytWS1DCo35MdKUocU1LfLbZA0RyK79Gyu7lvd6Rg,2593 -logilab/common/configuration.py,sha256=s4rg7Qa1_4bpWlTg-bEaHYUcrgvuoDt75ZJgRnlFsME,42160 -logilab/common/daemon.py,sha256=Eqwo_oKjrHtS9SLrtSfeghRTCjqvveGho43s7vMkd7A,3337 -logilab/common/date.py,sha256=nnUN-4onEaWSR8r4PvtmJyn5ukfFzasjEcOGzEdrvqQ,11230 -logilab/common/debugger.py,sha256=Bw2-yI9KrvSgPLDksda4F8nuK_DvxnSCS-ymPSVc778,7094 -logilab/common/decorators.py,sha256=4DD3iNgEQPVz5hPp-SbbgD-ZObXhaeazGqKleyHdXaw,8868 -logilab/common/deprecation.py,sha256=MAxc_Ds9H_j6C7d4VQqMQPB1j-Ib8vy7iBWoQa8aRHs,7417 -logilab/common/fileutils.py,sha256=kCk_8odmAKnYPHPhUruuV-6og8N9kT8fplV-pvwwd4A,12738 -logilab/common/graph.py,sha256=GTSN-kP40EHjnHXk1vxO-56rEszo-esu1S3hf-SOddw,10247 -logilab/common/interface.py,sha256=dXl6kiuXSpefxauu7J6CUv0soe09wjT4_vXbeWQFgJ8,2593 -logilab/common/logging_ext.py,sha256=Yi8k2fGqr_tt-YApT1JjroNpXETxfj84HKmgTgO22Nw,6975 -logilab/common/modutils.py,sha256=w2LVy_vzhGoyBRrKivx0hqx8n326KrtTUezelEwDAcc,24002 -logilab/common/optik_ext.py,sha256=_aZgWKTKCC8_vYIpstNCOk8wewwZ4jfrpvXWrmPzn5Y,13451 -logilab/common/optparser.py,sha256=QgDoAyVoRy7U1fG9BSZ0O7LQsyNayo1HAelZaKlb4kY,3386 -logilab/common/proc.py,sha256=RGMlPuc11FfrIsqzqNFO3Q6buqt8dvMwXfXKXfwAHks,9352 -logilab/common/pytest.py,sha256=ac7hVpAb06TstSjPV586h1wW21Y__XH5bjrwX55dDOE,46736 -logilab/common/registry.py,sha256=0qIJfNJiqM1HkI-twKHfXiTPU5HKSGRrS-P0Dsj56qw,41550 -logilab/common/shellutils.py,sha256=ZFZ19eX0TCcDrsbOWiy7sr1oqnhQsLixv9n8HakcJiM,14363 -logilab/common/sphinx_ext.py,sha256=pbKN0ObMDY_jy9ehP_7NOKMo40LbQLjf0xntmxHnGr8,3329 -logilab/common/sphinxutils.py,sha256=piY1R04GNR-i1mIb4PRhbGbmbDZPhDsn1FBAiA_Bbrg,4444 -logilab/common/table.py,sha256=5NEx4Ju-jk2CV6W-jxTpOoYArt2BlRpaTZZUBGwu1kg,31408 -logilab/common/tasksqueue.py,sha256=wFE0C0FiuHGBoCnvU-_Kno1eM_Em6yYxYvND6emRN34,2987 -logilab/common/testlib.py,sha256=2Ra9OPs5QpQv7hoZod3M2yYCUdtqSaN3LAvVyiQyA1k,50506 -logilab/common/textutils.py,sha256=TgPGqkN3JsJuR7VxnkoWaOWfkwHiVNB9gpId_3S2xO4,17277 -logilab/common/tree.py,sha256=Y-sa_pfI17cCb-vkyJMaBW3XKVNrreexBgBMPpQJDy0,10606 -logilab/common/umessage.py,sha256=2BuxspHkPEXhlf-XVDye25Mt0RUELneay-K1KNLcS9c,6551 -logilab/common/urllib2ext.py,sha256=FOpxVrbAPtY_6ssq3Qui3zxzckAqLJe9kGkp8tLR0Ic,3416 -logilab/common/vcgutils.py,sha256=tNfi6jxZ4xdUvrjw1cKOodecRlcD0U3MQvTb5HrY5fE,7673 -logilab/common/visitor.py,sha256=5Oc9Y88Kx4wiZ6JAFYFeXwKrMS8jNph9ENVWG3oim1E,3444 -logilab/common/xmlutils.py,sha256=2e4FM-X1PLKBaTG6etLHsAIrtZQiDEA9U7WqM3KjNks,2273 -logilab/common/ureports/__init__.py,sha256=b3_8f4mAm6T3O_-klutleWZ99XjlR-AELfuLEyCbzQ8,6113 -logilab/common/ureports/docbook_writer.py,sha256=KSkIk0W4C4E6DR-Ul_Y9jgnd4_tgVVu15LnU8p2RoeM,5706 -logilab/common/ureports/html_writer.py,sha256=Ee_x9rXjx2NZp290e-0C7nu7VYuKpkCsrl79m4HLI5g,4956 -logilab/common/ureports/nodes.py,sha256=t2NQiL6LQV94D8ugitklVnZRVbz6kP5QkUrl8zGsmMQ,5838 -logilab/common/ureports/text_writer.py,sha256=cMBHbA36_1NrKKnx5LBKczGQmBRg4aObkpr1d581ORU,5212 -../../bin/pytest,sha256=vkYcOC21mDzGBrz4-ajilr8TGxa9tRabxQhyYyXeEDE,124 -logilab_common-1.0.2.dist-info/DESCRIPTION.rst,sha256=bMLyPRBRS-tSzW5zhchxcLlPbYHRv0XEMqs6Oln2z5U,4426 -logilab_common-1.0.2.dist-info/METADATA,sha256=3_iFYhN84fXSjkdjzHv3grHBY2xIZVLSkmuBeTSnLQE,4934 -logilab_common-1.0.2.dist-info/metadata.json,sha256=dTwpZUieC7dZFkKiNdtgVExm2w1B44k4ZDSaCP3ASXo,742 -logilab_common-1.0.2.dist-info/namespace_packages.txt,sha256=xXemaIbd-285ANf3yiCDkMHRTZSuLvlqL_MTLEJKMuk,8 -logilab_common-1.0.2.dist-info/RECORD,, -logilab_common-1.0.2.dist-info/top_level.txt,sha256=xXemaIbd-285ANf3yiCDkMHRTZSuLvlqL_MTLEJKMuk,8 -logilab_common-1.0.2.dist-info/WHEEL,sha256=54bVun1KfEBTJ68SHUmbxNPj80VxlQ0sHi4gZdGZXEY,92 -logilab/common/logging_ext.pyc,, -logilab/common/date.pyc,, -logilab/common/modutils.pyc,, -logilab/common/ureports/__init__.pyc,, -logilab/common/sphinxutils.pyc,, -logilab/common/ureports/text_writer.pyc,, -logilab/common/optik_ext.pyc,, -logilab/common/visitor.pyc,, -logilab/common/debugger.pyc,, -logilab/common/compat.pyc,, -logilab/common/decorators.pyc,, -logilab/common/textutils.pyc,, -logilab/common/ureports/docbook_writer.pyc,, -logilab/common/shellutils.pyc,, -logilab/common/changelog.pyc,, -logilab/common/interface.pyc,, -logilab/common/ureports/nodes.pyc,, -logilab/common/pytest.pyc,, -logilab/common/sphinx_ext.pyc,, -logilab/common/xmlutils.pyc,, -logilab/common/__init__.pyc,, -logilab/common/tree.pyc,, -logilab/common/umessage.pyc,, -logilab/common/registry.pyc,, -logilab/common/proc.pyc,, -logilab/common/urllib2ext.pyc,, -logilab/common/testlib.pyc,, -logilab/common/clcommands.pyc,, -logilab/common/ureports/html_writer.pyc,, -logilab/common/vcgutils.pyc,, -logilab/common/daemon.pyc,, -logilab/common/table.pyc,, -logilab/common/optparser.pyc,, -logilab/common/deprecation.pyc,, -logilab/common/tasksqueue.pyc,, -logilab/common/fileutils.pyc,, -logilab/common/graph.pyc,, -logilab/common/cache.pyc,, -logilab/common/configuration.pyc,, diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL b/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL deleted file mode 100644 index 45a0cd88..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) -Root-Is-Purelib: true -Tag: py2-none-any - diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/metadata.json b/pymode/libs/logilab_common-1.0.2.dist-info/metadata.json deleted file mode 100644 index 54212666..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"license": "LGPL", "name": "logilab-common", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "test_requires": [{"requires": ["pytz"]}], "summary": "collection of low-level Python packages and modules used by Logilab projects", "run_requires": [{"requires": ["setuptools", "six (>=1.4.0)"]}], "version": "1.0.2", "extensions": {"python.details": {"project_urls": {"Home": "http://www.logilab.org/project/logilab-common"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "contact@logilab.fr", "name": "Logilab"}]}}, "classifiers": ["Topic :: Utilities", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3"], "extras": []} \ No newline at end of file diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt b/pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt deleted file mode 100644 index 3ac267a9..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt +++ /dev/null @@ -1 +0,0 @@ -logilab diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt b/pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt deleted file mode 100644 index 3ac267a9..00000000 --- a/pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -logilab diff --git a/pymode/libs/mccabe.py b/pymode/libs/mccabe.py deleted file mode 100644 index 90bf19cd..00000000 --- a/pymode/libs/mccabe.py +++ /dev/null @@ -1,311 +0,0 @@ -""" Meager code path measurement tool. - Ned Batchelder - http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html - MIT License. -""" -from __future__ import with_statement - -import optparse -import sys -from collections import defaultdict -try: - import ast - from ast import iter_child_nodes -except ImportError: # Python 2.5 - from flake8.util import ast, iter_child_nodes - -__version__ = '0.3.1' - - -class ASTVisitor(object): - """Performs a depth-first walk of the AST.""" - - def __init__(self): - self.node = None - self._cache = {} - - def default(self, node, *args): - for child in iter_child_nodes(node): - self.dispatch(child, *args) - - def dispatch(self, node, *args): - self.node = node - klass = node.__class__ - meth = self._cache.get(klass) - if meth is None: - className = klass.__name__ - meth = getattr(self.visitor, 'visit' + className, self.default) - self._cache[klass] = meth - return meth(node, *args) - - def preorder(self, tree, visitor, *args): - """Do preorder walk of tree using visitor""" - self.visitor = visitor - visitor.visit = self.dispatch - self.dispatch(tree, *args) # XXX *args make sense? - - -class PathNode(object): - def __init__(self, name, look="circle"): - self.name = name - self.look = look - - def to_dot(self): - print('node [shape=%s,label="%s"] %d;' % ( - self.look, self.name, self.dot_id())) - - def dot_id(self): - return id(self) - - -class PathGraph(object): - def __init__(self, name, entity, lineno): - self.name = name - self.entity = entity - self.lineno = lineno - self.nodes = defaultdict(list) - - def connect(self, n1, n2): - self.nodes[n1].append(n2) - # Ensure that the destination node is always counted. - self.nodes[n2] = [] - - def to_dot(self): - print('subgraph {') - for node in self.nodes: - node.to_dot() - for node, nexts in self.nodes.items(): - for next in nexts: - print('%s -- %s;' % (node.dot_id(), next.dot_id())) - print('}') - - def complexity(self): - """ Return the McCabe complexity for the graph. - V-E+2 - """ - num_edges = sum([len(n) for n in self.nodes.values()]) - num_nodes = len(self.nodes) - return num_edges - num_nodes + 2 - - -class PathGraphingAstVisitor(ASTVisitor): - """ A visitor for a parsed Abstract Syntax Tree which finds executable - statements. - """ - - def __init__(self): - super(PathGraphingAstVisitor, self).__init__() - self.classname = "" - self.graphs = {} - self.reset() - - def reset(self): - self.graph = None - self.tail = None - - def dispatch_list(self, node_list): - for node in node_list: - self.dispatch(node) - - def visitFunctionDef(self, node): - - if self.classname: - entity = '%s%s' % (self.classname, node.name) - else: - entity = node.name - - name = '%d:1: %r' % (node.lineno, entity) - - if self.graph is not None: - # closure - pathnode = self.appendPathNode(name) - self.tail = pathnode - self.dispatch_list(node.body) - bottom = PathNode("", look='point') - self.graph.connect(self.tail, bottom) - self.graph.connect(pathnode, bottom) - self.tail = bottom - else: - self.graph = PathGraph(name, entity, node.lineno) - pathnode = PathNode(name) - self.tail = pathnode - self.dispatch_list(node.body) - self.graphs["%s%s" % (self.classname, node.name)] = self.graph - self.reset() - - def visitClassDef(self, node): - old_classname = self.classname - self.classname += node.name + "." - self.dispatch_list(node.body) - self.classname = old_classname - - def appendPathNode(self, name): - if not self.tail: - return - pathnode = PathNode(name) - self.graph.connect(self.tail, pathnode) - self.tail = pathnode - return pathnode - - def visitSimpleStatement(self, node): - if node.lineno is None: - lineno = 0 - else: - lineno = node.lineno - name = "Stmt %d" % lineno - self.appendPathNode(name) - - visitAssert = visitAssign = visitAugAssign = visitDelete = visitPrint = \ - visitRaise = visitYield = visitImport = visitCall = visitSubscript = \ - visitPass = visitContinue = visitBreak = visitGlobal = visitReturn = \ - visitSimpleStatement - - def visitLoop(self, node): - name = "Loop %d" % node.lineno - self._subgraph(node, name) - - visitFor = visitWhile = visitLoop - - def visitIf(self, node): - name = "If %d" % node.lineno - self._subgraph(node, name) - - def _subgraph(self, node, name, extra_blocks=()): - """create the subgraphs representing any `if` and `for` statements""" - if self.graph is None: - # global loop - self.graph = PathGraph(name, name, node.lineno) - pathnode = PathNode(name) - self._subgraph_parse(node, pathnode, extra_blocks) - self.graphs["%s%s" % (self.classname, name)] = self.graph - self.reset() - else: - pathnode = self.appendPathNode(name) - self._subgraph_parse(node, pathnode, extra_blocks) - - def _subgraph_parse(self, node, pathnode, extra_blocks): - """parse the body and any `else` block of `if` and `for` statements""" - loose_ends = [] - self.tail = pathnode - self.dispatch_list(node.body) - loose_ends.append(self.tail) - for extra in extra_blocks: - self.tail = pathnode - self.dispatch_list(extra.body) - loose_ends.append(self.tail) - if node.orelse: - self.tail = pathnode - self.dispatch_list(node.orelse) - loose_ends.append(self.tail) - else: - loose_ends.append(pathnode) - if pathnode: - bottom = PathNode("", look='point') - for le in loose_ends: - self.graph.connect(le, bottom) - self.tail = bottom - - def visitTryExcept(self, node): - name = "TryExcept %d" % node.lineno - self._subgraph(node, name, extra_blocks=node.handlers) - - visitTry = visitTryExcept - - def visitWith(self, node): - name = "With %d" % node.lineno - self.appendPathNode(name) - self.dispatch_list(node.body) - - -class McCabeChecker(object): - """McCabe cyclomatic complexity checker.""" - name = 'mccabe' - version = __version__ - _code = 'C901' - _error_tmpl = "C901 %r is too complex (%d)" - max_complexity = 0 - - def __init__(self, tree, filename): - self.tree = tree - - @classmethod - def add_options(cls, parser): - parser.add_option('--max-complexity', default=-1, action='store', - type='int', help="McCabe complexity threshold") - parser.config_options.append('max-complexity') - - @classmethod - def parse_options(cls, options): - cls.max_complexity = int(options.max_complexity) - - def run(self): - if self.max_complexity < 0: - return - visitor = PathGraphingAstVisitor() - visitor.preorder(self.tree, visitor) - for graph in visitor.graphs.values(): - if graph.complexity() > self.max_complexity: - text = self._error_tmpl % (graph.entity, graph.complexity()) - yield graph.lineno, 0, text, type(self) - - -def get_code_complexity(code, threshold=7, filename='stdin'): - try: - tree = compile(code, filename, "exec", ast.PyCF_ONLY_AST) - except SyntaxError: - e = sys.exc_info()[1] - sys.stderr.write("Unable to parse %s: %s\n" % (filename, e)) - return 0 - - complx = [] - McCabeChecker.max_complexity = threshold - for lineno, offset, text, check in McCabeChecker(tree, filename).run(): - complx.append('%s:%d:1: %s' % (filename, lineno, text)) - - if len(complx) == 0: - return 0 - print('\n'.join(complx)) - return len(complx) - - -def get_module_complexity(module_path, threshold=7): - """Returns the complexity of a module""" - with open(module_path, "rU") as mod: - code = mod.read() - return get_code_complexity(code, threshold, filename=module_path) - - -def main(argv=None): - if argv is None: - argv = sys.argv[1:] - opar = optparse.OptionParser() - opar.add_option("-d", "--dot", dest="dot", - help="output a graphviz dot file", action="store_true") - opar.add_option("-m", "--min", dest="threshold", - help="minimum complexity for output", type="int", - default=1) - - options, args = opar.parse_args(argv) - - with open(args[0], "rU") as mod: - code = mod.read() - tree = compile(code, args[0], "exec", ast.PyCF_ONLY_AST) - visitor = PathGraphingAstVisitor() - visitor.preorder(tree, visitor) - - if options.dot: - print('graph {') - for graph in visitor.graphs.values(): - if (not options.threshold or - graph.complexity() >= options.threshold): - graph.to_dot() - print('}') - else: - for graph in visitor.graphs.values(): - if graph.complexity() >= options.threshold: - print(graph.name, graph.complexity()) - - -if __name__ == '__main__': - main(sys.argv[1:]) - diff --git a/pymode/libs/pep257.py b/pymode/libs/pep257.py deleted file mode 100644 index 79d9eee1..00000000 --- a/pymode/libs/pep257.py +++ /dev/null @@ -1,1187 +0,0 @@ -#! /usr/bin/env python -"""Static analysis tool for checking docstring conventions and style. - -Implemented checks cover PEP257: -http://www.python.org/dev/peps/pep-0257/ - -Other checks can be added, e.g. NumPy docstring conventions: -https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt - -The repository is located at: -http://github.com/GreenSteam/pep257 - -""" -from __future__ import with_statement - -import os -import sys -import logging -import tokenize as tk -from itertools import takewhile, dropwhile, chain -from optparse import OptionParser -from re import compile as re -import itertools - -try: # Python 3.x - from ConfigParser import RawConfigParser -except ImportError: # Python 2.x - from configparser import RawConfigParser - -log = logging.getLogger(__name__) - - -try: - from StringIO import StringIO -except ImportError: # Python 3.0 and later - from io import StringIO - - -try: - next -except NameError: # Python 2.5 and earlier - nothing = object() - - def next(obj, default=nothing): - if default == nothing: - return obj.next() - else: - try: - return obj.next() - except StopIteration: - return default - - -# If possible (python >= 3.2) use tokenize.open to open files, so PEP 263 -# encoding markers are interpreted. -try: - tokenize_open = tk.open -except AttributeError: - tokenize_open = open - - -__version__ = '0.6.1-alpha' -__all__ = ('check', 'collect') - -PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep257') -NO_VIOLATIONS_RETURN_CODE = 0 -VIOLATIONS_RETURN_CODE = 1 -INVALID_OPTIONS_RETURN_CODE = 2 - - -def humanize(string): - return re(r'(.)([A-Z]+)').sub(r'\1 \2', string).lower() - - -def is_magic(name): - return name.startswith('__') and name.endswith('__') - - -def is_ascii(string): - return all(ord(char) < 128 for char in string) - - -def is_blank(string): - return not string.strip() - - -def leading_space(string): - return re('\s*').match(string).group() - - -class Value(object): - - def __init__(self, *args): - vars(self).update(zip(self._fields, args)) - - def __hash__(self): - return hash(repr(self)) - - def __eq__(self, other): - return other and vars(self) == vars(other) - - def __repr__(self): - kwargs = ', '.join('{}={!r}'.format(field, getattr(self, field)) - for field in self._fields) - return '{}({})'.format(self.__class__.__name__, kwargs) - - -class Definition(Value): - - _fields = ('name', '_source', 'start', 'end', 'decorators', 'docstring', - 'children', 'parent') - - _human = property(lambda self: humanize(type(self).__name__)) - kind = property(lambda self: self._human.split()[-1]) - module = property(lambda self: self.parent.module) - all = property(lambda self: self.module.all) - _slice = property(lambda self: slice(self.start - 1, self.end)) - source = property(lambda self: ''.join(self._source[self._slice])) - - def __iter__(self): - return chain([self], *self.children) - - @property - def _publicity(self): - return {True: 'public', False: 'private'}[self.is_public] - - def __str__(self): - return 'in %s %s `%s`' % (self._publicity, self._human, self.name) - - -class Module(Definition): - - _fields = ('name', '_source', 'start', 'end', 'decorators', 'docstring', - 'children', 'parent', '_all') - is_public = True - _nest = staticmethod(lambda s: {'def': Function, 'class': Class}[s]) - module = property(lambda self: self) - all = property(lambda self: self._all) - - def __str__(self): - return 'at module level' - - -class Package(Module): - - """A package is a __init__.py module.""" - - -class Function(Definition): - - _nest = staticmethod(lambda s: {'def': NestedFunction, - 'class': NestedClass}[s]) - - @property - def is_public(self): - if self.all is not None: - return self.name in self.all - else: # TODO: are there any magic functions? not methods - return not self.name.startswith('_') or is_magic(self.name) - - -class NestedFunction(Function): - - is_public = False - - -class Method(Function): - - @property - def is_public(self): - # Check if we are a setter/deleter method, and mark as private if so. - for decorator in self.decorators: - # Given 'foo', match 'foo.bar' but not 'foobar' or 'sfoo' - if re(r"^{0}\.".format(self.name)).match(decorator.name): - return False - name_is_public = not self.name.startswith('_') or is_magic(self.name) - return self.parent.is_public and name_is_public - - -class Class(Definition): - - _nest = staticmethod(lambda s: {'def': Method, 'class': NestedClass}[s]) - is_public = Function.is_public - - -class NestedClass(Class): - - is_public = False - - -class Decorator(Value): - - """A decorator for function, method or class.""" - - _fields = 'name arguments'.split() - - -class TokenKind(int): - def __repr__(self): - return "tk.{}".format(tk.tok_name[self]) - - -class Token(Value): - - _fields = 'kind value start end source'.split() - - def __init__(self, *args): - super(Token, self).__init__(*args) - self.kind = TokenKind(self.kind) - - -class TokenStream(object): - - def __init__(self, filelike): - self._generator = tk.generate_tokens(filelike.readline) - self.current = Token(*next(self._generator, None)) - self.line = self.current.start[0] - - def move(self): - previous = self.current - current = next(self._generator, None) - self.current = None if current is None else Token(*current) - self.line = self.current.start[0] if self.current else self.line - return previous - - def __iter__(self): - while True: - if self.current is not None: - yield self.current - else: - return - self.move() - - -class AllError(Exception): - - def __init__(self, message): - Exception.__init__( - self, message + - 'That means pep257 cannot decide which definitions are public. ' - 'Variable __all__ should be present at most once in each file, ' - "in form `__all__ = ('a_public_function', 'APublicClass', ...)`. " - 'More info on __all__: http://stackoverflow.com/q/44834/. ') - - -class Parser(object): - - def __call__(self, filelike, filename): - self.source = filelike.readlines() - src = ''.join(self.source) - self.stream = TokenStream(StringIO(src)) - self.filename = filename - self.all = None - self._accumulated_decorators = [] - return self.parse_module() - - current = property(lambda self: self.stream.current) - line = property(lambda self: self.stream.line) - - def consume(self, kind): - assert self.stream.move().kind == kind - - def leapfrog(self, kind, value=None): - """Skip tokens in the stream until a certain token kind is reached. - - If `value` is specified, tokens whose values are different will also - be skipped. - """ - while self.current is not None: - if (self.current.kind == kind and - (value is None or self.current.value == value)): - self.consume(kind) - return - self.stream.move() - - def parse_docstring(self): - """Parse a single docstring and return its value.""" - log.debug("parsing docstring, token is %r (%s)", - self.current.kind, self.current.value) - while self.current.kind in (tk.COMMENT, tk.NEWLINE, tk.NL): - self.stream.move() - log.debug("parsing docstring, token is %r (%s)", - self.current.kind, self.current.value) - if self.current.kind == tk.STRING: - docstring = self.current.value - self.stream.move() - return docstring - return None - - def parse_decorators(self): - """Called after first @ is found. - - Parse decorators into self._accumulated_decorators. - Continue to do so until encountering the 'def' or 'class' start token. - """ - name = [] - arguments = [] - at_arguments = False - - while self.current is not None: - if (self.current.kind == tk.NAME and - self.current.value in ['def', 'class']): - # Done with decorators - found function or class proper - break - elif self.current.kind == tk.OP and self.current.value == '@': - # New decorator found. Store the decorator accumulated so far: - self._accumulated_decorators.append( - Decorator(''.join(name), ''.join(arguments))) - # Now reset to begin accumulating the new decorator: - name = [] - arguments = [] - at_arguments = False - elif self.current.kind == tk.OP and self.current.value == '(': - at_arguments = True - elif self.current.kind == tk.OP and self.current.value == ')': - # Ignore close parenthesis - pass - elif self.current.kind == tk.NEWLINE or self.current.kind == tk.NL: - # Ignore newlines - pass - else: - # Keep accumulating current decorator's name or argument. - if not at_arguments: - name.append(self.current.value) - else: - arguments.append(self.current.value) - self.stream.move() - - # Add decorator accumulated so far - self._accumulated_decorators.append( - Decorator(''.join(name), ''.join(arguments))) - - def parse_definitions(self, class_, all=False): - """Parse multiple defintions and yield them.""" - while self.current is not None: - log.debug("parsing defintion list, current token is %r (%s)", - self.current.kind, self.current.value) - if all and self.current.value == '__all__': - self.parse_all() - elif self.current.kind == tk.OP and self.current.value == '@': - self.consume(tk.OP) - self.parse_decorators() - elif self.current.value in ['def', 'class']: - yield self.parse_definition(class_._nest(self.current.value)) - elif self.current.kind == tk.INDENT: - self.consume(tk.INDENT) - for definition in self.parse_definitions(class_): - yield definition - elif self.current.kind == tk.DEDENT: - self.consume(tk.DEDENT) - return - else: - self.stream.move() - - def parse_all(self): - """Parse the __all__ definition in a module.""" - assert self.current.value == '__all__' - self.consume(tk.NAME) - if self.current.value != '=': - raise AllError('Could not evaluate contents of __all__. ') - self.consume(tk.OP) - if self.current.value not in '([': - raise AllError('Could not evaluate contents of __all__. ') - if self.current.value == '[': - msg = ("%s WARNING: __all__ is defined as a list, this means " - "pep257 cannot reliably detect contents of the __all__ " - "variable, because it can be mutated. Change __all__ to be " - "an (immutable) tuple, to remove this warning. Note, " - "pep257 uses __all__ to detect which definitions are " - "public, to warn if public definitions are missing " - "docstrings. If __all__ is a (mutable) list, pep257 cannot " - "reliably assume its contents. pep257 will proceed " - "assuming __all__ is not mutated.\n" % self.filename) - sys.stderr.write(msg) - self.consume(tk.OP) - - self.all = [] - all_content = "(" - while self.current.kind != tk.OP or self.current.value not in ")]": - if self.current.kind in (tk.NL, tk.COMMENT): - pass - elif (self.current.kind == tk.STRING or - self.current.value == ','): - all_content += self.current.value - else: - kind = token.tok_name[self.current.kind] - raise AllError('Unexpected token kind in __all__: %s' % kind) - self.stream.move() - self.consume(tk.OP) - all_content += ")" - try: - self.all = eval(all_content, {}) - except BaseException as e: - raise AllError('Could not evaluate contents of __all__.' - '\bThe value was %s. The exception was:\n%s' - % (all_content, e)) - - def parse_module(self): - """Parse a module (and its children) and return a Module object.""" - log.debug("parsing module.") - start = self.line - docstring = self.parse_docstring() - children = list(self.parse_definitions(Module, all=True)) - assert self.current is None, self.current - end = self.line - cls = Module - if self.filename.endswith('__init__.py'): - cls = Package - module = cls(self.filename, self.source, start, end, - [], docstring, children, None, self.all) - for child in module.children: - child.parent = module - log.debug("finished parsing module.") - return module - - def parse_definition(self, class_): - """Parse a defintion and return its value in a `class_` object.""" - start = self.line - self.consume(tk.NAME) - name = self.current.value - log.debug("parsing %s '%s'", class_.__name__, name) - self.stream.move() - if self.current.kind == tk.OP and self.current.value == '(': - parenthesis_level = 0 - while True: - if self.current.kind == tk.OP: - if self.current.value == '(': - parenthesis_level += 1 - elif self.current.value == ')': - parenthesis_level -= 1 - if parenthesis_level == 0: - break - self.stream.move() - if self.current.kind != tk.OP or self.current.value != ':': - self.leapfrog(tk.OP, value=":") - else: - self.consume(tk.OP) - if self.current.kind in (tk.NEWLINE, tk.COMMENT): - self.leapfrog(tk.INDENT) - assert self.current.kind != tk.INDENT - docstring = self.parse_docstring() - decorators = self._accumulated_decorators - self._accumulated_decorators = [] - log.debug("parsing nested defintions.") - children = list(self.parse_definitions(class_)) - log.debug("finished parsing nested defintions for '%s'", name) - end = self.line - 1 - else: # one-liner definition - docstring = self.parse_docstring() - decorators = [] # TODO - children = [] - end = self.line - self.leapfrog(tk.NEWLINE) - definition = class_(name, self.source, start, end, - decorators, docstring, children, None) - for child in definition.children: - child.parent = definition - log.debug("finished parsing %s '%s'. Next token is %r (%s)", - class_.__name__, name, self.current.kind, - self.current.value) - return definition - - -class Error(object): - - """Error in docstring style.""" - - # should be overridden by inheriting classes - code = None - short_desc = None - context = None - - # Options that define how errors are printed: - explain = False - source = False - - def __init__(self, *parameters): - self.parameters = parameters - self.definition = None - self.explanation = None - - def set_context(self, definition, explanation): - self.definition = definition - self.explanation = explanation - - filename = property(lambda self: self.definition.module.name) - line = property(lambda self: self.definition.start) - - @property - def message(self): - ret = '%s: %s' % (self.code, self.short_desc) - if self.context is not None: - ret += ' (' + self.context % self.parameters + ')' - return ret - - @property - def lines(self): - source = '' - lines = self.definition._source[self.definition._slice] - offset = self.definition.start - lines_stripped = list(reversed(list(dropwhile(is_blank, - reversed(lines))))) - numbers_width = 0 - for n, line in enumerate(lines_stripped): - numbers_width = max(numbers_width, n + offset) - numbers_width = len(str(numbers_width)) - numbers_width = 6 - for n, line in enumerate(lines_stripped): - source += '%*d: %s' % (numbers_width, n + offset, line) - if n > 5: - source += ' ...\n' - break - return source - - def __str__(self): - self.explanation = '\n'.join(l for l in self.explanation.split('\n') - if not is_blank(l)) - template = '%(filename)s:%(line)s %(definition)s:\n %(message)s' - if self.source and self.explain: - template += '\n\n%(explanation)s\n\n%(lines)s\n' - elif self.source and not self.explain: - template += '\n\n%(lines)s\n' - elif self.explain and not self.source: - template += '\n\n%(explanation)s\n\n' - return template % dict((name, getattr(self, name)) for name in - ['filename', 'line', 'definition', 'message', - 'explanation', 'lines']) - - __repr__ = __str__ - - def __lt__(self, other): - return (self.filename, self.line) < (other.filename, other.line) - - -class ErrorRegistry(object): - groups = [] - - class ErrorGroup(object): - - def __init__(self, prefix, name): - self.prefix = prefix - self.name = name - self.errors = [] - - def create_error(self, error_code, error_desc, error_context=None): - # TODO: check prefix - - class _Error(Error): - code = error_code - short_desc = error_desc - context = error_context - - self.errors.append(_Error) - return _Error - - @classmethod - def create_group(cls, prefix, name): - group = cls.ErrorGroup(prefix, name) - cls.groups.append(group) - return group - - @classmethod - def get_error_codes(cls): - for group in cls.groups: - for error in group.errors: - yield error.code - - @classmethod - def to_rst(cls): - sep_line = '+' + 6 * '-' + '+' + '-' * 71 + '+\n' - blank_line = '|' + 78 * ' ' + '|\n' - table = '' - for group in cls.groups: - table += sep_line - table += blank_line - table += '|' + ('**%s**' % group.name).center(78) + '|\n' - table += blank_line - for error in group.errors: - table += sep_line - table += ('|' + error.code.center(6) + '| ' + - error.short_desc.ljust(70) + '|\n') - table += sep_line - return table - - -D1xx = ErrorRegistry.create_group('D1', 'Missing Docstrings') -D100 = D1xx.create_error('D100', 'Missing docstring in public module') -D101 = D1xx.create_error('D101', 'Missing docstring in public class') -D102 = D1xx.create_error('D102', 'Missing docstring in public method') -D103 = D1xx.create_error('D103', 'Missing docstring in public function') -D104 = D1xx.create_error('D104', 'Missing docstring in public package') - -D2xx = ErrorRegistry.create_group('D2', 'Whitespace Issues') -D200 = D2xx.create_error('D200', 'One-line docstring should fit on one line ' - 'with quotes', 'found %s') -D201 = D2xx.create_error('D201', 'No blank lines allowed before function ' - 'docstring', 'found %s') -D202 = D2xx.create_error('D202', 'No blank lines allowed after function ' - 'docstring', 'found %s') -D203 = D2xx.create_error('D203', '1 blank line required before class ' - 'docstring', 'found %s') -D204 = D2xx.create_error('D204', '1 blank line required after class ' - 'docstring', 'found %s') -D205 = D2xx.create_error('D205', '1 blank line required between summary line ' - 'and description', 'found %s') -D206 = D2xx.create_error('D206', 'Docstring should be indented with spaces, ' - 'not tabs') -D207 = D2xx.create_error('D207', 'Docstring is under-indented') -D208 = D2xx.create_error('D208', 'Docstring is over-indented') -D209 = D2xx.create_error('D209', 'Multi-line docstring closing quotes should ' - 'be on a separate line') -D210 = D2xx.create_error('D210', 'No whitespaces allowed surrounding ' - 'docstring text') - -D3xx = ErrorRegistry.create_group('D3', 'Quotes Issues') -D300 = D3xx.create_error('D300', 'Use """triple double quotes"""', - 'found %s-quotes') -D301 = D3xx.create_error('D301', 'Use r""" if any backslashes in a docstring') -D302 = D3xx.create_error('D302', 'Use u""" for Unicode docstrings') - -D4xx = ErrorRegistry.create_group('D4', 'Docstring Content Issues') -D400 = D4xx.create_error('D400', 'First line should end with a period', - 'not %r') -D401 = D4xx.create_error('D401', 'First line should be in imperative mood', - '%r, not %r') -D402 = D4xx.create_error('D402', 'First line should not be the function\'s ' - '"signature"') - - -class Conventions(object): - pep257 = set(ErrorRegistry.get_error_codes()) - - -def get_option_parser(): - parser = OptionParser(version=__version__, - usage='Usage: pep257 [options] [...]') - parser.config_options = ('explain', 'source', 'ignore', 'match', 'select', - 'match-dir', 'debug', 'verbose', 'count', - 'convention') - option = parser.add_option - option('-e', '--explain', action='store_true', - help='show explanation of each error') - option('-s', '--source', action='store_true', - help='show source for each error') - option('--select', metavar='', default='', - help='choose the basic list of checked errors by specifying which ' - 'errors to check for (with a list of comma-separated error ' - 'codes). for example: --select=D101,D202') - option('--ignore', metavar='', default='', - help='choose the basic list of checked errors by specifying which ' - 'errors to ignore (with a list of comma-separated error ' - 'codes). for example: --ignore=D101,D202') - option('--convention', metavar='', default='', - help='choose the basic list of checked errors by specifying an ' - 'existing convention. for example: --convention=pep257') - option('--add-select', metavar='', default='', - help='amend the list of errors to check for by specifying more ' - 'error codes to check.') - option('--add-ignore', metavar='', default='', - help='amend the list of errors to check for by specifying more ' - 'error codes to ignore.') - option('--match', metavar='', default='(?!test_).*\.py', - help="check only files that exactly match regular " - "expression; default is --match='(?!test_).*\.py' which " - "matches files that don't start with 'test_' but end with " - "'.py'") - option('--match-dir', metavar='', default='[^\.].*', - help="search only dirs that exactly match regular " - "expression; default is --match-dir='[^\.].*', which matches " - "all dirs that don't start with a dot") - option('-d', '--debug', action='store_true', - help='print debug information') - option('-v', '--verbose', action='store_true', - help='print status information') - option('--count', action='store_true', - help='print total number of errors to stdout') - return parser - - -def collect(names, match=lambda name: True, match_dir=lambda name: True): - """Walk dir trees under `names` and generate filnames that `match`. - - Example - ------- - >>> sorted(collect(['non-dir.txt', './'], - ... match=lambda name: name.endswith('.py'))) - ['non-dir.txt', './pep257.py', './setup.py', './test_pep257.py'] - - """ - for name in names: # map(expanduser, names): - if os.path.isdir(name): - for root, dirs, filenames in os.walk(name): - # Skip any dirs that do not match match_dir - dirs[:] = [dir for dir in dirs if match_dir(dir)] - for filename in filenames: - if match(filename): - yield os.path.join(root, filename) - else: - yield name - - -def check(filenames, select=None, ignore=None): - """Generate PEP 257 errors that exist in `filenames` iterable. - - Only returns errors with error-codes defined in `checked_codes` iterable. - - Example - ------- - >>> check(['pep257.py'], checked_codes=['D100']) - - - """ - if select and ignore: - raise ValueError('Cannot pass both select and ignore. They are ' - 'mutually exclusive.') - elif select or ignore: - checked_codes = (select or - set(ErrorRegistry.get_error_codes()) - set(ignore)) - else: - checked_codes = Conventions.pep257 - - for filename in filenames: - log.info('Checking file %s.', filename) - try: - with tokenize_open(filename) as file: - source = file.read() - for error in PEP257Checker().check_source(source, filename): - code = getattr(error, 'code', None) - if code in checked_codes: - yield error - except (EnvironmentError, AllError): - yield sys.exc_info()[1] - except tk.TokenError: - yield SyntaxError('invalid syntax in file %s' % filename) - - -def get_options(args, opt_parser): - config = RawConfigParser() - parent = tail = os.path.abspath(os.path.commonprefix(args)) - config_found = False - while tail and not config_found: - log.info(tail) - for fn in PROJECT_CONFIG: - full_path = os.path.join(parent, fn) - if config.read(full_path): - log.info('local configuration: in %s.', full_path) - config_found = True - break - parent, tail = os.path.split(parent) - - new_options = None - if config.has_section('pep257'): - option_list = dict([(o.dest, o.type or o.action) - for o in opt_parser.option_list]) - - # First, read the default values - new_options, _ = opt_parser.parse_args([]) - - # Second, parse the configuration - pep257_section = 'pep257' - for opt in config.options(pep257_section): - if opt.replace('_', '-') not in opt_parser.config_options: - log.warning("Unknown option '{}' ignored".format(opt)) - continue - normalized_opt = opt.replace('-', '_') - opt_type = option_list[normalized_opt] - if opt_type in ('int', 'count'): - value = config.getint(pep257_section, opt) - elif opt_type == 'string': - value = config.get(pep257_section, opt) - else: - assert opt_type in ('store_true', 'store_false') - value = config.getboolean(pep257_section, opt) - setattr(new_options, normalized_opt, value) - - # Third, overwrite with the command-line options - options, _ = opt_parser.parse_args(values=new_options) - log.debug("options: %s", options) - return options - - -def setup_stream_handlers(options): - """Setup logging stream handlers according to the options.""" - class StdoutFilter(logging.Filter): - def filter(self, record): - return record.levelno in (logging.DEBUG, logging.INFO) - - if log.handlers: - for handler in log.handlers: - log.removeHandler(handler) - - stdout_handler = logging.StreamHandler(sys.stdout) - stdout_handler.setLevel(logging.WARNING) - stdout_handler.addFilter(StdoutFilter()) - if options.debug: - stdout_handler.setLevel(logging.DEBUG) - elif options.verbose: - stdout_handler.setLevel(logging.INFO) - else: - stdout_handler.setLevel(logging.WARNING) - log.addHandler(stdout_handler) - - stderr_handler = logging.StreamHandler(sys.stderr) - stderr_handler.setLevel(logging.WARNING) - log.addHandler(stderr_handler) - - -def get_checked_error_codes(options): - codes = set(ErrorRegistry.get_error_codes()) - if options.ignore: - checked_codes = codes - set(options.ignore.split(',')) - elif options.select: - checked_codes = set(options.select.split(',')) - elif options.convention: - checked_codes = getattr(Conventions, options.convention) - else: - checked_codes = Conventions.pep257 - checked_codes -= set(options.add_ignore.split(',')) - checked_codes |= set(options.add_select.split(',')) - return checked_codes - set('') - - -def validate_options(options): - mutually_exclusive = ('ignore', 'select', 'convention') - for opt1, opt2 in itertools.permutations(mutually_exclusive, 2): - if getattr(options, opt1) and getattr(options, opt2): - log.error('Cannot pass both {0} and {1}. They are ' - 'mutually exclusive.'.format(opt1, opt2)) - return False - if options.convention and not hasattr(Conventions, options.convention): - return False - return True - - -def run_pep257(): - log.setLevel(logging.DEBUG) - opt_parser = get_option_parser() - # setup the logger before parsing the config file, so that command line - # arguments for debug / verbose will be printed. - options, arguments = opt_parser.parse_args() - setup_stream_handlers(options) - # We parse the files before opening the config file, since it changes where - # we look for the file. - options = get_options(arguments, opt_parser) - if not validate_options(options): - return INVALID_OPTIONS_RETURN_CODE - # Setup the handler again with values from the config file. - setup_stream_handlers(options) - - collected = collect(arguments or ['.'], - match=re(options.match + '$').match, - match_dir=re(options.match_dir + '$').match) - - log.debug("starting pep257 in debug mode.") - - Error.explain = options.explain - Error.source = options.source - collected = list(collected) - checked_codes = get_checked_error_codes(options) - errors = check(collected, select=checked_codes) - code = NO_VIOLATIONS_RETURN_CODE - count = 0 - for error in errors: - sys.stderr.write('%s\n' % error) - code = VIOLATIONS_RETURN_CODE - count += 1 - if options.count: - print(count) - return code - - -parse = Parser() - - -def check_for(kind, terminal=False): - def decorator(f): - f._check_for = kind - f._terminal = terminal - return f - return decorator - - -class PEP257Checker(object): - - """Checker for PEP 257. - - D10x: Missing docstrings - D20x: Whitespace issues - D30x: Docstring formatting - D40x: Docstring content issues - - """ - - def check_source(self, source, filename): - module = parse(StringIO(source), filename) - for definition in module: - for check in self.checks: - terminate = False - if isinstance(definition, check._check_for): - error = check(None, definition, definition.docstring) - errors = error if hasattr(error, '__iter__') else [error] - for error in errors: - if error is not None: - partition = check.__doc__.partition('.\n') - message, _, explanation = partition - error.set_context(explanation=explanation, - definition=definition) - yield error - if check._terminal: - terminate = True - break - if terminate: - break - - @property - def checks(self): - all = [check for check in vars(type(self)).values() - if hasattr(check, '_check_for')] - return sorted(all, key=lambda check: not check._terminal) - - @check_for(Definition, terminal=True) - def check_docstring_missing(self, definition, docstring): - """D10{0,1,2,3}: Public definitions should have docstrings. - - All modules should normally have docstrings. [...] all functions and - classes exported by a module should also have docstrings. Public - methods (including the __init__ constructor) should also have - docstrings. - - Note: Public (exported) definitions are either those with names listed - in __all__ variable (if present), or those that do not start - with a single underscore. - - """ - if (not docstring and definition.is_public or - docstring and is_blank(eval(docstring))): - codes = {Module: D100, Class: D101, NestedClass: D101, - Method: D102, Function: D103, NestedFunction: D103, - Package: D104} - return codes[type(definition)]() - - @check_for(Definition) - def check_one_liners(self, definition, docstring): - """D200: One-liner docstrings should fit on one line with quotes. - - The closing quotes are on the same line as the opening quotes. - This looks better for one-liners. - - """ - if docstring: - lines = eval(docstring).split('\n') - if len(lines) > 1: - non_empty_lines = sum(1 for l in lines if not is_blank(l)) - if non_empty_lines == 1: - return D200(len(lines)) - - @check_for(Function) - def check_no_blank_before(self, function, docstring): # def - """D20{1,2}: No blank lines allowed around function/method docstring. - - There's no blank line either before or after the docstring. - - """ - # NOTE: This does not take comments into account. - # NOTE: This does not take into account functions with groups of code. - if docstring: - before, _, after = function.source.partition(docstring) - blanks_before = list(map(is_blank, before.split('\n')[:-1])) - blanks_after = list(map(is_blank, after.split('\n')[1:])) - blanks_before_count = sum(takewhile(bool, reversed(blanks_before))) - blanks_after_count = sum(takewhile(bool, blanks_after)) - if blanks_before_count != 0: - yield D201(blanks_before_count) - if not all(blanks_after) and blanks_after_count != 0: - yield D202(blanks_after_count) - - @check_for(Class) - def check_blank_before_after_class(slef, class_, docstring): - """D20{3,4}: Class docstring should have 1 blank line around them. - - Insert a blank line before and after all docstrings (one-line or - multi-line) that document a class -- generally speaking, the class's - methods are separated from each other by a single blank line, and the - docstring needs to be offset from the first method by a blank line; - for symmetry, put a blank line between the class header and the - docstring. - - """ - # NOTE: this gives false-positive in this case - # class Foo: - # - # """Docstring.""" - # - # - # # comment here - # def foo(): pass - if docstring: - before, _, after = class_.source.partition(docstring) - blanks_before = list(map(is_blank, before.split('\n')[:-1])) - blanks_after = list(map(is_blank, after.split('\n')[1:])) - blanks_before_count = sum(takewhile(bool, reversed(blanks_before))) - blanks_after_count = sum(takewhile(bool, blanks_after)) - if blanks_before_count != 1: - yield D203(blanks_before_count) - if not all(blanks_after) and blanks_after_count != 1: - yield D204(blanks_after_count) - - @check_for(Definition) - def check_blank_after_summary(self, definition, docstring): - """D205: Put one blank line between summary line and description. - - Multi-line docstrings consist of a summary line just like a one-line - docstring, followed by a blank line, followed by a more elaborate - description. The summary line may be used by automatic indexing tools; - it is important that it fits on one line and is separated from the - rest of the docstring by a blank line. - - """ - if docstring: - lines = eval(docstring).strip().split('\n') - if len(lines) > 1: - post_summary_blanks = list(map(is_blank, lines[1:])) - blanks_count = sum(takewhile(bool, post_summary_blanks)) - if blanks_count != 1: - return D205(blanks_count) - - @check_for(Definition) - def check_indent(self, definition, docstring): - """D20{6,7,8}: The entire docstring should be indented same as code. - - The entire docstring is indented the same as the quotes at its - first line. - - """ - if docstring: - before_docstring, _, _ = definition.source.partition(docstring) - _, _, indent = before_docstring.rpartition('\n') - lines = docstring.split('\n') - if len(lines) > 1: - lines = lines[1:] # First line does not need indent. - indents = [leading_space(l) for l in lines if not is_blank(l)] - if set(' \t') == set(''.join(indents) + indent): - yield D206() - if (len(indents) > 1 and min(indents[:-1]) > indent or - indents[-1] > indent): - yield D208() - if min(indents) < indent: - yield D207() - - @check_for(Definition) - def check_newline_after_last_paragraph(self, definition, docstring): - """D209: Put multi-line docstring closing quotes on separate line. - - Unless the entire docstring fits on a line, place the closing - quotes on a line by themselves. - - """ - if docstring: - lines = [l for l in eval(docstring).split('\n') if not is_blank(l)] - if len(lines) > 1: - if docstring.split("\n")[-1].strip() not in ['"""', "'''"]: - return D209() - - @check_for(Definition) - def check_surrounding_whitespaces(self, definition, docstring): - """D210: No whitespaces allowed surrounding docstring text.""" - if docstring: - lines = eval(docstring).split('\n') - if lines[0].startswith(' ') or \ - len(lines) == 1 and lines[0].endswith(' '): - return D210() - - @check_for(Definition) - def check_triple_double_quotes(self, definition, docstring): - r'''D300: Use """triple double quotes""". - - For consistency, always use """triple double quotes""" around - docstrings. Use r"""raw triple double quotes""" if you use any - backslashes in your docstrings. For Unicode docstrings, use - u"""Unicode triple-quoted strings""". - - Note: Exception to this is made if the docstring contains - """ quotes in its body. - - ''' - if docstring and '"""' in eval(docstring) and docstring.startswith( - ("'''", "r'''", "u'''", "ur'''")): - # Allow ''' quotes if docstring contains """, because otherwise """ - # quotes could not be expressed inside docstring. Not in PEP 257. - return - if docstring and not docstring.startswith( - ('"""', 'r"""', 'u"""', 'ur"""')): - quotes = "'''" if "'''" in docstring[:4] else "'" - return D300(quotes) - - @check_for(Definition) - def check_backslashes(self, definition, docstring): - r'''D301: Use r""" if any backslashes in a docstring. - - Use r"""raw triple double quotes""" if you use any backslashes - (\) in your docstrings. - - ''' - # Just check that docstring is raw, check_triple_double_quotes - # ensures the correct quotes. - if docstring and '\\' in docstring and not docstring.startswith( - ('r', 'ur')): - return D301() - - @check_for(Definition) - def check_unicode_docstring(self, definition, docstring): - r'''D302: Use u""" for docstrings with Unicode. - - For Unicode docstrings, use u"""Unicode triple-quoted strings""". - - ''' - # Just check that docstring is unicode, check_triple_double_quotes - # ensures the correct quotes. - if docstring and sys.version_info[0] <= 2: - if not is_ascii(docstring) and not docstring.startswith( - ('u', 'ur')): - return D302() - - @check_for(Definition) - def check_ends_with_period(self, definition, docstring): - """D400: First line should end with a period. - - The [first line of a] docstring is a phrase ending in a period. - - """ - if docstring: - summary_line = eval(docstring).strip().split('\n')[0] - if not summary_line.endswith('.'): - return D400(summary_line[-1]) - - @check_for(Function) - def check_imperative_mood(self, function, docstring): # def context - """D401: First line should be in imperative mood: 'Do', not 'Does'. - - [Docstring] prescribes the function or method's effect as a command: - ("Do this", "Return that"), not as a description; e.g. don't write - "Returns the pathname ...". - - """ - if docstring: - stripped = eval(docstring).strip() - if stripped: - first_word = stripped.split()[0] - if first_word.endswith('s') and not first_word.endswith('ss'): - return D401(first_word[:-1], first_word) - - @check_for(Function) - def check_no_signature(self, function, docstring): # def context - """D402: First line should not be function's or method's "signature". - - The one-line docstring should NOT be a "signature" reiterating the - function/method parameters (which can be obtained by introspection). - - """ - if docstring: - first_line = eval(docstring).strip().split('\n')[0] - if function.name + '(' in first_line.replace(' ', ''): - return D402() - - # Somewhat hard to determine if return value is mentioned. - # @check(Function) - def SKIP_check_return_type(self, function, docstring): - """D40x: Return value type should be mentioned. - - [T]he nature of the return value cannot be determined by - introspection, so it should be mentioned. - - """ - if docstring and function.returns_value: - if 'return' not in docstring.lower(): - return Error() - - -def main(): - try: - sys.exit(run_pep257()) - except KeyboardInterrupt: - pass - - -if __name__ == '__main__': - main() diff --git a/pymode/libs/pep8.py b/pymode/libs/pep8.py deleted file mode 100644 index 34ce07ae..00000000 --- a/pymode/libs/pep8.py +++ /dev/null @@ -1,2127 +0,0 @@ -#!/usr/bin/env python -# pep8.py - Check Python source code formatting, according to PEP 8 -# Copyright (C) 2006-2009 Johann C. Rocholl -# Copyright (C) 2009-2014 Florent Xicluna -# Copyright (C) 2014-2015 Ian Lee -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -r""" -Check Python source code formatting, according to PEP 8. - -For usage and a list of options, try this: -$ python pep8.py -h - -This program and its regression test suite live here: -http://github.com/jcrocholl/pep8 - -Groups of errors and warnings: -E errors -W warnings -100 indentation -200 whitespace -300 blank lines -400 imports -500 line length -600 deprecation -700 statements -900 syntax error -""" -from __future__ import with_statement - -import os -import sys -import re -import time -import inspect -import keyword -import tokenize -from optparse import OptionParser -from fnmatch import fnmatch -try: - from configparser import RawConfigParser - from io import TextIOWrapper -except ImportError: - from ConfigParser import RawConfigParser - -__version__ = '1.6.3a0' - -DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox' -DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704' -try: - if sys.platform == 'win32': - USER_CONFIG = os.path.expanduser(r'~\.pep8') - else: - USER_CONFIG = os.path.join( - os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'), - 'pep8' - ) -except ImportError: - USER_CONFIG = None - -PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8') -TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite') -MAX_LINE_LENGTH = 79 -REPORT_FORMAT = { - 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s', - 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s', -} - -PyCF_ONLY_AST = 1024 -SINGLETONS = frozenset(['False', 'None', 'True']) -KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS -UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-']) -ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-']) -WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%']) -WS_NEEDED_OPERATORS = frozenset([ - '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>', - '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=']) -WHITESPACE = frozenset(' \t') -NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE]) -SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT]) -# ERRORTOKEN is triggered by backticks in Python 3 -SKIP_COMMENTS = SKIP_TOKENS.union([tokenize.COMMENT, tokenize.ERRORTOKEN]) -BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines'] - -INDENT_REGEX = re.compile(r'([ \t]*)') -RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,') -RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$') -ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b') -DOCSTRING_REGEX = re.compile(r'u?r?["\']') -EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]') -WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') -COMPARE_SINGLETON_REGEX = re.compile(r'\b(None|False|True)?\s*([=!]=)' - r'\s*(?(1)|(None|False|True))\b') -COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+[^][)(}{ ]+\s+(in|is)\s') -COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type' - r'|\s*\(\s*([^)]*[^ )])\s*\))') -KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS)) -OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)') -LAMBDA_REGEX = re.compile(r'\blambda\b') -HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$') - -# Work around Python < 2.6 behaviour, which does not generate NL after -# a comment which is on a line by itself. -COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n' - - -############################################################################## -# Plugins (check functions) for physical lines -############################################################################## - - -def tabs_or_spaces(physical_line, indent_char): - r"""Never mix tabs and spaces. - - The most popular way of indenting Python is with spaces only. The - second-most popular way is with tabs only. Code indented with a mixture - of tabs and spaces should be converted to using spaces exclusively. When - invoking the Python command line interpreter with the -t option, it issues - warnings about code that illegally mixes tabs and spaces. When using -tt - these warnings become errors. These options are highly recommended! - - Okay: if a == 0:\n a = 1\n b = 1 - E101: if a == 0:\n a = 1\n\tb = 1 - """ - indent = INDENT_REGEX.match(physical_line).group(1) - for offset, char in enumerate(indent): - if char != indent_char: - return offset, "E101 indentation contains mixed spaces and tabs" - - -def tabs_obsolete(physical_line): - r"""For new projects, spaces-only are strongly recommended over tabs. - - Okay: if True:\n return - W191: if True:\n\treturn - """ - indent = INDENT_REGEX.match(physical_line).group(1) - if '\t' in indent: - return indent.index('\t'), "W191 indentation contains tabs" - - -def trailing_whitespace(physical_line): - r"""Trailing whitespace is superfluous. - - The warning returned varies on whether the line itself is blank, for easier - filtering for those who want to indent their blank lines. - - Okay: spam(1)\n# - W291: spam(1) \n# - W293: class Foo(object):\n \n bang = 12 - """ - physical_line = physical_line.rstrip('\n') # chr(10), newline - physical_line = physical_line.rstrip('\r') # chr(13), carriage return - physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L - stripped = physical_line.rstrip(' \t\v') - if physical_line != stripped: - if stripped: - return len(stripped), "W291 trailing whitespace" - else: - return 0, "W293 blank line contains whitespace" - - -def trailing_blank_lines(physical_line, lines, line_number, total_lines): - r"""Trailing blank lines are superfluous. - - Okay: spam(1) - W391: spam(1)\n - - However the last line should end with a new line (warning W292). - """ - if line_number == total_lines: - stripped_last_line = physical_line.rstrip() - if not stripped_last_line: - return 0, "W391 blank line at end of file" - if stripped_last_line == physical_line: - return len(physical_line), "W292 no newline at end of file" - - -def maximum_line_length(physical_line, max_line_length, multiline): - r"""Limit all lines to a maximum of 79 characters. - - There are still many devices around that are limited to 80 character - lines; plus, limiting windows to 80 characters makes it possible to have - several windows side-by-side. The default wrapping on such devices looks - ugly. Therefore, please limit all lines to a maximum of 79 characters. - For flowing long blocks of text (docstrings or comments), limiting the - length to 72 characters is recommended. - - Reports error E501. - """ - line = physical_line.rstrip() - length = len(line) - if length > max_line_length and not noqa(line): - # Special case for long URLs in multi-line docstrings or comments, - # but still report the error when the 72 first chars are whitespaces. - chunks = line.split() - if ((len(chunks) == 1 and multiline) or - (len(chunks) == 2 and chunks[0] == '#')) and \ - len(line) - len(chunks[-1]) < max_line_length - 7: - return - if hasattr(line, 'decode'): # Python 2 - # The line could contain multi-byte characters - try: - length = len(line.decode('utf-8')) - except UnicodeError: - pass - if length > max_line_length: - return (max_line_length, "E501 line too long " - "(%d > %d characters)" % (length, max_line_length)) - - -############################################################################## -# Plugins (check functions) for logical lines -############################################################################## - - -def blank_lines(logical_line, blank_lines, indent_level, line_number, - blank_before, previous_logical, previous_indent_level): - r"""Separate top-level function and class definitions with two blank lines. - - Method definitions inside a class are separated by a single blank line. - - Extra blank lines may be used (sparingly) to separate groups of related - functions. Blank lines may be omitted between a bunch of related - one-liners (e.g. a set of dummy implementations). - - Use blank lines in functions, sparingly, to indicate logical sections. - - Okay: def a():\n pass\n\n\ndef b():\n pass - Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass - - E301: class Foo:\n b = 0\n def bar():\n pass - E302: def a():\n pass\n\ndef b(n):\n pass - E303: def a():\n pass\n\n\n\ndef b(n):\n pass - E303: def a():\n\n\n\n pass - E304: @decorator\n\ndef a():\n pass - """ - if line_number < 3 and not previous_logical: - return # Don't expect blank lines before the first line - if previous_logical.startswith('@'): - if blank_lines: - yield 0, "E304 blank lines found after function decorator" - elif blank_lines > 2 or (indent_level and blank_lines == 2): - yield 0, "E303 too many blank lines (%d)" % blank_lines - elif logical_line.startswith(('def ', 'class ', '@')): - if indent_level: - if not (blank_before or previous_indent_level < indent_level or - DOCSTRING_REGEX.match(previous_logical)): - yield 0, "E301 expected 1 blank line, found 0" - elif blank_before != 2: - yield 0, "E302 expected 2 blank lines, found %d" % blank_before - - -def extraneous_whitespace(logical_line): - r"""Avoid extraneous whitespace. - - Avoid extraneous whitespace in these situations: - - Immediately inside parentheses, brackets or braces. - - Immediately before a comma, semicolon, or colon. - - Okay: spam(ham[1], {eggs: 2}) - E201: spam( ham[1], {eggs: 2}) - E201: spam(ham[ 1], {eggs: 2}) - E201: spam(ham[1], { eggs: 2}) - E202: spam(ham[1], {eggs: 2} ) - E202: spam(ham[1 ], {eggs: 2}) - E202: spam(ham[1], {eggs: 2 }) - - E203: if x == 4: print x, y; x, y = y , x - E203: if x == 4: print x, y ; x, y = y, x - E203: if x == 4 : print x, y; x, y = y, x - """ - line = logical_line - for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): - text = match.group() - char = text.strip() - found = match.start() - if text == char + ' ': - # assert char in '([{' - yield found + 1, "E201 whitespace after '%s'" % char - elif line[found - 1] != ',': - code = ('E202' if char in '}])' else 'E203') # if char in ',;:' - yield found, "%s whitespace before '%s'" % (code, char) - - -def whitespace_around_keywords(logical_line): - r"""Avoid extraneous whitespace around keywords. - - Okay: True and False - E271: True and False - E272: True and False - E273: True and\tFalse - E274: True\tand False - """ - for match in KEYWORD_REGEX.finditer(logical_line): - before, after = match.groups() - - if '\t' in before: - yield match.start(1), "E274 tab before keyword" - elif len(before) > 1: - yield match.start(1), "E272 multiple spaces before keyword" - - if '\t' in after: - yield match.start(2), "E273 tab after keyword" - elif len(after) > 1: - yield match.start(2), "E271 multiple spaces after keyword" - - -def missing_whitespace(logical_line): - r"""Each comma, semicolon or colon should be followed by whitespace. - - Okay: [a, b] - Okay: (3,) - Okay: a[1:4] - Okay: a[:4] - Okay: a[1:] - Okay: a[1:4:2] - E231: ['a','b'] - E231: foo(bar,baz) - E231: [{'a':'b'}] - """ - line = logical_line - for index in range(len(line) - 1): - char = line[index] - if char in ',;:' and line[index + 1] not in WHITESPACE: - before = line[:index] - if char == ':' and before.count('[') > before.count(']') and \ - before.rfind('{') < before.rfind('['): - continue # Slice syntax, no space required - if char == ',' and line[index + 1] == ')': - continue # Allow tuple with only one element: (3,) - yield index, "E231 missing whitespace after '%s'" % char - - -def indentation(logical_line, previous_logical, indent_char, - indent_level, previous_indent_level): - r"""Use 4 spaces per indentation level. - - For really old code that you don't want to mess up, you can continue to - use 8-space tabs. - - Okay: a = 1 - Okay: if a == 0:\n a = 1 - E111: a = 1 - E114: # a = 1 - - Okay: for item in items:\n pass - E112: for item in items:\npass - E115: for item in items:\n# Hi\n pass - - Okay: a = 1\nb = 2 - E113: a = 1\n b = 2 - E116: a = 1\n # b = 2 - """ - c = 0 if logical_line else 3 - tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)" - if indent_level % 4: - yield 0, tmpl % (1 + c, "indentation is not a multiple of four") - indent_expect = previous_logical.endswith(':') - if indent_expect and indent_level <= previous_indent_level: - yield 0, tmpl % (2 + c, "expected an indented block") - elif not indent_expect and indent_level > previous_indent_level: - yield 0, tmpl % (3 + c, "unexpected indentation") - - -def continued_indentation(logical_line, tokens, indent_level, hang_closing, - indent_char, noqa, verbose): - r"""Continuation lines indentation. - - Continuation lines should align wrapped elements either vertically - using Python's implicit line joining inside parentheses, brackets - and braces, or using a hanging indent. - - When using a hanging indent these considerations should be applied: - - there should be no arguments on the first line, and - - further indentation should be used to clearly distinguish itself as a - continuation line. - - Okay: a = (\n) - E123: a = (\n ) - - Okay: a = (\n 42) - E121: a = (\n 42) - E122: a = (\n42) - E123: a = (\n 42\n ) - E124: a = (24,\n 42\n) - E125: if (\n b):\n pass - E126: a = (\n 42) - E127: a = (24,\n 42) - E128: a = (24,\n 42) - E129: if (a or\n b):\n pass - E131: a = (\n 42\n 24) - """ - first_row = tokens[0][2][0] - nrows = 1 + tokens[-1][2][0] - first_row - if noqa or nrows == 1: - return - - # indent_next tells us whether the next block is indented; assuming - # that it is indented by 4 spaces, then we should not allow 4-space - # indents on the final continuation line; in turn, some other - # indents are allowed to have an extra 4 spaces. - indent_next = logical_line.endswith(':') - - row = depth = 0 - valid_hangs = (4,) if indent_char != '\t' else (4, 8) - # remember how many brackets were opened on each line - parens = [0] * nrows - # relative indents of physical lines - rel_indent = [0] * nrows - # for each depth, collect a list of opening rows - open_rows = [[0]] - # for each depth, memorize the hanging indentation - hangs = [None] - # visual indents - indent_chances = {} - last_indent = tokens[0][2] - visual_indent = None - last_token_multiline = False - # for each depth, memorize the visual indent column - indent = [last_indent[1]] - if verbose >= 3: - print(">>> " + tokens[0][4].rstrip()) - - for token_type, text, start, end, line in tokens: - - newline = row < start[0] - first_row - if newline: - row = start[0] - first_row - newline = not last_token_multiline and token_type not in NEWLINE - - if newline: - # this is the beginning of a continuation line. - last_indent = start - if verbose >= 3: - print("... " + line.rstrip()) - - # record the initial indent. - rel_indent[row] = expand_indent(line) - indent_level - - # identify closing bracket - close_bracket = (token_type == tokenize.OP and text in ']})') - - # is the indent relative to an opening bracket line? - for open_row in reversed(open_rows[depth]): - hang = rel_indent[row] - rel_indent[open_row] - hanging_indent = hang in valid_hangs - if hanging_indent: - break - if hangs[depth]: - hanging_indent = (hang == hangs[depth]) - # is there any chance of visual indent? - visual_indent = (not close_bracket and hang > 0 and - indent_chances.get(start[1])) - - if close_bracket and indent[depth]: - # closing bracket for visual indent - if start[1] != indent[depth]: - yield (start, "E124 closing bracket does not match " - "visual indentation") - elif close_bracket and not hang: - # closing bracket matches indentation of opening bracket's line - if hang_closing: - yield start, "E133 closing bracket is missing indentation" - elif indent[depth] and start[1] < indent[depth]: - if visual_indent is not True: - # visual indent is broken - yield (start, "E128 continuation line " - "under-indented for visual indent") - elif hanging_indent or (indent_next and rel_indent[row] == 8): - # hanging indent is verified - if close_bracket and not hang_closing: - yield (start, "E123 closing bracket does not match " - "indentation of opening bracket's line") - hangs[depth] = hang - elif visual_indent is True: - # visual indent is verified - indent[depth] = start[1] - elif visual_indent in (text, str): - # ignore token lined up with matching one from a previous line - pass - else: - # indent is broken - if hang <= 0: - error = "E122", "missing indentation or outdented" - elif indent[depth]: - error = "E127", "over-indented for visual indent" - elif not close_bracket and hangs[depth]: - error = "E131", "unaligned for hanging indent" - else: - hangs[depth] = hang - if hang > 4: - error = "E126", "over-indented for hanging indent" - else: - error = "E121", "under-indented for hanging indent" - yield start, "%s continuation line %s" % error - - # look for visual indenting - if (parens[row] and - token_type not in (tokenize.NL, tokenize.COMMENT) and - not indent[depth]): - indent[depth] = start[1] - indent_chances[start[1]] = True - if verbose >= 4: - print("bracket depth %s indent to %s" % (depth, start[1])) - # deal with implicit string concatenation - elif (token_type in (tokenize.STRING, tokenize.COMMENT) or - text in ('u', 'ur', 'b', 'br')): - indent_chances[start[1]] = str - # special case for the "if" statement because len("if (") == 4 - elif not indent_chances and not row and not depth and text == 'if': - indent_chances[end[1] + 1] = True - elif text == ':' and line[end[1]:].isspace(): - open_rows[depth].append(row) - - # keep track of bracket depth - if token_type == tokenize.OP: - if text in '([{': - depth += 1 - indent.append(0) - hangs.append(None) - if len(open_rows) == depth: - open_rows.append([]) - open_rows[depth].append(row) - parens[row] += 1 - if verbose >= 4: - print("bracket depth %s seen, col %s, visual min = %s" % - (depth, start[1], indent[depth])) - elif text in ')]}' and depth > 0: - # parent indents should not be more than this one - prev_indent = indent.pop() or last_indent[1] - hangs.pop() - for d in range(depth): - if indent[d] > prev_indent: - indent[d] = 0 - for ind in list(indent_chances): - if ind >= prev_indent: - del indent_chances[ind] - del open_rows[depth + 1:] - depth -= 1 - if depth: - indent_chances[indent[depth]] = True - for idx in range(row, -1, -1): - if parens[idx]: - parens[idx] -= 1 - break - assert len(indent) == depth + 1 - if start[1] not in indent_chances: - # allow to line up tokens - indent_chances[start[1]] = text - - last_token_multiline = (start[0] != end[0]) - if last_token_multiline: - rel_indent[end[0] - first_row] = rel_indent[row] - - if indent_next and expand_indent(line) == indent_level + 4: - pos = (start[0], indent[0] + 4) - if visual_indent: - code = "E129 visually indented line" - else: - code = "E125 continuation line" - yield pos, "%s with same indent as next logical line" % code - - -def whitespace_before_parameters(logical_line, tokens): - r"""Avoid extraneous whitespace. - - Avoid extraneous whitespace in the following situations: - - before the open parenthesis that starts the argument list of a - function call. - - before the open parenthesis that starts an indexing or slicing. - - Okay: spam(1) - E211: spam (1) - - Okay: dict['key'] = list[index] - E211: dict ['key'] = list[index] - E211: dict['key'] = list [index] - """ - prev_type, prev_text, __, prev_end, __ = tokens[0] - for index in range(1, len(tokens)): - token_type, text, start, end, __ = tokens[index] - if (token_type == tokenize.OP and - text in '([' and - start != prev_end and - (prev_type == tokenize.NAME or prev_text in '}])') and - # Syntax "class A (B):" is allowed, but avoid it - (index < 2 or tokens[index - 2][1] != 'class') and - # Allow "return (a.foo for a in range(5))" - not keyword.iskeyword(prev_text)): - yield prev_end, "E211 whitespace before '%s'" % text - prev_type = token_type - prev_text = text - prev_end = end - - -def whitespace_around_operator(logical_line): - r"""Avoid extraneous whitespace around an operator. - - Okay: a = 12 + 3 - E221: a = 4 + 5 - E222: a = 4 + 5 - E223: a = 4\t+ 5 - E224: a = 4 +\t5 - """ - for match in OPERATOR_REGEX.finditer(logical_line): - before, after = match.groups() - - if '\t' in before: - yield match.start(1), "E223 tab before operator" - elif len(before) > 1: - yield match.start(1), "E221 multiple spaces before operator" - - if '\t' in after: - yield match.start(2), "E224 tab after operator" - elif len(after) > 1: - yield match.start(2), "E222 multiple spaces after operator" - - -def missing_whitespace_around_operator(logical_line, tokens): - r"""Surround operators with a single space on either side. - - - Always surround these binary operators with a single space on - either side: assignment (=), augmented assignment (+=, -= etc.), - comparisons (==, <, >, !=, <=, >=, in, not in, is, is not), - Booleans (and, or, not). - - - If operators with different priorities are used, consider adding - whitespace around the operators with the lowest priorities. - - Okay: i = i + 1 - Okay: submitted += 1 - Okay: x = x * 2 - 1 - Okay: hypot2 = x * x + y * y - Okay: c = (a + b) * (a - b) - Okay: foo(bar, key='word', *args, **kwargs) - Okay: alpha[:-i] - - E225: i=i+1 - E225: submitted +=1 - E225: x = x /2 - 1 - E225: z = x **y - E226: c = (a+b) * (a-b) - E226: hypot2 = x*x + y*y - E227: c = a|b - E228: msg = fmt%(errno, errmsg) - """ - parens = 0 - need_space = False - prev_type = tokenize.OP - prev_text = prev_end = None - for token_type, text, start, end, line in tokens: - if token_type in SKIP_COMMENTS: - continue - if text in ('(', 'lambda'): - parens += 1 - elif text == ')': - parens -= 1 - if need_space: - if start != prev_end: - # Found a (probably) needed space - if need_space is not True and not need_space[1]: - yield (need_space[0], - "E225 missing whitespace around operator") - need_space = False - elif text == '>' and prev_text in ('<', '-'): - # Tolerate the "<>" operator, even if running Python 3 - # Deal with Python 3's annotated return value "->" - pass - else: - if need_space is True or need_space[1]: - # A needed trailing space was not found - yield prev_end, "E225 missing whitespace around operator" - elif prev_text != '**': - code, optype = 'E226', 'arithmetic' - if prev_text == '%': - code, optype = 'E228', 'modulo' - elif prev_text not in ARITHMETIC_OP: - code, optype = 'E227', 'bitwise or shift' - yield (need_space[0], "%s missing whitespace " - "around %s operator" % (code, optype)) - need_space = False - elif token_type == tokenize.OP and prev_end is not None: - if text == '=' and parens: - # Allow keyword args or defaults: foo(bar=None). - pass - elif text in WS_NEEDED_OPERATORS: - need_space = True - elif text in UNARY_OPERATORS: - # Check if the operator is being used as a binary operator - # Allow unary operators: -123, -x, +1. - # Allow argument unpacking: foo(*args, **kwargs). - if (prev_text in '}])' if prev_type == tokenize.OP - else prev_text not in KEYWORDS): - need_space = None - elif text in WS_OPTIONAL_OPERATORS: - need_space = None - - if need_space is None: - # Surrounding space is optional, but ensure that - # trailing space matches opening space - need_space = (prev_end, start != prev_end) - elif need_space and start == prev_end: - # A needed opening space was not found - yield prev_end, "E225 missing whitespace around operator" - need_space = False - prev_type = token_type - prev_text = text - prev_end = end - - -def whitespace_around_comma(logical_line): - r"""Avoid extraneous whitespace after a comma or a colon. - - Note: these checks are disabled by default - - Okay: a = (1, 2) - E241: a = (1, 2) - E242: a = (1,\t2) - """ - line = logical_line - for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line): - found = m.start() + 1 - if '\t' in m.group(): - yield found, "E242 tab after '%s'" % m.group()[0] - else: - yield found, "E241 multiple spaces after '%s'" % m.group()[0] - - -def whitespace_around_named_parameter_equals(logical_line, tokens): - r"""Don't use spaces around the '=' sign in function arguments. - - Don't use spaces around the '=' sign when used to indicate a - keyword argument or a default parameter value. - - Okay: def complex(real, imag=0.0): - Okay: return magic(r=real, i=imag) - Okay: boolean(a == b) - Okay: boolean(a != b) - Okay: boolean(a <= b) - Okay: boolean(a >= b) - Okay: def foo(arg: int = 42): - - E251: def complex(real, imag = 0.0): - E251: return magic(r = real, i = imag) - """ - parens = 0 - no_space = False - prev_end = None - annotated_func_arg = False - in_def = logical_line.startswith('def') - message = "E251 unexpected spaces around keyword / parameter equals" - for token_type, text, start, end, line in tokens: - if token_type == tokenize.NL: - continue - if no_space: - no_space = False - if start != prev_end: - yield (prev_end, message) - if token_type == tokenize.OP: - if text == '(': - parens += 1 - elif text == ')': - parens -= 1 - elif in_def and text == ':' and parens == 1: - annotated_func_arg = True - elif parens and text == ',' and parens == 1: - annotated_func_arg = False - elif parens and text == '=' and not annotated_func_arg: - no_space = True - if start != prev_end: - yield (prev_end, message) - if not parens: - annotated_func_arg = False - - prev_end = end - - -def whitespace_before_comment(logical_line, tokens): - r"""Separate inline comments by at least two spaces. - - An inline comment is a comment on the same line as a statement. Inline - comments should be separated by at least two spaces from the statement. - They should start with a # and a single space. - - Each line of a block comment starts with a # and a single space - (unless it is indented text inside the comment). - - Okay: x = x + 1 # Increment x - Okay: x = x + 1 # Increment x - Okay: # Block comment - E261: x = x + 1 # Increment x - E262: x = x + 1 #Increment x - E262: x = x + 1 # Increment x - E265: #Block comment - E266: ### Block comment - """ - prev_end = (0, 0) - for token_type, text, start, end, line in tokens: - if token_type == tokenize.COMMENT: - inline_comment = line[:start[1]].strip() - if inline_comment: - if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: - yield (prev_end, - "E261 at least two spaces before inline comment") - symbol, sp, comment = text.partition(' ') - bad_prefix = symbol not in '#:' and (symbol.lstrip('#')[:1] or '#') - if inline_comment: - if bad_prefix or comment[:1] in WHITESPACE: - yield start, "E262 inline comment should start with '# '" - elif bad_prefix and (bad_prefix != '!' or start[0] > 1): - if bad_prefix != '#': - yield start, "E265 block comment should start with '# '" - elif comment: - yield start, "E266 too many leading '#' for block comment" - elif token_type != tokenize.NL: - prev_end = end - - -def imports_on_separate_lines(logical_line): - r"""Imports should usually be on separate lines. - - Okay: import os\nimport sys - E401: import sys, os - - Okay: from subprocess import Popen, PIPE - Okay: from myclas import MyClass - Okay: from foo.bar.yourclass import YourClass - Okay: import myclass - Okay: import foo.bar.yourclass - """ - line = logical_line - if line.startswith('import '): - found = line.find(',') - if -1 < found and ';' not in line[:found]: - yield found, "E401 multiple imports on one line" - - -def module_imports_on_top_of_file( - logical_line, indent_level, checker_state, noqa): - r"""Imports are always put at the top of the file, just after any module - comments and docstrings, and before module globals and constants. - - Okay: import os - Okay: # this is a comment\nimport os - Okay: '''this is a module docstring'''\nimport os - Okay: r'''this is a module docstring'''\nimport os - Okay: try:\n import x\nexcept:\n pass\nelse:\n pass\nimport y - Okay: try:\n import x\nexcept:\n pass\nfinally:\n pass\nimport y - E402: a=1\nimport os - E402: 'One string'\n"Two string"\nimport os - E402: a=1\nfrom sys import x - - Okay: if x:\n import os - """ - def is_string_literal(line): - if line[0] in 'uUbB': - line = line[1:] - if line and line[0] in 'rR': - line = line[1:] - return line and (line[0] == '"' or line[0] == "'") - - allowed_try_keywords = ('try', 'except', 'else', 'finally') - - if indent_level: # Allow imports in conditional statements or functions - return - if not logical_line: # Allow empty lines or comments - return - if noqa: - return - line = logical_line - if line.startswith('import ') or line.startswith('from '): - if checker_state.get('seen_non_imports', False): - yield 0, "E402 module level import not at top of file" - elif any(line.startswith(kw) for kw in allowed_try_keywords): - # Allow try, except, else, finally keywords intermixed with imports in - # order to support conditional importing - return - elif is_string_literal(line): - # The first literal is a docstring, allow it. Otherwise, report error. - if checker_state.get('seen_docstring', False): - checker_state['seen_non_imports'] = True - else: - checker_state['seen_docstring'] = True - else: - checker_state['seen_non_imports'] = True - - -def compound_statements(logical_line): - r"""Compound statements (on the same line) are generally discouraged. - - While sometimes it's okay to put an if/for/while with a small body - on the same line, never do this for multi-clause statements. - Also avoid folding such long lines! - - Always use a def statement instead of an assignment statement that - binds a lambda expression directly to a name. - - Okay: if foo == 'blah':\n do_blah_thing() - Okay: do_one() - Okay: do_two() - Okay: do_three() - - E701: if foo == 'blah': do_blah_thing() - E701: for x in lst: total += x - E701: while t < 10: t = delay() - E701: if foo == 'blah': do_blah_thing() - E701: else: do_non_blah_thing() - E701: try: something() - E701: finally: cleanup() - E701: if foo == 'blah': one(); two(); three() - E702: do_one(); do_two(); do_three() - E703: do_four(); # useless semicolon - E704: def f(x): return 2*x - E731: f = lambda x: 2*x - """ - line = logical_line - last_char = len(line) - 1 - found = line.find(':') - while -1 < found < last_char: - before = line[:found] - if ((before.count('{') <= before.count('}') and # {'a': 1} (dict) - before.count('[') <= before.count(']') and # [1:2] (slice) - before.count('(') <= before.count(')'))): # (annotation) - lambda_kw = LAMBDA_REGEX.search(before) - if lambda_kw: - before = line[:lambda_kw.start()].rstrip() - if before[-1:] == '=' and isidentifier(before[:-1].strip()): - yield 0, ("E731 do not assign a lambda expression, use a " - "def") - break - if before.startswith('def '): - yield 0, "E704 multiple statements on one line (def)" - else: - yield found, "E701 multiple statements on one line (colon)" - found = line.find(':', found + 1) - found = line.find(';') - while -1 < found: - if found < last_char: - yield found, "E702 multiple statements on one line (semicolon)" - else: - yield found, "E703 statement ends with a semicolon" - found = line.find(';', found + 1) - - -def explicit_line_join(logical_line, tokens): - r"""Avoid explicit line join between brackets. - - The preferred way of wrapping long lines is by using Python's implied line - continuation inside parentheses, brackets and braces. Long lines can be - broken over multiple lines by wrapping expressions in parentheses. These - should be used in preference to using a backslash for line continuation. - - E502: aaa = [123, \\n 123] - E502: aaa = ("bbb " \\n "ccc") - - Okay: aaa = [123,\n 123] - Okay: aaa = ("bbb "\n "ccc") - Okay: aaa = "bbb " \\n "ccc" - Okay: aaa = 123 # \\ - """ - prev_start = prev_end = parens = 0 - comment = False - backslash = None - for token_type, text, start, end, line in tokens: - if token_type == tokenize.COMMENT: - comment = True - if start[0] != prev_start and parens and backslash and not comment: - yield backslash, "E502 the backslash is redundant between brackets" - if end[0] != prev_end: - if line.rstrip('\r\n').endswith('\\'): - backslash = (end[0], len(line.splitlines()[-1]) - 1) - else: - backslash = None - prev_start = prev_end = end[0] - else: - prev_start = start[0] - if token_type == tokenize.OP: - if text in '([{': - parens += 1 - elif text in ')]}': - parens -= 1 - - -def break_around_binary_operator(logical_line, tokens): - r""" - Avoid breaks before binary operators. - - The preferred place to break around a binary operator is after the - operator, not before it. - - W503: (width == 0\n + height == 0) - W503: (width == 0\n and height == 0) - - Okay: (width == 0 +\n height == 0) - Okay: foo(\n -x) - Okay: foo(x\n []) - Okay: x = '''\n''' + '' - Okay: foo(x,\n -y) - Okay: foo(x, # comment\n -y) - """ - def is_binary_operator(token_type, text): - # The % character is strictly speaking a binary operator, but the - # common usage seems to be to put it next to the format parameters, - # after a line break. - return ((token_type == tokenize.OP or text in ['and', 'or']) and - text not in "()[]{},:.;@=%") - - line_break = False - unary_context = True - for token_type, text, start, end, line in tokens: - if token_type == tokenize.COMMENT: - continue - if ('\n' in text or '\r' in text) and token_type != tokenize.STRING: - line_break = True - else: - if (is_binary_operator(token_type, text) and line_break and - not unary_context): - yield start, "W503 line break before binary operator" - unary_context = text in '([{,;' - line_break = False - - -def comparison_to_singleton(logical_line, noqa): - r"""Comparison to singletons should use "is" or "is not". - - Comparisons to singletons like None should always be done - with "is" or "is not", never the equality operators. - - Okay: if arg is not None: - E711: if arg != None: - E711: if None == arg: - E712: if arg == True: - E712: if False == arg: - - Also, beware of writing if x when you really mean if x is not None -- - e.g. when testing whether a variable or argument that defaults to None was - set to some other value. The other value might have a type (such as a - container) that could be false in a boolean context! - """ - match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line) - if match: - singleton = match.group(1) or match.group(3) - same = (match.group(2) == '==') - - msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton) - if singleton in ('None',): - code = 'E711' - else: - code = 'E712' - nonzero = ((singleton == 'True' and same) or - (singleton == 'False' and not same)) - msg += " or 'if %scond:'" % ('' if nonzero else 'not ') - yield match.start(2), ("%s comparison to %s should be %s" % - (code, singleton, msg)) - - -def comparison_negative(logical_line): - r"""Negative comparison should be done using "not in" and "is not". - - Okay: if x not in y:\n pass - Okay: assert (X in Y or X is Z) - Okay: if not (X in Y):\n pass - Okay: zz = x is not y - E713: Z = not X in Y - E713: if not X.B in Y:\n pass - E714: if not X is Y:\n pass - E714: Z = not X.B is Y - """ - match = COMPARE_NEGATIVE_REGEX.search(logical_line) - if match: - pos = match.start(1) - if match.group(2) == 'in': - yield pos, "E713 test for membership should be 'not in'" - else: - yield pos, "E714 test for object identity should be 'is not'" - - -def comparison_type(logical_line, noqa): - r"""Object type comparisons should always use isinstance(). - - Do not compare types directly. - - Okay: if isinstance(obj, int): - E721: if type(obj) is type(1): - - When checking if an object is a string, keep in mind that it might be a - unicode string too! In Python 2.3, str and unicode have a common base - class, basestring, so you can do: - - Okay: if isinstance(obj, basestring): - Okay: if type(a1) is type(b1): - """ - match = COMPARE_TYPE_REGEX.search(logical_line) - if match and not noqa: - inst = match.group(1) - if inst and isidentifier(inst) and inst not in SINGLETONS: - return # Allow comparison for types which are not obvious - yield match.start(), "E721 do not compare types, use 'isinstance()'" - - -def python_3000_has_key(logical_line, noqa): - r"""The {}.has_key() method is removed in Python 3: use the 'in' operator. - - Okay: if "alph" in d:\n print d["alph"] - W601: assert d.has_key('alph') - """ - pos = logical_line.find('.has_key(') - if pos > -1 and not noqa: - yield pos, "W601 .has_key() is deprecated, use 'in'" - - -def python_3000_raise_comma(logical_line): - r"""When raising an exception, use "raise ValueError('message')". - - The older form is removed in Python 3. - - Okay: raise DummyError("Message") - W602: raise DummyError, "Message" - """ - match = RAISE_COMMA_REGEX.match(logical_line) - if match and not RERAISE_COMMA_REGEX.match(logical_line): - yield match.end() - 1, "W602 deprecated form of raising exception" - - -def python_3000_not_equal(logical_line): - r"""New code should always use != instead of <>. - - The older syntax is removed in Python 3. - - Okay: if a != 'no': - W603: if a <> 'no': - """ - pos = logical_line.find('<>') - if pos > -1: - yield pos, "W603 '<>' is deprecated, use '!='" - - -def python_3000_backticks(logical_line): - r"""Backticks are removed in Python 3: use repr() instead. - - Okay: val = repr(1 + 2) - W604: val = `1 + 2` - """ - pos = logical_line.find('`') - if pos > -1: - yield pos, "W604 backticks are deprecated, use 'repr()'" - - -############################################################################## -# Helper functions -############################################################################## - - -if '' == ''.encode(): - # Python 2: implicit encoding. - def readlines(filename): - """Read the source code.""" - with open(filename, 'rU') as f: - return f.readlines() - isidentifier = re.compile(r'[a-zA-Z_]\w*$').match - stdin_get_value = sys.stdin.read -else: - # Python 3 - def readlines(filename): - """Read the source code.""" - try: - with open(filename, 'rb') as f: - (coding, lines) = tokenize.detect_encoding(f.readline) - f = TextIOWrapper(f, coding, line_buffering=True) - return [l.decode(coding) for l in lines] + f.readlines() - except (LookupError, SyntaxError, UnicodeError): - # Fall back if file encoding is improperly declared - with open(filename, encoding='latin-1') as f: - return f.readlines() - isidentifier = str.isidentifier - - def stdin_get_value(): - return TextIOWrapper(sys.stdin.buffer, errors='ignore').read() -noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search - - -def expand_indent(line): - r"""Return the amount of indentation. - - Tabs are expanded to the next multiple of 8. - - >>> expand_indent(' ') - 4 - >>> expand_indent('\t') - 8 - >>> expand_indent(' \t') - 8 - >>> expand_indent(' \t') - 16 - """ - if '\t' not in line: - return len(line) - len(line.lstrip()) - result = 0 - for char in line: - if char == '\t': - result = result // 8 * 8 + 8 - elif char == ' ': - result += 1 - else: - break - return result - - -def mute_string(text): - """Replace contents with 'xxx' to prevent syntax matching. - - >>> mute_string('"abc"') - '"xxx"' - >>> mute_string("'''abc'''") - "'''xxx'''" - >>> mute_string("r'abc'") - "r'xxx'" - """ - # String modifiers (e.g. u or r) - start = text.index(text[-1]) + 1 - end = len(text) - 1 - # Triple quotes - if text[-3:] in ('"""', "'''"): - start += 2 - end -= 2 - return text[:start] + 'x' * (end - start) + text[end:] - - -def parse_udiff(diff, patterns=None, parent='.'): - """Return a dictionary of matching lines.""" - # For each file of the diff, the entry key is the filename, - # and the value is a set of row numbers to consider. - rv = {} - path = nrows = None - for line in diff.splitlines(): - if nrows: - if line[:1] != '-': - nrows -= 1 - continue - if line[:3] == '@@ ': - hunk_match = HUNK_REGEX.match(line) - (row, nrows) = [int(g or '1') for g in hunk_match.groups()] - rv[path].update(range(row, row + nrows)) - elif line[:3] == '+++': - path = line[4:].split('\t', 1)[0] - if path[:2] == 'b/': - path = path[2:] - rv[path] = set() - return dict([(os.path.join(parent, path), rows) - for (path, rows) in rv.items() - if rows and filename_match(path, patterns)]) - - -def normalize_paths(value, parent=os.curdir): - """Parse a comma-separated list of paths. - - Return a list of absolute paths. - """ - if not value: - return [] - if isinstance(value, list): - return value - paths = [] - for path in value.split(','): - path = path.strip() - if '/' in path: - path = os.path.abspath(os.path.join(parent, path)) - paths.append(path.rstrip('/')) - return paths - - -def filename_match(filename, patterns, default=True): - """Check if patterns contains a pattern that matches filename. - - If patterns is unspecified, this always returns True. - """ - if not patterns: - return default - return any(fnmatch(filename, pattern) for pattern in patterns) - - -def _is_eol_token(token): - return token[0] in NEWLINE or token[4][token[3][1]:].lstrip() == '\\\n' -if COMMENT_WITH_NL: - def _is_eol_token(token, _eol_token=_is_eol_token): - return _eol_token(token) or (token[0] == tokenize.COMMENT and - token[1] == token[4]) - -############################################################################## -# Framework to run all checks -############################################################################## - - -_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} - - -def _get_parameters(function): - if sys.version_info >= (3, 3): - return list(inspect.signature(function).parameters) - else: - return inspect.getargspec(function)[0] - - -def register_check(check, codes=None): - """Register a new check object.""" - def _add_check(check, kind, codes, args): - if check in _checks[kind]: - _checks[kind][check][0].extend(codes or []) - else: - _checks[kind][check] = (codes or [''], args) - if inspect.isfunction(check): - args = _get_parameters(check) - if args and args[0] in ('physical_line', 'logical_line'): - if codes is None: - codes = ERRORCODE_REGEX.findall(check.__doc__ or '') - _add_check(check, args[0], codes, args) - elif inspect.isclass(check): - if _get_parameters(check.__init__)[:2] == ['self', 'tree']: - _add_check(check, 'tree', codes, None) - - -def init_checks_registry(): - """Register all globally visible functions. - - The first argument name is either 'physical_line' or 'logical_line'. - """ - mod = inspect.getmodule(register_check) - for (name, function) in inspect.getmembers(mod, inspect.isfunction): - register_check(function) -init_checks_registry() - - -class Checker(object): - """Load a Python source file, tokenize it, check coding style.""" - - def __init__(self, filename=None, lines=None, - options=None, report=None, **kwargs): - if options is None: - options = StyleGuide(kwargs).options - else: - assert not kwargs - self._io_error = None - self._physical_checks = options.physical_checks - self._logical_checks = options.logical_checks - self._ast_checks = options.ast_checks - self.max_line_length = options.max_line_length - self.multiline = False # in a multiline string? - self.hang_closing = options.hang_closing - self.verbose = options.verbose - self.filename = filename - # Dictionary where a checker can store its custom state. - self._checker_states = {} - if filename is None: - self.filename = 'stdin' - self.lines = lines or [] - elif filename == '-': - self.filename = 'stdin' - self.lines = stdin_get_value().splitlines(True) - elif lines is None: - try: - self.lines = readlines(filename) - except IOError: - (exc_type, exc) = sys.exc_info()[:2] - self._io_error = '%s: %s' % (exc_type.__name__, exc) - self.lines = [] - else: - self.lines = lines - if self.lines: - ord0 = ord(self.lines[0][0]) - if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM - if ord0 == 0xfeff: - self.lines[0] = self.lines[0][1:] - elif self.lines[0][:3] == '\xef\xbb\xbf': - self.lines[0] = self.lines[0][3:] - self.report = report or options.report - self.report_error = self.report.error - - def report_invalid_syntax(self): - """Check if the syntax is valid.""" - (exc_type, exc) = sys.exc_info()[:2] - if len(exc.args) > 1: - offset = exc.args[1] - if len(offset) > 2: - offset = offset[1:3] - else: - offset = (1, 0) - self.report_error(offset[0], offset[1] or 0, - 'E901 %s: %s' % (exc_type.__name__, exc.args[0]), - self.report_invalid_syntax) - - def readline(self): - """Get the next line from the input buffer.""" - if self.line_number >= self.total_lines: - return '' - line = self.lines[self.line_number] - self.line_number += 1 - if self.indent_char is None and line[:1] in WHITESPACE: - self.indent_char = line[0] - return line - - def run_check(self, check, argument_names): - """Run a check plugin.""" - arguments = [] - for name in argument_names: - arguments.append(getattr(self, name)) - return check(*arguments) - - def init_checker_state(self, name, argument_names): - """ Prepares a custom state for the specific checker plugin.""" - if 'checker_state' in argument_names: - self.checker_state = self._checker_states.setdefault(name, {}) - - def check_physical(self, line): - """Run all physical checks on a raw input line.""" - self.physical_line = line - for name, check, argument_names in self._physical_checks: - self.init_checker_state(name, argument_names) - result = self.run_check(check, argument_names) - if result is not None: - (offset, text) = result - self.report_error(self.line_number, offset, text, check) - if text[:4] == 'E101': - self.indent_char = line[0] - - def build_tokens_line(self): - """Build a logical line from tokens.""" - logical = [] - comments = [] - length = 0 - prev_row = prev_col = mapping = None - for token_type, text, start, end, line in self.tokens: - if token_type in SKIP_TOKENS: - continue - if not mapping: - mapping = [(0, start)] - if token_type == tokenize.COMMENT: - comments.append(text) - continue - if token_type == tokenize.STRING: - text = mute_string(text) - if prev_row: - (start_row, start_col) = start - if prev_row != start_row: # different row - prev_text = self.lines[prev_row - 1][prev_col - 1] - if prev_text == ',' or (prev_text not in '{[(' and - text not in '}])'): - text = ' ' + text - elif prev_col != start_col: # different column - text = line[prev_col:start_col] + text - logical.append(text) - length += len(text) - mapping.append((length, end)) - (prev_row, prev_col) = end - self.logical_line = ''.join(logical) - self.noqa = comments and noqa(''.join(comments)) - return mapping - - def check_logical(self): - """Build a line from tokens and run all logical checks on it.""" - self.report.increment_logical_line() - mapping = self.build_tokens_line() - - if not mapping: - return - - (start_row, start_col) = mapping[0][1] - start_line = self.lines[start_row - 1] - self.indent_level = expand_indent(start_line[:start_col]) - if self.blank_before < self.blank_lines: - self.blank_before = self.blank_lines - if self.verbose >= 2: - print(self.logical_line[:80].rstrip()) - for name, check, argument_names in self._logical_checks: - if self.verbose >= 4: - print(' ' + name) - self.init_checker_state(name, argument_names) - for offset, text in self.run_check(check, argument_names) or (): - if not isinstance(offset, tuple): - for token_offset, pos in mapping: - if offset <= token_offset: - break - offset = (pos[0], pos[1] + offset - token_offset) - self.report_error(offset[0], offset[1], text, check) - if self.logical_line: - self.previous_indent_level = self.indent_level - self.previous_logical = self.logical_line - self.blank_lines = 0 - self.tokens = [] - - def check_ast(self): - """Build the file's AST and run all AST checks.""" - try: - tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST) - except (ValueError, SyntaxError, TypeError): - return self.report_invalid_syntax() - for name, cls, __ in self._ast_checks: - checker = cls(tree, self.filename) - for lineno, offset, text, check in checker.run(): - if not self.lines or not noqa(self.lines[lineno - 1]): - self.report_error(lineno, offset, text, check) - - def generate_tokens(self): - """Tokenize the file, run physical line checks and yield tokens.""" - if self._io_error: - self.report_error(1, 0, 'E902 %s' % self._io_error, readlines) - tokengen = tokenize.generate_tokens(self.readline) - try: - for token in tokengen: - if token[2][0] > self.total_lines: - return - self.maybe_check_physical(token) - yield token - except (SyntaxError, tokenize.TokenError): - self.report_invalid_syntax() - - def maybe_check_physical(self, token): - """If appropriate (based on token), check current physical line(s).""" - # Called after every token, but act only on end of line. - if _is_eol_token(token): - # Obviously, a newline token ends a single physical line. - self.check_physical(token[4]) - elif token[0] == tokenize.STRING and '\n' in token[1]: - # Less obviously, a string that contains newlines is a - # multiline string, either triple-quoted or with internal - # newlines backslash-escaped. Check every physical line in the - # string *except* for the last one: its newline is outside of - # the multiline string, so we consider it a regular physical - # line, and will check it like any other physical line. - # - # Subtleties: - # - we don't *completely* ignore the last line; if it contains - # the magical "# noqa" comment, we disable all physical - # checks for the entire multiline string - # - have to wind self.line_number back because initially it - # points to the last line of the string, and we want - # check_physical() to give accurate feedback - if noqa(token[4]): - return - self.multiline = True - self.line_number = token[2][0] - for line in token[1].split('\n')[:-1]: - self.check_physical(line + '\n') - self.line_number += 1 - self.multiline = False - - def check_all(self, expected=None, line_offset=0): - """Run all checks on the input file.""" - self.report.init_file(self.filename, self.lines, expected, line_offset) - self.total_lines = len(self.lines) - if self._ast_checks: - self.check_ast() - self.line_number = 0 - self.indent_char = None - self.indent_level = self.previous_indent_level = 0 - self.previous_logical = '' - self.tokens = [] - self.blank_lines = self.blank_before = 0 - parens = 0 - for token in self.generate_tokens(): - self.tokens.append(token) - token_type, text = token[0:2] - if self.verbose >= 3: - if token[2][0] == token[3][0]: - pos = '[%s:%s]' % (token[2][1] or '', token[3][1]) - else: - pos = 'l.%s' % token[3][0] - print('l.%s\t%s\t%s\t%r' % - (token[2][0], pos, tokenize.tok_name[token[0]], text)) - if token_type == tokenize.OP: - if text in '([{': - parens += 1 - elif text in '}])': - parens -= 1 - elif not parens: - if token_type in NEWLINE: - if token_type == tokenize.NEWLINE: - self.check_logical() - self.blank_before = 0 - elif len(self.tokens) == 1: - # The physical line contains only this token. - self.blank_lines += 1 - del self.tokens[0] - else: - self.check_logical() - elif COMMENT_WITH_NL and token_type == tokenize.COMMENT: - if len(self.tokens) == 1: - # The comment also ends a physical line - token = list(token) - token[1] = text.rstrip('\r\n') - token[3] = (token[2][0], token[2][1] + len(token[1])) - self.tokens = [tuple(token)] - self.check_logical() - if self.tokens: - self.check_physical(self.lines[-1]) - self.check_logical() - return self.report.get_file_results() - - -class BaseReport(object): - """Collect the results of the checks.""" - - print_filename = False - - def __init__(self, options): - self._benchmark_keys = options.benchmark_keys - self._ignore_code = options.ignore_code - # Results - self.elapsed = 0 - self.total_errors = 0 - self.counters = dict.fromkeys(self._benchmark_keys, 0) - self.messages = {} - - def start(self): - """Start the timer.""" - self._start_time = time.time() - - def stop(self): - """Stop the timer.""" - self.elapsed = time.time() - self._start_time - - def init_file(self, filename, lines, expected, line_offset): - """Signal a new file.""" - self.filename = filename - self.lines = lines - self.expected = expected or () - self.line_offset = line_offset - self.file_errors = 0 - self.counters['files'] += 1 - self.counters['physical lines'] += len(lines) - - def increment_logical_line(self): - """Signal a new logical line.""" - self.counters['logical lines'] += 1 - - def error(self, line_number, offset, text, check): - """Report an error, according to options.""" - code = text[:4] - if self._ignore_code(code): - return - if code in self.counters: - self.counters[code] += 1 - else: - self.counters[code] = 1 - self.messages[code] = text[5:] - # Don't care about expected errors or warnings - if code in self.expected: - return - if self.print_filename and not self.file_errors: - print(self.filename) - self.file_errors += 1 - self.total_errors += 1 - return code - - def get_file_results(self): - """Return the count of errors and warnings for this file.""" - return self.file_errors - - def get_count(self, prefix=''): - """Return the total count of errors and warnings.""" - return sum([self.counters[key] - for key in self.messages if key.startswith(prefix)]) - - def get_statistics(self, prefix=''): - """Get statistics for message codes that start with the prefix. - - prefix='' matches all errors and warnings - prefix='E' matches all errors - prefix='W' matches all warnings - prefix='E4' matches all errors that have to do with imports - """ - return ['%-7s %s %s' % (self.counters[key], key, self.messages[key]) - for key in sorted(self.messages) if key.startswith(prefix)] - - def print_statistics(self, prefix=''): - """Print overall statistics (number of errors and warnings).""" - for line in self.get_statistics(prefix): - print(line) - - def print_benchmark(self): - """Print benchmark numbers.""" - print('%-7.2f %s' % (self.elapsed, 'seconds elapsed')) - if self.elapsed: - for key in self._benchmark_keys: - print('%-7d %s per second (%d total)' % - (self.counters[key] / self.elapsed, key, - self.counters[key])) - - -class FileReport(BaseReport): - """Collect the results of the checks and print only the filenames.""" - print_filename = True - - -class StandardReport(BaseReport): - """Collect and print the results of the checks.""" - - def __init__(self, options): - super(StandardReport, self).__init__(options) - self._fmt = REPORT_FORMAT.get(options.format.lower(), - options.format) - self._repeat = options.repeat - self._show_source = options.show_source - self._show_pep8 = options.show_pep8 - - def init_file(self, filename, lines, expected, line_offset): - """Signal a new file.""" - self._deferred_print = [] - return super(StandardReport, self).init_file( - filename, lines, expected, line_offset) - - def error(self, line_number, offset, text, check): - """Report an error, according to options.""" - code = super(StandardReport, self).error(line_number, offset, - text, check) - if code and (self.counters[code] == 1 or self._repeat): - self._deferred_print.append( - (line_number, offset, code, text[5:], check.__doc__)) - return code - - def get_file_results(self): - """Print the result and return the overall count for this file.""" - self._deferred_print.sort() - for line_number, offset, code, text, doc in self._deferred_print: - print(self._fmt % { - 'path': self.filename, - 'row': self.line_offset + line_number, 'col': offset + 1, - 'code': code, 'text': text, - }) - if self._show_source: - if line_number > len(self.lines): - line = '' - else: - line = self.lines[line_number - 1] - print(line.rstrip()) - print(re.sub(r'\S', ' ', line[:offset]) + '^') - if self._show_pep8 and doc: - print(' ' + doc.strip()) - - # stdout is block buffered when not stdout.isatty(). - # line can be broken where buffer boundary since other processes - # write to same file. - # flush() after print() to avoid buffer boundary. - # Typical buffer size is 8192. line written safely when - # len(line) < 8192. - sys.stdout.flush() - return self.file_errors - - -class DiffReport(StandardReport): - """Collect and print the results for the changed lines only.""" - - def __init__(self, options): - super(DiffReport, self).__init__(options) - self._selected = options.selected_lines - - def error(self, line_number, offset, text, check): - if line_number not in self._selected[self.filename]: - return - return super(DiffReport, self).error(line_number, offset, text, check) - - -class StyleGuide(object): - """Initialize a PEP-8 instance with few options.""" - - def __init__(self, *args, **kwargs): - # build options from the command line - self.checker_class = kwargs.pop('checker_class', Checker) - parse_argv = kwargs.pop('parse_argv', False) - config_file = kwargs.pop('config_file', False) - parser = kwargs.pop('parser', None) - # build options from dict - options_dict = dict(*args, **kwargs) - arglist = None if parse_argv else options_dict.get('paths', None) - options, self.paths = process_options( - arglist, parse_argv, config_file, parser) - if options_dict: - options.__dict__.update(options_dict) - if 'paths' in options_dict: - self.paths = options_dict['paths'] - - self.runner = self.input_file - self.options = options - - if not options.reporter: - options.reporter = BaseReport if options.quiet else StandardReport - - options.select = tuple(options.select or ()) - if not (options.select or options.ignore or - options.testsuite or options.doctest) and DEFAULT_IGNORE: - # The default choice: ignore controversial checks - options.ignore = tuple(DEFAULT_IGNORE.split(',')) - else: - # Ignore all checks which are not explicitly selected - options.ignore = ('',) if options.select else tuple(options.ignore) - options.benchmark_keys = BENCHMARK_KEYS[:] - options.ignore_code = self.ignore_code - options.physical_checks = self.get_checks('physical_line') - options.logical_checks = self.get_checks('logical_line') - options.ast_checks = self.get_checks('tree') - self.init_report() - - def init_report(self, reporter=None): - """Initialize the report instance.""" - self.options.report = (reporter or self.options.reporter)(self.options) - return self.options.report - - def check_files(self, paths=None): - """Run all checks on the paths.""" - if paths is None: - paths = self.paths - report = self.options.report - runner = self.runner - report.start() - try: - for path in paths: - if os.path.isdir(path): - self.input_dir(path) - elif not self.excluded(path): - runner(path) - except KeyboardInterrupt: - print('... stopped') - report.stop() - return report - - def input_file(self, filename, lines=None, expected=None, line_offset=0): - """Run all checks on a Python source file.""" - if self.options.verbose: - print('checking %s' % filename) - fchecker = self.checker_class( - filename, lines=lines, options=self.options) - return fchecker.check_all(expected=expected, line_offset=line_offset) - - def input_dir(self, dirname): - """Check all files in this directory and all subdirectories.""" - dirname = dirname.rstrip('/') - if self.excluded(dirname): - return 0 - counters = self.options.report.counters - verbose = self.options.verbose - filepatterns = self.options.filename - runner = self.runner - for root, dirs, files in os.walk(dirname): - if verbose: - print('directory ' + root) - counters['directories'] += 1 - for subdir in sorted(dirs): - if self.excluded(subdir, root): - dirs.remove(subdir) - for filename in sorted(files): - # contain a pattern that matches? - if ((filename_match(filename, filepatterns) and - not self.excluded(filename, root))): - runner(os.path.join(root, filename)) - - def excluded(self, filename, parent=None): - """Check if the file should be excluded. - - Check if 'options.exclude' contains a pattern that matches filename. - """ - if not self.options.exclude: - return False - basename = os.path.basename(filename) - if filename_match(basename, self.options.exclude): - return True - if parent: - filename = os.path.join(parent, filename) - filename = os.path.abspath(filename) - return filename_match(filename, self.options.exclude) - - def ignore_code(self, code): - """Check if the error code should be ignored. - - If 'options.select' contains a prefix of the error code, - return False. Else, if 'options.ignore' contains a prefix of - the error code, return True. - """ - if len(code) < 4 and any(s.startswith(code) - for s in self.options.select): - return False - return (code.startswith(self.options.ignore) and - not code.startswith(self.options.select)) - - def get_checks(self, argument_name): - """Get all the checks for this category. - - Find all globally visible functions where the first argument name - starts with argument_name and which contain selected tests. - """ - checks = [] - for check, attrs in _checks[argument_name].items(): - (codes, args) = attrs - if any(not (code and self.ignore_code(code)) for code in codes): - checks.append((check.__name__, check, args)) - return sorted(checks) - - -def get_parser(prog='pep8', version=__version__): - parser = OptionParser(prog=prog, version=version, - usage="%prog [options] input ...") - parser.config_options = [ - 'exclude', 'filename', 'select', 'ignore', 'max-line-length', - 'hang-closing', 'count', 'format', 'quiet', 'show-pep8', - 'show-source', 'statistics', 'verbose'] - parser.add_option('-v', '--verbose', default=0, action='count', - help="print status messages, or debug with -vv") - parser.add_option('-q', '--quiet', default=0, action='count', - help="report only file names, or nothing with -qq") - parser.add_option('-r', '--repeat', default=True, action='store_true', - help="(obsolete) show all occurrences of the same error") - parser.add_option('--first', action='store_false', dest='repeat', - help="show first occurrence of each error") - parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, - help="exclude files or directories which match these " - "comma separated patterns (default: %default)") - parser.add_option('--filename', metavar='patterns', default='*.py', - help="when parsing directories, only check filenames " - "matching these comma separated patterns " - "(default: %default)") - parser.add_option('--select', metavar='errors', default='', - help="select errors and warnings (e.g. E,W6)") - parser.add_option('--ignore', metavar='errors', default='', - help="skip errors and warnings (e.g. E4,W) " - "(default: %s)" % DEFAULT_IGNORE) - parser.add_option('--show-source', action='store_true', - help="show source code for each error") - parser.add_option('--show-pep8', action='store_true', - help="show text of PEP 8 for each error " - "(implies --first)") - parser.add_option('--statistics', action='store_true', - help="count errors and warnings") - parser.add_option('--count', action='store_true', - help="print total number of errors and warnings " - "to standard error and set exit code to 1 if " - "total is not null") - parser.add_option('--max-line-length', type='int', metavar='n', - default=MAX_LINE_LENGTH, - help="set maximum allowed line length " - "(default: %default)") - parser.add_option('--hang-closing', action='store_true', - help="hang closing bracket instead of matching " - "indentation of opening bracket's line") - parser.add_option('--format', metavar='format', default='default', - help="set the error format [default|pylint|]") - parser.add_option('--diff', action='store_true', - help="report only lines changed according to the " - "unified diff received on STDIN") - group = parser.add_option_group("Testing Options") - if os.path.exists(TESTSUITE_PATH): - group.add_option('--testsuite', metavar='dir', - help="run regression tests from dir") - group.add_option('--doctest', action='store_true', - help="run doctest on myself") - group.add_option('--benchmark', action='store_true', - help="measure processing speed") - return parser - - -def read_config(options, args, arglist, parser): - """Read and parse configurations - - If a config file is specified on the command line with the "--config" - option, then only it is used for configuration. - - Otherwise, the user configuration (~/.config/pep8) and any local - configurations in the current directory or above will be merged together - (in that order) using the read method of ConfigParser. - """ - config = RawConfigParser() - - cli_conf = options.config - - local_dir = os.curdir - - if USER_CONFIG and os.path.isfile(USER_CONFIG): - if options.verbose: - print('user configuration: %s' % USER_CONFIG) - config.read(USER_CONFIG) - - parent = tail = args and os.path.abspath(os.path.commonprefix(args)) - while tail: - if config.read(os.path.join(parent, fn) for fn in PROJECT_CONFIG): - local_dir = parent - if options.verbose: - print('local configuration: in %s' % parent) - break - (parent, tail) = os.path.split(parent) - - if cli_conf and os.path.isfile(cli_conf): - if options.verbose: - print('cli configuration: %s' % cli_conf) - config.read(cli_conf) - - pep8_section = parser.prog - if config.has_section(pep8_section): - option_list = dict([(o.dest, o.type or o.action) - for o in parser.option_list]) - - # First, read the default values - (new_options, __) = parser.parse_args([]) - - # Second, parse the configuration - for opt in config.options(pep8_section): - if opt.replace('_', '-') not in parser.config_options: - print(" unknown option '%s' ignored" % opt) - continue - if options.verbose > 1: - print(" %s = %s" % (opt, config.get(pep8_section, opt))) - normalized_opt = opt.replace('-', '_') - opt_type = option_list[normalized_opt] - if opt_type in ('int', 'count'): - value = config.getint(pep8_section, opt) - elif opt_type == 'string': - value = config.get(pep8_section, opt) - if normalized_opt == 'exclude': - value = normalize_paths(value, local_dir) - else: - assert opt_type in ('store_true', 'store_false') - value = config.getboolean(pep8_section, opt) - setattr(new_options, normalized_opt, value) - - # Third, overwrite with the command-line options - (options, __) = parser.parse_args(arglist, values=new_options) - options.doctest = options.testsuite = False - return options - - -def process_options(arglist=None, parse_argv=False, config_file=None, - parser=None): - """Process options passed either via arglist or via command line args. - - Passing in the ``config_file`` parameter allows other tools, such as flake8 - to specify their own options to be processed in pep8. - """ - if not parser: - parser = get_parser() - if not parser.has_option('--config'): - group = parser.add_option_group("Configuration", description=( - "The project options are read from the [%s] section of the " - "tox.ini file or the setup.cfg file located in any parent folder " - "of the path(s) being processed. Allowed options are: %s." % - (parser.prog, ', '.join(parser.config_options)))) - group.add_option('--config', metavar='path', default=config_file, - help="user config file location") - # Don't read the command line if the module is used as a library. - if not arglist and not parse_argv: - arglist = [] - # If parse_argv is True and arglist is None, arguments are - # parsed from the command line (sys.argv) - (options, args) = parser.parse_args(arglist) - options.reporter = None - - if options.ensure_value('testsuite', False): - args.append(options.testsuite) - elif not options.ensure_value('doctest', False): - if parse_argv and not args: - if options.diff or any(os.path.exists(name) - for name in PROJECT_CONFIG): - args = ['.'] - else: - parser.error('input not specified') - options = read_config(options, args, arglist, parser) - options.reporter = parse_argv and options.quiet == 1 and FileReport - - options.filename = options.filename and options.filename.split(',') - options.exclude = normalize_paths(options.exclude) - options.select = options.select and options.select.split(',') - options.ignore = options.ignore and options.ignore.split(',') - - if options.diff: - options.reporter = DiffReport - stdin = stdin_get_value() - options.selected_lines = parse_udiff(stdin, options.filename, args[0]) - args = sorted(options.selected_lines) - - return options, args - - -def _main(): - """Parse options and run checks on Python source.""" - import signal - - # Handle "Broken pipe" gracefully - try: - signal.signal(signal.SIGPIPE, lambda signum, frame: sys.exit(1)) - except AttributeError: - pass # not supported on Windows - - pep8style = StyleGuide(parse_argv=True) - options = pep8style.options - if options.doctest or options.testsuite: - from testsuite.support import run_tests - report = run_tests(pep8style) - else: - report = pep8style.check_files() - if options.statistics: - report.print_statistics() - if options.benchmark: - report.print_benchmark() - if options.testsuite and not options.quiet: - report.print_results() - if report.total_errors: - if options.count: - sys.stderr.write(str(report.total_errors) + '\n') - sys.exit(1) - -if __name__ == '__main__': - _main() diff --git a/pymode/libs/pkg_resources/__init__.py b/pymode/libs/pkg_resources/__init__.py deleted file mode 100644 index 42ddcf7c..00000000 --- a/pymode/libs/pkg_resources/__init__.py +++ /dev/null @@ -1,3113 +0,0 @@ -""" -Package resource API --------------------- - -A resource is a logical file contained within a package, or a logical -subdirectory thereof. The package resource API expects resource names -to have their path parts separated with ``/``, *not* whatever the local -path separator is. Do not use os.path operations to manipulate resource -names being passed into the API. - -The package resource API is designed to work with normal filesystem packages, -.egg files, and unpacked .egg files. It can also work in a limited way with -.zip files and with custom PEP 302 loaders that support the ``get_data()`` -method. -""" - -from __future__ import absolute_import - -import sys -import os -import io -import time -import re -import types -import zipfile -import zipimport -import warnings -import stat -import functools -import pkgutil -import token -import symbol -import operator -import platform -import collections -import plistlib -import email.parser -import tempfile -import textwrap -from pkgutil import get_importer - -try: - import _imp -except ImportError: - # Python 3.2 compatibility - import imp as _imp - -PY3 = sys.version_info > (3,) -PY2 = not PY3 - -if PY3: - from urllib.parse import urlparse, urlunparse - -if PY2: - from urlparse import urlparse, urlunparse - -if PY3: - string_types = str, -else: - string_types = str, eval('unicode') - -iteritems = (lambda i: i.items()) if PY3 else lambda i: i.iteritems() - -# capture these to bypass sandboxing -from os import utime -try: - from os import mkdir, rename, unlink - WRITE_SUPPORT = True -except ImportError: - # no write support, probably under GAE - WRITE_SUPPORT = False - -from os import open as os_open -from os.path import isdir, split - -# Avoid try/except due to potential problems with delayed import mechanisms. -if sys.version_info >= (3, 3) and sys.implementation.name == "cpython": - import importlib.machinery as importlib_machinery -else: - importlib_machinery = None - -try: - import parser -except ImportError: - pass - -try: - import pkg_resources._vendor.packaging.version - import pkg_resources._vendor.packaging.specifiers - packaging = pkg_resources._vendor.packaging -except ImportError: - # fallback to naturally-installed version; allows system packagers to - # omit vendored packages. - import packaging.version - import packaging.specifiers - - -# declare some globals that will be defined later to -# satisfy the linters. -require = None -working_set = None - - -class PEP440Warning(RuntimeWarning): - """ - Used when there is an issue with a version or specifier not complying with - PEP 440. - """ - - -class _SetuptoolsVersionMixin(object): - - def __hash__(self): - return super(_SetuptoolsVersionMixin, self).__hash__() - - def __lt__(self, other): - if isinstance(other, tuple): - return tuple(self) < other - else: - return super(_SetuptoolsVersionMixin, self).__lt__(other) - - def __le__(self, other): - if isinstance(other, tuple): - return tuple(self) <= other - else: - return super(_SetuptoolsVersionMixin, self).__le__(other) - - def __eq__(self, other): - if isinstance(other, tuple): - return tuple(self) == other - else: - return super(_SetuptoolsVersionMixin, self).__eq__(other) - - def __ge__(self, other): - if isinstance(other, tuple): - return tuple(self) >= other - else: - return super(_SetuptoolsVersionMixin, self).__ge__(other) - - def __gt__(self, other): - if isinstance(other, tuple): - return tuple(self) > other - else: - return super(_SetuptoolsVersionMixin, self).__gt__(other) - - def __ne__(self, other): - if isinstance(other, tuple): - return tuple(self) != other - else: - return super(_SetuptoolsVersionMixin, self).__ne__(other) - - def __getitem__(self, key): - return tuple(self)[key] - - def __iter__(self): - component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) - replace = { - 'pre': 'c', - 'preview': 'c', - '-': 'final-', - 'rc': 'c', - 'dev': '@', - }.get - - def _parse_version_parts(s): - for part in component_re.split(s): - part = replace(part, part) - if not part or part == '.': - continue - if part[:1] in '0123456789': - # pad for numeric comparison - yield part.zfill(8) - else: - yield '*'+part - - # ensure that alpha/beta/candidate are before final - yield '*final' - - def old_parse_version(s): - parts = [] - for part in _parse_version_parts(s.lower()): - if part.startswith('*'): - # remove '-' before a prerelease tag - if part < '*final': - while parts and parts[-1] == '*final-': - parts.pop() - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == '00000000': - parts.pop() - parts.append(part) - return tuple(parts) - - # Warn for use of this function - warnings.warn( - "You have iterated over the result of " - "pkg_resources.parse_version. This is a legacy behavior which is " - "inconsistent with the new version class introduced in setuptools " - "8.0. In most cases, conversion to a tuple is unnecessary. For " - "comparison of versions, sort the Version instances directly. If " - "you have another use case requiring the tuple, please file a " - "bug with the setuptools project describing that need.", - RuntimeWarning, - stacklevel=1, - ) - - for part in old_parse_version(str(self)): - yield part - - -class SetuptoolsVersion(_SetuptoolsVersionMixin, packaging.version.Version): - pass - - -class SetuptoolsLegacyVersion(_SetuptoolsVersionMixin, - packaging.version.LegacyVersion): - pass - - -def parse_version(v): - try: - return SetuptoolsVersion(v) - except packaging.version.InvalidVersion: - return SetuptoolsLegacyVersion(v) - - -_state_vars = {} - -def _declare_state(vartype, **kw): - globals().update(kw) - _state_vars.update(dict.fromkeys(kw, vartype)) - -def __getstate__(): - state = {} - g = globals() - for k, v in _state_vars.items(): - state[k] = g['_sget_'+v](g[k]) - return state - -def __setstate__(state): - g = globals() - for k, v in state.items(): - g['_sset_'+_state_vars[k]](k, g[k], v) - return state - -def _sget_dict(val): - return val.copy() - -def _sset_dict(key, ob, state): - ob.clear() - ob.update(state) - -def _sget_object(val): - return val.__getstate__() - -def _sset_object(key, ob, state): - ob.__setstate__(state) - -_sget_none = _sset_none = lambda *args: None - - -def get_supported_platform(): - """Return this platform's maximum compatible version. - - distutils.util.get_platform() normally reports the minimum version - of Mac OS X that would be required to *use* extensions produced by - distutils. But what we want when checking compatibility is to know the - version of Mac OS X that we are *running*. To allow usage of packages that - explicitly require a newer version of Mac OS X, we must also know the - current version of the OS. - - If this condition occurs for any other platform with a version in its - platform strings, this function should be extended accordingly. - """ - plat = get_build_platform() - m = macosVersionString.match(plat) - if m is not None and sys.platform == "darwin": - try: - plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) - except ValueError: - # not Mac OS X - pass - return plat - -__all__ = [ - # Basic resource access and distribution/entry point discovery - 'require', 'run_script', 'get_provider', 'get_distribution', - 'load_entry_point', 'get_entry_map', 'get_entry_info', - 'iter_entry_points', - 'resource_string', 'resource_stream', 'resource_filename', - 'resource_listdir', 'resource_exists', 'resource_isdir', - - # Environmental control - 'declare_namespace', 'working_set', 'add_activation_listener', - 'find_distributions', 'set_extraction_path', 'cleanup_resources', - 'get_default_cache', - - # Primary implementation classes - 'Environment', 'WorkingSet', 'ResourceManager', - 'Distribution', 'Requirement', 'EntryPoint', - - # Exceptions - 'ResolutionError', 'VersionConflict', 'DistributionNotFound', - 'UnknownExtra', 'ExtractionError', - - # Warnings - 'PEP440Warning', - - # Parsing functions and string utilities - 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', - 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', - 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', - - # filesystem utilities - 'ensure_directory', 'normalize_path', - - # Distribution "precedence" constants - 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', - - # "Provider" interfaces, implementations, and registration/lookup APIs - 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', - 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', - 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', - 'register_finder', 'register_namespace_handler', 'register_loader_type', - 'fixup_namespace_packages', 'get_importer', - - # Deprecated/backward compatibility only - 'run_main', 'AvailableDistributions', -] - -class ResolutionError(Exception): - """Abstract base for dependency resolution errors""" - def __repr__(self): - return self.__class__.__name__+repr(self.args) - - -class VersionConflict(ResolutionError): - """ - An already-installed version conflicts with the requested version. - - Should be initialized with the installed Distribution and the requested - Requirement. - """ - - _template = "{self.dist} is installed but {self.req} is required" - - @property - def dist(self): - return self.args[0] - - @property - def req(self): - return self.args[1] - - def report(self): - return self._template.format(**locals()) - - def with_context(self, required_by): - """ - If required_by is non-empty, return a version of self that is a - ContextualVersionConflict. - """ - if not required_by: - return self - args = self.args + (required_by,) - return ContextualVersionConflict(*args) - - -class ContextualVersionConflict(VersionConflict): - """ - A VersionConflict that accepts a third parameter, the set of the - requirements that required the installed Distribution. - """ - - _template = VersionConflict._template + ' by {self.required_by}' - - @property - def required_by(self): - return self.args[2] - - -class DistributionNotFound(ResolutionError): - """A requested distribution was not found""" - - _template = ("The '{self.req}' distribution was not found " - "and is required by {self.requirers_str}") - - @property - def req(self): - return self.args[0] - - @property - def requirers(self): - return self.args[1] - - @property - def requirers_str(self): - if not self.requirers: - return 'the application' - return ', '.join(self.requirers) - - def report(self): - return self._template.format(**locals()) - - def __str__(self): - return self.report() - - -class UnknownExtra(ResolutionError): - """Distribution doesn't have an "extra feature" of the given name""" -_provider_factories = {} - -PY_MAJOR = sys.version[:3] -EGG_DIST = 3 -BINARY_DIST = 2 -SOURCE_DIST = 1 -CHECKOUT_DIST = 0 -DEVELOP_DIST = -1 - -def register_loader_type(loader_type, provider_factory): - """Register `provider_factory` to make providers for `loader_type` - - `loader_type` is the type or class of a PEP 302 ``module.__loader__``, - and `provider_factory` is a function that, passed a *module* object, - returns an ``IResourceProvider`` for that module. - """ - _provider_factories[loader_type] = provider_factory - -def get_provider(moduleOrReq): - """Return an IResourceProvider for the named module or requirement""" - if isinstance(moduleOrReq, Requirement): - return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] - try: - module = sys.modules[moduleOrReq] - except KeyError: - __import__(moduleOrReq) - module = sys.modules[moduleOrReq] - loader = getattr(module, '__loader__', None) - return _find_adapter(_provider_factories, loader)(module) - -def _macosx_vers(_cache=[]): - if not _cache: - version = platform.mac_ver()[0] - # fallback for MacPorts - if version == '': - plist = '/System/Library/CoreServices/SystemVersion.plist' - if os.path.exists(plist): - if hasattr(plistlib, 'readPlist'): - plist_content = plistlib.readPlist(plist) - if 'ProductVersion' in plist_content: - version = plist_content['ProductVersion'] - - _cache.append(version.split('.')) - return _cache[0] - -def _macosx_arch(machine): - return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) - -def get_build_platform(): - """Return this platform's string for platform-specific distributions - - XXX Currently this is the same as ``distutils.util.get_platform()``, but it - needs some hacks for Linux and Mac OS X. - """ - try: - # Python 2.7 or >=3.2 - from sysconfig import get_platform - except ImportError: - from distutils.util import get_platform - - plat = get_platform() - if sys.platform == "darwin" and not plat.startswith('macosx-'): - try: - version = _macosx_vers() - machine = os.uname()[4].replace(" ", "_") - return "macosx-%d.%d-%s" % (int(version[0]), int(version[1]), - _macosx_arch(machine)) - except ValueError: - # if someone is running a non-Mac darwin system, this will fall - # through to the default implementation - pass - return plat - -macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") -darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") -# XXX backward compat -get_platform = get_build_platform - - -def compatible_platforms(provided, required): - """Can code for the `provided` platform run on the `required` platform? - - Returns true if either platform is ``None``, or the platforms are equal. - - XXX Needs compatibility checks for Linux and other unixy OSes. - """ - if provided is None or required is None or provided==required: - # easy case - return True - - # Mac OS X special cases - reqMac = macosVersionString.match(required) - if reqMac: - provMac = macosVersionString.match(provided) - - # is this a Mac package? - if not provMac: - # this is backwards compatibility for packages built before - # setuptools 0.6. All packages built after this point will - # use the new macosx designation. - provDarwin = darwinVersionString.match(provided) - if provDarwin: - dversion = int(provDarwin.group(1)) - macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) - if dversion == 7 and macosversion >= "10.3" or \ - dversion == 8 and macosversion >= "10.4": - return True - # egg isn't macosx or legacy darwin - return False - - # are they the same major version and machine type? - if provMac.group(1) != reqMac.group(1) or \ - provMac.group(3) != reqMac.group(3): - return False - - # is the required OS major update >= the provided one? - if int(provMac.group(2)) > int(reqMac.group(2)): - return False - - return True - - # XXX Linux and other platforms' special cases should go here - return False - - -def run_script(dist_spec, script_name): - """Locate distribution `dist_spec` and run its `script_name` script""" - ns = sys._getframe(1).f_globals - name = ns['__name__'] - ns.clear() - ns['__name__'] = name - require(dist_spec)[0].run_script(script_name, ns) - -# backward compatibility -run_main = run_script - -def get_distribution(dist): - """Return a current distribution object for a Requirement or string""" - if isinstance(dist, string_types): - dist = Requirement.parse(dist) - if isinstance(dist, Requirement): - dist = get_provider(dist) - if not isinstance(dist, Distribution): - raise TypeError("Expected string, Requirement, or Distribution", dist) - return dist - -def load_entry_point(dist, group, name): - """Return `name` entry point of `group` for `dist` or raise ImportError""" - return get_distribution(dist).load_entry_point(group, name) - -def get_entry_map(dist, group=None): - """Return the entry point map for `group`, or the full entry map""" - return get_distribution(dist).get_entry_map(group) - -def get_entry_info(dist, group, name): - """Return the EntryPoint object for `group`+`name`, or ``None``""" - return get_distribution(dist).get_entry_info(group, name) - - -class IMetadataProvider: - - def has_metadata(name): - """Does the package's distribution contain the named metadata?""" - - def get_metadata(name): - """The named metadata resource as a string""" - - def get_metadata_lines(name): - """Yield named metadata resource as list of non-blank non-comment lines - - Leading and trailing whitespace is stripped from each line, and lines - with ``#`` as the first non-blank character are omitted.""" - - def metadata_isdir(name): - """Is the named metadata a directory? (like ``os.path.isdir()``)""" - - def metadata_listdir(name): - """List of metadata names in the directory (like ``os.listdir()``)""" - - def run_script(script_name, namespace): - """Execute the named script in the supplied namespace dictionary""" - - -class IResourceProvider(IMetadataProvider): - """An object that provides access to package resources""" - - def get_resource_filename(manager, resource_name): - """Return a true filesystem path for `resource_name` - - `manager` must be an ``IResourceManager``""" - - def get_resource_stream(manager, resource_name): - """Return a readable file-like object for `resource_name` - - `manager` must be an ``IResourceManager``""" - - def get_resource_string(manager, resource_name): - """Return a string containing the contents of `resource_name` - - `manager` must be an ``IResourceManager``""" - - def has_resource(resource_name): - """Does the package contain the named resource?""" - - def resource_isdir(resource_name): - """Is the named resource a directory? (like ``os.path.isdir()``)""" - - def resource_listdir(resource_name): - """List of resource names in the directory (like ``os.listdir()``)""" - - -class WorkingSet(object): - """A collection of active distributions on sys.path (or a similar list)""" - - def __init__(self, entries=None): - """Create working set from list of path entries (default=sys.path)""" - self.entries = [] - self.entry_keys = {} - self.by_key = {} - self.callbacks = [] - - if entries is None: - entries = sys.path - - for entry in entries: - self.add_entry(entry) - - @classmethod - def _build_master(cls): - """ - Prepare the master working set. - """ - ws = cls() - try: - from __main__ import __requires__ - except ImportError: - # The main program does not list any requirements - return ws - - # ensure the requirements are met - try: - ws.require(__requires__) - except VersionConflict: - return cls._build_from_requirements(__requires__) - - return ws - - @classmethod - def _build_from_requirements(cls, req_spec): - """ - Build a working set from a requirement spec. Rewrites sys.path. - """ - # try it without defaults already on sys.path - # by starting with an empty path - ws = cls([]) - reqs = parse_requirements(req_spec) - dists = ws.resolve(reqs, Environment()) - for dist in dists: - ws.add(dist) - - # add any missing entries from sys.path - for entry in sys.path: - if entry not in ws.entries: - ws.add_entry(entry) - - # then copy back to sys.path - sys.path[:] = ws.entries - return ws - - def add_entry(self, entry): - """Add a path item to ``.entries``, finding any distributions on it - - ``find_distributions(entry, True)`` is used to find distributions - corresponding to the path entry, and they are added. `entry` is - always appended to ``.entries``, even if it is already present. - (This is because ``sys.path`` can contain the same value more than - once, and the ``.entries`` of the ``sys.path`` WorkingSet should always - equal ``sys.path``.) - """ - self.entry_keys.setdefault(entry, []) - self.entries.append(entry) - for dist in find_distributions(entry, True): - self.add(dist, entry, False) - - def __contains__(self, dist): - """True if `dist` is the active distribution for its project""" - return self.by_key.get(dist.key) == dist - - def find(self, req): - """Find a distribution matching requirement `req` - - If there is an active distribution for the requested project, this - returns it as long as it meets the version requirement specified by - `req`. But, if there is an active distribution for the project and it - does *not* meet the `req` requirement, ``VersionConflict`` is raised. - If there is no active distribution for the requested project, ``None`` - is returned. - """ - dist = self.by_key.get(req.key) - if dist is not None and dist not in req: - # XXX add more info - raise VersionConflict(dist, req) - return dist - - def iter_entry_points(self, group, name=None): - """Yield entry point objects from `group` matching `name` - - If `name` is None, yields all entry points in `group` from all - distributions in the working set, otherwise only ones matching - both `group` and `name` are yielded (in distribution order). - """ - for dist in self: - entries = dist.get_entry_map(group) - if name is None: - for ep in entries.values(): - yield ep - elif name in entries: - yield entries[name] - - def run_script(self, requires, script_name): - """Locate distribution for `requires` and run `script_name` script""" - ns = sys._getframe(1).f_globals - name = ns['__name__'] - ns.clear() - ns['__name__'] = name - self.require(requires)[0].run_script(script_name, ns) - - def __iter__(self): - """Yield distributions for non-duplicate projects in the working set - - The yield order is the order in which the items' path entries were - added to the working set. - """ - seen = {} - for item in self.entries: - if item not in self.entry_keys: - # workaround a cache issue - continue - - for key in self.entry_keys[item]: - if key not in seen: - seen[key]=1 - yield self.by_key[key] - - def add(self, dist, entry=None, insert=True, replace=False): - """Add `dist` to working set, associated with `entry` - - If `entry` is unspecified, it defaults to the ``.location`` of `dist`. - On exit from this routine, `entry` is added to the end of the working - set's ``.entries`` (if it wasn't already present). - - `dist` is only added to the working set if it's for a project that - doesn't already have a distribution in the set, unless `replace=True`. - If it's added, any callbacks registered with the ``subscribe()`` method - will be called. - """ - if insert: - dist.insert_on(self.entries, entry) - - if entry is None: - entry = dist.location - keys = self.entry_keys.setdefault(entry,[]) - keys2 = self.entry_keys.setdefault(dist.location,[]) - if not replace and dist.key in self.by_key: - # ignore hidden distros - return - - self.by_key[dist.key] = dist - if dist.key not in keys: - keys.append(dist.key) - if dist.key not in keys2: - keys2.append(dist.key) - self._added_new(dist) - - def resolve(self, requirements, env=None, installer=None, - replace_conflicting=False): - """List all distributions needed to (recursively) meet `requirements` - - `requirements` must be a sequence of ``Requirement`` objects. `env`, - if supplied, should be an ``Environment`` instance. If - not supplied, it defaults to all distributions available within any - entry or distribution in the working set. `installer`, if supplied, - will be invoked with each requirement that cannot be met by an - already-installed distribution; it should return a ``Distribution`` or - ``None``. - - Unless `replace_conflicting=True`, raises a VersionConflict exception if - any requirements are found on the path that have the correct name but - the wrong version. Otherwise, if an `installer` is supplied it will be - invoked to obtain the correct version of the requirement and activate - it. - """ - - # set up the stack - requirements = list(requirements)[::-1] - # set of processed requirements - processed = {} - # key -> dist - best = {} - to_activate = [] - - # Mapping of requirement to set of distributions that required it; - # useful for reporting info about conflicts. - required_by = collections.defaultdict(set) - - while requirements: - # process dependencies breadth-first - req = requirements.pop(0) - if req in processed: - # Ignore cyclic or redundant dependencies - continue - dist = best.get(req.key) - if dist is None: - # Find the best distribution and add it to the map - dist = self.by_key.get(req.key) - if dist is None or (dist not in req and replace_conflicting): - ws = self - if env is None: - if dist is None: - env = Environment(self.entries) - else: - # Use an empty environment and workingset to avoid - # any further conflicts with the conflicting - # distribution - env = Environment([]) - ws = WorkingSet([]) - dist = best[req.key] = env.best_match(req, ws, installer) - if dist is None: - requirers = required_by.get(req, None) - raise DistributionNotFound(req, requirers) - to_activate.append(dist) - if dist not in req: - # Oops, the "best" so far conflicts with a dependency - dependent_req = required_by[req] - raise VersionConflict(dist, req).with_context(dependent_req) - - # push the new requirements onto the stack - new_requirements = dist.requires(req.extras)[::-1] - requirements.extend(new_requirements) - - # Register the new requirements needed by req - for new_requirement in new_requirements: - required_by[new_requirement].add(req.project_name) - - processed[req] = True - - # return list of distros to activate - return to_activate - - def find_plugins(self, plugin_env, full_env=None, installer=None, - fallback=True): - """Find all activatable distributions in `plugin_env` - - Example usage:: - - distributions, errors = working_set.find_plugins( - Environment(plugin_dirlist) - ) - # add plugins+libs to sys.path - map(working_set.add, distributions) - # display errors - print('Could not load', errors) - - The `plugin_env` should be an ``Environment`` instance that contains - only distributions that are in the project's "plugin directory" or - directories. The `full_env`, if supplied, should be an ``Environment`` - contains all currently-available distributions. If `full_env` is not - supplied, one is created automatically from the ``WorkingSet`` this - method is called on, which will typically mean that every directory on - ``sys.path`` will be scanned for distributions. - - `installer` is a standard installer callback as used by the - ``resolve()`` method. The `fallback` flag indicates whether we should - attempt to resolve older versions of a plugin if the newest version - cannot be resolved. - - This method returns a 2-tuple: (`distributions`, `error_info`), where - `distributions` is a list of the distributions found in `plugin_env` - that were loadable, along with any other distributions that are needed - to resolve their dependencies. `error_info` is a dictionary mapping - unloadable plugin distributions to an exception instance describing the - error that occurred. Usually this will be a ``DistributionNotFound`` or - ``VersionConflict`` instance. - """ - - plugin_projects = list(plugin_env) - # scan project names in alphabetic order - plugin_projects.sort() - - error_info = {} - distributions = {} - - if full_env is None: - env = Environment(self.entries) - env += plugin_env - else: - env = full_env + plugin_env - - shadow_set = self.__class__([]) - # put all our entries in shadow_set - list(map(shadow_set.add, self)) - - for project_name in plugin_projects: - - for dist in plugin_env[project_name]: - - req = [dist.as_requirement()] - - try: - resolvees = shadow_set.resolve(req, env, installer) - - except ResolutionError as v: - # save error info - error_info[dist] = v - if fallback: - # try the next older version of project - continue - else: - # give up on this project, keep going - break - - else: - list(map(shadow_set.add, resolvees)) - distributions.update(dict.fromkeys(resolvees)) - - # success, no need to try any more versions of this project - break - - distributions = list(distributions) - distributions.sort() - - return distributions, error_info - - def require(self, *requirements): - """Ensure that distributions matching `requirements` are activated - - `requirements` must be a string or a (possibly-nested) sequence - thereof, specifying the distributions and versions required. The - return value is a sequence of the distributions that needed to be - activated to fulfill the requirements; all relevant distributions are - included, even if they were already activated in this working set. - """ - needed = self.resolve(parse_requirements(requirements)) - - for dist in needed: - self.add(dist) - - return needed - - def subscribe(self, callback): - """Invoke `callback` for all distributions (including existing ones)""" - if callback in self.callbacks: - return - self.callbacks.append(callback) - for dist in self: - callback(dist) - - def _added_new(self, dist): - for callback in self.callbacks: - callback(dist) - - def __getstate__(self): - return ( - self.entries[:], self.entry_keys.copy(), self.by_key.copy(), - self.callbacks[:] - ) - - def __setstate__(self, e_k_b_c): - entries, keys, by_key, callbacks = e_k_b_c - self.entries = entries[:] - self.entry_keys = keys.copy() - self.by_key = by_key.copy() - self.callbacks = callbacks[:] - - -class Environment(object): - """Searchable snapshot of distributions on a search path""" - - def __init__(self, search_path=None, platform=get_supported_platform(), - python=PY_MAJOR): - """Snapshot distributions available on a search path - - Any distributions found on `search_path` are added to the environment. - `search_path` should be a sequence of ``sys.path`` items. If not - supplied, ``sys.path`` is used. - - `platform` is an optional string specifying the name of the platform - that platform-specific distributions must be compatible with. If - unspecified, it defaults to the current platform. `python` is an - optional string naming the desired version of Python (e.g. ``'3.3'``); - it defaults to the current version. - - You may explicitly set `platform` (and/or `python`) to ``None`` if you - wish to map *all* distributions, not just those compatible with the - running platform or Python version. - """ - self._distmap = {} - self.platform = platform - self.python = python - self.scan(search_path) - - def can_add(self, dist): - """Is distribution `dist` acceptable for this environment? - - The distribution must match the platform and python version - requirements specified when this environment was created, or False - is returned. - """ - return (self.python is None or dist.py_version is None - or dist.py_version==self.python) \ - and compatible_platforms(dist.platform, self.platform) - - def remove(self, dist): - """Remove `dist` from the environment""" - self._distmap[dist.key].remove(dist) - - def scan(self, search_path=None): - """Scan `search_path` for distributions usable in this environment - - Any distributions found are added to the environment. - `search_path` should be a sequence of ``sys.path`` items. If not - supplied, ``sys.path`` is used. Only distributions conforming to - the platform/python version defined at initialization are added. - """ - if search_path is None: - search_path = sys.path - - for item in search_path: - for dist in find_distributions(item): - self.add(dist) - - def __getitem__(self, project_name): - """Return a newest-to-oldest list of distributions for `project_name` - - Uses case-insensitive `project_name` comparison, assuming all the - project's distributions use their project's name converted to all - lowercase as their key. - - """ - distribution_key = project_name.lower() - return self._distmap.get(distribution_key, []) - - def add(self, dist): - """Add `dist` if we ``can_add()`` it and it has not already been added - """ - if self.can_add(dist) and dist.has_version(): - dists = self._distmap.setdefault(dist.key, []) - if dist not in dists: - dists.append(dist) - dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) - - def best_match(self, req, working_set, installer=None): - """Find distribution best matching `req` and usable on `working_set` - - This calls the ``find(req)`` method of the `working_set` to see if a - suitable distribution is already active. (This may raise - ``VersionConflict`` if an unsuitable version of the project is already - active in the specified `working_set`.) If a suitable distribution - isn't active, this method returns the newest distribution in the - environment that meets the ``Requirement`` in `req`. If no suitable - distribution is found, and `installer` is supplied, then the result of - calling the environment's ``obtain(req, installer)`` method will be - returned. - """ - dist = working_set.find(req) - if dist is not None: - return dist - for dist in self[req.key]: - if dist in req: - return dist - # try to download/install - return self.obtain(req, installer) - - def obtain(self, requirement, installer=None): - """Obtain a distribution matching `requirement` (e.g. via download) - - Obtain a distro that matches requirement (e.g. via download). In the - base ``Environment`` class, this routine just returns - ``installer(requirement)``, unless `installer` is None, in which case - None is returned instead. This method is a hook that allows subclasses - to attempt other ways of obtaining a distribution before falling back - to the `installer` argument.""" - if installer is not None: - return installer(requirement) - - def __iter__(self): - """Yield the unique project names of the available distributions""" - for key in self._distmap.keys(): - if self[key]: - yield key - - def __iadd__(self, other): - """In-place addition of a distribution or environment""" - if isinstance(other, Distribution): - self.add(other) - elif isinstance(other, Environment): - for project in other: - for dist in other[project]: - self.add(dist) - else: - raise TypeError("Can't add %r to environment" % (other,)) - return self - - def __add__(self, other): - """Add an environment or distribution to an environment""" - new = self.__class__([], platform=None, python=None) - for env in self, other: - new += env - return new - - -# XXX backward compatibility -AvailableDistributions = Environment - - -class ExtractionError(RuntimeError): - """An error occurred extracting a resource - - The following attributes are available from instances of this exception: - - manager - The resource manager that raised this exception - - cache_path - The base directory for resource extraction - - original_error - The exception instance that caused extraction to fail - """ - - -class ResourceManager: - """Manage resource extraction and packages""" - extraction_path = None - - def __init__(self): - self.cached_files = {} - - def resource_exists(self, package_or_requirement, resource_name): - """Does the named resource exist?""" - return get_provider(package_or_requirement).has_resource(resource_name) - - def resource_isdir(self, package_or_requirement, resource_name): - """Is the named resource an existing directory?""" - return get_provider(package_or_requirement).resource_isdir( - resource_name - ) - - def resource_filename(self, package_or_requirement, resource_name): - """Return a true filesystem path for specified resource""" - return get_provider(package_or_requirement).get_resource_filename( - self, resource_name - ) - - def resource_stream(self, package_or_requirement, resource_name): - """Return a readable file-like object for specified resource""" - return get_provider(package_or_requirement).get_resource_stream( - self, resource_name - ) - - def resource_string(self, package_or_requirement, resource_name): - """Return specified resource as a string""" - return get_provider(package_or_requirement).get_resource_string( - self, resource_name - ) - - def resource_listdir(self, package_or_requirement, resource_name): - """List the contents of the named resource directory""" - return get_provider(package_or_requirement).resource_listdir( - resource_name - ) - - def extraction_error(self): - """Give an error message for problems extracting file(s)""" - - old_exc = sys.exc_info()[1] - cache_path = self.extraction_path or get_default_cache() - - err = ExtractionError("""Can't extract file(s) to egg cache - -The following error occurred while trying to extract file(s) to the Python egg -cache: - - %s - -The Python egg cache directory is currently set to: - - %s - -Perhaps your account does not have write access to this directory? You can -change the cache directory by setting the PYTHON_EGG_CACHE environment -variable to point to an accessible directory. -""" % (old_exc, cache_path) - ) - err.manager = self - err.cache_path = cache_path - err.original_error = old_exc - raise err - - def get_cache_path(self, archive_name, names=()): - """Return absolute location in cache for `archive_name` and `names` - - The parent directory of the resulting path will be created if it does - not already exist. `archive_name` should be the base filename of the - enclosing egg (which may not be the name of the enclosing zipfile!), - including its ".egg" extension. `names`, if provided, should be a - sequence of path name parts "under" the egg's extraction location. - - This method should only be called by resource providers that need to - obtain an extraction location, and only for names they intend to - extract, as it tracks the generated names for possible cleanup later. - """ - extract_path = self.extraction_path or get_default_cache() - target_path = os.path.join(extract_path, archive_name+'-tmp', *names) - try: - _bypass_ensure_directory(target_path) - except: - self.extraction_error() - - self._warn_unsafe_extraction_path(extract_path) - - self.cached_files[target_path] = 1 - return target_path - - @staticmethod - def _warn_unsafe_extraction_path(path): - """ - If the default extraction path is overridden and set to an insecure - location, such as /tmp, it opens up an opportunity for an attacker to - replace an extracted file with an unauthorized payload. Warn the user - if a known insecure location is used. - - See Distribute #375 for more details. - """ - if os.name == 'nt' and not path.startswith(os.environ['windir']): - # On Windows, permissions are generally restrictive by default - # and temp directories are not writable by other users, so - # bypass the warning. - return - mode = os.stat(path).st_mode - if mode & stat.S_IWOTH or mode & stat.S_IWGRP: - msg = ("%s is writable by group/others and vulnerable to attack " - "when " - "used with get_resource_filename. Consider a more secure " - "location (set with .set_extraction_path or the " - "PYTHON_EGG_CACHE environment variable)." % path) - warnings.warn(msg, UserWarning) - - def postprocess(self, tempname, filename): - """Perform any platform-specific postprocessing of `tempname` - - This is where Mac header rewrites should be done; other platforms don't - have anything special they should do. - - Resource providers should call this method ONLY after successfully - extracting a compressed resource. They must NOT call it on resources - that are already in the filesystem. - - `tempname` is the current (temporary) name of the file, and `filename` - is the name it will be renamed to by the caller after this routine - returns. - """ - - if os.name == 'posix': - # Make the resource executable - mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 - os.chmod(tempname, mode) - - def set_extraction_path(self, path): - """Set the base path where resources will be extracted to, if needed. - - If you do not call this routine before any extractions take place, the - path defaults to the return value of ``get_default_cache()``. (Which - is based on the ``PYTHON_EGG_CACHE`` environment variable, with various - platform-specific fallbacks. See that routine's documentation for more - details.) - - Resources are extracted to subdirectories of this path based upon - information given by the ``IResourceProvider``. You may set this to a - temporary directory, but then you must call ``cleanup_resources()`` to - delete the extracted files when done. There is no guarantee that - ``cleanup_resources()`` will be able to remove all extracted files. - - (Note: you may not change the extraction path for a given resource - manager once resources have been extracted, unless you first call - ``cleanup_resources()``.) - """ - if self.cached_files: - raise ValueError( - "Can't change extraction path, files already extracted" - ) - - self.extraction_path = path - - def cleanup_resources(self, force=False): - """ - Delete all extracted resource files and directories, returning a list - of the file and directory names that could not be successfully removed. - This function does not have any concurrency protection, so it should - generally only be called when the extraction path is a temporary - directory exclusive to a single process. This method is not - automatically called; you must call it explicitly or register it as an - ``atexit`` function if you wish to ensure cleanup of a temporary - directory used for extractions. - """ - # XXX - -def get_default_cache(): - """Determine the default cache location - - This returns the ``PYTHON_EGG_CACHE`` environment variable, if set. - Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of the - "Application Data" directory. On all other systems, it's "~/.python-eggs". - """ - try: - return os.environ['PYTHON_EGG_CACHE'] - except KeyError: - pass - - if os.name!='nt': - return os.path.expanduser('~/.python-eggs') - - # XXX this may be locale-specific! - app_data = 'Application Data' - app_homes = [ - # best option, should be locale-safe - (('APPDATA',), None), - (('USERPROFILE',), app_data), - (('HOMEDRIVE','HOMEPATH'), app_data), - (('HOMEPATH',), app_data), - (('HOME',), None), - # 95/98/ME - (('WINDIR',), app_data), - ] - - for keys, subdir in app_homes: - dirname = '' - for key in keys: - if key in os.environ: - dirname = os.path.join(dirname, os.environ[key]) - else: - break - else: - if subdir: - dirname = os.path.join(dirname, subdir) - return os.path.join(dirname, 'Python-Eggs') - else: - raise RuntimeError( - "Please set the PYTHON_EGG_CACHE enviroment variable" - ) - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """ - Convert an arbitrary string to a standard version string - """ - try: - # normalize the version - return str(packaging.version.Version(version)) - except packaging.version.InvalidVersion: - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def safe_extra(extra): - """Convert an arbitrary string to a standard 'extra' name - - Any runs of non-alphanumeric characters are replaced with a single '_', - and the result is always lowercased. - """ - return re.sub('[^A-Za-z0-9.]+', '_', extra).lower() - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') - - -class MarkerEvaluation(object): - values = { - 'os_name': lambda: os.name, - 'sys_platform': lambda: sys.platform, - 'python_full_version': platform.python_version, - 'python_version': lambda: platform.python_version()[:3], - 'platform_version': platform.version, - 'platform_machine': platform.machine, - 'python_implementation': platform.python_implementation, - } - - @classmethod - def is_invalid_marker(cls, text): - """ - Validate text as a PEP 426 environment marker; return an exception - if invalid or False otherwise. - """ - try: - cls.evaluate_marker(text) - except SyntaxError as e: - return cls.normalize_exception(e) - return False - - @staticmethod - def normalize_exception(exc): - """ - Given a SyntaxError from a marker evaluation, normalize the error - message: - - Remove indications of filename and line number. - - Replace platform-specific error messages with standard error - messages. - """ - subs = { - 'unexpected EOF while parsing': 'invalid syntax', - 'parenthesis is never closed': 'invalid syntax', - } - exc.filename = None - exc.lineno = None - exc.msg = subs.get(exc.msg, exc.msg) - return exc - - @classmethod - def and_test(cls, nodelist): - # MUST NOT short-circuit evaluation, or invalid syntax can be skipped! - items = [ - cls.interpret(nodelist[i]) - for i in range(1, len(nodelist), 2) - ] - return functools.reduce(operator.and_, items) - - @classmethod - def test(cls, nodelist): - # MUST NOT short-circuit evaluation, or invalid syntax can be skipped! - items = [ - cls.interpret(nodelist[i]) - for i in range(1, len(nodelist), 2) - ] - return functools.reduce(operator.or_, items) - - @classmethod - def atom(cls, nodelist): - t = nodelist[1][0] - if t == token.LPAR: - if nodelist[2][0] == token.RPAR: - raise SyntaxError("Empty parentheses") - return cls.interpret(nodelist[2]) - msg = "Language feature not supported in environment markers" - raise SyntaxError(msg) - - @classmethod - def comparison(cls, nodelist): - if len(nodelist) > 4: - msg = "Chained comparison not allowed in environment markers" - raise SyntaxError(msg) - comp = nodelist[2][1] - cop = comp[1] - if comp[0] == token.NAME: - if len(nodelist[2]) == 3: - if cop == 'not': - cop = 'not in' - else: - cop = 'is not' - try: - cop = cls.get_op(cop) - except KeyError: - msg = repr(cop) + " operator not allowed in environment markers" - raise SyntaxError(msg) - return cop(cls.evaluate(nodelist[1]), cls.evaluate(nodelist[3])) - - @classmethod - def get_op(cls, op): - ops = { - symbol.test: cls.test, - symbol.and_test: cls.and_test, - symbol.atom: cls.atom, - symbol.comparison: cls.comparison, - 'not in': lambda x, y: x not in y, - 'in': lambda x, y: x in y, - '==': operator.eq, - '!=': operator.ne, - '<': operator.lt, - '>': operator.gt, - '<=': operator.le, - '>=': operator.ge, - } - if hasattr(symbol, 'or_test'): - ops[symbol.or_test] = cls.test - return ops[op] - - @classmethod - def evaluate_marker(cls, text, extra=None): - """ - Evaluate a PEP 426 environment marker on CPython 2.4+. - Return a boolean indicating the marker result in this environment. - Raise SyntaxError if marker is invalid. - - This implementation uses the 'parser' module, which is not implemented - on - Jython and has been superseded by the 'ast' module in Python 2.6 and - later. - """ - return cls.interpret(parser.expr(text).totuple(1)[1]) - - @classmethod - def _markerlib_evaluate(cls, text): - """ - Evaluate a PEP 426 environment marker using markerlib. - Return a boolean indicating the marker result in this environment. - Raise SyntaxError if marker is invalid. - """ - import _markerlib - # markerlib implements Metadata 1.2 (PEP 345) environment markers. - # Translate the variables to Metadata 2.0 (PEP 426). - env = _markerlib.default_environment() - for key in env.keys(): - new_key = key.replace('.', '_') - env[new_key] = env.pop(key) - try: - result = _markerlib.interpret(text, env) - except NameError as e: - raise SyntaxError(e.args[0]) - return result - - if 'parser' not in globals(): - # Fall back to less-complete _markerlib implementation if 'parser' module - # is not available. - evaluate_marker = _markerlib_evaluate - - @classmethod - def interpret(cls, nodelist): - while len(nodelist)==2: nodelist = nodelist[1] - try: - op = cls.get_op(nodelist[0]) - except KeyError: - raise SyntaxError("Comparison or logical expression expected") - return op(nodelist) - - @classmethod - def evaluate(cls, nodelist): - while len(nodelist)==2: nodelist = nodelist[1] - kind = nodelist[0] - name = nodelist[1] - if kind==token.NAME: - try: - op = cls.values[name] - except KeyError: - raise SyntaxError("Unknown name %r" % name) - return op() - if kind==token.STRING: - s = nodelist[1] - if not cls._safe_string(s): - raise SyntaxError( - "Only plain strings allowed in environment markers") - return s[1:-1] - msg = "Language feature not supported in environment markers" - raise SyntaxError(msg) - - @staticmethod - def _safe_string(cand): - return ( - cand[:1] in "'\"" and - not cand.startswith('"""') and - not cand.startswith("'''") and - '\\' not in cand - ) - -invalid_marker = MarkerEvaluation.is_invalid_marker -evaluate_marker = MarkerEvaluation.evaluate_marker - -class NullProvider: - """Try to implement resources and metadata for arbitrary PEP 302 loaders""" - - egg_name = None - egg_info = None - loader = None - - def __init__(self, module): - self.loader = getattr(module, '__loader__', None) - self.module_path = os.path.dirname(getattr(module, '__file__', '')) - - def get_resource_filename(self, manager, resource_name): - return self._fn(self.module_path, resource_name) - - def get_resource_stream(self, manager, resource_name): - return io.BytesIO(self.get_resource_string(manager, resource_name)) - - def get_resource_string(self, manager, resource_name): - return self._get(self._fn(self.module_path, resource_name)) - - def has_resource(self, resource_name): - return self._has(self._fn(self.module_path, resource_name)) - - def has_metadata(self, name): - return self.egg_info and self._has(self._fn(self.egg_info, name)) - - if sys.version_info <= (3,): - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info, name)) - else: - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info, name)).decode("utf-8") - - def get_metadata_lines(self, name): - return yield_lines(self.get_metadata(name)) - - def resource_isdir(self, resource_name): - return self._isdir(self._fn(self.module_path, resource_name)) - - def metadata_isdir(self, name): - return self.egg_info and self._isdir(self._fn(self.egg_info, name)) - - def resource_listdir(self, resource_name): - return self._listdir(self._fn(self.module_path, resource_name)) - - def metadata_listdir(self, name): - if self.egg_info: - return self._listdir(self._fn(self.egg_info, name)) - return [] - - def run_script(self, script_name, namespace): - script = 'scripts/'+script_name - if not self.has_metadata(script): - raise ResolutionError("No script named %r" % script_name) - script_text = self.get_metadata(script).replace('\r\n', '\n') - script_text = script_text.replace('\r', '\n') - script_filename = self._fn(self.egg_info, script) - namespace['__file__'] = script_filename - if os.path.exists(script_filename): - source = open(script_filename).read() - code = compile(source, script_filename, 'exec') - exec(code, namespace, namespace) - else: - from linecache import cache - cache[script_filename] = ( - len(script_text), 0, script_text.split('\n'), script_filename - ) - script_code = compile(script_text, script_filename,'exec') - exec(script_code, namespace, namespace) - - def _has(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _isdir(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _listdir(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _fn(self, base, resource_name): - if resource_name: - return os.path.join(base, *resource_name.split('/')) - return base - - def _get(self, path): - if hasattr(self.loader, 'get_data'): - return self.loader.get_data(path) - raise NotImplementedError( - "Can't perform this operation for loaders without 'get_data()'" - ) - -register_loader_type(object, NullProvider) - - -class EggProvider(NullProvider): - """Provider based on a virtual filesystem""" - - def __init__(self, module): - NullProvider.__init__(self, module) - self._setup_prefix() - - def _setup_prefix(self): - # we assume here that our metadata may be nested inside a "basket" - # of multiple eggs; that's why we use module_path instead of .archive - path = self.module_path - old = None - while path!=old: - if path.lower().endswith('.egg'): - self.egg_name = os.path.basename(path) - self.egg_info = os.path.join(path, 'EGG-INFO') - self.egg_root = path - break - old = path - path, base = os.path.split(path) - -class DefaultProvider(EggProvider): - """Provides access to package resources in the filesystem""" - - def _has(self, path): - return os.path.exists(path) - - def _isdir(self, path): - return os.path.isdir(path) - - def _listdir(self, path): - return os.listdir(path) - - def get_resource_stream(self, manager, resource_name): - return open(self._fn(self.module_path, resource_name), 'rb') - - def _get(self, path): - with open(path, 'rb') as stream: - return stream.read() - -register_loader_type(type(None), DefaultProvider) - -if importlib_machinery is not None: - register_loader_type(importlib_machinery.SourceFileLoader, DefaultProvider) - - -class EmptyProvider(NullProvider): - """Provider that returns nothing for all requests""" - - _isdir = _has = lambda self, path: False - _get = lambda self, path: '' - _listdir = lambda self, path: [] - module_path = None - - def __init__(self): - pass - -empty_provider = EmptyProvider() - - -class ZipManifests(dict): - """ - zip manifest builder - """ - - @classmethod - def build(cls, path): - """ - Build a dictionary similar to the zipimport directory - caches, except instead of tuples, store ZipInfo objects. - - Use a platform-specific path separator (os.sep) for the path keys - for compatibility with pypy on Windows. - """ - with ContextualZipFile(path) as zfile: - items = ( - ( - name.replace('/', os.sep), - zfile.getinfo(name), - ) - for name in zfile.namelist() - ) - return dict(items) - - load = build - - -class MemoizedZipManifests(ZipManifests): - """ - Memoized zipfile manifests. - """ - manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') - - def load(self, path): - """ - Load a manifest at path or return a suitable manifest already loaded. - """ - path = os.path.normpath(path) - mtime = os.stat(path).st_mtime - - if path not in self or self[path].mtime != mtime: - manifest = self.build(path) - self[path] = self.manifest_mod(manifest, mtime) - - return self[path].manifest - - -class ContextualZipFile(zipfile.ZipFile): - """ - Supplement ZipFile class to support context manager for Python 2.6 - """ - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.close() - - def __new__(cls, *args, **kwargs): - """ - Construct a ZipFile or ContextualZipFile as appropriate - """ - if hasattr(zipfile.ZipFile, '__exit__'): - return zipfile.ZipFile(*args, **kwargs) - return super(ContextualZipFile, cls).__new__(cls) - - -class ZipProvider(EggProvider): - """Resource support for zips and eggs""" - - eagers = None - _zip_manifests = MemoizedZipManifests() - - def __init__(self, module): - EggProvider.__init__(self, module) - self.zip_pre = self.loader.archive+os.sep - - def _zipinfo_name(self, fspath): - # Convert a virtual filename (full path to file) into a zipfile subpath - # usable with the zipimport directory cache for our target archive - if fspath.startswith(self.zip_pre): - return fspath[len(self.zip_pre):] - raise AssertionError( - "%s is not a subpath of %s" % (fspath, self.zip_pre) - ) - - def _parts(self, zip_path): - # Convert a zipfile subpath into an egg-relative path part list. - # pseudo-fs path - fspath = self.zip_pre+zip_path - if fspath.startswith(self.egg_root+os.sep): - return fspath[len(self.egg_root)+1:].split(os.sep) - raise AssertionError( - "%s is not a subpath of %s" % (fspath, self.egg_root) - ) - - @property - def zipinfo(self): - return self._zip_manifests.load(self.loader.archive) - - def get_resource_filename(self, manager, resource_name): - if not self.egg_name: - raise NotImplementedError( - "resource_filename() only supported for .egg, not .zip" - ) - # no need to lock for extraction, since we use temp names - zip_path = self._resource_to_zip(resource_name) - eagers = self._get_eager_resources() - if '/'.join(self._parts(zip_path)) in eagers: - for name in eagers: - self._extract_resource(manager, self._eager_to_zip(name)) - return self._extract_resource(manager, zip_path) - - @staticmethod - def _get_date_and_size(zip_stat): - size = zip_stat.file_size - # ymdhms+wday, yday, dst - date_time = zip_stat.date_time + (0, 0, -1) - # 1980 offset already done - timestamp = time.mktime(date_time) - return timestamp, size - - def _extract_resource(self, manager, zip_path): - - if zip_path in self._index(): - for name in self._index()[zip_path]: - last = self._extract_resource( - manager, os.path.join(zip_path, name) - ) - # return the extracted directory name - return os.path.dirname(last) - - timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) - - if not WRITE_SUPPORT: - raise IOError('"os.rename" and "os.unlink" are not supported ' - 'on this platform') - try: - - real_path = manager.get_cache_path( - self.egg_name, self._parts(zip_path) - ) - - if self._is_current(real_path, zip_path): - return real_path - - outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path)) - os.write(outf, self.loader.get_data(zip_path)) - os.close(outf) - utime(tmpnam, (timestamp, timestamp)) - manager.postprocess(tmpnam, real_path) - - try: - rename(tmpnam, real_path) - - except os.error: - if os.path.isfile(real_path): - if self._is_current(real_path, zip_path): - # the file became current since it was checked above, - # so proceed. - return real_path - # Windows, del old file and retry - elif os.name=='nt': - unlink(real_path) - rename(tmpnam, real_path) - return real_path - raise - - except os.error: - # report a user-friendly error - manager.extraction_error() - - return real_path - - def _is_current(self, file_path, zip_path): - """ - Return True if the file_path is current for this zip_path - """ - timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) - if not os.path.isfile(file_path): - return False - stat = os.stat(file_path) - if stat.st_size!=size or stat.st_mtime!=timestamp: - return False - # check that the contents match - zip_contents = self.loader.get_data(zip_path) - with open(file_path, 'rb') as f: - file_contents = f.read() - return zip_contents == file_contents - - def _get_eager_resources(self): - if self.eagers is None: - eagers = [] - for name in ('native_libs.txt', 'eager_resources.txt'): - if self.has_metadata(name): - eagers.extend(self.get_metadata_lines(name)) - self.eagers = eagers - return self.eagers - - def _index(self): - try: - return self._dirindex - except AttributeError: - ind = {} - for path in self.zipinfo: - parts = path.split(os.sep) - while parts: - parent = os.sep.join(parts[:-1]) - if parent in ind: - ind[parent].append(parts[-1]) - break - else: - ind[parent] = [parts.pop()] - self._dirindex = ind - return ind - - def _has(self, fspath): - zip_path = self._zipinfo_name(fspath) - return zip_path in self.zipinfo or zip_path in self._index() - - def _isdir(self, fspath): - return self._zipinfo_name(fspath) in self._index() - - def _listdir(self, fspath): - return list(self._index().get(self._zipinfo_name(fspath), ())) - - def _eager_to_zip(self, resource_name): - return self._zipinfo_name(self._fn(self.egg_root, resource_name)) - - def _resource_to_zip(self, resource_name): - return self._zipinfo_name(self._fn(self.module_path, resource_name)) - -register_loader_type(zipimport.zipimporter, ZipProvider) - - -class FileMetadata(EmptyProvider): - """Metadata handler for standalone PKG-INFO files - - Usage:: - - metadata = FileMetadata("/path/to/PKG-INFO") - - This provider rejects all data and metadata requests except for PKG-INFO, - which is treated as existing, and will be the contents of the file at - the provided location. - """ - - def __init__(self, path): - self.path = path - - def has_metadata(self, name): - return name=='PKG-INFO' - - def get_metadata(self, name): - if name=='PKG-INFO': - with open(self.path,'rU') as f: - metadata = f.read() - return metadata - raise KeyError("No metadata except PKG-INFO is available") - - def get_metadata_lines(self, name): - return yield_lines(self.get_metadata(name)) - - -class PathMetadata(DefaultProvider): - """Metadata provider for egg directories - - Usage:: - - # Development eggs: - - egg_info = "/path/to/PackageName.egg-info" - base_dir = os.path.dirname(egg_info) - metadata = PathMetadata(base_dir, egg_info) - dist_name = os.path.splitext(os.path.basename(egg_info))[0] - dist = Distribution(basedir, project_name=dist_name, metadata=metadata) - - # Unpacked egg directories: - - egg_path = "/path/to/PackageName-ver-pyver-etc.egg" - metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) - dist = Distribution.from_filename(egg_path, metadata=metadata) - """ - - def __init__(self, path, egg_info): - self.module_path = path - self.egg_info = egg_info - - -class EggMetadata(ZipProvider): - """Metadata provider for .egg files""" - - def __init__(self, importer): - """Create a metadata provider from a zipimporter""" - - self.zip_pre = importer.archive+os.sep - self.loader = importer - if importer.prefix: - self.module_path = os.path.join(importer.archive, importer.prefix) - else: - self.module_path = importer.archive - self._setup_prefix() - -_declare_state('dict', _distribution_finders = {}) - -def register_finder(importer_type, distribution_finder): - """Register `distribution_finder` to find distributions in sys.path items - - `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item - handler), and `distribution_finder` is a callable that, passed a path - item and the importer instance, yields ``Distribution`` instances found on - that path item. See ``pkg_resources.find_on_path`` for an example.""" - _distribution_finders[importer_type] = distribution_finder - - -def find_distributions(path_item, only=False): - """Yield distributions accessible via `path_item`""" - importer = get_importer(path_item) - finder = _find_adapter(_distribution_finders, importer) - return finder(importer, path_item, only) - -def find_eggs_in_zip(importer, path_item, only=False): - """ - Find eggs in zip files; possibly multiple nested eggs. - """ - if importer.archive.endswith('.whl'): - # wheels are not supported with this finder - # they don't have PKG-INFO metadata, and won't ever contain eggs - return - metadata = EggMetadata(importer) - if metadata.has_metadata('PKG-INFO'): - yield Distribution.from_filename(path_item, metadata=metadata) - if only: - # don't yield nested distros - return - for subitem in metadata.resource_listdir('/'): - if subitem.endswith('.egg'): - subpath = os.path.join(path_item, subitem) - for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath): - yield dist - -register_finder(zipimport.zipimporter, find_eggs_in_zip) - -def find_nothing(importer, path_item, only=False): - return () -register_finder(object, find_nothing) - -def find_on_path(importer, path_item, only=False): - """Yield distributions accessible on a sys.path directory""" - path_item = _normalize_cached(path_item) - - if os.path.isdir(path_item) and os.access(path_item, os.R_OK): - if path_item.lower().endswith('.egg'): - # unpacked egg - yield Distribution.from_filename( - path_item, metadata=PathMetadata( - path_item, os.path.join(path_item,'EGG-INFO') - ) - ) - else: - # scan for .egg and .egg-info in directory - for entry in os.listdir(path_item): - lower = entry.lower() - if lower.endswith('.egg-info') or lower.endswith('.dist-info'): - fullpath = os.path.join(path_item, entry) - if os.path.isdir(fullpath): - # egg-info directory, allow getting metadata - metadata = PathMetadata(path_item, fullpath) - else: - metadata = FileMetadata(fullpath) - yield Distribution.from_location( - path_item, entry, metadata, precedence=DEVELOP_DIST - ) - elif not only and lower.endswith('.egg'): - dists = find_distributions(os.path.join(path_item, entry)) - for dist in dists: - yield dist - elif not only and lower.endswith('.egg-link'): - with open(os.path.join(path_item, entry)) as entry_file: - entry_lines = entry_file.readlines() - for line in entry_lines: - if not line.strip(): - continue - path = os.path.join(path_item, line.rstrip()) - dists = find_distributions(path) - for item in dists: - yield item - break -register_finder(pkgutil.ImpImporter, find_on_path) - -if importlib_machinery is not None: - register_finder(importlib_machinery.FileFinder, find_on_path) - -_declare_state('dict', _namespace_handlers={}) -_declare_state('dict', _namespace_packages={}) - - -def register_namespace_handler(importer_type, namespace_handler): - """Register `namespace_handler` to declare namespace packages - - `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item - handler), and `namespace_handler` is a callable like this:: - - def namespace_handler(importer, path_entry, moduleName, module): - # return a path_entry to use for child packages - - Namespace handlers are only called if the importer object has already - agreed that it can handle the relevant path item, and they should only - return a subpath if the module __path__ does not already contain an - equivalent subpath. For an example namespace handler, see - ``pkg_resources.file_ns_handler``. - """ - _namespace_handlers[importer_type] = namespace_handler - -def _handle_ns(packageName, path_item): - """Ensure that named package includes a subpath of path_item (if needed)""" - - importer = get_importer(path_item) - if importer is None: - return None - loader = importer.find_module(packageName) - if loader is None: - return None - module = sys.modules.get(packageName) - if module is None: - module = sys.modules[packageName] = types.ModuleType(packageName) - module.__path__ = [] - _set_parent_ns(packageName) - elif not hasattr(module,'__path__'): - raise TypeError("Not a package:", packageName) - handler = _find_adapter(_namespace_handlers, importer) - subpath = handler(importer, path_item, packageName, module) - if subpath is not None: - path = module.__path__ - path.append(subpath) - loader.load_module(packageName) - for path_item in path: - if path_item not in module.__path__: - module.__path__.append(path_item) - return subpath - -def declare_namespace(packageName): - """Declare that package 'packageName' is a namespace package""" - - _imp.acquire_lock() - try: - if packageName in _namespace_packages: - return - - path, parent = sys.path, None - if '.' in packageName: - parent = '.'.join(packageName.split('.')[:-1]) - declare_namespace(parent) - if parent not in _namespace_packages: - __import__(parent) - try: - path = sys.modules[parent].__path__ - except AttributeError: - raise TypeError("Not a package:", parent) - - # Track what packages are namespaces, so when new path items are added, - # they can be updated - _namespace_packages.setdefault(parent,[]).append(packageName) - _namespace_packages.setdefault(packageName,[]) - - for path_item in path: - # Ensure all the parent's path items are reflected in the child, - # if they apply - _handle_ns(packageName, path_item) - - finally: - _imp.release_lock() - -def fixup_namespace_packages(path_item, parent=None): - """Ensure that previously-declared namespace packages include path_item""" - _imp.acquire_lock() - try: - for package in _namespace_packages.get(parent,()): - subpath = _handle_ns(package, path_item) - if subpath: - fixup_namespace_packages(subpath, package) - finally: - _imp.release_lock() - -def file_ns_handler(importer, path_item, packageName, module): - """Compute an ns-package subpath for a filesystem or zipfile importer""" - - subpath = os.path.join(path_item, packageName.split('.')[-1]) - normalized = _normalize_cached(subpath) - for item in module.__path__: - if _normalize_cached(item)==normalized: - break - else: - # Only return the path if it's not already there - return subpath - -register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) -register_namespace_handler(zipimport.zipimporter, file_ns_handler) - -if importlib_machinery is not None: - register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) - - -def null_ns_handler(importer, path_item, packageName, module): - return None - -register_namespace_handler(object, null_ns_handler) - - -def normalize_path(filename): - """Normalize a file/dir name for comparison purposes""" - return os.path.normcase(os.path.realpath(filename)) - -def _normalize_cached(filename, _cache={}): - try: - return _cache[filename] - except KeyError: - _cache[filename] = result = normalize_path(filename) - return result - -def _set_parent_ns(packageName): - parts = packageName.split('.') - name = parts.pop() - if parts: - parent = '.'.join(parts) - setattr(sys.modules[parent], name, sys.modules[packageName]) - - -def yield_lines(strs): - """Yield non-empty/non-comment lines of a string or sequence""" - if isinstance(strs, string_types): - for s in strs.splitlines(): - s = s.strip() - # skip blank lines/comments - if s and not s.startswith('#'): - yield s - else: - for ss in strs: - for s in yield_lines(ss): - yield s - -# whitespace and comment -LINE_END = re.compile(r"\s*(#.*)?$").match -# line continuation -CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match -# Distribution or extra -DISTRO = re.compile(r"\s*((\w|[-.])+)").match -# ver. info -VERSION = re.compile(r"\s*(<=?|>=?|===?|!=|~=)\s*((\w|[-.*_!+])+)").match -# comma between items -COMMA = re.compile(r"\s*,").match -OBRACKET = re.compile(r"\s*\[").match -CBRACKET = re.compile(r"\s*\]").match -MODULE = re.compile(r"\w+(\.\w+)*$").match -EGG_NAME = re.compile( - r""" - (?P[^-]+) ( - -(?P[^-]+) ( - -py(?P[^-]+) ( - -(?P.+) - )? - )? - )? - """, - re.VERBOSE | re.IGNORECASE, -).match - - -class EntryPoint(object): - """Object representing an advertised importable object""" - - def __init__(self, name, module_name, attrs=(), extras=(), dist=None): - if not MODULE(module_name): - raise ValueError("Invalid module name", module_name) - self.name = name - self.module_name = module_name - self.attrs = tuple(attrs) - self.extras = Requirement.parse(("x[%s]" % ','.join(extras))).extras - self.dist = dist - - def __str__(self): - s = "%s = %s" % (self.name, self.module_name) - if self.attrs: - s += ':' + '.'.join(self.attrs) - if self.extras: - s += ' [%s]' % ','.join(self.extras) - return s - - def __repr__(self): - return "EntryPoint.parse(%r)" % str(self) - - def load(self, require=True, *args, **kwargs): - """ - Require packages for this EntryPoint, then resolve it. - """ - if not require or args or kwargs: - warnings.warn( - "Parameters to load are deprecated. Call .resolve and " - ".require separately.", - DeprecationWarning, - stacklevel=2, - ) - if require: - self.require(*args, **kwargs) - return self.resolve() - - def resolve(self): - """ - Resolve the entry point from its module and attrs. - """ - module = __import__(self.module_name, fromlist=['__name__'], level=0) - try: - return functools.reduce(getattr, self.attrs, module) - except AttributeError as exc: - raise ImportError(str(exc)) - - def require(self, env=None, installer=None): - if self.extras and not self.dist: - raise UnknownExtra("Can't require() without a distribution", self) - reqs = self.dist.requires(self.extras) - items = working_set.resolve(reqs, env, installer) - list(map(working_set.add, items)) - - pattern = re.compile( - r'\s*' - r'(?P.+?)\s*' - r'=\s*' - r'(?P[\w.]+)\s*' - r'(:\s*(?P[\w.]+))?\s*' - r'(?P\[.*\])?\s*$' - ) - - @classmethod - def parse(cls, src, dist=None): - """Parse a single entry point from string `src` - - Entry point syntax follows the form:: - - name = some.module:some.attr [extra1, extra2] - - The entry name and module name are required, but the ``:attrs`` and - ``[extras]`` parts are optional - """ - m = cls.pattern.match(src) - if not m: - msg = "EntryPoint must be in 'name=module:attrs [extras]' format" - raise ValueError(msg, src) - res = m.groupdict() - extras = cls._parse_extras(res['extras']) - attrs = res['attr'].split('.') if res['attr'] else () - return cls(res['name'], res['module'], attrs, extras, dist) - - @classmethod - def _parse_extras(cls, extras_spec): - if not extras_spec: - return () - req = Requirement.parse('x' + extras_spec) - if req.specs: - raise ValueError() - return req.extras - - @classmethod - def parse_group(cls, group, lines, dist=None): - """Parse an entry point group""" - if not MODULE(group): - raise ValueError("Invalid group name", group) - this = {} - for line in yield_lines(lines): - ep = cls.parse(line, dist) - if ep.name in this: - raise ValueError("Duplicate entry point", group, ep.name) - this[ep.name]=ep - return this - - @classmethod - def parse_map(cls, data, dist=None): - """Parse a map of entry point groups""" - if isinstance(data, dict): - data = data.items() - else: - data = split_sections(data) - maps = {} - for group, lines in data: - if group is None: - if not lines: - continue - raise ValueError("Entry points must be listed in groups") - group = group.strip() - if group in maps: - raise ValueError("Duplicate group name", group) - maps[group] = cls.parse_group(group, lines, dist) - return maps - - -def _remove_md5_fragment(location): - if not location: - return '' - parsed = urlparse(location) - if parsed[-1].startswith('md5='): - return urlunparse(parsed[:-1] + ('',)) - return location - - -class Distribution(object): - """Wrap an actual or potential sys.path entry w/metadata""" - PKG_INFO = 'PKG-INFO' - - def __init__(self, location=None, metadata=None, project_name=None, - version=None, py_version=PY_MAJOR, platform=None, - precedence=EGG_DIST): - self.project_name = safe_name(project_name or 'Unknown') - if version is not None: - self._version = safe_version(version) - self.py_version = py_version - self.platform = platform - self.location = location - self.precedence = precedence - self._provider = metadata or empty_provider - - @classmethod - def from_location(cls, location, basename, metadata=None,**kw): - project_name, version, py_version, platform = [None]*4 - basename, ext = os.path.splitext(basename) - if ext.lower() in _distributionImpl: - # .dist-info gets much metadata differently - match = EGG_NAME(basename) - if match: - project_name, version, py_version, platform = match.group( - 'name','ver','pyver','plat' - ) - cls = _distributionImpl[ext.lower()] - return cls( - location, metadata, project_name=project_name, version=version, - py_version=py_version, platform=platform, **kw - ) - - @property - def hashcmp(self): - return ( - self.parsed_version, - self.precedence, - self.key, - _remove_md5_fragment(self.location), - self.py_version or '', - self.platform or '', - ) - - def __hash__(self): - return hash(self.hashcmp) - - def __lt__(self, other): - return self.hashcmp < other.hashcmp - - def __le__(self, other): - return self.hashcmp <= other.hashcmp - - def __gt__(self, other): - return self.hashcmp > other.hashcmp - - def __ge__(self, other): - return self.hashcmp >= other.hashcmp - - def __eq__(self, other): - if not isinstance(other, self.__class__): - # It's not a Distribution, so they are not equal - return False - return self.hashcmp == other.hashcmp - - def __ne__(self, other): - return not self == other - - # These properties have to be lazy so that we don't have to load any - # metadata until/unless it's actually needed. (i.e., some distributions - # may not know their name or version without loading PKG-INFO) - - @property - def key(self): - try: - return self._key - except AttributeError: - self._key = key = self.project_name.lower() - return key - - @property - def parsed_version(self): - if not hasattr(self, "_parsed_version"): - self._parsed_version = parse_version(self.version) - - return self._parsed_version - - def _warn_legacy_version(self): - LV = packaging.version.LegacyVersion - is_legacy = isinstance(self._parsed_version, LV) - if not is_legacy: - return - - # While an empty version is technically a legacy version and - # is not a valid PEP 440 version, it's also unlikely to - # actually come from someone and instead it is more likely that - # it comes from setuptools attempting to parse a filename and - # including it in the list. So for that we'll gate this warning - # on if the version is anything at all or not. - if not self.version: - return - - tmpl = textwrap.dedent(""" - '{project_name} ({version})' is being parsed as a legacy, - non PEP 440, - version. You may find odd behavior and sort order. - In particular it will be sorted as less than 0.0. It - is recommended to migrate to PEP 440 compatible - versions. - """).strip().replace('\n', ' ') - - warnings.warn(tmpl.format(**vars(self)), PEP440Warning) - - @property - def version(self): - try: - return self._version - except AttributeError: - for line in self._get_metadata(self.PKG_INFO): - if line.lower().startswith('version:'): - self._version = safe_version(line.split(':',1)[1].strip()) - return self._version - else: - tmpl = "Missing 'Version:' header and/or %s file" - raise ValueError(tmpl % self.PKG_INFO, self) - - @property - def _dep_map(self): - try: - return self.__dep_map - except AttributeError: - dm = self.__dep_map = {None: []} - for name in 'requires.txt', 'depends.txt': - for extra, reqs in split_sections(self._get_metadata(name)): - if extra: - if ':' in extra: - extra, marker = extra.split(':', 1) - if invalid_marker(marker): - # XXX warn - reqs=[] - elif not evaluate_marker(marker): - reqs=[] - extra = safe_extra(extra) or None - dm.setdefault(extra,[]).extend(parse_requirements(reqs)) - return dm - - def requires(self, extras=()): - """List of Requirements needed for this distro if `extras` are used""" - dm = self._dep_map - deps = [] - deps.extend(dm.get(None, ())) - for ext in extras: - try: - deps.extend(dm[safe_extra(ext)]) - except KeyError: - raise UnknownExtra( - "%s has no such extra feature %r" % (self, ext) - ) - return deps - - def _get_metadata(self, name): - if self.has_metadata(name): - for line in self.get_metadata_lines(name): - yield line - - def activate(self, path=None): - """Ensure distribution is importable on `path` (default=sys.path)""" - if path is None: - path = sys.path - self.insert_on(path) - if path is sys.path: - fixup_namespace_packages(self.location) - for pkg in self._get_metadata('namespace_packages.txt'): - if pkg in sys.modules: - declare_namespace(pkg) - - def egg_name(self): - """Return what this distribution's standard .egg filename should be""" - filename = "%s-%s-py%s" % ( - to_filename(self.project_name), to_filename(self.version), - self.py_version or PY_MAJOR - ) - - if self.platform: - filename += '-' + self.platform - return filename - - def __repr__(self): - if self.location: - return "%s (%s)" % (self, self.location) - else: - return str(self) - - def __str__(self): - try: - version = getattr(self, 'version', None) - except ValueError: - version = None - version = version or "[unknown version]" - return "%s %s" % (self.project_name, version) - - def __getattr__(self, attr): - """Delegate all unrecognized public attributes to .metadata provider""" - if attr.startswith('_'): - raise AttributeError(attr) - return getattr(self._provider, attr) - - @classmethod - def from_filename(cls, filename, metadata=None, **kw): - return cls.from_location( - _normalize_cached(filename), os.path.basename(filename), metadata, - **kw - ) - - def as_requirement(self): - """Return a ``Requirement`` that matches this distribution exactly""" - if isinstance(self.parsed_version, packaging.version.Version): - spec = "%s==%s" % (self.project_name, self.parsed_version) - else: - spec = "%s===%s" % (self.project_name, self.parsed_version) - - return Requirement.parse(spec) - - def load_entry_point(self, group, name): - """Return the `name` entry point of `group` or raise ImportError""" - ep = self.get_entry_info(group, name) - if ep is None: - raise ImportError("Entry point %r not found" % ((group, name),)) - return ep.load() - - def get_entry_map(self, group=None): - """Return the entry point map for `group`, or the full entry map""" - try: - ep_map = self._ep_map - except AttributeError: - ep_map = self._ep_map = EntryPoint.parse_map( - self._get_metadata('entry_points.txt'), self - ) - if group is not None: - return ep_map.get(group,{}) - return ep_map - - def get_entry_info(self, group, name): - """Return the EntryPoint object for `group`+`name`, or ``None``""" - return self.get_entry_map(group).get(name) - - def insert_on(self, path, loc = None): - """Insert self.location in path before its nearest parent directory""" - - loc = loc or self.location - if not loc: - return - - nloc = _normalize_cached(loc) - bdir = os.path.dirname(nloc) - npath= [(p and _normalize_cached(p) or p) for p in path] - - for p, item in enumerate(npath): - if item == nloc: - break - elif item == bdir and self.precedence == EGG_DIST: - # if it's an .egg, give it precedence over its directory - if path is sys.path: - self.check_version_conflict() - path.insert(p, loc) - npath.insert(p, nloc) - break - else: - if path is sys.path: - self.check_version_conflict() - path.append(loc) - return - - # p is the spot where we found or inserted loc; now remove duplicates - while True: - try: - np = npath.index(nloc, p+1) - except ValueError: - break - else: - del npath[np], path[np] - # ha! - p = np - - return - - def check_version_conflict(self): - if self.key == 'setuptools': - # ignore the inevitable setuptools self-conflicts :( - return - - nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) - loc = normalize_path(self.location) - for modname in self._get_metadata('top_level.txt'): - if (modname not in sys.modules or modname in nsp - or modname in _namespace_packages): - continue - if modname in ('pkg_resources', 'setuptools', 'site'): - continue - fn = getattr(sys.modules[modname], '__file__', None) - if fn and (normalize_path(fn).startswith(loc) or - fn.startswith(self.location)): - continue - issue_warning( - "Module %s was already imported from %s, but %s is being added" - " to sys.path" % (modname, fn, self.location), - ) - - def has_version(self): - try: - self.version - except ValueError: - issue_warning("Unbuilt egg for " + repr(self)) - return False - return True - - def clone(self,**kw): - """Copy this distribution, substituting in any changed keyword args""" - names = 'project_name version py_version platform location precedence' - for attr in names.split(): - kw.setdefault(attr, getattr(self, attr, None)) - kw.setdefault('metadata', self._provider) - return self.__class__(**kw) - - @property - def extras(self): - return [dep for dep in self._dep_map if dep] - - -class DistInfoDistribution(Distribution): - """Wrap an actual or potential sys.path entry w/metadata, .dist-info style""" - PKG_INFO = 'METADATA' - EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") - - @property - def _parsed_pkg_info(self): - """Parse and cache metadata""" - try: - return self._pkg_info - except AttributeError: - metadata = self.get_metadata(self.PKG_INFO) - self._pkg_info = email.parser.Parser().parsestr(metadata) - return self._pkg_info - - @property - def _dep_map(self): - try: - return self.__dep_map - except AttributeError: - self.__dep_map = self._compute_dependencies() - return self.__dep_map - - def _preparse_requirement(self, requires_dist): - """Convert 'Foobar (1); baz' to ('Foobar ==1', 'baz') - Split environment marker, add == prefix to version specifiers as - necessary, and remove parenthesis. - """ - parts = requires_dist.split(';', 1) + [''] - distvers = parts[0].strip() - mark = parts[1].strip() - distvers = re.sub(self.EQEQ, r"\1==\2\3", distvers) - distvers = distvers.replace('(', '').replace(')', '') - return (distvers, mark) - - def _compute_dependencies(self): - """Recompute this distribution's dependencies.""" - from _markerlib import compile as compile_marker - dm = self.__dep_map = {None: []} - - reqs = [] - # Including any condition expressions - for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: - distvers, mark = self._preparse_requirement(req) - parsed = next(parse_requirements(distvers)) - parsed.marker_fn = compile_marker(mark) - reqs.append(parsed) - - def reqs_for_extra(extra): - for req in reqs: - if req.marker_fn(override={'extra':extra}): - yield req - - common = frozenset(reqs_for_extra(None)) - dm[None].extend(common) - - for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: - extra = safe_extra(extra.strip()) - dm[extra] = list(frozenset(reqs_for_extra(extra)) - common) - - return dm - - -_distributionImpl = { - '.egg': Distribution, - '.egg-info': Distribution, - '.dist-info': DistInfoDistribution, - } - - -def issue_warning(*args,**kw): - level = 1 - g = globals() - try: - # find the first stack frame that is *not* code in - # the pkg_resources module, to use for the warning - while sys._getframe(level).f_globals is g: - level += 1 - except ValueError: - pass - warnings.warn(stacklevel=level + 1, *args, **kw) - - -class RequirementParseError(ValueError): - def __str__(self): - return ' '.join(self.args) - - -def parse_requirements(strs): - """Yield ``Requirement`` objects for each specification in `strs` - - `strs` must be a string, or a (possibly-nested) iterable thereof. - """ - # create a steppable iterator, so we can handle \-continuations - lines = iter(yield_lines(strs)) - - def scan_list(ITEM, TERMINATOR, line, p, groups, item_name): - - items = [] - - while not TERMINATOR(line, p): - if CONTINUE(line, p): - try: - line = next(lines) - p = 0 - except StopIteration: - msg = "\\ must not appear on the last nonblank line" - raise RequirementParseError(msg) - - match = ITEM(line, p) - if not match: - msg = "Expected " + item_name + " in" - raise RequirementParseError(msg, line, "at", line[p:]) - - items.append(match.group(*groups)) - p = match.end() - - match = COMMA(line, p) - if match: - # skip the comma - p = match.end() - elif not TERMINATOR(line, p): - msg = "Expected ',' or end-of-list in" - raise RequirementParseError(msg, line, "at", line[p:]) - - match = TERMINATOR(line, p) - # skip the terminator, if any - if match: - p = match.end() - return line, p, items - - for line in lines: - match = DISTRO(line) - if not match: - raise RequirementParseError("Missing distribution spec", line) - project_name = match.group(1) - p = match.end() - extras = [] - - match = OBRACKET(line, p) - if match: - p = match.end() - line, p, extras = scan_list( - DISTRO, CBRACKET, line, p, (1,), "'extra' name" - ) - - line, p, specs = scan_list(VERSION, LINE_END, line, p, (1, 2), - "version spec") - specs = [(op, val) for op, val in specs] - yield Requirement(project_name, specs, extras) - - -class Requirement: - def __init__(self, project_name, specs, extras): - """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" - self.unsafe_name, project_name = project_name, safe_name(project_name) - self.project_name, self.key = project_name, project_name.lower() - self.specifier = packaging.specifiers.SpecifierSet( - ",".join(["".join([x, y]) for x, y in specs]) - ) - self.specs = specs - self.extras = tuple(map(safe_extra, extras)) - self.hashCmp = ( - self.key, - self.specifier, - frozenset(self.extras), - ) - self.__hash = hash(self.hashCmp) - - def __str__(self): - extras = ','.join(self.extras) - if extras: - extras = '[%s]' % extras - return '%s%s%s' % (self.project_name, extras, self.specifier) - - def __eq__(self, other): - return ( - isinstance(other, Requirement) and - self.hashCmp == other.hashCmp - ) - - def __ne__(self, other): - return not self == other - - def __contains__(self, item): - if isinstance(item, Distribution): - if item.key != self.key: - return False - - item = item.version - - # Allow prereleases always in order to match the previous behavior of - # this method. In the future this should be smarter and follow PEP 440 - # more accurately. - return self.specifier.contains(item, prereleases=True) - - def __hash__(self): - return self.__hash - - def __repr__(self): return "Requirement.parse(%r)" % str(self) - - @staticmethod - def parse(s): - reqs = list(parse_requirements(s)) - if reqs: - if len(reqs) == 1: - return reqs[0] - raise ValueError("Expected only one requirement", s) - raise ValueError("No requirements found", s) - - -def _get_mro(cls): - """Get an mro for a type or classic class""" - if not isinstance(cls, type): - class cls(cls, object): pass - return cls.__mro__[1:] - return cls.__mro__ - -def _find_adapter(registry, ob): - """Return an adapter factory for `ob` from `registry`""" - for t in _get_mro(getattr(ob, '__class__', type(ob))): - if t in registry: - return registry[t] - - -def ensure_directory(path): - """Ensure that the parent directory of `path` exists""" - dirname = os.path.dirname(path) - if not os.path.isdir(dirname): - os.makedirs(dirname) - - -def _bypass_ensure_directory(path): - """Sandbox-bypassing version of ensure_directory()""" - if not WRITE_SUPPORT: - raise IOError('"os.mkdir" not supported on this platform.') - dirname, filename = split(path) - if dirname and filename and not isdir(dirname): - _bypass_ensure_directory(dirname) - mkdir(dirname, 0o755) - - -def split_sections(s): - """Split a string or iterable thereof into (section, content) pairs - - Each ``section`` is a stripped version of the section header ("[section]") - and each ``content`` is a list of stripped lines excluding blank lines and - comment-only lines. If there are any such lines before the first section - header, they're returned in a first ``section`` of ``None``. - """ - section = None - content = [] - for line in yield_lines(s): - if line.startswith("["): - if line.endswith("]"): - if section or content: - yield section, content - section = line[1:-1].strip() - content = [] - else: - raise ValueError("Invalid section heading", line) - else: - content.append(line) - - # wrap up last segment - yield section, content - -def _mkstemp(*args,**kw): - old_open = os.open - try: - # temporarily bypass sandboxing - os.open = os_open - return tempfile.mkstemp(*args,**kw) - finally: - # and then put it back - os.open = old_open - - -# Silence the PEP440Warning by default, so that end users don't get hit by it -# randomly just because they use pkg_resources. We want to append the rule -# because we want earlier uses of filterwarnings to take precedence over this -# one. -warnings.filterwarnings("ignore", category=PEP440Warning, append=True) - - -# from jaraco.functools 1.3 -def _call_aside(f, *args, **kwargs): - f(*args, **kwargs) - return f - - -@_call_aside -def _initialize(g=globals()): - "Set up global resource manager (deliberately not state-saved)" - manager = ResourceManager() - g['_manager'] = manager - for name in dir(manager): - if not name.startswith('_'): - g[name] = getattr(manager, name) - - -@_call_aside -def _initialize_master_working_set(): - """ - Prepare the master working set and make the ``require()`` - API available. - - This function has explicit effects on the global state - of pkg_resources. It is intended to be invoked once at - the initialization of this module. - - Invocation by other packages is unsupported and done - at their own risk. - """ - working_set = WorkingSet._build_master() - _declare_state('object', working_set=working_set) - - require = working_set.require - iter_entry_points = working_set.iter_entry_points - add_activation_listener = working_set.subscribe - run_script = working_set.run_script - # backward compatibility - run_main = run_script - # Activate all distributions already on sys.path, and ensure that - # all distributions added to the working set in the future (e.g. by - # calling ``require()``) will get activated as well. - add_activation_listener(lambda dist: dist.activate()) - working_set.entries=[] - # match order - list(map(working_set.add_entry, sys.path)) - globals().update(locals()) diff --git a/pymode/libs/pkg_resources/_vendor/__init__.py b/pymode/libs/pkg_resources/_vendor/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pymode/libs/pkg_resources/_vendor/packaging/__about__.py b/pymode/libs/pkg_resources/_vendor/packaging/__about__.py deleted file mode 100644 index eadb794e..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/__about__.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -__all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "15.3" - -__author__ = "Donald Stufft" -__email__ = "donald@stufft.io" - -__license__ = "Apache License, Version 2.0" -__copyright__ = "Copyright 2014 %s" % __author__ diff --git a/pymode/libs/pkg_resources/_vendor/packaging/__init__.py b/pymode/libs/pkg_resources/_vendor/packaging/__init__.py deleted file mode 100644 index c39a8eab..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -from .__about__ import ( - __author__, __copyright__, __email__, __license__, __summary__, __title__, - __uri__, __version__ -) - -__all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", -] diff --git a/pymode/libs/pkg_resources/_vendor/packaging/_compat.py b/pymode/libs/pkg_resources/_vendor/packaging/_compat.py deleted file mode 100644 index 5c396cea..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/_compat.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -import sys - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = str, -else: - string_types = basestring, - - -def with_metaclass(meta, *bases): - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/pymode/libs/pkg_resources/_vendor/packaging/_structures.py b/pymode/libs/pkg_resources/_vendor/packaging/_structures.py deleted file mode 100644 index 0ae9bb52..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/_structures.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - - -class Infinity(object): - - def __repr__(self): - return "Infinity" - - def __hash__(self): - return hash(repr(self)) - - def __lt__(self, other): - return False - - def __le__(self, other): - return False - - def __eq__(self, other): - return isinstance(other, self.__class__) - - def __ne__(self, other): - return not isinstance(other, self.__class__) - - def __gt__(self, other): - return True - - def __ge__(self, other): - return True - - def __neg__(self): - return NegativeInfinity - -Infinity = Infinity() - - -class NegativeInfinity(object): - - def __repr__(self): - return "-Infinity" - - def __hash__(self): - return hash(repr(self)) - - def __lt__(self, other): - return True - - def __le__(self, other): - return True - - def __eq__(self, other): - return isinstance(other, self.__class__) - - def __ne__(self, other): - return not isinstance(other, self.__class__) - - def __gt__(self, other): - return False - - def __ge__(self, other): - return False - - def __neg__(self): - return Infinity - -NegativeInfinity = NegativeInfinity() diff --git a/pymode/libs/pkg_resources/_vendor/packaging/specifiers.py b/pymode/libs/pkg_resources/_vendor/packaging/specifiers.py deleted file mode 100644 index 891664f0..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/specifiers.py +++ /dev/null @@ -1,784 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -import abc -import functools -import itertools -import re - -from ._compat import string_types, with_metaclass -from .version import Version, LegacyVersion, parse - - -class InvalidSpecifier(ValueError): - """ - An invalid specifier was found, users should refer to PEP 440. - """ - - -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): - - @abc.abstractmethod - def __str__(self): - """ - Returns the str representation of this Specifier like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self): - """ - Returns a hash value for this Specifier like object. - """ - - @abc.abstractmethod - def __eq__(self, other): - """ - Returns a boolean representing whether or not the two Specifier like - objects are equal. - """ - - @abc.abstractmethod - def __ne__(self, other): - """ - Returns a boolean representing whether or not the two Specifier like - objects are not equal. - """ - - @abc.abstractproperty - def prereleases(self): - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @prereleases.setter - def prereleases(self, value): - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @abc.abstractmethod - def contains(self, item, prereleases=None): - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter(self, iterable, prereleases=None): - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class _IndividualSpecifier(BaseSpecifier): - - _operators = {} - - def __init__(self, spec="", prereleases=None): - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - - self._spec = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self): - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "<{0}({1!r}{2})>".format( - self.__class__.__name__, - str(self), - pre, - ) - - def __str__(self): - return "{0}{1}".format(*self._spec) - - def __hash__(self): - return hash(self._spec) - - def __eq__(self, other): - if isinstance(other, string_types): - try: - other = self.__class__(other) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec == other._spec - - def __ne__(self, other): - if isinstance(other, string_types): - try: - other = self.__class__(other) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec != other._spec - - def _get_operator(self, op): - return getattr(self, "_compare_{0}".format(self._operators[op])) - - def _coerce_version(self, version): - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version - - @property - def operator(self): - return self._spec[0] - - @property - def version(self): - return self._spec[1] - - @property - def prereleases(self): - return self._prereleases - - @prereleases.setter - def prereleases(self, value): - self._prereleases = value - - def __contains__(self, item): - return self.contains(item) - - def contains(self, item, prereleases=None): - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - return self._get_operator(self.operator)(item, self.version) - - def filter(self, iterable, prereleases=None): - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing - # else matches this specifier. - if (parsed_version.is_prerelease - and not (prereleases or self.prereleases)): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the begining. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex = re.compile( - r""" - ^ - \s* - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^\s]* # We just match everything, except for whitespace since this - # is a "legacy" specifier and the version string can be just - # about anything. - ) - \s* - $ - """, - re.VERBOSE | re.IGNORECASE, - ) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def _coerce_version(self, version): - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective, spec): - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective, spec): - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective, spec): - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal(self, prospective, spec): - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective, spec): - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective, spec): - return prospective > self._coerce_version(spec) - - -def _require_version_compare(fn): - @functools.wraps(fn) - def wrapped(self, prospective, spec): - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - return wrapped - - -class Specifier(_IndividualSpecifier): - - _regex = re.compile( - r""" - ^ - \s* - (?P(~=|==|!=|<=|>=|<|>|===)) - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. - (?: - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - @_require_version_compare - def _compare_compatible(self, prospective, spec): - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. - prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") - and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return (self._get_operator(">=")(prospective, spec) - and self._get_operator("==")(prospective, prefix)) - - @_require_version_compare - def _compare_equal(self, prospective, spec): - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # Split the spec out by dots, and pretend that there is an implicit - # dot in between a release segment and a pre-release segment. - spec = _version_split(spec[:-2]) # Remove the trailing .* - - # Split the prospective version out by dots, and pretend that there - # is an implicit dot in between a release segment and a pre-release - # segment. - prospective = _version_split(str(prospective)) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - prospective = prospective[:len(spec)] - - # Pad out our two sides with zeros so that they both equal the same - # length. - spec, prospective = _pad_version(spec, prospective) - else: - # Convert our spec string into a Version - spec = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec.local: - prospective = Version(prospective.public) - - return prospective == spec - - @_require_version_compare - def _compare_not_equal(self, prospective, spec): - return not self._compare_equal(prospective, spec) - - @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - return prospective <= Version(spec) - - @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - return prospective >= Version(spec) - - @_require_version_compare - def _compare_less_than(self, prospective, spec): - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - @_require_version_compare - def _compare_greater_than(self, prospective, spec): - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is techincally greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective, spec): - return str(prospective).lower() == str(spec).lower() - - @property - def prereleases(self): - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value): - self._prereleases = value - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version): - result = [] - for item in version.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _pad_version(left, right): - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split):]) - right_split.append(left[len(right_split):]) - - # Insert our padding - left_split.insert( - 1, - ["0"] * max(0, len(right_split[0]) - len(left_split[0])), - ) - right_split.insert( - 1, - ["0"] * max(0, len(left_split[0]) - len(right_split[0])), - ) - - return ( - list(itertools.chain(*left_split)), - list(itertools.chain(*right_split)), - ) - - -class SpecifierSet(BaseSpecifier): - - def __init__(self, specifiers="", prereleases=None): - # Split on , to break each indidivual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed = set() - for specifier in specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) - - # Turn our parsed specifiers into a frozen set and save them for later. - self._specs = frozenset(parsed) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - def __repr__(self): - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "".format(str(self), pre) - - def __str__(self): - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self): - return hash(self._specs) - - def __and__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __ne__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs != other._specs - - def __len__(self): - return len(self._specs) - - def __iter__(self): - return iter(self._specs) - - @property - def prereleases(self): - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value): - self._prereleases = value - - def __contains__(self, item): - return self.contains(item) - - def contains(self, item, prereleases=None): - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all( - s.contains(item, prereleases=prereleases) - for s in self._specs - ) - - def filter(self, iterable, prereleases=None): - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. - else: - filtered = [] - found_prereleases = [] - - for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return found_prereleases - - return filtered diff --git a/pymode/libs/pkg_resources/_vendor/packaging/version.py b/pymode/libs/pkg_resources/_vendor/packaging/version.py deleted file mode 100644 index 4ba574b9..00000000 --- a/pymode/libs/pkg_resources/_vendor/packaging/version.py +++ /dev/null @@ -1,403 +0,0 @@ -# Copyright 2014 Donald Stufft -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import absolute_import, division, print_function - -import collections -import itertools -import re - -from ._structures import Infinity - - -__all__ = [ - "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" -] - - -_Version = collections.namedtuple( - "_Version", - ["epoch", "release", "dev", "pre", "post", "local"], -) - - -def parse(version): - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. - """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) - - -class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. - """ - - -class _BaseVersion(object): - - def __hash__(self): - return hash(self._key) - - def __lt__(self, other): - return self._compare(other, lambda s, o: s < o) - - def __le__(self, other): - return self._compare(other, lambda s, o: s <= o) - - def __eq__(self, other): - return self._compare(other, lambda s, o: s == o) - - def __ge__(self, other): - return self._compare(other, lambda s, o: s >= o) - - def __gt__(self, other): - return self._compare(other, lambda s, o: s > o) - - def __ne__(self, other): - return self._compare(other, lambda s, o: s != o) - - def _compare(self, other, method): - if not isinstance(other, _BaseVersion): - return NotImplemented - - return method(self._key, other._key) - - -class LegacyVersion(_BaseVersion): - - def __init__(self, version): - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - def __str__(self): - return self._version - - def __repr__(self): - return "".format(repr(str(self))) - - @property - def public(self): - return self._version - - @property - def base_version(self): - return self._version - - @property - def local(self): - return None - - @property - def is_prerelease(self): - return False - - @property - def is_postrelease(self): - return False - - -_legacy_version_component_re = re.compile( - r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, -) - -_legacy_version_replacement_map = { - "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", -} - - -def _parse_version_parts(s): - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version): - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts = [] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - parts = tuple(parts) - - return epoch, parts - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -VERSION_PATTERN = r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                              # pre-release
    -            [-_\.]?
    -            (?P(a|b|c|rc|alpha|beta|pre|preview))
    -            [-_\.]?
    -            (?P[0-9]+)?
    -        )?
    -        (?P                                         # post release
    -            (?:-(?P[0-9]+))
    -            |
    -            (?:
    -                [-_\.]?
    -                (?Ppost|rev|r)
    -                [-_\.]?
    -                (?P[0-9]+)?
    -            )
    -        )?
    -        (?P                                          # dev release
    -            [-_\.]?
    -            (?Pdev)
    -            [-_\.]?
    -            (?P[0-9]+)?
    -        )?
    -    )
    -    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
    -"""
    -
    -
    -class Version(_BaseVersion):
    -
    -    _regex = re.compile(
    -        r"^\s*" + VERSION_PATTERN + r"\s*$",
    -        re.VERBOSE | re.IGNORECASE,
    -    )
    -
    -    def __init__(self, version):
    -        # Validate the version and parse it into pieces
    -        match = self._regex.search(version)
    -        if not match:
    -            raise InvalidVersion("Invalid version: '{0}'".format(version))
    -
    -        # Store the parsed out pieces of the version
    -        self._version = _Version(
    -            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
    -            release=tuple(int(i) for i in match.group("release").split(".")),
    -            pre=_parse_letter_version(
    -                match.group("pre_l"),
    -                match.group("pre_n"),
    -            ),
    -            post=_parse_letter_version(
    -                match.group("post_l"),
    -                match.group("post_n1") or match.group("post_n2"),
    -            ),
    -            dev=_parse_letter_version(
    -                match.group("dev_l"),
    -                match.group("dev_n"),
    -            ),
    -            local=_parse_local_version(match.group("local")),
    -        )
    -
    -        # Generate a key which will be used for sorting
    -        self._key = _cmpkey(
    -            self._version.epoch,
    -            self._version.release,
    -            self._version.pre,
    -            self._version.post,
    -            self._version.dev,
    -            self._version.local,
    -        )
    -
    -    def __repr__(self):
    -        return "".format(repr(str(self)))
    -
    -    def __str__(self):
    -        parts = []
    -
    -        # Epoch
    -        if self._version.epoch != 0:
    -            parts.append("{0}!".format(self._version.epoch))
    -
    -        # Release segment
    -        parts.append(".".join(str(x) for x in self._version.release))
    -
    -        # Pre-release
    -        if self._version.pre is not None:
    -            parts.append("".join(str(x) for x in self._version.pre))
    -
    -        # Post-release
    -        if self._version.post is not None:
    -            parts.append(".post{0}".format(self._version.post[1]))
    -
    -        # Development release
    -        if self._version.dev is not None:
    -            parts.append(".dev{0}".format(self._version.dev[1]))
    -
    -        # Local version segment
    -        if self._version.local is not None:
    -            parts.append(
    -                "+{0}".format(".".join(str(x) for x in self._version.local))
    -            )
    -
    -        return "".join(parts)
    -
    -    @property
    -    def public(self):
    -        return str(self).split("+", 1)[0]
    -
    -    @property
    -    def base_version(self):
    -        parts = []
    -
    -        # Epoch
    -        if self._version.epoch != 0:
    -            parts.append("{0}!".format(self._version.epoch))
    -
    -        # Release segment
    -        parts.append(".".join(str(x) for x in self._version.release))
    -
    -        return "".join(parts)
    -
    -    @property
    -    def local(self):
    -        version_string = str(self)
    -        if "+" in version_string:
    -            return version_string.split("+", 1)[1]
    -
    -    @property
    -    def is_prerelease(self):
    -        return bool(self._version.dev or self._version.pre)
    -
    -    @property
    -    def is_postrelease(self):
    -        return bool(self._version.post)
    -
    -
    -def _parse_letter_version(letter, number):
    -    if letter:
    -        # We consider there to be an implicit 0 in a pre-release if there is
    -        # not a numeral associated with it.
    -        if number is None:
    -            number = 0
    -
    -        # We normalize any letters to their lower case form
    -        letter = letter.lower()
    -
    -        # We consider some words to be alternate spellings of other words and
    -        # in those cases we want to normalize the spellings to our preferred
    -        # spelling.
    -        if letter == "alpha":
    -            letter = "a"
    -        elif letter == "beta":
    -            letter = "b"
    -        elif letter in ["c", "pre", "preview"]:
    -            letter = "rc"
    -        elif letter in ["rev", "r"]:
    -            letter = "post"
    -
    -        return letter, int(number)
    -    if not letter and number:
    -        # We assume if we are given a number, but we are not given a letter
    -        # then this is using the implicit post release syntax (e.g. 1.0-1)
    -        letter = "post"
    -
    -        return letter, int(number)
    -
    -
    -_local_version_seperators = re.compile(r"[\._-]")
    -
    -
    -def _parse_local_version(local):
    -    """
    -    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
    -    """
    -    if local is not None:
    -        return tuple(
    -            part.lower() if not part.isdigit() else int(part)
    -            for part in _local_version_seperators.split(local)
    -        )
    -
    -
    -def _cmpkey(epoch, release, pre, post, dev, local):
    -    # When we compare a release version, we want to compare it with all of the
    -    # trailing zeros removed. So we'll use a reverse the list, drop all the now
    -    # leading zeros until we come to something non zero, then take the rest
    -    # re-reverse it back into the correct order and make it a tuple and use
    -    # that for our sorting key.
    -    release = tuple(
    -        reversed(list(
    -            itertools.dropwhile(
    -                lambda x: x == 0,
    -                reversed(release),
    -            )
    -        ))
    -    )
    -
    -    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
    -    # We'll do this by abusing the pre segment, but we _only_ want to do this
    -    # if there is not a pre or a post segment. If we have one of those then
    -    # the normal sorting rules will handle this case correctly.
    -    if pre is None and post is None and dev is not None:
    -        pre = -Infinity
    -    # Versions without a pre-release (except as noted above) should sort after
    -    # those with one.
    -    elif pre is None:
    -        pre = Infinity
    -
    -    # Versions without a post segment should sort before those with one.
    -    if post is None:
    -        post = -Infinity
    -
    -    # Versions without a development segment should sort after those with one.
    -    if dev is None:
    -        dev = Infinity
    -
    -    if local is None:
    -        # Versions without a local segment should sort before those with one.
    -        local = -Infinity
    -    else:
    -        # Versions with a local segment need that segment parsed to implement
    -        # the sorting rules in PEP440.
    -        # - Alpha numeric segments sort before numeric segments
    -        # - Alpha numeric segments sort lexicographically
    -        # - Numeric segments sort numerically
    -        # - Shorter versions sort before longer versions when the prefixes
    -        #   match exactly
    -        local = tuple(
    -            (i, "") if isinstance(i, int) else (-Infinity, i)
    -            for i in local
    -        )
    -
    -    return epoch, release, pre, post, dev, local
    diff --git a/pymode/libs/pyflakes/__init__.py b/pymode/libs/pyflakes/__init__.py
    deleted file mode 100644
    index 1f047803..00000000
    --- a/pymode/libs/pyflakes/__init__.py
    +++ /dev/null
    @@ -1 +0,0 @@
    -__version__ = '0.9.2'
    diff --git a/pymode/libs/pyflakes/__main__.py b/pymode/libs/pyflakes/__main__.py
    deleted file mode 100644
    index a69e6891..00000000
    --- a/pymode/libs/pyflakes/__main__.py
    +++ /dev/null
    @@ -1,5 +0,0 @@
    -from pyflakes.api import main
    -
    -# python -m pyflakes (with Python >= 2.7)
    -if __name__ == '__main__':
    -    main(prog='pyflakes')
    diff --git a/pymode/libs/pyflakes/api.py b/pymode/libs/pyflakes/api.py
    deleted file mode 100644
    index 3bc23306..00000000
    --- a/pymode/libs/pyflakes/api.py
    +++ /dev/null
    @@ -1,175 +0,0 @@
    -"""
    -API for the command-line I{pyflakes} tool.
    -"""
    -from __future__ import with_statement
    -
    -import sys
    -import os
    -import _ast
    -
    -from pyflakes import checker, __version__
    -from pyflakes import reporter as modReporter
    -
    -__all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
    -
    -
    -def check(codeString, filename, reporter=None):
    -    """
    -    Check the Python source given by C{codeString} for flakes.
    -
    -    @param codeString: The Python source to check.
    -    @type codeString: C{str}
    -
    -    @param filename: The name of the file the source came from, used to report
    -        errors.
    -    @type filename: C{str}
    -
    -    @param reporter: A L{Reporter} instance, where errors and warnings will be
    -        reported.
    -
    -    @return: The number of warnings emitted.
    -    @rtype: C{int}
    -    """
    -    if reporter is None:
    -        reporter = modReporter._makeDefaultReporter()
    -    # First, compile into an AST and handle syntax errors.
    -    try:
    -        tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
    -    except SyntaxError:
    -        value = sys.exc_info()[1]
    -        msg = value.args[0]
    -
    -        (lineno, offset, text) = value.lineno, value.offset, value.text
    -
    -        # If there's an encoding problem with the file, the text is None.
    -        if text is None:
    -            # Avoid using msg, since for the only known case, it contains a
    -            # bogus message that claims the encoding the file declared was
    -            # unknown.
    -            reporter.unexpectedError(filename, 'problem decoding source')
    -        else:
    -            reporter.syntaxError(filename, msg, lineno, offset, text)
    -        return 1
    -    except Exception:
    -        reporter.unexpectedError(filename, 'problem decoding source')
    -        return 1
    -    # Okay, it's syntactically valid.  Now check it.
    -    w = checker.Checker(tree, filename)
    -    w.messages.sort(key=lambda m: m.lineno)
    -    for warning in w.messages:
    -        reporter.flake(warning)
    -    return len(w.messages)
    -
    -
    -def checkPath(filename, reporter=None):
    -    """
    -    Check the given path, printing out any warnings detected.
    -
    -    @param reporter: A L{Reporter} instance, where errors and warnings will be
    -        reported.
    -
    -    @return: the number of warnings printed
    -    """
    -    if reporter is None:
    -        reporter = modReporter._makeDefaultReporter()
    -    try:
    -        # in Python 2.6, compile() will choke on \r\n line endings. In later
    -        # versions of python it's smarter, and we want binary mode to give
    -        # compile() the best opportunity to do the right thing WRT text
    -        # encodings.
    -        if sys.version_info < (2, 7):
    -            mode = 'rU'
    -        else:
    -            mode = 'rb'
    -
    -        with open(filename, mode) as f:
    -            codestr = f.read()
    -        if sys.version_info < (2, 7):
    -            codestr += '\n'     # Work around for Python <= 2.6
    -    except UnicodeError:
    -        reporter.unexpectedError(filename, 'problem decoding source')
    -        return 1
    -    except IOError:
    -        msg = sys.exc_info()[1]
    -        reporter.unexpectedError(filename, msg.args[1])
    -        return 1
    -    return check(codestr, filename, reporter)
    -
    -
    -def iterSourceCode(paths):
    -    """
    -    Iterate over all Python source files in C{paths}.
    -
    -    @param paths: A list of paths.  Directories will be recursed into and
    -        any .py files found will be yielded.  Any non-directories will be
    -        yielded as-is.
    -    """
    -    for path in paths:
    -        if os.path.isdir(path):
    -            for dirpath, dirnames, filenames in os.walk(path):
    -                for filename in filenames:
    -                    if filename.endswith('.py'):
    -                        yield os.path.join(dirpath, filename)
    -        else:
    -            yield path
    -
    -
    -def checkRecursive(paths, reporter):
    -    """
    -    Recursively check all source files in C{paths}.
    -
    -    @param paths: A list of paths to Python source files and directories
    -        containing Python source files.
    -    @param reporter: A L{Reporter} where all of the warnings and errors
    -        will be reported to.
    -    @return: The number of warnings found.
    -    """
    -    warnings = 0
    -    for sourcePath in iterSourceCode(paths):
    -        warnings += checkPath(sourcePath, reporter)
    -    return warnings
    -
    -
    -def _exitOnSignal(sigName, message):
    -    """Handles a signal with sys.exit.
    -
    -    Some of these signals (SIGPIPE, for example) don't exist or are invalid on
    -    Windows. So, ignore errors that might arise.
    -    """
    -    import signal
    -
    -    try:
    -        sigNumber = getattr(signal, sigName)
    -    except AttributeError:
    -        # the signal constants defined in the signal module are defined by
    -        # whether the C library supports them or not. So, SIGPIPE might not
    -        # even be defined.
    -        return
    -
    -    def handler(sig, f):
    -        sys.exit(message)
    -
    -    try:
    -        signal.signal(sigNumber, handler)
    -    except ValueError:
    -        # It's also possible the signal is defined, but then it's invalid. In
    -        # this case, signal.signal raises ValueError.
    -        pass
    -
    -
    -def main(prog=None):
    -    """Entry point for the script "pyflakes"."""
    -    import optparse
    -
    -    # Handle "Keyboard Interrupt" and "Broken pipe" gracefully
    -    _exitOnSignal('SIGINT', '... stopped')
    -    _exitOnSignal('SIGPIPE', 1)
    -
    -    parser = optparse.OptionParser(prog=prog, version=__version__)
    -    (__, args) = parser.parse_args()
    -    reporter = modReporter._makeDefaultReporter()
    -    if args:
    -        warnings = checkRecursive(args, reporter)
    -    else:
    -        warnings = check(sys.stdin.read(), '', reporter)
    -    raise SystemExit(warnings > 0)
    diff --git a/pymode/libs/pyflakes/checker.py b/pymode/libs/pyflakes/checker.py
    deleted file mode 100644
    index e6e19427..00000000
    --- a/pymode/libs/pyflakes/checker.py
    +++ /dev/null
    @@ -1,925 +0,0 @@
    -"""
    -Main module.
    -
    -Implement the central Checker class.
    -Also, it models the Bindings and Scopes.
    -"""
    -import doctest
    -import os
    -import sys
    -
    -PY2 = sys.version_info < (3, 0)
    -PY32 = sys.version_info < (3, 3)    # Python 2.5 to 3.2
    -PY33 = sys.version_info < (3, 4)    # Python 2.5 to 3.3
    -builtin_vars = dir(__import__('__builtin__' if PY2 else 'builtins'))
    -
    -try:
    -    import ast
    -except ImportError:     # Python 2.5
    -    import _ast as ast
    -
    -    if 'decorator_list' not in ast.ClassDef._fields:
    -        # Patch the missing attribute 'decorator_list'
    -        ast.ClassDef.decorator_list = ()
    -        ast.FunctionDef.decorator_list = property(lambda s: s.decorators)
    -
    -from pyflakes import messages
    -
    -
    -if PY2:
    -    def getNodeType(node_class):
    -        # workaround str.upper() which is locale-dependent
    -        return str(unicode(node_class.__name__).upper())
    -else:
    -    def getNodeType(node_class):
    -        return node_class.__name__.upper()
    -
    -# Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally)
    -if PY32:
    -    def getAlternatives(n):
    -        if isinstance(n, (ast.If, ast.TryFinally)):
    -            return [n.body]
    -        if isinstance(n, ast.TryExcept):
    -            return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
    -else:
    -    def getAlternatives(n):
    -        if isinstance(n, ast.If):
    -            return [n.body]
    -        if isinstance(n, ast.Try):
    -            return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
    -
    -
    -class _FieldsOrder(dict):
    -    """Fix order of AST node fields."""
    -
    -    def _get_fields(self, node_class):
    -        # handle iter before target, and generators before element
    -        fields = node_class._fields
    -        if 'iter' in fields:
    -            key_first = 'iter'.find
    -        elif 'generators' in fields:
    -            key_first = 'generators'.find
    -        else:
    -            key_first = 'value'.find
    -        return tuple(sorted(fields, key=key_first, reverse=True))
    -
    -    def __missing__(self, node_class):
    -        self[node_class] = fields = self._get_fields(node_class)
    -        return fields
    -
    -
    -def iter_child_nodes(node, omit=None, _fields_order=_FieldsOrder()):
    -    """
    -    Yield all direct child nodes of *node*, that is, all fields that
    -    are nodes and all items of fields that are lists of nodes.
    -    """
    -    for name in _fields_order[node.__class__]:
    -        if name == omit:
    -            continue
    -        field = getattr(node, name, None)
    -        if isinstance(field, ast.AST):
    -            yield field
    -        elif isinstance(field, list):
    -            for item in field:
    -                yield item
    -
    -
    -class Binding(object):
    -    """
    -    Represents the binding of a value to a name.
    -
    -    The checker uses this to keep track of which names have been bound and
    -    which names have not. See L{Assignment} for a special type of binding that
    -    is checked with stricter rules.
    -
    -    @ivar used: pair of (L{Scope}, line-number) indicating the scope and
    -                line number that this binding was last used
    -    """
    -
    -    def __init__(self, name, source):
    -        self.name = name
    -        self.source = source
    -        self.used = False
    -
    -    def __str__(self):
    -        return self.name
    -
    -    def __repr__(self):
    -        return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
    -                                                        self.name,
    -                                                        self.source.lineno,
    -                                                        id(self))
    -
    -    def redefines(self, other):
    -        return isinstance(other, Definition) and self.name == other.name
    -
    -
    -class Definition(Binding):
    -    """
    -    A binding that defines a function or a class.
    -    """
    -
    -
    -class Importation(Definition):
    -    """
    -    A binding created by an import statement.
    -
    -    @ivar fullName: The complete name given to the import statement,
    -        possibly including multiple dotted components.
    -    @type fullName: C{str}
    -    """
    -
    -    def __init__(self, name, source):
    -        self.fullName = name
    -        self.redefined = []
    -        name = name.split('.')[0]
    -        super(Importation, self).__init__(name, source)
    -
    -    def redefines(self, other):
    -        if isinstance(other, Importation):
    -            return self.fullName == other.fullName
    -        return isinstance(other, Definition) and self.name == other.name
    -
    -
    -class Argument(Binding):
    -    """
    -    Represents binding a name as an argument.
    -    """
    -
    -
    -class Assignment(Binding):
    -    """
    -    Represents binding a name with an explicit assignment.
    -
    -    The checker will raise warnings for any Assignment that isn't used. Also,
    -    the checker does not consider assignments in tuple/list unpacking to be
    -    Assignments, rather it treats them as simple Bindings.
    -    """
    -
    -
    -class FunctionDefinition(Definition):
    -    pass
    -
    -
    -class ClassDefinition(Definition):
    -    pass
    -
    -
    -class ExportBinding(Binding):
    -    """
    -    A binding created by an C{__all__} assignment.  If the names in the list
    -    can be determined statically, they will be treated as names for export and
    -    additional checking applied to them.
    -
    -    The only C{__all__} assignment that can be recognized is one which takes
    -    the value of a literal list containing literal strings.  For example::
    -
    -        __all__ = ["foo", "bar"]
    -
    -    Names which are imported and not otherwise used but appear in the value of
    -    C{__all__} will not have an unused import warning reported for them.
    -    """
    -
    -    def __init__(self, name, source, scope):
    -        if '__all__' in scope and isinstance(source, ast.AugAssign):
    -            self.names = list(scope['__all__'].names)
    -        else:
    -            self.names = []
    -        if isinstance(source.value, (ast.List, ast.Tuple)):
    -            for node in source.value.elts:
    -                if isinstance(node, ast.Str):
    -                    self.names.append(node.s)
    -        super(ExportBinding, self).__init__(name, source)
    -
    -
    -class Scope(dict):
    -    importStarred = False       # set to True when import * is found
    -
    -    def __repr__(self):
    -        scope_cls = self.__class__.__name__
    -        return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
    -
    -
    -class ClassScope(Scope):
    -    pass
    -
    -
    -class FunctionScope(Scope):
    -    """
    -    I represent a name scope for a function.
    -
    -    @ivar globals: Names declared 'global' in this function.
    -    """
    -    usesLocals = False
    -    alwaysUsed = set(['__tracebackhide__',
    -                      '__traceback_info__', '__traceback_supplement__'])
    -
    -    def __init__(self):
    -        super(FunctionScope, self).__init__()
    -        # Simplify: manage the special locals as globals
    -        self.globals = self.alwaysUsed.copy()
    -        self.returnValue = None     # First non-empty return
    -        self.isGenerator = False    # Detect a generator
    -
    -    def unusedAssignments(self):
    -        """
    -        Return a generator for the assignments which have not been used.
    -        """
    -        for name, binding in self.items():
    -            if (not binding.used and name not in self.globals
    -                    and not self.usesLocals
    -                    and isinstance(binding, Assignment)):
    -                yield name, binding
    -
    -
    -class GeneratorScope(Scope):
    -    pass
    -
    -
    -class ModuleScope(Scope):
    -    pass
    -
    -
    -# Globally defined names which are not attributes of the builtins module, or
    -# are only present on some platforms.
    -_MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
    -
    -
    -def getNodeName(node):
    -    # Returns node.id, or node.name, or None
    -    if hasattr(node, 'id'):     # One of the many nodes with an id
    -        return node.id
    -    if hasattr(node, 'name'):   # a ExceptHandler node
    -        return node.name
    -
    -
    -class Checker(object):
    -    """
    -    I check the cleanliness and sanity of Python code.
    -
    -    @ivar _deferredFunctions: Tracking list used by L{deferFunction}.  Elements
    -        of the list are two-tuples.  The first element is the callable passed
    -        to L{deferFunction}.  The second element is a copy of the scope stack
    -        at the time L{deferFunction} was called.
    -
    -    @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
    -        callables which are deferred assignment checks.
    -    """
    -
    -    nodeDepth = 0
    -    offset = None
    -    traceTree = False
    -
    -    builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
    -    _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
    -    if _customBuiltIns:
    -        builtIns.update(_customBuiltIns.split(','))
    -    del _customBuiltIns
    -
    -    def __init__(self, tree, filename='(none)', builtins=None,
    -                 withDoctest='PYFLAKES_DOCTEST' in os.environ):
    -        self._nodeHandlers = {}
    -        self._deferredFunctions = []
    -        self._deferredAssignments = []
    -        self.deadScopes = []
    -        self.messages = []
    -        self.filename = filename
    -        if builtins:
    -            self.builtIns = self.builtIns.union(builtins)
    -        self.withDoctest = withDoctest
    -        self.scopeStack = [ModuleScope()]
    -        self.exceptHandlers = [()]
    -        self.futuresAllowed = True
    -        self.root = tree
    -        self.handleChildren(tree)
    -        self.runDeferred(self._deferredFunctions)
    -        # Set _deferredFunctions to None so that deferFunction will fail
    -        # noisily if called after we've run through the deferred functions.
    -        self._deferredFunctions = None
    -        self.runDeferred(self._deferredAssignments)
    -        # Set _deferredAssignments to None so that deferAssignment will fail
    -        # noisily if called after we've run through the deferred assignments.
    -        self._deferredAssignments = None
    -        del self.scopeStack[1:]
    -        self.popScope()
    -        self.checkDeadScopes()
    -
    -    def deferFunction(self, callable):
    -        """
    -        Schedule a function handler to be called just before completion.
    -
    -        This is used for handling function bodies, which must be deferred
    -        because code later in the file might modify the global scope. When
    -        `callable` is called, the scope at the time this is called will be
    -        restored, however it will contain any new bindings added to it.
    -        """
    -        self._deferredFunctions.append((callable, self.scopeStack[:], self.offset))
    -
    -    def deferAssignment(self, callable):
    -        """
    -        Schedule an assignment handler to be called just after deferred
    -        function handlers.
    -        """
    -        self._deferredAssignments.append((callable, self.scopeStack[:], self.offset))
    -
    -    def runDeferred(self, deferred):
    -        """
    -        Run the callables in C{deferred} using their associated scope stack.
    -        """
    -        for handler, scope, offset in deferred:
    -            self.scopeStack = scope
    -            self.offset = offset
    -            handler()
    -
    -    @property
    -    def scope(self):
    -        return self.scopeStack[-1]
    -
    -    def popScope(self):
    -        self.deadScopes.append(self.scopeStack.pop())
    -
    -    def checkDeadScopes(self):
    -        """
    -        Look at scopes which have been fully examined and report names in them
    -        which were imported but unused.
    -        """
    -        for scope in self.deadScopes:
    -            if isinstance(scope.get('__all__'), ExportBinding):
    -                all_names = set(scope['__all__'].names)
    -                if not scope.importStarred and \
    -                   os.path.basename(self.filename) != '__init__.py':
    -                    # Look for possible mistakes in the export list
    -                    undefined = all_names.difference(scope)
    -                    for name in undefined:
    -                        self.report(messages.UndefinedExport,
    -                                    scope['__all__'].source, name)
    -            else:
    -                all_names = []
    -
    -            # Look for imported names that aren't used.
    -            for value in scope.values():
    -                if isinstance(value, Importation):
    -                    used = value.used or value.name in all_names
    -                    if not used:
    -                        messg = messages.UnusedImport
    -                        self.report(messg, value.source, value.name)
    -                    for node in value.redefined:
    -                        if isinstance(self.getParent(node), ast.For):
    -                            messg = messages.ImportShadowedByLoopVar
    -                        elif used:
    -                            continue
    -                        else:
    -                            messg = messages.RedefinedWhileUnused
    -                        self.report(messg, node, value.name, value.source)
    -
    -    def pushScope(self, scopeClass=FunctionScope):
    -        self.scopeStack.append(scopeClass())
    -
    -    def report(self, messageClass, *args, **kwargs):
    -        self.messages.append(messageClass(self.filename, *args, **kwargs))
    -
    -    def getParent(self, node):
    -        # Lookup the first parent which is not Tuple, List or Starred
    -        while True:
    -            node = node.parent
    -            if not hasattr(node, 'elts') and not hasattr(node, 'ctx'):
    -                return node
    -
    -    def getCommonAncestor(self, lnode, rnode, stop):
    -        if stop in (lnode, rnode) or not (hasattr(lnode, 'parent') and
    -                                          hasattr(rnode, 'parent')):
    -            return None
    -        if lnode is rnode:
    -            return lnode
    -
    -        if (lnode.depth > rnode.depth):
    -            return self.getCommonAncestor(lnode.parent, rnode, stop)
    -        if (lnode.depth < rnode.depth):
    -            return self.getCommonAncestor(lnode, rnode.parent, stop)
    -        return self.getCommonAncestor(lnode.parent, rnode.parent, stop)
    -
    -    def descendantOf(self, node, ancestors, stop):
    -        for a in ancestors:
    -            if self.getCommonAncestor(node, a, stop):
    -                return True
    -        return False
    -
    -    def differentForks(self, lnode, rnode):
    -        """True, if lnode and rnode are located on different forks of IF/TRY"""
    -        ancestor = self.getCommonAncestor(lnode, rnode, self.root)
    -        parts = getAlternatives(ancestor)
    -        if parts:
    -            for items in parts:
    -                if self.descendantOf(lnode, items, ancestor) ^ \
    -                   self.descendantOf(rnode, items, ancestor):
    -                    return True
    -        return False
    -
    -    def addBinding(self, node, value):
    -        """
    -        Called when a binding is altered.
    -
    -        - `node` is the statement responsible for the change
    -        - `value` is the new value, a Binding instance
    -        """
    -        # assert value.source in (node, node.parent):
    -        for scope in self.scopeStack[::-1]:
    -            if value.name in scope:
    -                break
    -        existing = scope.get(value.name)
    -
    -        if existing and not self.differentForks(node, existing.source):
    -
    -            parent_stmt = self.getParent(value.source)
    -            if isinstance(existing, Importation) and isinstance(parent_stmt, ast.For):
    -                self.report(messages.ImportShadowedByLoopVar,
    -                            node, value.name, existing.source)
    -
    -            elif scope is self.scope:
    -                if (isinstance(parent_stmt, ast.comprehension) and
    -                        not isinstance(self.getParent(existing.source),
    -                                       (ast.For, ast.comprehension))):
    -                    self.report(messages.RedefinedInListComp,
    -                                node, value.name, existing.source)
    -                elif not existing.used and value.redefines(existing):
    -                    self.report(messages.RedefinedWhileUnused,
    -                                node, value.name, existing.source)
    -
    -            elif isinstance(existing, Importation) and value.redefines(existing):
    -                existing.redefined.append(node)
    -
    -        if value.name in self.scope:
    -            # then assume the rebound name is used as a global or within a loop
    -            value.used = self.scope[value.name].used
    -
    -        self.scope[value.name] = value
    -
    -    def getNodeHandler(self, node_class):
    -        try:
    -            return self._nodeHandlers[node_class]
    -        except KeyError:
    -            nodeType = getNodeType(node_class)
    -        self._nodeHandlers[node_class] = handler = getattr(self, nodeType)
    -        return handler
    -
    -    def handleNodeLoad(self, node):
    -        name = getNodeName(node)
    -        if not name:
    -            return
    -        # try local scope
    -        try:
    -            self.scope[name].used = (self.scope, node)
    -        except KeyError:
    -            pass
    -        else:
    -            return
    -
    -        scopes = [scope for scope in self.scopeStack[:-1]
    -                  if isinstance(scope, (FunctionScope, ModuleScope, GeneratorScope))]
    -        if isinstance(self.scope, GeneratorScope) and scopes[-1] != self.scopeStack[-2]:
    -            scopes.append(self.scopeStack[-2])
    -
    -        # try enclosing function scopes and global scope
    -        importStarred = self.scope.importStarred
    -        for scope in reversed(scopes):
    -            importStarred = importStarred or scope.importStarred
    -            try:
    -                scope[name].used = (self.scope, node)
    -            except KeyError:
    -                pass
    -            else:
    -                return
    -
    -        # look in the built-ins
    -        if importStarred or name in self.builtIns:
    -            return
    -        if name == '__path__' and os.path.basename(self.filename) == '__init__.py':
    -            # the special name __path__ is valid only in packages
    -            return
    -
    -        # protected with a NameError handler?
    -        if 'NameError' not in self.exceptHandlers[-1]:
    -            self.report(messages.UndefinedName, node, name)
    -
    -    def handleNodeStore(self, node):
    -        name = getNodeName(node)
    -        if not name:
    -            return
    -        # if the name hasn't already been defined in the current scope
    -        if isinstance(self.scope, FunctionScope) and name not in self.scope:
    -            # for each function or module scope above us
    -            for scope in self.scopeStack[:-1]:
    -                if not isinstance(scope, (FunctionScope, ModuleScope)):
    -                    continue
    -                # if the name was defined in that scope, and the name has
    -                # been accessed already in the current scope, and hasn't
    -                # been declared global
    -                used = name in scope and scope[name].used
    -                if used and used[0] is self.scope and name not in self.scope.globals:
    -                    # then it's probably a mistake
    -                    self.report(messages.UndefinedLocal,
    -                                scope[name].used[1], name, scope[name].source)
    -                    break
    -
    -        parent_stmt = self.getParent(node)
    -        if isinstance(parent_stmt, (ast.For, ast.comprehension)) or (
    -                parent_stmt != node.parent and
    -                not self.isLiteralTupleUnpacking(parent_stmt)):
    -            binding = Binding(name, node)
    -        elif name == '__all__' and isinstance(self.scope, ModuleScope):
    -            binding = ExportBinding(name, node.parent, self.scope)
    -        else:
    -            binding = Assignment(name, node)
    -        self.addBinding(node, binding)
    -
    -    def handleNodeDelete(self, node):
    -
    -        def on_conditional_branch():
    -            """
    -            Return `True` if node is part of a conditional body.
    -            """
    -            current = getattr(node, 'parent', None)
    -            while current:
    -                if isinstance(current, (ast.If, ast.While, ast.IfExp)):
    -                    return True
    -                current = getattr(current, 'parent', None)
    -            return False
    -
    -        name = getNodeName(node)
    -        if not name:
    -            return
    -
    -        if on_conditional_branch():
    -            # We can not predict if this conditional branch is going to
    -            # be executed.
    -            return
    -
    -        if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
    -            self.scope.globals.remove(name)
    -        else:
    -            try:
    -                del self.scope[name]
    -            except KeyError:
    -                self.report(messages.UndefinedName, node, name)
    -
    -    def handleChildren(self, tree, omit=None):
    -        for node in iter_child_nodes(tree, omit=omit):
    -            self.handleNode(node, tree)
    -
    -    def isLiteralTupleUnpacking(self, node):
    -        if isinstance(node, ast.Assign):
    -            for child in node.targets + [node.value]:
    -                if not hasattr(child, 'elts'):
    -                    return False
    -            return True
    -
    -    def isDocstring(self, node):
    -        """
    -        Determine if the given node is a docstring, as long as it is at the
    -        correct place in the node tree.
    -        """
    -        return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and
    -                                             isinstance(node.value, ast.Str))
    -
    -    def getDocstring(self, node):
    -        if isinstance(node, ast.Expr):
    -            node = node.value
    -        if not isinstance(node, ast.Str):
    -            return (None, None)
    -        # Computed incorrectly if the docstring has backslash
    -        doctest_lineno = node.lineno - node.s.count('\n') - 1
    -        return (node.s, doctest_lineno)
    -
    -    def handleNode(self, node, parent):
    -        if node is None:
    -            return
    -        if self.offset and getattr(node, 'lineno', None) is not None:
    -            node.lineno += self.offset[0]
    -            node.col_offset += self.offset[1]
    -        if self.traceTree:
    -            print('  ' * self.nodeDepth + node.__class__.__name__)
    -        if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
    -                                        self.isDocstring(node)):
    -            self.futuresAllowed = False
    -        self.nodeDepth += 1
    -        node.depth = self.nodeDepth
    -        node.parent = parent
    -        try:
    -            handler = self.getNodeHandler(node.__class__)
    -            handler(node)
    -        finally:
    -            self.nodeDepth -= 1
    -        if self.traceTree:
    -            print('  ' * self.nodeDepth + 'end ' + node.__class__.__name__)
    -
    -    _getDoctestExamples = doctest.DocTestParser().get_examples
    -
    -    def handleDoctests(self, node):
    -        try:
    -            (docstring, node_lineno) = self.getDocstring(node.body[0])
    -            examples = docstring and self._getDoctestExamples(docstring)
    -        except (ValueError, IndexError):
    -            # e.g. line 6 of the docstring for  has inconsistent
    -            # leading whitespace: ...
    -            return
    -        if not examples:
    -            return
    -        node_offset = self.offset or (0, 0)
    -        self.pushScope()
    -        underscore_in_builtins = '_' in self.builtIns
    -        if not underscore_in_builtins:
    -            self.builtIns.add('_')
    -        for example in examples:
    -            try:
    -                tree = compile(example.source, "", "exec", ast.PyCF_ONLY_AST)
    -            except SyntaxError:
    -                e = sys.exc_info()[1]
    -                position = (node_lineno + example.lineno + e.lineno,
    -                            example.indent + 4 + (e.offset or 0))
    -                self.report(messages.DoctestSyntaxError, node, position)
    -            else:
    -                self.offset = (node_offset[0] + node_lineno + example.lineno,
    -                               node_offset[1] + example.indent + 4)
    -                self.handleChildren(tree)
    -                self.offset = node_offset
    -        if not underscore_in_builtins:
    -            self.builtIns.remove('_')
    -        self.popScope()
    -
    -    def ignore(self, node):
    -        pass
    -
    -    # "stmt" type nodes
    -    DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \
    -        ASYNCWITH = ASYNCWITHITEM = RAISE = TRYFINALLY = ASSERT = EXEC = \
    -        EXPR = ASSIGN = handleChildren
    -
    -    CONTINUE = BREAK = PASS = ignore
    -
    -    # "expr" type nodes
    -    BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = \
    -        COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \
    -        STARRED = NAMECONSTANT = handleChildren
    -
    -    NUM = STR = BYTES = ELLIPSIS = ignore
    -
    -    # "slice" type nodes
    -    SLICE = EXTSLICE = INDEX = handleChildren
    -
    -    # expression contexts are node instances too, though being constants
    -    LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
    -
    -    # same for operators
    -    AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
    -        BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
    -        EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
    -
    -    # additional node types
    -    COMPREHENSION = KEYWORD = handleChildren
    -
    -    def GLOBAL(self, node):
    -        """
    -        Keep track of globals declarations.
    -        """
    -        # In doctests, the global scope is an anonymous function at index 1.
    -        global_scope_index = 1 if self.withDoctest else 0
    -        global_scope = self.scopeStack[global_scope_index]
    -
    -        # Ignore 'global' statement in global scope.
    -        if self.scope is not global_scope:
    -
    -            # One 'global' statement can bind multiple (comma-delimited) names.
    -            for node_name in node.names:
    -                node_value = Assignment(node_name, node)
    -
    -                # Remove UndefinedName messages already reported for this name.
    -                self.messages = [
    -                    m for m in self.messages if not
    -                    isinstance(m, messages.UndefinedName) and not
    -                    m.message_args[0] == node_name]
    -
    -                # Bind name to global scope if it doesn't exist already.
    -                global_scope.setdefault(node_name, node_value)
    -
    -                # Bind name to non-global scopes, but as already "used".
    -                node_value.used = (global_scope, node)
    -                for scope in self.scopeStack[global_scope_index + 1:]:
    -                    scope[node_name] = node_value
    -
    -    NONLOCAL = GLOBAL
    -
    -    def GENERATOREXP(self, node):
    -        self.pushScope(GeneratorScope)
    -        self.handleChildren(node)
    -        self.popScope()
    -
    -    LISTCOMP = handleChildren if PY2 else GENERATOREXP
    -
    -    DICTCOMP = SETCOMP = GENERATOREXP
    -
    -    def NAME(self, node):
    -        """
    -        Handle occurrence of Name (which can be a load/store/delete access.)
    -        """
    -        # Locate the name in locals / function / globals scopes.
    -        if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
    -            self.handleNodeLoad(node)
    -            if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
    -                    and isinstance(node.parent, ast.Call)):
    -                # we are doing locals() call in current scope
    -                self.scope.usesLocals = True
    -        elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
    -            self.handleNodeStore(node)
    -        elif isinstance(node.ctx, ast.Del):
    -            self.handleNodeDelete(node)
    -        else:
    -            # must be a Param context -- this only happens for names in function
    -            # arguments, but these aren't dispatched through here
    -            raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
    -
    -    def RETURN(self, node):
    -        if isinstance(self.scope, ClassScope):
    -            self.report(messages.ReturnOutsideFunction, node)
    -            return
    -
    -        if (
    -            node.value and
    -            hasattr(self.scope, 'returnValue') and
    -            not self.scope.returnValue
    -        ):
    -            self.scope.returnValue = node.value
    -        self.handleNode(node.value, node)
    -
    -    def YIELD(self, node):
    -        self.scope.isGenerator = True
    -        self.handleNode(node.value, node)
    -
    -    AWAIT = YIELDFROM = YIELD
    -
    -    def FUNCTIONDEF(self, node):
    -        for deco in node.decorator_list:
    -            self.handleNode(deco, node)
    -        self.LAMBDA(node)
    -        self.addBinding(node, FunctionDefinition(node.name, node))
    -        if self.withDoctest:
    -            self.deferFunction(lambda: self.handleDoctests(node))
    -
    -    ASYNCFUNCTIONDEF = FUNCTIONDEF
    -
    -    def LAMBDA(self, node):
    -        args = []
    -        annotations = []
    -
    -        if PY2:
    -            def addArgs(arglist):
    -                for arg in arglist:
    -                    if isinstance(arg, ast.Tuple):
    -                        addArgs(arg.elts)
    -                    else:
    -                        args.append(arg.id)
    -            addArgs(node.args.args)
    -            defaults = node.args.defaults
    -        else:
    -            for arg in node.args.args + node.args.kwonlyargs:
    -                args.append(arg.arg)
    -                annotations.append(arg.annotation)
    -            defaults = node.args.defaults + node.args.kw_defaults
    -
    -        # Only for Python3 FunctionDefs
    -        is_py3_func = hasattr(node, 'returns')
    -
    -        for arg_name in ('vararg', 'kwarg'):
    -            wildcard = getattr(node.args, arg_name)
    -            if not wildcard:
    -                continue
    -            args.append(wildcard if PY33 else wildcard.arg)
    -            if is_py3_func:
    -                if PY33:  # Python 2.5 to 3.3
    -                    argannotation = arg_name + 'annotation'
    -                    annotations.append(getattr(node.args, argannotation))
    -                else:     # Python >= 3.4
    -                    annotations.append(wildcard.annotation)
    -
    -        if is_py3_func:
    -            annotations.append(node.returns)
    -
    -        if len(set(args)) < len(args):
    -            for (idx, arg) in enumerate(args):
    -                if arg in args[:idx]:
    -                    self.report(messages.DuplicateArgument, node, arg)
    -
    -        for child in annotations + defaults:
    -            if child:
    -                self.handleNode(child, node)
    -
    -        def runFunction():
    -
    -            self.pushScope()
    -            for name in args:
    -                self.addBinding(node, Argument(name, node))
    -            if isinstance(node.body, list):
    -                # case for FunctionDefs
    -                for stmt in node.body:
    -                    self.handleNode(stmt, node)
    -            else:
    -                # case for Lambdas
    -                self.handleNode(node.body, node)
    -
    -            def checkUnusedAssignments():
    -                """
    -                Check to see if any assignments have not been used.
    -                """
    -                for name, binding in self.scope.unusedAssignments():
    -                    self.report(messages.UnusedVariable, binding.source, name)
    -            self.deferAssignment(checkUnusedAssignments)
    -
    -            if PY32:
    -                def checkReturnWithArgumentInsideGenerator():
    -                    """
    -                    Check to see if there is any return statement with
    -                    arguments but the function is a generator.
    -                    """
    -                    if self.scope.isGenerator and self.scope.returnValue:
    -                        self.report(messages.ReturnWithArgsInsideGenerator,
    -                                    self.scope.returnValue)
    -                self.deferAssignment(checkReturnWithArgumentInsideGenerator)
    -            self.popScope()
    -
    -        self.deferFunction(runFunction)
    -
    -    def CLASSDEF(self, node):
    -        """
    -        Check names used in a class definition, including its decorators, base
    -        classes, and the body of its definition.  Additionally, add its name to
    -        the current scope.
    -        """
    -        for deco in node.decorator_list:
    -            self.handleNode(deco, node)
    -        for baseNode in node.bases:
    -            self.handleNode(baseNode, node)
    -        if not PY2:
    -            for keywordNode in node.keywords:
    -                self.handleNode(keywordNode, node)
    -        self.pushScope(ClassScope)
    -        if self.withDoctest:
    -            self.deferFunction(lambda: self.handleDoctests(node))
    -        for stmt in node.body:
    -            self.handleNode(stmt, node)
    -        self.popScope()
    -        self.addBinding(node, ClassDefinition(node.name, node))
    -
    -    def AUGASSIGN(self, node):
    -        self.handleNodeLoad(node.target)
    -        self.handleNode(node.value, node)
    -        self.handleNode(node.target, node)
    -
    -    def IMPORT(self, node):
    -        for alias in node.names:
    -            name = alias.asname or alias.name
    -            importation = Importation(name, node)
    -            self.addBinding(node, importation)
    -
    -    def IMPORTFROM(self, node):
    -        if node.module == '__future__':
    -            if not self.futuresAllowed:
    -                self.report(messages.LateFutureImport,
    -                            node, [n.name for n in node.names])
    -        else:
    -            self.futuresAllowed = False
    -
    -        for alias in node.names:
    -            if alias.name == '*':
    -                self.scope.importStarred = True
    -                self.report(messages.ImportStarUsed, node, node.module)
    -                continue
    -            name = alias.asname or alias.name
    -            importation = Importation(name, node)
    -            if node.module == '__future__':
    -                importation.used = (self.scope, node)
    -            self.addBinding(node, importation)
    -
    -    def TRY(self, node):
    -        handler_names = []
    -        # List the exception handlers
    -        for handler in node.handlers:
    -            if isinstance(handler.type, ast.Tuple):
    -                for exc_type in handler.type.elts:
    -                    handler_names.append(getNodeName(exc_type))
    -            elif handler.type:
    -                handler_names.append(getNodeName(handler.type))
    -        # Memorize the except handlers and process the body
    -        self.exceptHandlers.append(handler_names)
    -        for child in node.body:
    -            self.handleNode(child, node)
    -        self.exceptHandlers.pop()
    -        # Process the other nodes: "except:", "else:", "finally:"
    -        self.handleChildren(node, omit='body')
    -
    -    TRYEXCEPT = TRY
    -
    -    def EXCEPTHANDLER(self, node):
    -        # 3.x: in addition to handling children, we must handle the name of
    -        # the exception, which is not a Name node, but a simple string.
    -        if isinstance(node.name, str):
    -            self.handleNodeStore(node)
    -        self.handleChildren(node)
    diff --git a/pymode/libs/pyflakes/messages.py b/pymode/libs/pyflakes/messages.py
    deleted file mode 100644
    index 8899b7b0..00000000
    --- a/pymode/libs/pyflakes/messages.py
    +++ /dev/null
    @@ -1,134 +0,0 @@
    -"""
    -Provide the class Message and its subclasses.
    -"""
    -
    -
    -class Message(object):
    -    message = ''
    -    message_args = ()
    -
    -    def __init__(self, filename, loc):
    -        self.filename = filename
    -        self.lineno = loc.lineno
    -        self.col = getattr(loc, 'col_offset', 0)
    -
    -    def __str__(self):
    -        return '%s:%s: %s' % (self.filename, self.lineno,
    -                              self.message % self.message_args)
    -
    -
    -class UnusedImport(Message):
    -    message = '%r imported but unused'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class RedefinedWhileUnused(Message):
    -    message = 'redefinition of unused %r from line %r'
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class RedefinedInListComp(Message):
    -    message = 'list comprehension redefines %r from line %r'
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class ImportShadowedByLoopVar(Message):
    -    message = 'import %r from line %r shadowed by loop variable'
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class ImportStarUsed(Message):
    -    message = "'from %s import *' used; unable to detect undefined names"
    -
    -    def __init__(self, filename, loc, modname):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (modname,)
    -
    -
    -class UndefinedName(Message):
    -    message = 'undefined name %r'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class DoctestSyntaxError(Message):
    -    message = 'syntax error in doctest'
    -
    -    def __init__(self, filename, loc, position=None):
    -        Message.__init__(self, filename, loc)
    -        if position:
    -            (self.lineno, self.col) = position
    -        self.message_args = ()
    -
    -
    -class UndefinedExport(Message):
    -    message = 'undefined name %r in __all__'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class UndefinedLocal(Message):
    -    message = ('local variable %r (defined in enclosing scope on line %r) '
    -               'referenced before assignment')
    -
    -    def __init__(self, filename, loc, name, orig_loc):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name, orig_loc.lineno)
    -
    -
    -class DuplicateArgument(Message):
    -    message = 'duplicate argument %r in function definition'
    -
    -    def __init__(self, filename, loc, name):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (name,)
    -
    -
    -class LateFutureImport(Message):
    -    message = 'future import(s) %r after other statements'
    -
    -    def __init__(self, filename, loc, names):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (names,)
    -
    -
    -class UnusedVariable(Message):
    -    """
    -    Indicates that a variable has been explicity assigned to but not actually
    -    used.
    -    """
    -    message = 'local variable %r is assigned to but never used'
    -
    -    def __init__(self, filename, loc, names):
    -        Message.__init__(self, filename, loc)
    -        self.message_args = (names,)
    -
    -
    -class ReturnWithArgsInsideGenerator(Message):
    -    """
    -    Indicates a return statement with arguments inside a generator.
    -    """
    -    message = '\'return\' with argument inside generator'
    -
    -
    -class ReturnOutsideFunction(Message):
    -    """
    -    Indicates a return statement outside of a function/method.
    -    """
    -    message = '\'return\' outside function'
    diff --git a/pymode/libs/pyflakes/reporter.py b/pymode/libs/pyflakes/reporter.py
    deleted file mode 100644
    index ae645bdf..00000000
    --- a/pymode/libs/pyflakes/reporter.py
    +++ /dev/null
    @@ -1,81 +0,0 @@
    -"""
    -Provide the Reporter class.
    -"""
    -
    -import re
    -import sys
    -
    -
    -class Reporter(object):
    -    """
    -    Formats the results of pyflakes checks to users.
    -    """
    -
    -    def __init__(self, warningStream, errorStream):
    -        """
    -        Construct a L{Reporter}.
    -
    -        @param warningStream: A file-like object where warnings will be
    -            written to.  The stream's C{write} method must accept unicode.
    -            C{sys.stdout} is a good value.
    -        @param errorStream: A file-like object where error output will be
    -            written to.  The stream's C{write} method must accept unicode.
    -            C{sys.stderr} is a good value.
    -        """
    -        self._stdout = warningStream
    -        self._stderr = errorStream
    -
    -    def unexpectedError(self, filename, msg):
    -        """
    -        An unexpected error occurred trying to process C{filename}.
    -
    -        @param filename: The path to a file that we could not process.
    -        @ptype filename: C{unicode}
    -        @param msg: A message explaining the problem.
    -        @ptype msg: C{unicode}
    -        """
    -        self._stderr.write("%s: %s\n" % (filename, msg))
    -
    -    def syntaxError(self, filename, msg, lineno, offset, text):
    -        """
    -        There was a syntax errror in C{filename}.
    -
    -        @param filename: The path to the file with the syntax error.
    -        @ptype filename: C{unicode}
    -        @param msg: An explanation of the syntax error.
    -        @ptype msg: C{unicode}
    -        @param lineno: The line number where the syntax error occurred.
    -        @ptype lineno: C{int}
    -        @param offset: The column on which the syntax error occurred, or None.
    -        @ptype offset: C{int}
    -        @param text: The source code containing the syntax error.
    -        @ptype text: C{unicode}
    -        """
    -        line = text.splitlines()[-1]
    -        if offset is not None:
    -            offset = offset - (len(text) - len(line))
    -            self._stderr.write('%s:%d:%d: %s\n' %
    -                               (filename, lineno, offset + 1, msg))
    -        else:
    -            self._stderr.write('%s:%d: %s\n' % (filename, lineno, msg))
    -        self._stderr.write(line)
    -        self._stderr.write('\n')
    -        if offset is not None:
    -            self._stderr.write(re.sub(r'\S', ' ', line[:offset]) +
    -                               "^\n")
    -
    -    def flake(self, message):
    -        """
    -        pyflakes found something wrong with the code.
    -
    -        @param: A L{pyflakes.messages.Message}.
    -        """
    -        self._stdout.write(str(message))
    -        self._stdout.write('\n')
    -
    -
    -def _makeDefaultReporter():
    -    """
    -    Make a reporter that can be used when no reporter is specified.
    -    """
    -    return Reporter(sys.stdout, sys.stderr)
    diff --git a/pymode/libs/pylama/__init__.py b/pymode/libs/pylama/__init__.py
    deleted file mode 100644
    index 3fb8051b..00000000
    --- a/pymode/libs/pylama/__init__.py
    +++ /dev/null
    @@ -1,11 +0,0 @@
    -"""
    -Code audit tool for python.
    -
    -:copyright: 2013 by Kirill Klenov.
    -:license: BSD, see LICENSE for more details.
    -"""
    -
    -__version__ = "7.0.6"
    -__project__ = "pylama"
    -__author__ = "Kirill Klenov "
    -__license__ = "GNU LGPL"
    diff --git a/pymode/libs/pylama/__main__.py b/pymode/libs/pylama/__main__.py
    deleted file mode 100644
    index 64994e75..00000000
    --- a/pymode/libs/pylama/__main__.py
    +++ /dev/null
    @@ -1,6 +0,0 @@
    -"""Support the module execution."""
    -
    -from .main import shell
    -
    -if __name__ == '__main__':
    -    shell()
    diff --git a/pymode/libs/pylama/async.py b/pymode/libs/pylama/async.py
    deleted file mode 100644
    index 12f929fa..00000000
    --- a/pymode/libs/pylama/async.py
    +++ /dev/null
    @@ -1,76 +0,0 @@
    -""" Support for asyncronious checking. """
    -
    -import logging
    -import threading
    -
    -from .core import run
    -
    -
    -try:
    -    import Queue
    -except ImportError:
    -    import queue as Queue
    -
    -
    -try:
    -    import multiprocessing
    -
    -    CPU_COUNT = multiprocessing.cpu_count()
    -
    -except (ImportError, NotImplementedError):
    -    CPU_COUNT = 1
    -
    -LOGGER = logging.getLogger('pylama')
    -
    -
    -class Worker(threading.Thread):
    -
    -    """ Get tasks from queue and run. """
    -
    -    def __init__(self, path_queue, result_queue):
    -        """ Init worker. """
    -        threading.Thread.__init__(self)
    -        self.path_queue = path_queue
    -        self.result_queue = result_queue
    -
    -    def run(self):
    -        """ Run tasks from queue. """
    -        while True:
    -            path, params = self.path_queue.get()
    -            errors = run(path, **params)
    -            self.result_queue.put(errors)
    -            self.path_queue.task_done()
    -
    -
    -def check_async(paths, options, rootdir=None):
    -    """ Check given paths asynchronously.
    -
    -    :return list: list of errors
    -
    -    """
    -    LOGGER.info('Async code checking is enabled.')
    -    path_queue = Queue.Queue()
    -    result_queue = Queue.Queue()
    -
    -    for num in range(CPU_COUNT):
    -        worker = Worker(path_queue, result_queue)
    -        worker.setDaemon(True)
    -        LOGGER.info('Start worker #%s', (num + 1))
    -        worker.start()
    -
    -    for path in paths:
    -        path_queue.put((path, dict(options=options, rootdir=rootdir)))
    -
    -    path_queue.join()
    -
    -    errors = []
    -    while True:
    -        try:
    -            errors += result_queue.get(False)
    -        except Queue.Empty:
    -            break
    -
    -    return errors
    -
    -
    -# pylama:ignore=W0212,D210,F0001
    diff --git a/pymode/libs/pylama/config.py b/pymode/libs/pylama/config.py
    deleted file mode 100644
    index 3df38829..00000000
    --- a/pymode/libs/pylama/config.py
    +++ /dev/null
    @@ -1,241 +0,0 @@
    -""" Parse arguments from command line and configuration files. """
    -import fnmatch
    -import os
    -import sys
    -import re
    -
    -import logging
    -from argparse import ArgumentParser
    -
    -from . import __version__
    -from .libs.inirama import Namespace
    -from .lint.extensions import LINTERS
    -
    -#: A default checkers
    -DEFAULT_LINTERS = 'pep8', 'pyflakes', 'mccabe'
    -
    -CURDIR = os.getcwd()
    -CONFIG_FILES = 'pylama.ini', 'setup.cfg', 'tox.ini', 'pytest.ini'
    -
    -#: The skip pattern
    -SKIP_PATTERN = re.compile(r'# *noqa\b', re.I).search
    -
    -# Parse a modelines
    -MODELINE_RE = re.compile(r'^\s*#\s+(?:pylama:)\s*((?:[\w_]*=[^:\n\s]+:?)+)', re.I | re.M)
    -
    -# Setup a logger
    -LOGGER = logging.getLogger('pylama')
    -LOGGER.propagate = False
    -STREAM = logging.StreamHandler(sys.stdout)
    -LOGGER.addHandler(STREAM)
    -
    -
    -class _Default(object):
    -
    -    def __init__(self, value=None):
    -        self.value = value
    -
    -    def __str__(self):
    -        return str(self.value)
    -
    -    def __repr__(self):
    -        return "<_Default [%s]>" % self.value
    -
    -
    -def split_csp_str(s):
    -    """ Split commaseparated string.
    -
    -    :returns: list of splitted values
    -
    -    """
    -    if isinstance(s, (list, tuple)):
    -        return s
    -    return list(set(i for i in s.strip().split(',') if i))
    -
    -
    -def parse_linters(linters):
    -    """ Initialize choosen linters.
    -
    -    :returns: list of inited linters
    -
    -    """
    -    result = list()
    -    for name in split_csp_str(linters):
    -        linter = LINTERS.get(name)
    -        if linter:
    -            result.append((name, linter))
    -        else:
    -            logging.warn("Linter `%s` not found.", name)
    -    return result
    -
    -
    -PARSER = ArgumentParser(description="Code audit tool for python.")
    -PARSER.add_argument(
    -    "paths", nargs='*', default=_Default([CURDIR]),
    -    help="Paths to files or directories for code check.")
    -
    -PARSER.add_argument(
    -    "--verbose", "-v", action='store_true', help="Verbose mode.")
    -
    -PARSER.add_argument('--version', action='version',
    -                    version='%(prog)s ' + __version__)
    -
    -PARSER.add_argument(
    -    "--format", "-f", default=_Default('pep8'), choices=['pep8', 'pylint'],
    -    help="Choose errors format (pep8, pylint).")
    -
    -PARSER.add_argument(
    -    "--select", "-s", default=_Default(''), type=split_csp_str,
    -    help="Select errors and warnings. (comma-separated list)")
    -
    -PARSER.add_argument(
    -    "--sort", default=_Default(''), type=split_csp_str,
    -    help="Sort result by error types. Ex. E,W,D")
    -
    -PARSER.add_argument(
    -    "--linters", "-l", default=_Default(','.join(DEFAULT_LINTERS)),
    -    type=parse_linters, help=(
    -        "Select linters. (comma-separated). Choices are %s."
    -        % ','.join(s for s in LINTERS.keys())
    -    ))
    -
    -PARSER.add_argument(
    -    "--ignore", "-i", default=_Default(''), type=split_csp_str,
    -    help="Ignore errors and warnings. (comma-separated)")
    -
    -PARSER.add_argument(
    -    "--skip", default=_Default(''),
    -    type=lambda s: [re.compile(fnmatch.translate(p)) for p in s.split(',') if p],
    -    help="Skip files by masks (comma-separated, Ex. */messages.py)")
    -
    -PARSER.add_argument("--report", "-r", help="Send report to file [REPORT]")
    -PARSER.add_argument(
    -    "--hook", action="store_true", help="Install Git (Mercurial) hook.")
    -
    -PARSER.add_argument(
    -    "--async", action="store_true",
    -    help="Enable async mode. Usefull for checking a lot of files. "
    -    "Dont supported with pylint.")
    -
    -PARSER.add_argument(
    -    "--options", "-o", default="",
    -    help="Select configuration file. By default is '/pylama.ini'")
    -
    -PARSER.add_argument(
    -    "--force", "-F", action='store_true', default=_Default(False),
    -    help="Force code checking (if linter doesnt allow)")
    -
    -PARSER.add_argument(
    -    "--abspath", "-a", action='store_true', default=_Default(False),
    -    help="Use absolute paths in output.")
    -
    -
    -ACTIONS = dict((a.dest, a) for a in PARSER._actions)
    -
    -
    -def parse_options(args=None, config=True, rootdir=CURDIR, **overrides): # noqa
    -    """ Parse options from command line and configuration files.
    -
    -    :return argparse.Namespace:
    -
    -    """
    -    if args is None:
    -        args = []
    -
    -    # Parse args from command string
    -    options = PARSER.parse_args(args)
    -    options.file_params = dict()
    -    options.linters_params = dict()
    -
    -    # Override options
    -    for k, v in overrides.items():
    -        passed_value = getattr(options, k, _Default())
    -        if isinstance(passed_value, _Default):
    -            setattr(options, k, _Default(v))
    -
    -    # Compile options from ini
    -    if config:
    -        cfg = get_config(str(options.options), rootdir=rootdir)
    -        for k, v in cfg.default.items():
    -            LOGGER.info('Find option %s (%s)', k, v)
    -            passed_value = getattr(options, k, _Default())
    -            if isinstance(passed_value, _Default):
    -                if k == 'paths':
    -                    v = v.split()
    -                setattr(options, k, _Default(v))
    -
    -        # Parse file related options
    -        for name, opts in cfg.sections.items():
    -
    -            if not name.startswith('pylama'):
    -                continue
    -
    -            if name == cfg.default_section:
    -                continue
    -
    -            name = name[7:]
    -
    -            if name in LINTERS:
    -                options.linters_params[name] = dict(opts)
    -                continue
    -
    -            mask = re.compile(fnmatch.translate(name))
    -            options.file_params[mask] = dict(opts)
    -
    -    # Postprocess options
    -    opts = dict(options.__dict__.items())
    -    for name, value in opts.items():
    -        if isinstance(value, _Default):
    -            setattr(options, name, process_value(name, value.value))
    -
    -    if options.async and 'pylint' in options.linters:
    -        LOGGER.warn('Cant parse code asynchronously while pylint is enabled.')
    -        options.async = False
    -
    -    return options
    -
    -
    -def process_value(name, value):
    -    """ Compile option value. """
    -    action = ACTIONS.get(name)
    -    if not action:
    -        return value
    -
    -    if callable(action.type):
    -        return action.type(value)
    -
    -    if action.const:
    -        return bool(int(value))
    -
    -    return value
    -
    -
    -def get_config(ini_path=None, rootdir=CURDIR):
    -    """ Load configuration from INI.
    -
    -    :return Namespace:
    -
    -    """
    -    config = Namespace()
    -    config.default_section = 'pylama'
    -
    -    if not ini_path:
    -        for path in CONFIG_FILES:
    -            path = os.path.join(rootdir, path)
    -            if os.path.isfile(path) and os.access(path, os.R_OK):
    -                config.read(path)
    -    else:
    -        config.read(ini_path)
    -
    -    return config
    -
    -
    -def setup_logger(options):
    -    """ Setup logger with options. """
    -    LOGGER.setLevel(logging.INFO if options.verbose else logging.WARN)
    -    if options.report:
    -        LOGGER.removeHandler(STREAM)
    -        LOGGER.addHandler(logging.FileHandler(options.report, mode='w'))
    -    LOGGER.info('Try to read configuration from: ' + options.options)
    -
    -# pylama:ignore=W0212,D210,F0001
    diff --git a/pymode/libs/pylama/core.py b/pymode/libs/pylama/core.py
    deleted file mode 100644
    index 69d3c59e..00000000
    --- a/pymode/libs/pylama/core.py
    +++ /dev/null
    @@ -1,200 +0,0 @@
    -""" Pylama's core functionality.
    -
    -Prepare params, check a modeline and run the checkers.
    -
    -"""
    -import logging
    -
    -import os.path as op
    -from .config import process_value, LOGGER, MODELINE_RE, SKIP_PATTERN, CURDIR
    -from .errors import Error, remove_duplicates
    -from .lint.extensions import LINTERS
    -
    -
    -def run(path='', code=None, rootdir=CURDIR, options=None):
    -    """ Run code checkers with given params.
    -
    -    :param path: (str) A file's path.
    -    :param code: (str) A code source
    -    :return errors: list of dictionaries with error's information
    -
    -    """
    -    errors = []
    -    fileconfig = dict()
    -    linters = LINTERS
    -    linters_params = dict()
    -    lname = 'undefined'
    -    params = dict()
    -    path = op.relpath(path, rootdir)
    -
    -    if options:
    -        linters = options.linters
    -        linters_params = options.linters_params
    -        for mask in options.file_params:
    -            if mask.match(path):
    -                fileconfig.update(options.file_params[mask])
    -
    -        if options.skip and any(p.match(path) for p in options.skip):
    -            LOGGER.info('Skip checking for path: %s', path)
    -            return []
    -
    -    try:
    -        with CodeContext(code, path) as ctx:
    -            code = ctx.code
    -            params = prepare_params(parse_modeline(code), fileconfig, options)
    -            LOGGER.debug('Checking params: %s', params)
    -
    -            if params.get('skip'):
    -                return errors
    -
    -            for item in params.get('linters') or linters:
    -
    -                if not isinstance(item, tuple):
    -                    item = (item, LINTERS.get(item))
    -
    -                lname, linter = item
    -
    -                if not linter:
    -                    continue
    -
    -                lparams = linters_params.get(lname, dict())
    -                LOGGER.info("Run %s %s", lname, lparams)
    -
    -                for er in linter.run(
    -                        path, code=code, ignore=params.get("ignore", set()),
    -                        select=params.get("select", set()), params=lparams):
    -                    errors.append(Error(filename=path, linter=lname, **er))
    -
    -    except IOError as e:
    -        LOGGER.debug("IOError %s", e)
    -        errors.append(Error(text=str(e), filename=path, linter=lname))
    -
    -    except SyntaxError as e:
    -        LOGGER.debug("SyntaxError %s", e)
    -        errors.append(
    -            Error(linter=lname, lnum=e.lineno, col=e.offset, text=e.args[0],
    -                  filename=path))
    -
    -    except Exception as e: # noqa
    -        import traceback
    -        LOGGER.info(traceback.format_exc())
    -
    -    errors = filter_errors(errors, **params)
    -
    -    errors = list(remove_duplicates(errors))
    -
    -    if code and errors:
    -        errors = filter_skiplines(code, errors)
    -
    -    key = lambda e: e.lnum
    -    if options and options.sort:
    -        sort = dict((v, n) for n, v in enumerate(options.sort, 1))
    -        key = lambda e: (sort.get(e.type, 999), e.lnum)
    -    return sorted(errors, key=key)
    -
    -
    -def parse_modeline(code):
    -    """ Parse params from file's modeline.
    -
    -    :return dict: Linter params.
    -
    -    """
    -    seek = MODELINE_RE.search(code)
    -    if seek:
    -        return dict(v.split('=') for v in seek.group(1).split(':'))
    -
    -    return dict()
    -
    -
    -def prepare_params(modeline, fileconfig, options):
    -    """ Prepare and merge a params from modelines and configs.
    -
    -    :return dict:
    -
    -    """
    -    params = dict(skip=False, ignore=[], select=[], linters=[])
    -    if options:
    -        params['ignore'] = list(options.ignore)
    -        params['select'] = list(options.select)
    -
    -    for config in filter(None, [modeline, fileconfig]):
    -        for key in ('ignore', 'select', 'linters'):
    -            params[key] += process_value(key, config.get(key, []))
    -        params['skip'] = bool(int(config.get('skip', False)))
    -
    -    params['ignore'] = set(params['ignore'])
    -    params['select'] = set(params['select'])
    -
    -    return params
    -
    -
    -def filter_errors(errors, select=None, ignore=None, **params):
    -    """ Filter a erros by select and ignore options.
    -
    -    :return bool:
    -
    -    """
    -    select = select or []
    -    ignore = ignore or []
    -
    -    for e in errors:
    -        for s in select:
    -            if e.number.startswith(s):
    -                yield e
    -                break
    -        else:
    -            for s in ignore:
    -                if e.number.startswith(s):
    -                    break
    -            else:
    -                yield e
    -
    -
    -def filter_skiplines(code, errors):
    -    """ Filter lines by `noqa`.
    -
    -    :return list: A filtered errors
    -
    -    """
    -    if not errors:
    -        return errors
    -
    -    enums = set(er.lnum for er in errors)
    -    removed = set([
    -        num for num, l in enumerate(code.split('\n'), 1)
    -        if num in enums and SKIP_PATTERN(l)
    -    ])
    -
    -    if removed:
    -        errors = [er for er in errors if er.lnum not in removed]
    -
    -    return errors
    -
    -
    -class CodeContext(object):
    -
    -    """ Read file if code is None. """
    -
    -    def __init__(self, code, path):
    -        """ Init context. """
    -        self.code = code
    -        self.path = path
    -        self._file = None
    -
    -    def __enter__(self):
    -        """ Open a file and read it. """
    -        if self.code is None:
    -            LOGGER.info("File is reading: %s", self.path)
    -            self._file = open(self.path, 'rU')
    -            self.code = self._file.read()
    -        return self
    -
    -    def __exit__(self, t, value, traceback):
    -        """ Close the file which was opened. """
    -        if self._file is not None:
    -            self._file.close()
    -
    -        if t and LOGGER.level == logging.DEBUG:
    -            LOGGER.debug(traceback)
    -
    -# pylama:ignore=R0912,D210,F0001
    diff --git a/pymode/libs/pylama/errors.py b/pymode/libs/pylama/errors.py
    deleted file mode 100644
    index 7f6c0a11..00000000
    --- a/pymode/libs/pylama/errors.py
    +++ /dev/null
    @@ -1,97 +0,0 @@
    -""" Don't duplicate same errors from different linters. """
    -
    -from collections import defaultdict
    -
    -
    -DUPLICATES = (
    -
    -    # multiple statements on one line
    -    [('pep8', 'E701'), ('pylint', 'C0321')],
    -
    -    # unused variable
    -    [('pylint', 'W0612'), ('pyflakes', 'W0612')],
    -
    -    # undefined variable
    -    [('pylint', 'E0602'), ('pyflakes', 'E0602')],
    -
    -    # unused import
    -    [('pylint', 'W0611'), ('pyflakes', 'W0611')],
    -
    -    # whitespace before ')'
    -    [('pylint', 'C0326'), ('pep8', 'E202')],
    -
    -    # whitespace before '('
    -    [('pylint', 'C0326'), ('pep8', 'E211')],
    -
    -    # multiple spaces after operator
    -    [('pylint', 'C0326'), ('pep8', 'E222')],
    -
    -    # missing whitespace around operator
    -    [('pylint', 'C0326'), ('pep8', 'E225')],
    -
    -    # unexpected spaces
    -    [('pylint', 'C0326'), ('pep8', 'E251')],
    -
    -    # long lines
    -    [('pylint', 'C0301'), ('pep8', 'E501')],
    -
    -    # statement ends with a semicolon
    -    [('pylint', 'W0301'), ('pep8', 'E703')],
    -
    -    # multiple statements on one line
    -    [('pylint', 'C0321'), ('pep8', 'E702')],
    -
    -    # bad indentation
    -    [('pylint', 'W0311'), ('pep8', 'E111')],
    -
    -    # wildcart import
    -    [('pylint', 'W00401'), ('pyflakes', 'W0401')],
    -
    -    # module docstring
    -    [('pep257', 'D100'), ('pylint', 'C0111')],
    -
    -)
    -
    -DUPLICATES = dict((key, values) for values in DUPLICATES for key in values)
    -
    -
    -def remove_duplicates(errors):
    -    """ Filter duplicates from given error's list. """
    -    passed = defaultdict(list)
    -    for error in errors:
    -        key = error.linter, error.number
    -        if key in DUPLICATES:
    -            if key in passed[error.lnum]:
    -                continue
    -            passed[error.lnum] = DUPLICATES[key]
    -        yield error
    -
    -
    -class Error(object):
    -
    -    """ Store an error's information. """
    -
    -    def __init__(self, linter="", col=1, lnum=1, type="E",
    -                 text="unknown error", filename="", **kwargs):
    -        """ Init error information with default values. """
    -        text = ' '.join(str(text).strip().split('\n'))
    -        if linter:
    -            text = "%s [%s]" % (text, linter)
    -        number = text.split(' ', 1)[0]
    -        self._info = dict(linter=linter, col=col, lnum=lnum, type=type[:1],
    -                          text=text, filename=filename, number=number)
    -
    -    def __getattr__(self, name):
    -        return self._info[name]
    -
    -    def __getitem__(self, name):
    -        return self._info[name]
    -
    -    def get(self, name, default=None):
    -        """ Implement dictionary `get` method. """
    -        return self._info.get(name, default)
    -
    -    def __repr__(self):
    -        return "" % (self.number, self.linter)
    -
    -# pylama:ignore=W0622,D,R0924
    diff --git a/pymode/libs/pylama/hook.py b/pymode/libs/pylama/hook.py
    deleted file mode 100644
    index f65ef46f..00000000
    --- a/pymode/libs/pylama/hook.py
    +++ /dev/null
    @@ -1,111 +0,0 @@
    -""" SCM hooks. Integration with git and mercurial. """
    -
    -from __future__ import absolute_import
    -
    -import sys
    -from os import path as op, chmod
    -from subprocess import Popen, PIPE
    -
    -from .main import LOGGER, process_paths
    -from .config import parse_options, setup_logger
    -
    -
    -try:
    -    from configparser import ConfigParser  # noqa
    -except ImportError:   # Python 2
    -    from ConfigParser import ConfigParser
    -
    -
    -def run(command):
    -    """ Run a shell command.
    -
    -    :return str: Stdout
    -
    -    """
    -    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    -    (stdout, stderr) = p.communicate()
    -    return (p.returncode, [line.strip() for line in stdout.splitlines()],
    -            [line.strip() for line in stderr.splitlines()])
    -
    -
    -def git_hook():
    -    """ Run pylama after git commit. """
    -    _, files_modified, _ = run("git diff-index --cached --name-only HEAD")
    -
    -    options = parse_options()
    -    setup_logger(options)
    -    candidates = list(map(str, files_modified))
    -    if candidates:
    -        process_paths(options, candidates=candidates)
    -
    -
    -def hg_hook(ui, repo, node=None, **kwargs):
    -    """ Run pylama after mercurial commit. """
    -    seen = set()
    -    paths = []
    -    if len(repo):
    -        for rev in range(repo[node], len(repo)):
    -            for file_ in repo[rev].files():
    -                file_ = op.join(repo.root, file_)
    -                if file_ in seen or not op.exists(file_):
    -                    continue
    -                seen.add(file_)
    -                paths.append(file_)
    -
    -    options = parse_options()
    -    setup_logger(options)
    -    if paths:
    -        process_paths(options, candidates=paths)
    -
    -
    -def install_git(path):
    -    """ Install hook in Git repository. """
    -    hook = op.join(path, 'pre-commit')
    -    with open(hook, 'w') as fd:
    -        fd.write("""#!/usr/bin/env python
    -import sys
    -from pylama.hook import git_hook
    -
    -if __name__ == '__main__':
    -    sys.exit(git_hook())
    -""")
    -    chmod(hook, 484)
    -
    -
    -def install_hg(path):
    -    """ Install hook in Mercurial repository. """
    -    hook = op.join(path, 'hgrc')
    -    if not op.isfile(hook):
    -        open(hook, 'w+').close()
    -
    -    c = ConfigParser()
    -    c.readfp(open(hook, 'r'))
    -    if not c.has_section('hooks'):
    -        c.add_section('hooks')
    -
    -    if not c.has_option('hooks', 'commit'):
    -        c.set('hooks', 'commit', 'python:pylama.hooks.hg_hook')
    -
    -    if not c.has_option('hooks', 'qrefresh'):
    -        c.set('hooks', 'qrefresh', 'python:pylama.hooks.hg_hook')
    -
    -    c.write(open(hook, 'w+'))
    -
    -
    -def install_hook(path):
    -    """ Auto definition of SCM and hook installation. """
    -    git = op.join(path, '.git', 'hooks')
    -    hg = op.join(path, '.hg')
    -    if op.exists(git):
    -        install_git(git)
    -        LOGGER.warn('Git hook has been installed.')
    -
    -    elif op.exists(hg):
    -        install_hg(hg)
    -        LOGGER.warn('Mercurial hook has been installed.')
    -
    -    else:
    -        LOGGER.error('VCS has not found. Check your path.')
    -        sys.exit(1)
    -
    -# pylama:ignore=F0401,E1103,D210,F0001
    diff --git a/pymode/libs/pylama/libs/__init__.py b/pymode/libs/pylama/libs/__init__.py
    deleted file mode 100644
    index 95fec137..00000000
    --- a/pymode/libs/pylama/libs/__init__.py
    +++ /dev/null
    @@ -1 +0,0 @@
    -""" Support libs. """
    diff --git a/pymode/libs/pylama/libs/importlib.py b/pymode/libs/pylama/libs/importlib.py
    deleted file mode 100644
    index ad31a1ac..00000000
    --- a/pymode/libs/pylama/libs/importlib.py
    +++ /dev/null
    @@ -1,38 +0,0 @@
    -"""Backport of importlib.import_module from 3.x."""
    -# While not critical (and in no way guaranteed!), it would be nice to keep this
    -# code compatible with Python 2.3.
    -import sys
    -
    -def _resolve_name(name, package, level):
    -    """Return the absolute name of the module to be imported."""
    -    if not hasattr(package, 'rindex'):
    -        raise ValueError("'package' not set to a string")
    -    dot = len(package)
    -    for x in xrange(level, 1, -1):
    -        try:
    -            dot = package.rindex('.', 0, dot)
    -        except ValueError:
    -            raise ValueError("attempted relative import beyond top-level "
    -                              "package")
    -    return "%s.%s" % (package[:dot], name)
    -
    -
    -def import_module(name, package=None):
    -    """Import a module.
    -
    -    The 'package' argument is required when performing a relative import. It
    -    specifies the package to use as the anchor point from which to resolve the
    -    relative import to an absolute import.
    -
    -    """
    -    if name.startswith('.'):
    -        if not package:
    -            raise TypeError("relative imports require the 'package' argument")
    -        level = 0
    -        for character in name:
    -            if character != '.':
    -                break
    -            level += 1
    -        name = _resolve_name(name[level:], package, level)
    -    __import__(name)
    -    return sys.modules[name]
    diff --git a/pymode/libs/pylama/libs/inirama.py b/pymode/libs/pylama/libs/inirama.py
    deleted file mode 100644
    index 2437fd3c..00000000
    --- a/pymode/libs/pylama/libs/inirama.py
    +++ /dev/null
    @@ -1,405 +0,0 @@
    -"""
    -    Inirama is a python module that parses INI files.
    -
    -    .. _badges:
    -    .. include:: ../README.rst
    -        :start-after: .. _badges:
    -        :end-before: .. _contents:
    -
    -    .. _description:
    -    .. include:: ../README.rst
    -        :start-after: .. _description:
    -        :end-before: .. _badges:
    -
    -    :copyright: 2013 by Kirill Klenov.
    -    :license: BSD, see LICENSE for more details.
    -"""
    -from __future__ import unicode_literals, print_function
    -
    -
    -__version__ = "0.8.0"
    -__project__ = "Inirama"
    -__author__ = "Kirill Klenov "
    -__license__ = "BSD"
    -
    -
    -import io
    -import re
    -import logging
    -try:
    -    from collections import OrderedDict
    -except ImportError:
    -    from UserDict import DictMixin
    -
    -    class OrderedDict(dict, DictMixin):
    -
    -        null = object()
    -
    -        def __init__(self, *args, **kwargs):
    -            self.clear()
    -            self.update(*args, **kwargs)
    -
    -        def clear(self):
    -            self.__map = dict()
    -            self.__order = list()
    -            dict.clear(self)
    -
    -    def __setitem__(self, key, value):
    -        if key not in self:
    -            self.__map[key] = len(self.__order)
    -            self.__order.append(key)
    -        dict.__setitem__(self, key, value)
    -
    -    def __delitem__(self, key):
    -        dict.__delitem__(self, key)
    -        self.__map.pop(key)
    -        self.__order = self.null
    -
    -    def __iter__(self):
    -        for key in self.__order:
    -            if key is not self.null:
    -                yield key
    -
    -    def keys(self):
    -        return list(self)
    -
    -    setdefault = DictMixin.setdefault
    -    update = DictMixin.update
    -    pop = DictMixin.pop
    -    values = DictMixin.values
    -    items = DictMixin.items
    -    iterkeys = DictMixin.iterkeys
    -    itervalues = DictMixin.itervalues
    -    iteritems = DictMixin.iteritems
    -
    -
    -NS_LOGGER = logging.getLogger('inirama')
    -
    -
    -class Scanner(object):
    -
    -    """ Split a code string on tokens. """
    -
    -    def __init__(self, source, ignore=None, patterns=None):
    -        """ Init Scanner instance.
    -
    -        :param patterns: List of token patterns [(token, regexp)]
    -        :param ignore: List of ignored tokens
    -
    -        """
    -        self.reset(source)
    -        if patterns:
    -            self.patterns = []
    -            for k, r in patterns:
    -                self.patterns.append((k, re.compile(r)))
    -
    -        if ignore:
    -            self.ignore = ignore
    -
    -    def reset(self, source):
    -        """ Reset scanner's state.
    -
    -        :param source: Source for parsing
    -
    -        """
    -        self.tokens = []
    -        self.source = source
    -        self.pos = 0
    -
    -    def scan(self):
    -        """ Scan source and grab tokens. """
    -
    -        self.pre_scan()
    -
    -        token = None
    -        end = len(self.source)
    -
    -        while self.pos < end:
    -
    -            best_pat = None
    -            best_pat_len = 0
    -
    -            # Check patterns
    -            for p, regexp in self.patterns:
    -                m = regexp.match(self.source, self.pos)
    -                if m:
    -                    best_pat = p
    -                    best_pat_len = len(m.group(0))
    -                    break
    -
    -            if best_pat is None:
    -                raise SyntaxError(
    -                    "SyntaxError[@char {0}: {1}]".format(
    -                        self.pos, "Bad token."))
    -
    -            # Ignore patterns
    -            if best_pat in self.ignore:
    -                self.pos += best_pat_len
    -                continue
    -
    -            # Create token
    -            token = (
    -                best_pat,
    -                self.source[self.pos:self.pos + best_pat_len],
    -                self.pos,
    -                self.pos + best_pat_len,
    -            )
    -
    -            self.pos = token[-1]
    -            self.tokens.append(token)
    -
    -    def pre_scan(self):
    -        """ Prepare source. """
    -        pass
    -
    -    def __repr__(self):
    -        """ Print the last 5 tokens that have been scanned in.
    -
    -        :return str:
    -
    -        """
    -        return '"
    -
    -
    -class INIScanner(Scanner):
    -
    -    """ Get tokens for INI. """
    -
    -    patterns = [
    -        ('SECTION', re.compile(r'\[[^]]+\]')),
    -        ('IGNORE', re.compile(r'[ \r\t\n]+')),
    -        ('COMMENT', re.compile(r'[;#].*')),
    -        ('KEY_VALUE', re.compile(r'[^=\s]+\s*[:=].*')),
    -        ('CONTINUATION', re.compile(r'.*'))
    -    ]
    -
    -    ignore = ['IGNORE']
    -
    -    def pre_scan(self):
    -        """ Prepare string for scaning. """
    -        escape_re = re.compile(r'\\\n[\t ]+')
    -        self.source = escape_re.sub('', self.source)
    -
    -
    -undefined = object()
    -
    -
    -class Section(OrderedDict):
    -
    -    """ Representation of INI section. """
    -
    -    def __init__(self, namespace, *args, **kwargs):
    -        super(Section, self).__init__(*args, **kwargs)
    -        self.namespace = namespace
    -
    -    def __setitem__(self, name, value):
    -        value = str(value)
    -        if value.isdigit():
    -            value = int(value)
    -
    -        super(Section, self).__setitem__(name, value)
    -
    -
    -class InterpolationSection(Section):
    -
    -    """ INI section with interpolation support. """
    -
    -    var_re = re.compile('{([^}]+)}')
    -
    -    def get(self, name, default=None):
    -        """ Get item by name.
    -
    -        :return object: value or None if name not exists
    -
    -        """
    -
    -        if name in self:
    -            return self[name]
    -        return default
    -
    -    def __interpolate__(self, math):
    -        try:
    -            key = math.group(1).strip()
    -            return self.namespace.default.get(key) or self[key]
    -        except KeyError:
    -            return ''
    -
    -    def __getitem__(self, name, raw=False):
    -        value = super(InterpolationSection, self).__getitem__(name)
    -        if not raw:
    -            sample = undefined
    -            while sample != value:
    -                try:
    -                    sample, value = value, self.var_re.sub(
    -                        self.__interpolate__, value)
    -                except RuntimeError:
    -                    message = "Interpolation failed: {0}".format(name)
    -                    NS_LOGGER.error(message)
    -                    raise ValueError(message)
    -        return value
    -
    -    def iteritems(self, raw=False):
    -        """ Iterate self items. """
    -
    -        for key in self:
    -            yield key, self.__getitem__(key, raw=raw)
    -
    -    items = iteritems
    -
    -
    -class Namespace(object):
    -
    -    """ Default class for parsing INI.
    -
    -    :param **default_items: Default items for default section.
    -
    -    Usage
    -    -----
    -
    -    ::
    -
    -        from inirama import Namespace
    -
    -        ns = Namespace()
    -        ns.read('config.ini')
    -
    -        print ns['section']['key']
    -
    -        ns['other']['new'] = 'value'
    -        ns.write('new_config.ini')
    -
    -    """
    -
    -    #: Name of default section (:attr:`~inirama.Namespace.default`)
    -    default_section = 'DEFAULT'
    -
    -    #: Dont raise any exception on file reading erorrs
    -    silent_read = True
    -
    -    #: Class for generating sections
    -    section_type = Section
    -
    -    def __init__(self, **default_items):
    -        self.sections = OrderedDict()
    -        for k, v in default_items.items():
    -            self[self.default_section][k] = v
    -
    -    @property
    -    def default(self):
    -        """ Return default section or empty dict.
    -
    -        :return :class:`inirama.Section`: section
    -
    -        """
    -        return self.sections.get(self.default_section, dict())
    -
    -    def read(self, *files, **params):
    -        """ Read and parse INI files.
    -
    -        :param *files: Files for reading
    -        :param **params: Params for parsing
    -
    -        Set `update=False` for prevent values redefinition.
    -
    -        """
    -        for f in files:
    -            try:
    -                with io.open(f, encoding='utf-8') as ff:
    -                    NS_LOGGER.info('Read from `{0}`'.format(ff.name))
    -                    self.parse(ff.read(), **params)
    -            except (IOError, TypeError, SyntaxError, io.UnsupportedOperation):
    -                if not self.silent_read:
    -                    NS_LOGGER.error('Reading error `{0}`'.format(ff.name))
    -                    raise
    -
    -    def write(self, f):
    -        """ Write namespace as INI file.
    -
    -        :param f: File object or path to file.
    -
    -        """
    -        if isinstance(f, str):
    -            f = io.open(f, 'w', encoding='utf-8')
    -
    -        if not hasattr(f, 'read'):
    -            raise AttributeError("Wrong type of file: {0}".format(type(f)))
    -
    -        NS_LOGGER.info('Write to `{0}`'.format(f.name))
    -        for section in self.sections.keys():
    -            f.write('[{0}]\n'.format(section))
    -            for k, v in self[section].items():
    -                f.write('{0:15}= {1}\n'.format(k, v))
    -            f.write('\n')
    -        f.close()
    -
    -    def parse(self, source, update=True, **params):
    -        """ Parse INI source as string.
    -
    -        :param source: Source of INI
    -        :param update: Replace alredy defined items
    -
    -        """
    -        scanner = INIScanner(source)
    -        scanner.scan()
    -
    -        section = self.default_section
    -        name = None
    -
    -        for token in scanner.tokens:
    -            if token[0] == 'KEY_VALUE':
    -                name, value = re.split('[=:]', token[1], 1)
    -                name, value = name.strip(), value.strip()
    -                if not update and name in self[section]:
    -                    continue
    -                self[section][name] = value
    -
    -            elif token[0] == 'SECTION':
    -                section = token[1].strip('[]')
    -
    -            elif token[0] == 'CONTINUATION':
    -                if not name:
    -                    raise SyntaxError(
    -                        "SyntaxError[@char {0}: {1}]".format(
    -                            token[2], "Bad continuation."))
    -                self[section][name] += '\n' + token[1].strip()
    -
    -    def __getitem__(self, name):
    -        """ Look name in self sections.
    -
    -        :return :class:`inirama.Section`: section
    -
    -        """
    -        if name not in self.sections:
    -            self.sections[name] = self.section_type(self)
    -        return self.sections[name]
    -
    -    def __contains__(self, name):
    -        return name in self.sections
    -
    -    def __repr__(self):
    -        return "".format(self.sections)
    -
    -
    -class InterpolationNamespace(Namespace):
    -
    -    """ That implements the interpolation feature.
    -
    -    ::
    -
    -        from inirama import InterpolationNamespace
    -
    -        ns = InterpolationNamespace()
    -        ns.parse('''
    -            [main]
    -            test = value
    -            foo = bar {test}
    -            more_deep = wow {foo}
    -        ''')
    -        print ns['main']['more_deep']  # wow bar value
    -
    -    """
    -
    -    section_type = InterpolationSection
    -
    -# pylama:ignore=D,W02,E731,W0621
    diff --git a/pymode/libs/pylama/lint/__init__.py b/pymode/libs/pylama/lint/__init__.py
    deleted file mode 100644
    index bd8e8da7..00000000
    --- a/pymode/libs/pylama/lint/__init__.py
    +++ /dev/null
    @@ -1,19 +0,0 @@
    -"""Custom module loader."""
    -
    -
    -class Linter(object):
    -
    -    """Abstract class for linter plugin."""
    -
    -    @staticmethod
    -    def allow(path):
    -        """Check path is relevant for linter.
    -
    -        :return bool:
    -        """
    -        return path.endswith('.py')
    -
    -    @staticmethod
    -    def run(path, **meta):
    -        """Method 'run' should be defined."""
    -        raise NotImplementedError(__doc__)
    diff --git a/pymode/libs/pylama/lint/extensions.py b/pymode/libs/pylama/lint/extensions.py
    deleted file mode 100644
    index bfd83d88..00000000
    --- a/pymode/libs/pylama/lint/extensions.py
    +++ /dev/null
    @@ -1,39 +0,0 @@
    -"""Load extensions."""
    -
    -LINTERS = {}
    -
    -try:
    -    from pylama.lint.pylama_mccabe import Linter
    -    LINTERS['mccabe'] = Linter()
    -except ImportError:
    -    pass
    -
    -try:
    -    from pylama.lint.pylama_pep257 import Linter
    -    LINTERS['pep257'] = Linter()
    -except ImportError:
    -    pass
    -
    -try:
    -    from pylama.lint.pylama_pep8 import Linter
    -    LINTERS['pep8'] = Linter()
    -except ImportError:
    -    pass
    -
    -try:
    -    from pylama.lint.pylama_pyflakes import Linter
    -    LINTERS['pyflakes'] = Linter()
    -except ImportError:
    -    pass
    -
    -
    -from pkg_resources import iter_entry_points
    -
    -for entry in iter_entry_points('pylama.linter'):
    -    if entry.name not in LINTERS:
    -        try:
    -            LINTERS[entry.name] = entry.load()()
    -        except ImportError:
    -            pass
    -
    -#  pylama:ignore=E0611
    diff --git a/pymode/libs/pylama/lint/pylama_mccabe.py b/pymode/libs/pylama/lint/pylama_mccabe.py
    deleted file mode 100644
    index fc191004..00000000
    --- a/pymode/libs/pylama/lint/pylama_mccabe.py
    +++ /dev/null
    @@ -1,29 +0,0 @@
    -"""Code complexity checking."""
    -from mccabe import McCabeChecker
    -
    -from pylama.lint import Linter as Abstract
    -import ast
    -
    -
    -class Linter(Abstract):
    -
    -    """Run complexity checking."""
    -
    -    @staticmethod
    -    def run(path, code=None, params=None, **meta):
    -        """MCCabe code checking.
    -
    -        :return list: List of errors.
    -        """
    -        try:
    -            tree = compile(code, path, "exec", ast.PyCF_ONLY_AST)
    -        except SyntaxError as exc:
    -            return [{'lnum': exc.lineno, 'text': 'Invalid syntax: %s' % exc.text.strip()}]
    -
    -        McCabeChecker.max_complexity = int(params.get('complexity', 10))
    -        return [
    -            {'lnum': lineno, 'offset': offset, 'text': text, 'type': McCabeChecker._code}
    -            for lineno, offset, text, _ in McCabeChecker(tree, path).run()
    -        ]
    -
    -#  pylama:ignore=W0212
    diff --git a/pymode/libs/pylama/lint/pylama_pep257.py b/pymode/libs/pylama/lint/pylama_pep257.py
    deleted file mode 100644
    index 5e1f785c..00000000
    --- a/pymode/libs/pylama/lint/pylama_pep257.py
    +++ /dev/null
    @@ -1,21 +0,0 @@
    -"""PEP257 support."""
    -
    -from pep257 import PEP257Checker
    -
    -from pylama.lint import Linter as Abstract
    -
    -
    -class Linter(Abstract):
    -
    -    """Check PEP257 errors."""
    -
    -    @staticmethod
    -    def run(path, code=None, **meta):
    -        """PEP257 code checking.
    -
    -        :return list: List of errors.
    -        """
    -        return [
    -            {'lnum': e.line, 'text': e.message, 'type': 'D'}
    -            for e in PEP257Checker().check_source(code, path)
    -        ]
    diff --git a/pymode/libs/pylama/lint/pylama_pep8.py b/pymode/libs/pylama/lint/pylama_pep8.py
    deleted file mode 100644
    index 30329d80..00000000
    --- a/pymode/libs/pylama/lint/pylama_pep8.py
    +++ /dev/null
    @@ -1,66 +0,0 @@
    -"""PEP8 support."""
    -from pep8 import BaseReport, StyleGuide, get_parser
    -
    -from pylama.lint import Linter as Abstract
    -
    -
    -try:
    -    from StringIO import StringIO
    -except ImportError:
    -    from io import StringIO
    -
    -
    -class Linter(Abstract):
    -
    -    """PEP8 runner."""
    -
    -    @staticmethod
    -    def run(path, code=None, params=None, **meta):
    -        """Check code with PEP8.
    -
    -        :return list: List of errors.
    -        """
    -        parser = get_parser()
    -        for option in parser.option_list:
    -            if option.dest and option.dest in params:
    -                value = params[option.dest]
    -                if not isinstance(value, str):
    -                    continue
    -                params[option.dest] = option.convert_value(option, params[option.dest])
    -        P8Style = StyleGuide(reporter=_PEP8Report, **params)
    -        buf = StringIO(code)
    -        return P8Style.input_file(path, lines=buf.readlines())
    -
    -
    -class _PEP8Report(BaseReport):
    -
    -    def __init__(self, *args, **kwargs):
    -        super(_PEP8Report, self).__init__(*args, **kwargs)
    -        self.errors = []
    -
    -    def init_file(self, filename, lines, expected, line_offset):
    -        """Prepare storage for errors."""
    -        super(_PEP8Report, self).init_file(
    -            filename, lines, expected, line_offset)
    -        self.errors = []
    -
    -    def error(self, line_number, offset, text, check):
    -        """Save errors."""
    -        code = super(_PEP8Report, self).error(
    -            line_number, offset, text, check)
    -
    -        if code:
    -            self.errors.append(dict(
    -                text=text,
    -                type=code.replace('E', 'C'),
    -                col=offset + 1,
    -                lnum=line_number,
    -            ))
    -
    -    def get_file_results(self):
    -        """Get errors.
    -
    -        :return list: List of errors.
    -
    -        """
    -        return self.errors
    diff --git a/pymode/libs/pylama/lint/pylama_pyflakes.py b/pymode/libs/pylama/lint/pylama_pyflakes.py
    deleted file mode 100644
    index 184d969f..00000000
    --- a/pymode/libs/pylama/lint/pylama_pyflakes.py
    +++ /dev/null
    @@ -1,49 +0,0 @@
    -"""Pyflakes support."""
    -
    -from pyflakes import checker
    -
    -from pylama.lint import Linter as Abstract
    -
    -
    -checker.messages.UnusedImport.message = "W0611 %r imported but unused"
    -checker.messages.RedefinedWhileUnused.message = "W0404 redefinition of unused %r from line %r"
    -checker.messages.RedefinedInListComp.message = "W0621 list comprehension redefines %r from line %r"
    -checker.messages.ImportShadowedByLoopVar.message = "W0621 import %r from line %r shadowed by loop variable"
    -checker.messages.ImportStarUsed.message = "W0401 'from %s import *' used; unable to detect undefined names"
    -checker.messages.UndefinedName.message = "E0602 undefined name %r"
    -checker.messages.DoctestSyntaxError.message = "W0511 syntax error in doctest"
    -checker.messages.UndefinedExport.message = "E0603 undefined name %r in __all__"
    -checker.messages.UndefinedLocal.message = "E0602 local variable %r (defined in enclosing scope on line %r) referenced before assignment"
    -checker.messages.DuplicateArgument.message = "E1122 duplicate argument %r in function definition"
    -checker.messages.LateFutureImport.message = "W0410 future import(s) %r after other statements"
    -checker.messages.UnusedVariable.message = "W0612 local variable %r is assigned to but never used"
    -checker.messages.ReturnWithArgsInsideGenerator.message = "E0106 'return' with argument inside generator"
    -checker.messages.ReturnOutsideFunction.message = "E0104 'return' outside function"
    -
    -
    -class Linter(Abstract):
    -
    -    """Pyflakes runner."""
    -
    -    @staticmethod
    -    def run(path, code=None, params=None, **meta):
    -        """Check code with pyflakes.
    -
    -        :return list: List of errors.
    -        """
    -        import _ast
    -
    -        builtins = params.get("builtins", "")
    -
    -        if builtins:
    -            builtins = builtins.split(",")
    -
    -        tree = compile(code, path, "exec", _ast.PyCF_ONLY_AST)
    -        w = checker.Checker(tree, path, builtins=builtins)
    -        w.messages = sorted(w.messages, key=lambda m: m.lineno)
    -        return [
    -            {'lnum': m.lineno, 'text': m.message % m.message_args}
    -            for m in sorted(w.messages, key=lambda m: m.lineno)
    -        ]
    -
    -#  pylama:ignore=E501,C0301
    diff --git a/pymode/libs/pylama/lint/pylama_pylint/__init__.py b/pymode/libs/pylama/lint/pylama_pylint/__init__.py
    deleted file mode 100644
    index 74e6bc22..00000000
    --- a/pymode/libs/pylama/lint/pylama_pylint/__init__.py
    +++ /dev/null
    @@ -1,12 +0,0 @@
    -""" Description. """
    -
    -# Module information
    -# ==================
    -
    -
    -__version__ = "2.1.1"
    -__project__ = "pylama_pylint"
    -__author__ = "horneds "
    -__license__ = "BSD"
    -
    -from .main import Linter  # noqa
    diff --git a/pymode/libs/pylama/lint/pylama_pylint/main.py b/pymode/libs/pylama/lint/pylama_pylint/main.py
    deleted file mode 100644
    index f50b6647..00000000
    --- a/pymode/libs/pylama/lint/pylama_pylint/main.py
    +++ /dev/null
    @@ -1,111 +0,0 @@
    -""" Pylint support. """
    -from os import path as op, environ
    -import logging
    -
    -from pylama.lint import Linter as BaseLinter
    -
    -CURDIR = op.abspath(op.dirname(__file__))
    -
    -from astroid import MANAGER
    -from pylint.lint import Run
    -from pylint.reporters import BaseReporter
    -
    -HOME_RCFILE = op.abspath(op.join(environ.get('HOME', ''), '.pylintrc'))
    -LAMA_RCFILE = op.abspath(op.join(CURDIR, 'pylint.rc'))
    -
    -
    -logger = logging.getLogger('pylama')
    -
    -
    -class Linter(BaseLinter):
    -
    -    """ Check code with pylint. """
    -
    -    @staticmethod
    -    def run(path, code, params=None, ignore=None, select=None, **meta):
    -        """ Pylint code checking.
    -
    -        :return list: List of errors.
    -
    -        """
    -        logger.debug('Start pylint')
    -
    -        MANAGER.astroid_cache.clear()
    -
    -        class Reporter(BaseReporter):
    -
    -            def __init__(self):
    -                self.errors = []
    -                super(Reporter, self).__init__()
    -
    -            def _display(self, layout):
    -                pass
    -
    -            def add_message(self, msg_id, location, msg):
    -                _, _, line, col = location[1:]
    -                self.errors.append(dict(
    -                    lnum=line,
    -                    col=col,
    -                    text="%s %s" % (msg_id, msg),
    -                    type=msg_id[0]
    -                ))
    -
    -        params = _Params(ignore=ignore, select=select, params=params)
    -        logger.debug(params)
    -
    -        runner = Run(
    -            [path] + params.to_attrs(), reporter=Reporter(), exit=False)
    -
    -        return runner.linter.reporter.errors
    -
    -
    -class _Params(object):
    -
    -    """ Store pylint params. """
    -
    -    def __init__(self, select=None, ignore=None, params=None):
    -
    -        params = dict(params.items())
    -        rcfile = params.get('rcfile', LAMA_RCFILE)
    -        enable = params.get('enable', None)
    -        disable = params.get('disable', None)
    -
    -        if op.exists(HOME_RCFILE):
    -            rcfile = HOME_RCFILE
    -
    -        if select:
    -            enable = select | set(enable.split(",") if enable else [])
    -
    -        if ignore:
    -            disable = ignore | set(disable.split(",") if disable else [])
    -
    -        params.update(dict(
    -            report=params.get('report', False), rcfile=rcfile,
    -            enable=enable, disable=disable))
    -
    -        self.params = dict(
    -            (name.replace('_', '-'), self.prepare_value(value))
    -            for name, value in params.items() if value is not None)
    -
    -    @staticmethod
    -    def prepare_value(value):
    -        """ Prepare value to pylint. """
    -        if isinstance(value, (list, tuple, set)):
    -            return ",".join(value)
    -
    -        if isinstance(value, bool):
    -            return "y" if value else "n"
    -
    -        return str(value)
    -
    -    def to_attrs(self):
    -        """ Convert to argument list. """
    -        return ["--%s=%s" % item for item in self.params.items()]
    -
    -    def __str__(self):
    -        return " ".join(self.to_attrs())
    -
    -    def __repr__(self):
    -        return "" % self
    -
    -# pylama:ignore=W0403
    diff --git a/pymode/libs/pylama/lint/pylama_pylint/pylint.rc b/pymode/libs/pylama/lint/pylama_pylint/pylint.rc
    deleted file mode 100644
    index 799c62f6..00000000
    --- a/pymode/libs/pylama/lint/pylama_pylint/pylint.rc
    +++ /dev/null
    @@ -1,23 +0,0 @@
    -[MESSAGES CONTROL]
    -# Disable the message(s) with the given id(s).
    -# http://pylint-messages.wikidot.com/all-codes
    -#
    -# C0103: Invalid name "%s" (should match %s)
    -# E1101: %s %r has no %r member
    -# R0901: Too many ancestors (%s/%s)
    -# R0902: Too many instance attributes (%s/%s)
    -# R0903: Too few public methods (%s/%s)
    -# R0904: Too many public methods (%s/%s)
    -# R0913: Too many arguments (%s/%s)
    -# R0915: Too many statements (%s/%s)
    -# W0141: Used builtin function %r
    -# W0142: Used * or ** magic
    -# W0221: Arguments number differs from %s method
    -# W0232: Class has no __init__ method
    -# W0613: Unused argument %r
    -# W0631: Using possibly undefined loop variable %r
    -#
    -disable = C0103,E1101,R0901,R0902,R0903,R0904,R0913,R0915,W0141,W0142,W0221,W0232,W0613,W0631
    -
    -[TYPECHECK]
    -generated-members = REQUEST,acl_users,aq_parent,objects,DoesNotExist,_meta,status_code,content,context
    diff --git a/pymode/libs/pylama/main.py b/pymode/libs/pylama/main.py
    deleted file mode 100644
    index 2926ccdb..00000000
    --- a/pymode/libs/pylama/main.py
    +++ /dev/null
    @@ -1,101 +0,0 @@
    -"""Pylama's shell support."""
    -
    -from __future__ import absolute_import, with_statement
    -
    -import sys
    -from os import walk, path as op
    -
    -from .config import parse_options, CURDIR, setup_logger
    -from .core import LOGGER, run
    -from .async import check_async
    -
    -
    -def check_path(options, rootdir=None, candidates=None, code=None):
    -    """Check path.
    -
    -    :param rootdir: Root directory (for making relative file paths)
    -    :param options: Parsed pylama options (from pylama.config.parse_options)
    -
    -    :returns: (list) Errors list
    -
    -    """
    -    if not candidates:
    -        candidates = []
    -        for path_ in options.paths:
    -            path = op.abspath(path_)
    -            if op.isdir(path):
    -                for root, _, files in walk(path):
    -                    candidates += [op.relpath(op.join(root, f), CURDIR) for f in files]
    -            else:
    -                candidates.append(path)
    -
    -    if rootdir is None:
    -        rootdir = path if op.isdir(path) else op.dirname(path)
    -
    -    paths = []
    -    for path in candidates:
    -
    -        if not options.force and not any(l.allow(path) for _, l in options.linters):
    -            continue
    -
    -        if not op.exists(path):
    -            continue
    -
    -        paths.append(path)
    -
    -    if options.async:
    -        return check_async(paths, options, rootdir)
    -
    -    errors = []
    -    for path in paths:
    -        errors += run(path=path, code=code, rootdir=rootdir, options=options)
    -    return errors
    -
    -
    -def shell(args=None, error=True):
    -    """Endpoint for console.
    -
    -    Parse a command arguments, configuration files and run a checkers.
    -
    -    :return list: list of errors
    -    :raise SystemExit:
    -
    -    """
    -    if args is None:
    -        args = sys.argv[1:]
    -
    -    options = parse_options(args)
    -    setup_logger(options)
    -    LOGGER.info(options)
    -
    -    # Install VSC hook
    -    if options.hook:
    -        from .hook import install_hook
    -        return install_hook(options.path)
    -
    -    return process_paths(options, error=error)
    -
    -
    -def process_paths(options, candidates=None, error=True):
    -    """Process files and log errors."""
    -    errors = check_path(options, rootdir=CURDIR, candidates=candidates)
    -
    -    pattern = "%(filename)s:%(lnum)s:%(col)s: %(text)s"
    -    if options.format == 'pylint':
    -        pattern = "%(filename)s:%(lnum)s: [%(type)s] %(text)s"
    -
    -    for er in errors:
    -        if options.abspath:
    -            er._info['filename'] = op.abspath(er.filename)
    -        LOGGER.warning(pattern, er._info)
    -
    -    if error:
    -        sys.exit(int(bool(errors)))
    -
    -    return errors
    -
    -
    -if __name__ == '__main__':
    -    shell()
    -
    -# pylama:ignore=F0001
    diff --git a/pymode/libs/pylama/pytest.py b/pymode/libs/pylama/pytest.py
    deleted file mode 100644
    index eeaa58ce..00000000
    --- a/pymode/libs/pylama/pytest.py
    +++ /dev/null
    @@ -1,87 +0,0 @@
    -""" py.test plugin for checking files with pylama. """
    -from __future__ import absolute_import
    -
    -from os import path as op
    -
    -import py # noqa
    -import pytest
    -
    -
    -HISTKEY = "pylama/mtimes"
    -
    -
    -def pytest_addoption(parser):
    -    group = parser.getgroup("general")
    -    group.addoption(
    -        '--pylama', action='store_true',
    -        help="perform some pylama code checks on .py files")
    -
    -
    -def pytest_sessionstart(session):
    -    config = session.config
    -    if config.option.pylama and getattr(config, 'cache', None):
    -        config._pylamamtimes = config.cache.get(HISTKEY, {})
    -
    -
    -def pytest_sessionfinish(session):
    -    config = session.config
    -    if hasattr(config, "_pylamamtimes"):
    -        config.cache.set(HISTKEY, config._pylamamtimes)
    -
    -
    -def pytest_collect_file(path, parent):
    -    config = parent.config
    -    if config.option.pylama and path.ext == '.py':
    -        return PylamaItem(path, parent)
    -
    -
    -class PylamaError(Exception):
    -    """ indicates an error during pylama checks. """
    -
    -
    -class PylamaItem(pytest.Item, pytest.File):
    -
    -    def __init__(self, path, parent):
    -        super(PylamaItem, self).__init__(path, parent)
    -        self.add_marker("pep8")
    -        self.cache = None
    -        self._pylamamtimes = None
    -
    -    def setup(self):
    -        if not getattr(self.config, 'cache', None):
    -            return False
    -
    -        self.cache = True
    -        self._pylamamtimes = self.fspath.mtime()
    -        pylamamtimes = self.config._pylamamtimes
    -        old = pylamamtimes.get(str(self.fspath), 0)
    -        if old == self._pylamamtimes:
    -            pytest.skip("file(s) previously passed Pylama checks")
    -
    -    def runtest(self):
    -        errors = check_file(self.fspath)
    -        if errors:
    -            pattern = "%(filename)s:%(lnum)s:%(col)s: %(text)s"
    -            out = "\n".join([pattern % e._info for e in errors])
    -            raise PylamaError(out)
    -
    -        # update mtime only if test passed
    -        # otherwise failures would not be re-run next time
    -        if self.cache:
    -            self.config._pylamamtimes[str(self.fspath)] = self._pylamamtimes
    -
    -    def repr_failure(self, excinfo):
    -        if excinfo.errisinstance(PylamaError):
    -            return excinfo.value.args[0]
    -        return super(PylamaItem, self).repr_failure(excinfo)
    -
    -
    -def check_file(path):
    -    from pylama.main import parse_options, process_paths
    -    from pylama.config import CURDIR
    -
    -    options = parse_options()
    -    path = op.relpath(str(path), CURDIR)
    -    return process_paths(options, candidates=[path], error=False)
    -
    -# pylama:ignore=D,E1002,W0212,F0001
    diff --git a/pymode/libs/pylint/__init__.py b/pymode/libs/pylint/__init__.py
    deleted file mode 100644
    index 82e557dc..00000000
    --- a/pymode/libs/pylint/__init__.py
    +++ /dev/null
    @@ -1,46 +0,0 @@
    -# Copyright (c) 2003-2012 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -import sys
    -
    -from .__pkginfo__ import version as __version__
    -
    -def run_pylint():
    -    """run pylint"""
    -    from pylint.lint import Run
    -    Run(sys.argv[1:])
    -
    -def run_pylint_gui():
    -    """run pylint-gui"""
    -    try:
    -        from pylint.gui import Run
    -        Run(sys.argv[1:])
    -    except ImportError:
    -        sys.exit('tkinter is not available')
    -
    -def run_epylint():
    -    """run pylint"""
    -    from pylint.epylint import Run
    -    Run()
    -
    -def run_pyreverse():
    -    """run pyreverse"""
    -    from pylint.pyreverse.main import Run
    -    Run(sys.argv[1:])
    -
    -def run_symilar():
    -    """run symilar"""
    -    from pylint.checkers.similar import Run
    -    Run(sys.argv[1:])
    diff --git a/pymode/libs/pylint/__main__.py b/pymode/libs/pylint/__main__.py
    deleted file mode 100644
    index 7716361d..00000000
    --- a/pymode/libs/pylint/__main__.py
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -#!/usr/bin/env python
    -import pylint
    -pylint.run_pylint()
    diff --git a/pymode/libs/pylint/__pkginfo__.py b/pymode/libs/pylint/__pkginfo__.py
    deleted file mode 100644
    index 33ae5b64..00000000
    --- a/pymode/libs/pylint/__pkginfo__.py
    +++ /dev/null
    @@ -1,70 +0,0 @@
    -# pylint: disable=W0622,C0103
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""pylint packaging information"""
    -from __future__ import absolute_import
    -
    -modname = distname = 'pylint'
    -
    -numversion = (1, 4, 4)
    -version = '.'.join([str(num) for num in numversion])
    -
    -install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.3.6', 'six']
    -
    -license = 'GPL'
    -description = "python code static checker"
    -web = 'http://www.pylint.org'
    -mailinglist = "mailto://code-quality@python.org"
    -author = 'Logilab'
    -author_email = 'python-projects@lists.logilab.org'
    -
    -classifiers = ['Development Status :: 4 - Beta',
    -               'Environment :: Console',
    -               'Intended Audience :: Developers',
    -               'License :: OSI Approved :: GNU General Public License (GPL)',
    -               'Operating System :: OS Independent',
    -               'Programming Language :: Python',
    -               'Programming Language :: Python :: 2',
    -               'Programming Language :: Python :: 3',
    -               'Topic :: Software Development :: Debuggers',
    -               'Topic :: Software Development :: Quality Assurance',
    -               'Topic :: Software Development :: Testing'
    -              ]
    -
    -
    -long_desc = """\
    - Pylint is a Python source code analyzer which looks for programming
    - errors, helps enforcing a coding standard and sniffs for some code
    - smells (as defined in Martin Fowler's Refactoring book)
    - .
    - Pylint can be seen as another PyChecker since nearly all tests you
    - can do with PyChecker can also be done with Pylint. However, Pylint
    - offers some more features, like checking length of lines of code,
    - checking if variable names are well-formed according to your coding
    - standard, or checking if declared interfaces are truly implemented,
    - and much more.
    - .
    - Additionally, it is possible to write plugins to add your own checks.
    - .
    - Pylint is shipped with "pylint-gui", "pyreverse" (UML diagram generator)
    - and "symilar" (an independent similarities checker)."""
    -
    -from os.path import join
    -scripts = [join('bin', filename)
    -           for filename in ('pylint', 'pylint-gui', "symilar", "epylint",
    -                            "pyreverse")]
    -
    -include_dirs = [join('pylint', 'test')]
    diff --git a/pymode/libs/pylint/checkers/__init__.py b/pymode/libs/pylint/checkers/__init__.py
    deleted file mode 100644
    index 51adb4d0..00000000
    --- a/pymode/libs/pylint/checkers/__init__.py
    +++ /dev/null
    @@ -1,124 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""utilities methods and classes for checkers
    -
    -Base id of standard checkers (used in msg and report ids):
    -01: base
    -02: classes
    -03: format
    -04: import
    -05: misc
    -06: variables
    -07: exceptions
    -08: similar
    -09: design_analysis
    -10: newstyle
    -11: typecheck
    -12: logging
    -13: string_format
    -14: string_constant
    -15: stdlib
    -16: python3
    -17-50: not yet used: reserved for future internal checkers.
    -51-99: perhaps used: reserved for external checkers
    -
    -The raw_metrics checker has no number associated since it doesn't emit any
    -messages nor reports. XXX not true, emit a 07 report !
    -
    -"""
    -
    -import sys
    -import tokenize
    -import warnings
    -
    -from logilab.common.configuration import OptionsProviderMixIn
    -
    -from pylint.reporters import diff_string
    -from pylint.utils import register_plugins
    -from pylint.interfaces import UNDEFINED
    -
    -
    -def table_lines_from_stats(stats, old_stats, columns):
    -    """get values listed in  from  and ,
    -    and return a formated list of values, designed to be given to a
    -    ureport.Table object
    -    """
    -    lines = []
    -    for m_type in columns:
    -        new = stats[m_type]
    -        format = str # pylint: disable=redefined-builtin
    -        if isinstance(new, float):
    -            format = lambda num: '%.3f' % num
    -        old = old_stats.get(m_type)
    -        if old is not None:
    -            diff_str = diff_string(old, new)
    -            old = format(old)
    -        else:
    -            old, diff_str = 'NC', 'NC'
    -        lines += (m_type.replace('_', ' '), format(new), old, diff_str)
    -    return lines
    -
    -
    -class BaseChecker(OptionsProviderMixIn):
    -    """base class for checkers"""
    -    # checker name (you may reuse an existing one)
    -    name = None
    -    # options level (0 will be displaying in --help, 1 in --long-help)
    -    level = 1
    -    # ordered list of options to control the ckecker behaviour
    -    options = ()
    -    # messages issued by this checker
    -    msgs = {}
    -    # reports issued by this checker
    -    reports = ()
    -    # mark this checker as enabled or not.
    -    enabled = True
    -
    -    def __init__(self, linter=None):
    -        """checker instances should have the linter as argument
    -
    -        linter is an object implementing ILinter
    -        """
    -        self.name = self.name.lower()
    -        OptionsProviderMixIn.__init__(self)
    -        self.linter = linter
    -
    -    def add_message(self, msg_id, line=None, node=None, args=None, confidence=UNDEFINED):
    -        """add a message of a given type"""
    -        self.linter.add_message(msg_id, line, node, args, confidence)
    -
    -    # dummy methods implementing the IChecker interface
    -
    -    def open(self):
    -        """called before visiting project (i.e set of modules)"""
    -
    -    def close(self):
    -        """called after visiting project (i.e set of modules)"""
    -
    -
    -class BaseTokenChecker(BaseChecker):
    -    """Base class for checkers that want to have access to the token stream."""
    -
    -    def process_tokens(self, tokens):
    -        """Should be overridden by subclasses."""
    -        raise NotImplementedError()
    -
    -
    -def initialize(linter):
    -    """initialize linter with checkers in this package """
    -    register_plugins(linter, __path__[0])
    -
    -__all__ = ('BaseChecker', 'initialize')
    diff --git a/pymode/libs/pylint/checkers/base.py b/pymode/libs/pylint/checkers/base.py
    deleted file mode 100644
    index 6ce88251..00000000
    --- a/pymode/libs/pylint/checkers/base.py
    +++ /dev/null
    @@ -1,1236 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# Copyright (c) 2009-2010 Arista Networks, Inc.
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""basic checker for Python code"""
    -
    -import collections
    -import itertools
    -import sys
    -import re
    -
    -import six
    -from six.moves import zip  # pylint: disable=redefined-builtin
    -
    -from logilab.common.ureports import Table
    -
    -import astroid
    -import astroid.bases
    -from astroid import are_exclusive, InferenceError
    -
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
    -from pylint.utils import EmptyReport
    -from pylint.reporters import diff_string
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    check_messages,
    -    clobber_in_except,
    -    is_builtin_object,
    -    is_inside_except,
    -    overrides_a_method,
    -    safe_infer,
    -    get_argument_from_call,
    -    has_known_bases,
    -    NoSuchArgumentError,
    -    is_import_error,
    -    unimplemented_abstract_methods,
    -    )
    -
    -
    -# regex for class/function/variable/constant name
    -CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$')
    -MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$')
    -CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$')
    -COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$')
    -DEFAULT_NAME_RGX = re.compile('[a-z_][a-z0-9_]{2,30}$')
    -CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$')
    -# do not require a doc string on system methods
    -NO_REQUIRED_DOC_RGX = re.compile('__.*__')
    -REVERSED_METHODS = (('__getitem__', '__len__'),
    -                    ('__reversed__', ))
    -
    -PY33 = sys.version_info >= (3, 3)
    -PY3K = sys.version_info >= (3, 0)
    -BAD_FUNCTIONS = ['map', 'filter']
    -if sys.version_info < (3, 0):
    -    BAD_FUNCTIONS.append('input')
    -
    -# Name categories that are always consistent with all naming conventions.
    -EXEMPT_NAME_CATEGORIES = set(('exempt', 'ignore'))
    -
    -# A mapping from builtin-qname -> symbol, to be used when generating messages
    -# about dangerous default values as arguments
    -DEFAULT_ARGUMENT_SYMBOLS = dict(
    -    zip(['.'.join([astroid.bases.BUILTINS, x]) for x in ('set', 'dict', 'list')],
    -        ['set()', '{}', '[]'])
    -)
    -
    -del re
    -
    -def _redefines_import(node):
    -    """ Detect that the given node (AssName) is inside an
    -    exception handler and redefines an import from the tryexcept body.
    -    Returns True if the node redefines an import, False otherwise.
    -    """
    -    current = node
    -    while current and not isinstance(current.parent, astroid.ExceptHandler):
    -        current = current.parent
    -    if not current or not is_import_error(current.parent):
    -        return False
    -    try_block = current.parent.parent
    -    for import_node in try_block.nodes_of_class((astroid.From, astroid.Import)):
    -        for name, alias in import_node.names:
    -            if alias:
    -                if alias == node.name:
    -                    return True
    -            elif name == node.name:
    -                return True
    -    return False
    -
    -def in_loop(node):
    -    """return True if the node is inside a kind of for loop"""
    -    parent = node.parent
    -    while parent is not None:
    -        if isinstance(parent, (astroid.For, astroid.ListComp, astroid.SetComp,
    -                               astroid.DictComp, astroid.GenExpr)):
    -            return True
    -        parent = parent.parent
    -    return False
    -
    -def in_nested_list(nested_list, obj):
    -    """return true if the object is an element of  or of a nested
    -    list
    -    """
    -    for elmt in nested_list:
    -        if isinstance(elmt, (list, tuple)):
    -            if in_nested_list(elmt, obj):
    -                return True
    -        elif elmt == obj:
    -            return True
    -    return False
    -
    -def _loop_exits_early(loop):
    -    """Returns true if a loop has a break statement in its body."""
    -    loop_nodes = (astroid.For, astroid.While)
    -    # Loop over body explicitly to avoid matching break statements
    -    # in orelse.
    -    for child in loop.body:
    -        if isinstance(child, loop_nodes):
    -            # break statement may be in orelse of child loop.
    -            # pylint: disable=superfluous-parens
    -            for orelse in (child.orelse or ()):
    -                for _ in orelse.nodes_of_class(astroid.Break, skip_klass=loop_nodes):
    -                    return True
    -            continue
    -        for _ in child.nodes_of_class(astroid.Break, skip_klass=loop_nodes):
    -            return True
    -    return False
    -
    -def _is_multi_naming_match(match, node_type, confidence):
    -    return (match is not None and
    -            match.lastgroup is not None and
    -            match.lastgroup not in EXEMPT_NAME_CATEGORIES
    -            and (node_type != 'method' or confidence != INFERENCE_FAILURE))
    -
    -
    -if sys.version_info < (3, 0):
    -    PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty'))
    -else:
    -    PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty'))
    -
    -
    -def _determine_function_name_type(node):
    -    """Determine the name type whose regex the a function's name should match.
    -
    -    :param node: A function node.
    -    :returns: One of ('function', 'method', 'attr')
    -    """
    -    if not node.is_method():
    -        return 'function'
    -    if node.decorators:
    -        decorators = node.decorators.nodes
    -    else:
    -        decorators = []
    -    for decorator in decorators:
    -        # If the function is a property (decorated with @property
    -        # or @abc.abstractproperty), the name type is 'attr'.
    -        if (isinstance(decorator, astroid.Name) or
    -                (isinstance(decorator, astroid.Getattr) and
    -                 decorator.attrname == 'abstractproperty')):
    -            infered = safe_infer(decorator)
    -            if infered and infered.qname() in PROPERTY_CLASSES:
    -                return 'attr'
    -        # If the function is decorated using the prop_method.{setter,getter}
    -        # form, treat it like an attribute as well.
    -        elif (isinstance(decorator, astroid.Getattr) and
    -              decorator.attrname in ('setter', 'deleter')):
    -            return 'attr'
    -    return 'method'
    -
    -
    -
    -def _has_abstract_methods(node):
    -    """
    -    Determine if the given `node` has abstract methods.
    -
    -    The methods should be made abstract by decorating them
    -    with `abc` decorators.
    -    """
    -    return len(unimplemented_abstract_methods(node)) > 0
    -
    -
    -def report_by_type_stats(sect, stats, old_stats):
    -    """make a report of
    -
    -    * percentage of different types documented
    -    * percentage of different types with a bad name
    -    """
    -    # percentage of different types documented and/or with a bad name
    -    nice_stats = {}
    -    for node_type in ('module', 'class', 'method', 'function'):
    -        try:
    -            total = stats[node_type]
    -        except KeyError:
    -            raise EmptyReport()
    -        nice_stats[node_type] = {}
    -        if total != 0:
    -            try:
    -                documented = total - stats['undocumented_'+node_type]
    -                percent = (documented * 100.) / total
    -                nice_stats[node_type]['percent_documented'] = '%.2f' % percent
    -            except KeyError:
    -                nice_stats[node_type]['percent_documented'] = 'NC'
    -            try:
    -                percent = (stats['badname_'+node_type] * 100.) / total
    -                nice_stats[node_type]['percent_badname'] = '%.2f' % percent
    -            except KeyError:
    -                nice_stats[node_type]['percent_badname'] = 'NC'
    -    lines = ('type', 'number', 'old number', 'difference',
    -             '%documented', '%badname')
    -    for node_type in ('module', 'class', 'method', 'function'):
    -        new = stats[node_type]
    -        old = old_stats.get(node_type, None)
    -        if old is not None:
    -            diff_str = diff_string(old, new)
    -        else:
    -            old, diff_str = 'NC', 'NC'
    -        lines += (node_type, str(new), str(old), diff_str,
    -                  nice_stats[node_type].get('percent_documented', '0'),
    -                  nice_stats[node_type].get('percent_badname', '0'))
    -    sect.append(Table(children=lines, cols=6, rheaders=1))
    -
    -def redefined_by_decorator(node):
    -    """return True if the object is a method redefined via decorator.
    -
    -    For example:
    -        @property
    -        def x(self): return self._x
    -        @x.setter
    -        def x(self, value): self._x = value
    -    """
    -    if node.decorators:
    -        for decorator in node.decorators.nodes:
    -            if (isinstance(decorator, astroid.Getattr) and
    -                    getattr(decorator.expr, 'name', None) == node.name):
    -                return True
    -    return False
    -
    -class _BasicChecker(BaseChecker):
    -    __implements__ = IAstroidChecker
    -    name = 'basic'
    -
    -class BasicErrorChecker(_BasicChecker):
    -    msgs = {
    -        'E0100': ('__init__ method is a generator',
    -                  'init-is-generator',
    -                  'Used when the special class method __init__ is turned into a '
    -                  'generator by a yield in its body.'),
    -        'E0101': ('Explicit return in __init__',
    -                  'return-in-init',
    -                  'Used when the special class method __init__ has an explicit '
    -                  'return value.'),
    -        'E0102': ('%s already defined line %s',
    -                  'function-redefined',
    -                  'Used when a function / class / method is redefined.'),
    -        'E0103': ('%r not properly in loop',
    -                  'not-in-loop',
    -                  'Used when break or continue keywords are used outside a loop.'),
    -        'E0104': ('Return outside function',
    -                  'return-outside-function',
    -                  'Used when a "return" statement is found outside a function or '
    -                  'method.'),
    -        'E0105': ('Yield outside function',
    -                  'yield-outside-function',
    -                  'Used when a "yield" statement is found outside a function or '
    -                  'method.'),
    -        'E0106': ('Return with argument inside generator',
    -                  'return-arg-in-generator',
    -                  'Used when a "return" statement with an argument is found '
    -                  'outside in a generator function or method (e.g. with some '
    -                  '"yield" statements).',
    -                  {'maxversion': (3, 3)}),
    -        'E0107': ("Use of the non-existent %s operator",
    -                  'nonexistent-operator',
    -                  "Used when you attempt to use the C-style pre-increment or"
    -                  "pre-decrement operator -- and ++, which doesn't exist in Python."),
    -        'E0108': ('Duplicate argument name %s in function definition',
    -                  'duplicate-argument-name',
    -                  'Duplicate argument names in function definitions are syntax'
    -                  ' errors.'),
    -        'E0110': ('Abstract class %r with abstract methods instantiated',
    -                  'abstract-class-instantiated',
    -                  'Used when an abstract class with `abc.ABCMeta` as metaclass '
    -                  'has abstract methods and is instantiated.'),
    -        'W0120': ('Else clause on loop without a break statement',
    -                  'useless-else-on-loop',
    -                  'Loops should only have an else clause if they can exit early '
    -                  'with a break statement, otherwise the statements under else '
    -                  'should be on the same scope as the loop itself.'),
    -        }
    -
    -    @check_messages('function-redefined')
    -    def visit_class(self, node):
    -        self._check_redefinition('class', node)
    -
    -    @check_messages('init-is-generator', 'return-in-init',
    -                    'function-redefined', 'return-arg-in-generator',
    -                    'duplicate-argument-name')
    -    def visit_function(self, node):
    -        if not redefined_by_decorator(node):
    -            self._check_redefinition(node.is_method() and 'method' or 'function', node)
    -        # checks for max returns, branch, return in __init__
    -        returns = node.nodes_of_class(astroid.Return,
    -                                      skip_klass=(astroid.Function, astroid.Class))
    -        if node.is_method() and node.name == '__init__':
    -            if node.is_generator():
    -                self.add_message('init-is-generator', node=node)
    -            else:
    -                values = [r.value for r in returns]
    -                # Are we returning anything but None from constructors
    -                if [v for v in values
    -                        if not (v is None or
    -                                (isinstance(v, astroid.Const) and v.value is None) or
    -                                (isinstance(v, astroid.Name)  and v.name == 'None')
    -                               )]:
    -                    self.add_message('return-in-init', node=node)
    -        elif node.is_generator():
    -            # make sure we don't mix non-None returns and yields
    -            if not PY33:
    -                for retnode in returns:
    -                    if isinstance(retnode.value, astroid.Const) and \
    -                           retnode.value.value is not None:
    -                        self.add_message('return-arg-in-generator', node=node,
    -                                         line=retnode.fromlineno)
    -        # Check for duplicate names
    -        args = set()
    -        for name in node.argnames():
    -            if name in args:
    -                self.add_message('duplicate-argument-name', node=node, args=(name,))
    -            else:
    -                args.add(name)
    -
    -
    -    @check_messages('return-outside-function')
    -    def visit_return(self, node):
    -        if not isinstance(node.frame(), astroid.Function):
    -            self.add_message('return-outside-function', node=node)
    -
    -    @check_messages('yield-outside-function')
    -    def visit_yield(self, node):
    -        if not isinstance(node.frame(), (astroid.Function, astroid.Lambda)):
    -            self.add_message('yield-outside-function', node=node)
    -
    -    @check_messages('not-in-loop')
    -    def visit_continue(self, node):
    -        self._check_in_loop(node, 'continue')
    -
    -    @check_messages('not-in-loop')
    -    def visit_break(self, node):
    -        self._check_in_loop(node, 'break')
    -
    -    @check_messages('useless-else-on-loop')
    -    def visit_for(self, node):
    -        self._check_else_on_loop(node)
    -
    -    @check_messages('useless-else-on-loop')
    -    def visit_while(self, node):
    -        self._check_else_on_loop(node)
    -
    -    @check_messages('nonexistent-operator')
    -    def visit_unaryop(self, node):
    -        """check use of the non-existent ++ and -- operator operator"""
    -        if ((node.op in '+-') and
    -                isinstance(node.operand, astroid.UnaryOp) and
    -                (node.operand.op == node.op)):
    -            self.add_message('nonexistent-operator', node=node, args=node.op*2)
    -
    -    @check_messages('abstract-class-instantiated')
    -    def visit_callfunc(self, node):
    -        """ Check instantiating abstract class with
    -        abc.ABCMeta as metaclass.
    -        """
    -        try:
    -            infered = next(node.func.infer())
    -        except astroid.InferenceError:
    -            return
    -        if not isinstance(infered, astroid.Class):
    -            return
    -        # __init__ was called
    -        metaclass = infered.metaclass()
    -        abstract_methods = _has_abstract_methods(infered)
    -        if metaclass is None:
    -            # Python 3.4 has `abc.ABC`, which won't be detected
    -            # by ClassNode.metaclass()
    -            for ancestor in infered.ancestors():
    -                if ancestor.qname() == 'abc.ABC' and abstract_methods:
    -                    self.add_message('abstract-class-instantiated',
    -                                     args=(infered.name, ),
    -                                     node=node)
    -                    break
    -            return
    -        if metaclass.qname() == 'abc.ABCMeta' and abstract_methods:
    -            self.add_message('abstract-class-instantiated',
    -                             args=(infered.name, ),
    -                             node=node)
    -
    -    def _check_else_on_loop(self, node):
    -        """Check that any loop with an else clause has a break statement."""
    -        if node.orelse and not _loop_exits_early(node):
    -            self.add_message('useless-else-on-loop', node=node,
    -                             # This is not optimal, but the line previous
    -                             # to the first statement in the else clause
    -                             # will usually be the one that contains the else:.
    -                             line=node.orelse[0].lineno - 1)
    -
    -    def _check_in_loop(self, node, node_name):
    -        """check that a node is inside a for or while loop"""
    -        _node = node.parent
    -        while _node:
    -            if isinstance(_node, (astroid.For, astroid.While)):
    -                break
    -            _node = _node.parent
    -        else:
    -            self.add_message('not-in-loop', node=node, args=node_name)
    -
    -    def _check_redefinition(self, redeftype, node):
    -        """check for redefinition of a function / method / class name"""
    -        defined_self = node.parent.frame()[node.name]
    -        if defined_self is not node and not are_exclusive(node, defined_self):
    -            self.add_message('function-redefined', node=node,
    -                             args=(redeftype, defined_self.fromlineno))
    -
    -
    -
    -class BasicChecker(_BasicChecker):
    -    """checks for :
    -    * doc strings
    -    * number of arguments, local variables, branches, returns and statements in
    -functions, methods
    -    * required module attributes
    -    * dangerous default values as arguments
    -    * redefinition of function / method / class
    -    * uses of the global statement
    -    """
    -
    -    __implements__ = IAstroidChecker
    -
    -    name = 'basic'
    -    msgs = {
    -        'W0101': ('Unreachable code',
    -                  'unreachable',
    -                  'Used when there is some code behind a "return" or "raise" '
    -                  'statement, which will never be accessed.'),
    -        'W0102': ('Dangerous default value %s as argument',
    -                  'dangerous-default-value',
    -                  'Used when a mutable value as list or dictionary is detected in '
    -                  'a default value for an argument.'),
    -        'W0104': ('Statement seems to have no effect',
    -                  'pointless-statement',
    -                  'Used when a statement doesn\'t have (or at least seems to) '
    -                  'any effect.'),
    -        'W0105': ('String statement has no effect',
    -                  'pointless-string-statement',
    -                  'Used when a string is used as a statement (which of course '
    -                  'has no effect). This is a particular case of W0104 with its '
    -                  'own message so you can easily disable it if you\'re using '
    -                  'those strings as documentation, instead of comments.'),
    -        'W0106': ('Expression "%s" is assigned to nothing',
    -                  'expression-not-assigned',
    -                  'Used when an expression that is not a function call is assigned '
    -                  'to nothing. Probably something else was intended.'),
    -        'W0108': ('Lambda may not be necessary',
    -                  'unnecessary-lambda',
    -                  'Used when the body of a lambda expression is a function call '
    -                  'on the same argument list as the lambda itself; such lambda '
    -                  'expressions are in all but a few cases replaceable with the '
    -                  'function being called in the body of the lambda.'),
    -        'W0109': ("Duplicate key %r in dictionary",
    -                  'duplicate-key',
    -                  'Used when a dictionary expression binds the same key multiple '
    -                  'times.'),
    -        'W0122': ('Use of exec',
    -                  'exec-used',
    -                  'Used when you use the "exec" statement (function for Python '
    -                  '3), to discourage its usage. That doesn\'t '
    -                  'mean you can not use it !'),
    -        'W0123': ('Use of eval',
    -                  'eval-used',
    -                  'Used when you use the "eval" function, to discourage its '
    -                  'usage. Consider using `ast.literal_eval` for safely evaluating '
    -                  'strings containing Python expressions '
    -                  'from untrusted sources. '),
    -        'W0141': ('Used builtin function %r',
    -                  'bad-builtin',
    -                  'Used when a black listed builtin function is used (see the '
    -                  'bad-function option). Usual black listed functions are the ones '
    -                  'like map, or filter , where Python offers now some cleaner '
    -                  'alternative like list comprehension.'),
    -        'W0150': ("%s statement in finally block may swallow exception",
    -                  'lost-exception',
    -                  'Used when a break or a return statement is found inside the '
    -                  'finally clause of a try...finally block: the exceptions raised '
    -                  'in the try clause will be silently swallowed instead of being '
    -                  're-raised.'),
    -        'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?',
    -                  'assert-on-tuple',
    -                  'A call of assert on a tuple will always evaluate to true if '
    -                  'the tuple is not empty, and will always evaluate to false if '
    -                  'it is.'),
    -        'C0121': ('Missing required attribute "%s"', # W0103
    -                  'missing-module-attribute',
    -                  'Used when an attribute required for modules is missing.'),
    -
    -        'E0109': ('Missing argument to reversed()',
    -                  'missing-reversed-argument',
    -                  'Used when reversed() builtin didn\'t receive an argument.'),
    -        'E0111': ('The first reversed() argument is not a sequence',
    -                  'bad-reversed-sequence',
    -                  'Used when the first argument to reversed() builtin '
    -                  'isn\'t a sequence (does not implement __reversed__, '
    -                  'nor __getitem__ and __len__'),
    -
    -    }
    -
    -    options = (('required-attributes',
    -                {'default' : (), 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'Required attributes for module, separated by a '
    -                          'comma'}
    -               ),
    -               ('bad-functions',
    -                {'default' : BAD_FUNCTIONS,
    -                 'type' :'csv', 'metavar' : '',
    -                 'help' : 'List of builtins function names that should not be '
    -                          'used, separated by a comma'}
    -               ),
    -              )
    -    reports = (('RP0101', 'Statistics by type', report_by_type_stats),)
    -
    -    def __init__(self, linter):
    -        _BasicChecker.__init__(self, linter)
    -        self.stats = None
    -        self._tryfinallys = None
    -
    -    def open(self):
    -        """initialize visit variables and statistics
    -        """
    -        self._tryfinallys = []
    -        self.stats = self.linter.add_stats(module=0, function=0,
    -                                           method=0, class_=0)
    -
    -    @check_messages('missing-module-attribute')
    -    def visit_module(self, node):
    -        """check module name, docstring and required arguments
    -        """
    -        self.stats['module'] += 1
    -        for attr in self.config.required_attributes:
    -            if attr not in node:
    -                self.add_message('missing-module-attribute', node=node, args=attr)
    -
    -    def visit_class(self, node): # pylint: disable=unused-argument
    -        """check module name, docstring and redefinition
    -        increment branch counter
    -        """
    -        self.stats['class'] += 1
    -
    -    @check_messages('pointless-statement', 'pointless-string-statement',
    -                    'expression-not-assigned')
    -    def visit_discard(self, node):
    -        """check for various kind of statements without effect"""
    -        expr = node.value
    -        if isinstance(expr, astroid.Const) and isinstance(expr.value,
    -                                                          six.string_types):
    -            # treat string statement in a separated message
    -            # Handle PEP-257 attribute docstrings.
    -            # An attribute docstring is defined as being a string right after
    -            # an assignment at the module level, class level or __init__ level.
    -            scope = expr.scope()
    -            if isinstance(scope, (astroid.Class, astroid.Module, astroid.Function)):
    -                if isinstance(scope, astroid.Function) and scope.name != '__init__':
    -                    pass
    -                else:
    -                    sibling = expr.previous_sibling()
    -                    if (sibling is not None and sibling.scope() is scope and
    -                            isinstance(sibling, astroid.Assign)):
    -                        return
    -            self.add_message('pointless-string-statement', node=node)
    -            return
    -        # ignore if this is :
    -        # * a direct function call
    -        # * the unique child of a try/except body
    -        # * a yield (which are wrapped by a discard node in _ast XXX)
    -        # warn W0106 if we have any underlying function call (we can't predict
    -        # side effects), else pointless-statement
    -        if (isinstance(expr, (astroid.Yield, astroid.CallFunc)) or
    -                (isinstance(node.parent, astroid.TryExcept) and
    -                 node.parent.body == [node])):
    -            return
    -        if any(expr.nodes_of_class(astroid.CallFunc)):
    -            self.add_message('expression-not-assigned', node=node,
    -                             args=expr.as_string())
    -        else:
    -            self.add_message('pointless-statement', node=node)
    -
    -    @check_messages('unnecessary-lambda')
    -    def visit_lambda(self, node):
    -        """check whether or not the lambda is suspicious
    -        """
    -        # if the body of the lambda is a call expression with the same
    -        # argument list as the lambda itself, then the lambda is
    -        # possibly unnecessary and at least suspicious.
    -        if node.args.defaults:
    -            # If the arguments of the lambda include defaults, then a
    -            # judgment cannot be made because there is no way to check
    -            # that the defaults defined by the lambda are the same as
    -            # the defaults defined by the function called in the body
    -            # of the lambda.
    -            return
    -        call = node.body
    -        if not isinstance(call, astroid.CallFunc):
    -            # The body of the lambda must be a function call expression
    -            # for the lambda to be unnecessary.
    -            return
    -        # XXX are lambda still different with astroid >= 0.18 ?
    -        # *args and **kwargs need to be treated specially, since they
    -        # are structured differently between the lambda and the function
    -        # call (in the lambda they appear in the args.args list and are
    -        # indicated as * and ** by two bits in the lambda's flags, but
    -        # in the function call they are omitted from the args list and
    -        # are indicated by separate attributes on the function call node).
    -        ordinary_args = list(node.args.args)
    -        if node.args.kwarg:
    -            if (not call.kwargs
    -                    or not isinstance(call.kwargs, astroid.Name)
    -                    or node.args.kwarg != call.kwargs.name):
    -                return
    -        elif call.kwargs:
    -            return
    -        if node.args.vararg:
    -            if (not call.starargs
    -                    or not isinstance(call.starargs, astroid.Name)
    -                    or node.args.vararg != call.starargs.name):
    -                return
    -        elif call.starargs:
    -            return
    -        # The "ordinary" arguments must be in a correspondence such that:
    -        # ordinary_args[i].name == call.args[i].name.
    -        if len(ordinary_args) != len(call.args):
    -            return
    -        for i in range(len(ordinary_args)):
    -            if not isinstance(call.args[i], astroid.Name):
    -                return
    -            if node.args.args[i].name != call.args[i].name:
    -                return
    -        if (isinstance(node.body.func, astroid.Getattr) and
    -                isinstance(node.body.func.expr, astroid.CallFunc)):
    -            # Chained call, the intermediate call might
    -            # return something else (but we don't check that, yet).
    -            return
    -        self.add_message('unnecessary-lambda', line=node.fromlineno, node=node)
    -
    -    @check_messages('dangerous-default-value')
    -    def visit_function(self, node):
    -        """check function name, docstring, arguments, redefinition,
    -        variable names, max locals
    -        """
    -        self.stats[node.is_method() and 'method' or 'function'] += 1
    -        self._check_dangerous_default(node)
    -
    -    def _check_dangerous_default(self, node):
    -        # check for dangerous default values as arguments
    -        is_iterable = lambda n: isinstance(n, (astroid.List,
    -                                               astroid.Set,
    -                                               astroid.Dict))
    -        for default in node.args.defaults:
    -            try:
    -                value = next(default.infer())
    -            except astroid.InferenceError:
    -                continue
    -
    -            if (isinstance(value, astroid.Instance) and
    -                    value.qname() in DEFAULT_ARGUMENT_SYMBOLS):
    -
    -                if value is default:
    -                    msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()]
    -                elif type(value) is astroid.Instance or is_iterable(value):
    -                    # We are here in the following situation(s):
    -                    #   * a dict/set/list/tuple call which wasn't inferred
    -                    #     to a syntax node ({}, () etc.). This can happen
    -                    #     when the arguments are invalid or unknown to
    -                    #     the inference.
    -                    #   * a variable from somewhere else, which turns out to be a list
    -                    #     or a dict.
    -                    if is_iterable(default):
    -                        msg = value.pytype()
    -                    elif isinstance(default, astroid.CallFunc):
    -                        msg = '%s() (%s)' % (value.name, value.qname())
    -                    else:
    -                        msg = '%s (%s)' % (default.as_string(), value.qname())
    -                else:
    -                    # this argument is a name
    -                    msg = '%s (%s)' % (default.as_string(),
    -                                       DEFAULT_ARGUMENT_SYMBOLS[value.qname()])
    -                self.add_message('dangerous-default-value',
    -                                 node=node,
    -                                 args=(msg, ))
    -
    -    @check_messages('unreachable', 'lost-exception')
    -    def visit_return(self, node):
    -        """1 - check is the node has a right sibling (if so, that's some
    -        unreachable code)
    -        2 - check is the node is inside the finally clause of a try...finally
    -        block
    -        """
    -        self._check_unreachable(node)
    -        # Is it inside final body of a try...finally bloc ?
    -        self._check_not_in_finally(node, 'return', (astroid.Function,))
    -
    -    @check_messages('unreachable')
    -    def visit_continue(self, node):
    -        """check is the node has a right sibling (if so, that's some unreachable
    -        code)
    -        """
    -        self._check_unreachable(node)
    -
    -    @check_messages('unreachable', 'lost-exception')
    -    def visit_break(self, node):
    -        """1 - check is the node has a right sibling (if so, that's some
    -        unreachable code)
    -        2 - check is the node is inside the finally clause of a try...finally
    -        block
    -        """
    -        # 1 - Is it right sibling ?
    -        self._check_unreachable(node)
    -        # 2 - Is it inside final body of a try...finally bloc ?
    -        self._check_not_in_finally(node, 'break', (astroid.For, astroid.While,))
    -
    -    @check_messages('unreachable')
    -    def visit_raise(self, node):
    -        """check if the node has a right sibling (if so, that's some unreachable
    -        code)
    -        """
    -        self._check_unreachable(node)
    -
    -    @check_messages('exec-used')
    -    def visit_exec(self, node):
    -        """just print a warning on exec statements"""
    -        self.add_message('exec-used', node=node)
    -
    -    @check_messages('bad-builtin', 'eval-used',
    -                    'exec-used', 'missing-reversed-argument',
    -                    'bad-reversed-sequence')
    -    def visit_callfunc(self, node):
    -        """visit a CallFunc node -> check if this is not a blacklisted builtin
    -        call and check for * or ** use
    -        """
    -        if isinstance(node.func, astroid.Name):
    -            name = node.func.name
    -            # ignore the name if it's not a builtin (i.e. not defined in the
    -            # locals nor globals scope)
    -            if not (name in node.frame() or
    -                    name in node.root()):
    -                if name == 'exec':
    -                    self.add_message('exec-used', node=node)
    -                elif name == 'reversed':
    -                    self._check_reversed(node)
    -                elif name == 'eval':
    -                    self.add_message('eval-used', node=node)
    -                if name in self.config.bad_functions:
    -                    self.add_message('bad-builtin', node=node, args=name)
    -
    -    @check_messages('assert-on-tuple')
    -    def visit_assert(self, node):
    -        """check the use of an assert statement on a tuple."""
    -        if node.fail is None and isinstance(node.test, astroid.Tuple) and \
    -                len(node.test.elts) == 2:
    -            self.add_message('assert-on-tuple', node=node)
    -
    -    @check_messages('duplicate-key')
    -    def visit_dict(self, node):
    -        """check duplicate key in dictionary"""
    -        keys = set()
    -        for k, _ in node.items:
    -            if isinstance(k, astroid.Const):
    -                key = k.value
    -                if key in keys:
    -                    self.add_message('duplicate-key', node=node, args=key)
    -                keys.add(key)
    -
    -    def visit_tryfinally(self, node):
    -        """update try...finally flag"""
    -        self._tryfinallys.append(node)
    -
    -    def leave_tryfinally(self, node): # pylint: disable=unused-argument
    -        """update try...finally flag"""
    -        self._tryfinallys.pop()
    -
    -    def _check_unreachable(self, node):
    -        """check unreachable code"""
    -        unreach_stmt = node.next_sibling()
    -        if unreach_stmt is not None:
    -            self.add_message('unreachable', node=unreach_stmt)
    -
    -    def _check_not_in_finally(self, node, node_name, breaker_classes=()):
    -        """check that a node is not inside a finally clause of a
    -        try...finally statement.
    -        If we found before a try...finally bloc a parent which its type is
    -        in breaker_classes, we skip the whole check."""
    -        # if self._tryfinallys is empty, we're not a in try...finally bloc
    -        if not self._tryfinallys:
    -            return
    -        # the node could be a grand-grand...-children of the try...finally
    -        _parent = node.parent
    -        _node = node
    -        while _parent and not isinstance(_parent, breaker_classes):
    -            if hasattr(_parent, 'finalbody') and _node in _parent.finalbody:
    -                self.add_message('lost-exception', node=node, args=node_name)
    -                return
    -            _node = _parent
    -            _parent = _node.parent
    -
    -    def _check_reversed(self, node):
    -        """ check that the argument to `reversed` is a sequence """
    -        try:
    -            argument = safe_infer(get_argument_from_call(node, position=0))
    -        except NoSuchArgumentError:
    -            self.add_message('missing-reversed-argument', node=node)
    -        else:
    -            if argument is astroid.YES:
    -                return
    -            if argument is None:
    -                # Nothing was infered.
    -                # Try to see if we have iter().
    -                if isinstance(node.args[0], astroid.CallFunc):
    -                    try:
    -                        func = next(node.args[0].func.infer())
    -                    except InferenceError:
    -                        return
    -                    if (getattr(func, 'name', None) == 'iter' and
    -                            is_builtin_object(func)):
    -                        self.add_message('bad-reversed-sequence', node=node)
    -                return
    -
    -            if isinstance(argument, astroid.Instance):
    -                if (argument._proxied.name == 'dict' and
    -                        is_builtin_object(argument._proxied)):
    -                    self.add_message('bad-reversed-sequence', node=node)
    -                    return
    -                elif any(ancestor.name == 'dict' and is_builtin_object(ancestor)
    -                         for ancestor in argument._proxied.ancestors()):
    -                    # mappings aren't accepted by reversed()
    -                    self.add_message('bad-reversed-sequence', node=node)
    -                    return
    -
    -                for methods in REVERSED_METHODS:
    -                    for meth in methods:
    -                        try:
    -                            argument.getattr(meth)
    -                        except astroid.NotFoundError:
    -                            break
    -                    else:
    -                        break
    -                else:
    -                    # Check if it is a .deque. It doesn't seem that
    -                    # we can retrieve special methods
    -                    # from C implemented constructs.
    -                    if argument._proxied.qname().endswith(".deque"):
    -                        return
    -                    self.add_message('bad-reversed-sequence', node=node)
    -            elif not isinstance(argument, (astroid.List, astroid.Tuple)):
    -                # everything else is not a proper sequence for reversed()
    -                self.add_message('bad-reversed-sequence', node=node)
    -
    -_NAME_TYPES = {
    -    'module': (MOD_NAME_RGX, 'module'),
    -    'const': (CONST_NAME_RGX, 'constant'),
    -    'class': (CLASS_NAME_RGX, 'class'),
    -    'function': (DEFAULT_NAME_RGX, 'function'),
    -    'method': (DEFAULT_NAME_RGX, 'method'),
    -    'attr': (DEFAULT_NAME_RGX, 'attribute'),
    -    'argument': (DEFAULT_NAME_RGX, 'argument'),
    -    'variable': (DEFAULT_NAME_RGX, 'variable'),
    -    'class_attribute': (CLASS_ATTRIBUTE_RGX, 'class attribute'),
    -    'inlinevar': (COMP_VAR_RGX, 'inline iteration'),
    -}
    -
    -def _create_naming_options():
    -    name_options = []
    -    for name_type, (rgx, human_readable_name) in six.iteritems(_NAME_TYPES):
    -        name_type = name_type.replace('_', '-')
    -        name_options.append((
    -            '%s-rgx' % (name_type,),
    -            {'default': rgx, 'type': 'regexp', 'metavar': '',
    -             'help': 'Regular expression matching correct %s names' % (human_readable_name,)}))
    -        name_options.append((
    -            '%s-name-hint' % (name_type,),
    -            {'default': rgx.pattern, 'type': 'string', 'metavar': '',
    -             'help': 'Naming hint for %s names' % (human_readable_name,)}))
    -    return tuple(name_options)
    -
    -class NameChecker(_BasicChecker):
    -    msgs = {
    -        'C0102': ('Black listed name "%s"',
    -                  'blacklisted-name',
    -                  'Used when the name is listed in the black list (unauthorized '
    -                  'names).'),
    -        'C0103': ('Invalid %s name "%s"%s',
    -                  'invalid-name',
    -                  'Used when the name doesn\'t match the regular expression '
    -                  'associated to its type (constant, variable, class...).'),
    -    }
    -
    -    options = (('good-names',
    -                {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'),
    -                 'type' :'csv', 'metavar' : '',
    -                 'help' : 'Good variable names which should always be accepted,'
    -                          ' separated by a comma'}
    -               ),
    -               ('bad-names',
    -                {'default' : ('foo', 'bar', 'baz', 'toto', 'tutu', 'tata'),
    -                 'type' :'csv', 'metavar' : '',
    -                 'help' : 'Bad variable names which should always be refused, '
    -                          'separated by a comma'}
    -               ),
    -               ('name-group',
    -                {'default' : (),
    -                 'type' :'csv', 'metavar' : '',
    -                 'help' : ('Colon-delimited sets of names that determine each'
    -                           ' other\'s naming style when the name regexes'
    -                           ' allow several styles.')}
    -               ),
    -               ('include-naming-hint',
    -                {'default': False, 'type' : 'yn', 'metavar' : '',
    -                 'help': 'Include a hint for the correct naming format with invalid-name'}
    -               ),
    -              ) + _create_naming_options()
    -
    -
    -    def __init__(self, linter):
    -        _BasicChecker.__init__(self, linter)
    -        self._name_category = {}
    -        self._name_group = {}
    -        self._bad_names = {}
    -
    -    def open(self):
    -        self.stats = self.linter.add_stats(badname_module=0,
    -                                           badname_class=0, badname_function=0,
    -                                           badname_method=0, badname_attr=0,
    -                                           badname_const=0,
    -                                           badname_variable=0,
    -                                           badname_inlinevar=0,
    -                                           badname_argument=0,
    -                                           badname_class_attribute=0)
    -        for group in self.config.name_group:
    -            for name_type in group.split(':'):
    -                self._name_group[name_type] = 'group_%s' % (group,)
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_module(self, node):
    -        self._check_name('module', node.name.split('.')[-1], node)
    -        self._bad_names = {}
    -
    -    def leave_module(self, node): # pylint: disable=unused-argument
    -        for all_groups in six.itervalues(self._bad_names):
    -            if len(all_groups) < 2:
    -                continue
    -            groups = collections.defaultdict(list)
    -            min_warnings = sys.maxsize
    -            for group in six.itervalues(all_groups):
    -                groups[len(group)].append(group)
    -                min_warnings = min(len(group), min_warnings)
    -            if len(groups[min_warnings]) > 1:
    -                by_line = sorted(groups[min_warnings],
    -                                 key=lambda group: min(warning[0].lineno for warning in group))
    -                warnings = itertools.chain(*by_line[1:])
    -            else:
    -                warnings = groups[min_warnings][0]
    -            for args in warnings:
    -                self._raise_name_warning(*args)
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_class(self, node):
    -        self._check_name('class', node.name, node)
    -        for attr, anodes in six.iteritems(node.instance_attrs):
    -            if not list(node.instance_attr_ancestors(attr)):
    -                self._check_name('attr', attr, anodes[0])
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_function(self, node):
    -        # Do not emit any warnings if the method is just an implementation
    -        # of a base class method.
    -        confidence = HIGH
    -        if node.is_method():
    -            if overrides_a_method(node.parent.frame(), node.name):
    -                return
    -            confidence = (INFERENCE if has_known_bases(node.parent.frame())
    -                          else INFERENCE_FAILURE)
    -
    -        self._check_name(_determine_function_name_type(node),
    -                         node.name, node, confidence)
    -        # Check argument names
    -        args = node.args.args
    -        if args is not None:
    -            self._recursive_check_names(args, node)
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_global(self, node):
    -        for name in node.names:
    -            self._check_name('const', name, node)
    -
    -    @check_messages('blacklisted-name', 'invalid-name')
    -    def visit_assname(self, node):
    -        """check module level assigned names"""
    -        frame = node.frame()
    -        ass_type = node.ass_type()
    -        if isinstance(ass_type, astroid.Comprehension):
    -            self._check_name('inlinevar', node.name, node)
    -        elif isinstance(frame, astroid.Module):
    -            if isinstance(ass_type, astroid.Assign) and not in_loop(ass_type):
    -                if isinstance(safe_infer(ass_type.value), astroid.Class):
    -                    self._check_name('class', node.name, node)
    -                else:
    -                    if not _redefines_import(node):
    -                        # Don't emit if the name redefines an import
    -                        # in an ImportError except handler.
    -                        self._check_name('const', node.name, node)
    -            elif isinstance(ass_type, astroid.ExceptHandler):
    -                self._check_name('variable', node.name, node)
    -        elif isinstance(frame, astroid.Function):
    -            # global introduced variable aren't in the function locals
    -            if node.name in frame and node.name not in frame.argnames():
    -                if not _redefines_import(node):
    -                    self._check_name('variable', node.name, node)
    -        elif isinstance(frame, astroid.Class):
    -            if not list(frame.local_attr_ancestors(node.name)):
    -                self._check_name('class_attribute', node.name, node)
    -
    -    def _recursive_check_names(self, args, node):
    -        """check names in a possibly recursive list """
    -        for arg in args:
    -            if isinstance(arg, astroid.AssName):
    -                self._check_name('argument', arg.name, node)
    -            else:
    -                self._recursive_check_names(arg.elts, node)
    -
    -    def _find_name_group(self, node_type):
    -        return self._name_group.get(node_type, node_type)
    -
    -    def _raise_name_warning(self, node, node_type, name, confidence):
    -        type_label = _NAME_TYPES[node_type][1]
    -        hint = ''
    -        if self.config.include_naming_hint:
    -            hint = ' (hint: %s)' % (getattr(self.config, node_type + '_name_hint'))
    -        self.add_message('invalid-name', node=node, args=(type_label, name, hint),
    -                         confidence=confidence)
    -        self.stats['badname_' + node_type] += 1
    -
    -    def _check_name(self, node_type, name, node, confidence=HIGH):
    -        """check for a name using the type's regexp"""
    -        if is_inside_except(node):
    -            clobbering, _ = clobber_in_except(node)
    -            if clobbering:
    -                return
    -        if name in self.config.good_names:
    -            return
    -        if name in self.config.bad_names:
    -            self.stats['badname_' + node_type] += 1
    -            self.add_message('blacklisted-name', node=node, args=name)
    -            return
    -        regexp = getattr(self.config, node_type + '_rgx')
    -        match = regexp.match(name)
    -
    -        if _is_multi_naming_match(match, node_type, confidence):
    -            name_group = self._find_name_group(node_type)
    -            bad_name_group = self._bad_names.setdefault(name_group, {})
    -            warnings = bad_name_group.setdefault(match.lastgroup, [])
    -            warnings.append((node, node_type, name, confidence))
    -
    -        if match is None:
    -            self._raise_name_warning(node, node_type, name, confidence)
    -
    -
    -class DocStringChecker(_BasicChecker):
    -    msgs = {
    -        'C0111': ('Missing %s docstring', # W0131
    -                  'missing-docstring',
    -                  'Used when a module, function, class or method has no docstring.'
    -                  'Some special methods like __init__ doesn\'t necessary require a '
    -                  'docstring.'),
    -        'C0112': ('Empty %s docstring', # W0132
    -                  'empty-docstring',
    -                  'Used when a module, function, class or method has an empty '
    -                  'docstring (it would be too easy ;).'),
    -        }
    -    options = (('no-docstring-rgx',
    -                {'default' : NO_REQUIRED_DOC_RGX,
    -                 'type' : 'regexp', 'metavar' : '',
    -                 'help' : 'Regular expression which should only match '
    -                          'function or class names that do not require a '
    -                          'docstring.'}
    -               ),
    -               ('docstring-min-length',
    -                {'default' : -1,
    -                 'type' : 'int', 'metavar' : '',
    -                 'help': ('Minimum line length for functions/classes that'
    -                          ' require docstrings, shorter ones are exempt.')}
    -               ),
    -              )
    -
    -
    -    def open(self):
    -        self.stats = self.linter.add_stats(undocumented_module=0,
    -                                           undocumented_function=0,
    -                                           undocumented_method=0,
    -                                           undocumented_class=0)
    -    @check_messages('missing-docstring', 'empty-docstring')
    -    def visit_module(self, node):
    -        self._check_docstring('module', node)
    -
    -    @check_messages('missing-docstring', 'empty-docstring')
    -    def visit_class(self, node):
    -        if self.config.no_docstring_rgx.match(node.name) is None:
    -            self._check_docstring('class', node)
    -
    -    @check_messages('missing-docstring', 'empty-docstring')
    -    def visit_function(self, node):
    -        if self.config.no_docstring_rgx.match(node.name) is None:
    -            ftype = node.is_method() and 'method' or 'function'
    -            if isinstance(node.parent.frame(), astroid.Class):
    -                overridden = False
    -                confidence = (INFERENCE if has_known_bases(node.parent.frame())
    -                              else INFERENCE_FAILURE)
    -                # check if node is from a method overridden by its ancestor
    -                for ancestor in node.parent.frame().ancestors():
    -                    if node.name in ancestor and \
    -                       isinstance(ancestor[node.name], astroid.Function):
    -                        overridden = True
    -                        break
    -                self._check_docstring(ftype, node,
    -                                      report_missing=not overridden,
    -                                      confidence=confidence)
    -            else:
    -                self._check_docstring(ftype, node)
    -
    -    def _check_docstring(self, node_type, node, report_missing=True,
    -                         confidence=HIGH):
    -        """check the node has a non empty docstring"""
    -        docstring = node.doc
    -        if docstring is None:
    -            if not report_missing:
    -                return
    -            if node.body:
    -                lines = node.body[-1].lineno - node.body[0].lineno + 1
    -            else:
    -                lines = 0
    -
    -            if node_type == 'module' and not lines:
    -                # If the module has no body, there's no reason
    -                # to require a docstring.
    -                return
    -            max_lines = self.config.docstring_min_length
    -
    -            if node_type != 'module' and max_lines > -1 and lines < max_lines:
    -                return
    -            self.stats['undocumented_'+node_type] += 1
    -            if (node.body and isinstance(node.body[0], astroid.Discard) and
    -                    isinstance(node.body[0].value, astroid.CallFunc)):
    -                # Most likely a string with a format call. Let's see.
    -                func = safe_infer(node.body[0].value.func)
    -                if (isinstance(func, astroid.BoundMethod)
    -                        and isinstance(func.bound, astroid.Instance)):
    -                    # Strings in Python 3, others in Python 2.
    -                    if PY3K and func.bound.name == 'str':
    -                        return
    -                    elif func.bound.name in ('str', 'unicode', 'bytes'):
    -                        return
    -            self.add_message('missing-docstring', node=node, args=(node_type,),
    -                             confidence=confidence)
    -        elif not docstring.strip():
    -            self.stats['undocumented_'+node_type] += 1
    -            self.add_message('empty-docstring', node=node, args=(node_type,),
    -                             confidence=confidence)
    -
    -
    -class PassChecker(_BasicChecker):
    -    """check if the pass statement is really necessary"""
    -    msgs = {'W0107': ('Unnecessary pass statement',
    -                      'unnecessary-pass',
    -                      'Used when a "pass" statement that can be avoided is '
    -                      'encountered.'),
    -           }
    -    @check_messages('unnecessary-pass')
    -    def visit_pass(self, node):
    -        if len(node.parent.child_sequence(node)) > 1:
    -            self.add_message('unnecessary-pass', node=node)
    -
    -
    -class LambdaForComprehensionChecker(_BasicChecker):
    -    """check for using a lambda where a comprehension would do.
    -
    -    See 
    -    where GvR says comprehensions would be clearer.
    -    """
    -
    -    msgs = {'W0110': ('map/filter on lambda could be replaced by comprehension',
    -                      'deprecated-lambda',
    -                      'Used when a lambda is the first argument to "map" or '
    -                      '"filter". It could be clearer as a list '
    -                      'comprehension or generator expression.',
    -                      {'maxversion': (3, 0)}),
    -           }
    -
    -    @check_messages('deprecated-lambda')
    -    def visit_callfunc(self, node):
    -        """visit a CallFunc node, check if map or filter are called with a
    -        lambda
    -        """
    -        if not node.args:
    -            return
    -        if not isinstance(node.args[0], astroid.Lambda):
    -            return
    -        infered = safe_infer(node.func)
    -        if (is_builtin_object(infered)
    -                and infered.name in ['map', 'filter']):
    -            self.add_message('deprecated-lambda', node=node)
    -
    -
    -def register(linter):
    -    """required method to auto register this checker"""
    -    linter.register_checker(BasicErrorChecker(linter))
    -    linter.register_checker(BasicChecker(linter))
    -    linter.register_checker(NameChecker(linter))
    -    linter.register_checker(DocStringChecker(linter))
    -    linter.register_checker(PassChecker(linter))
    -    linter.register_checker(LambdaForComprehensionChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/classes.py b/pymode/libs/pylint/checkers/classes.py
    deleted file mode 100644
    index 87e3bcfe..00000000
    --- a/pymode/libs/pylint/checkers/classes.py
    +++ /dev/null
    @@ -1,982 +0,0 @@
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""classes checker for Python code
    -"""
    -from __future__ import generators
    -
    -import sys
    -from collections import defaultdict
    -
    -import astroid
    -from astroid import YES, Instance, are_exclusive, AssAttr, Class
    -from astroid.bases import Generator, BUILTINS
    -from astroid.inference import InferenceContext
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    PYMETHODS, overrides_a_method, check_messages, is_attr_private,
    -    is_attr_protected, node_frame_class, safe_infer, is_builtin_object,
    -    decorated_with_property, unimplemented_abstract_methods)
    -import six
    -
    -if sys.version_info >= (3, 0):
    -    NEXT_METHOD = '__next__'
    -else:
    -    NEXT_METHOD = 'next'
    -ITER_METHODS = ('__iter__', '__getitem__')
    -
    -def _called_in_methods(func, klass, methods):
    -    """ Check if the func was called in any of the given methods,
    -    belonging to the *klass*. Returns True if so, False otherwise.
    -    """
    -    if not isinstance(func, astroid.Function):
    -        return False
    -    for method in methods:
    -        try:
    -            infered = klass.getattr(method)
    -        except astroid.NotFoundError:
    -            continue
    -        for infer_method in infered:
    -            for callfunc in infer_method.nodes_of_class(astroid.CallFunc):
    -                try:
    -                    bound = next(callfunc.func.infer())
    -                except (astroid.InferenceError, StopIteration):
    -                    continue
    -                if not isinstance(bound, astroid.BoundMethod):
    -                    continue
    -                func_obj = bound._proxied
    -                if isinstance(func_obj, astroid.UnboundMethod):
    -                    func_obj = func_obj._proxied
    -                if func_obj.name == func.name:
    -                    return True
    -    return False
    -
    -def class_is_abstract(node):
    -    """return true if the given class node should be considered as an abstract
    -    class
    -    """
    -    for method in node.methods():
    -        if method.parent.frame() is node:
    -            if method.is_abstract(pass_is_abstract=False):
    -                return True
    -    return False
    -
    -def _is_attribute_property(name, klass):
    -    """ Check if the given attribute *name* is a property
    -    in the given *klass*.
    -
    -    It will look for `property` calls or for functions
    -    with the given name, decorated by `property` or `property`
    -    subclasses.
    -    Returns ``True`` if the name is a property in the given klass,
    -    ``False`` otherwise.
    -    """
    -
    -    try:
    -        attributes = klass.getattr(name)
    -    except astroid.NotFoundError:
    -        return False
    -    property_name = "{0}.property".format(BUILTINS)
    -    for attr in attributes:
    -        try:
    -            infered = next(attr.infer())
    -        except astroid.InferenceError:
    -            continue
    -        if (isinstance(infered, astroid.Function) and
    -                decorated_with_property(infered)):
    -            return True
    -        if infered.pytype() == property_name:
    -            return True
    -    return False
    -
    -
    -MSGS = {
    -    'F0202': ('Unable to check methods signature (%s / %s)',
    -              'method-check-failed',
    -              'Used when Pylint has been unable to check methods signature '
    -              'compatibility for an unexpected reason. Please report this kind '
    -              'if you don\'t make sense of it.'),
    -
    -    'E0202': ('An attribute defined in %s line %s hides this method',
    -              'method-hidden',
    -              'Used when a class defines a method which is hidden by an '
    -              'instance attribute from an ancestor class or set by some '
    -              'client code.'),
    -    'E0203': ('Access to member %r before its definition line %s',
    -              'access-member-before-definition',
    -              'Used when an instance member is accessed before it\'s actually '
    -              'assigned.'),
    -    'W0201': ('Attribute %r defined outside __init__',
    -              'attribute-defined-outside-init',
    -              'Used when an instance attribute is defined outside the __init__ '
    -              'method.'),
    -
    -    'W0212': ('Access to a protected member %s of a client class', # E0214
    -              'protected-access',
    -              'Used when a protected member (i.e. class member with a name '
    -              'beginning with an underscore) is access outside the class or a '
    -              'descendant of the class where it\'s defined.'),
    -
    -    'E0211': ('Method has no argument',
    -              'no-method-argument',
    -              'Used when a method which should have the bound instance as '
    -              'first argument has no argument defined.'),
    -    'E0213': ('Method should have "self" as first argument',
    -              'no-self-argument',
    -              'Used when a method has an attribute different the "self" as '
    -              'first argument. This is considered as an error since this is '
    -              'a so common convention that you shouldn\'t break it!'),
    -    'C0202': ('Class method %s should have %s as first argument',
    -              'bad-classmethod-argument',
    -              'Used when a class method has a first argument named differently '
    -              'than the value specified in valid-classmethod-first-arg option '
    -              '(default to "cls"), recommended to easily differentiate them '
    -              'from regular instance methods.'),
    -    'C0203': ('Metaclass method %s should have %s as first argument',
    -              'bad-mcs-method-argument',
    -              'Used when a metaclass method has a first agument named '
    -              'differently than the value specified in valid-classmethod-first'
    -              '-arg option (default to "cls"), recommended to easily '
    -              'differentiate them from regular instance methods.'),
    -    'C0204': ('Metaclass class method %s should have %s as first argument',
    -              'bad-mcs-classmethod-argument',
    -              'Used when a metaclass class method has a first argument named '
    -              'differently than the value specified in valid-metaclass-'
    -              'classmethod-first-arg option (default to "mcs"), recommended to '
    -              'easily differentiate them from regular instance methods.'),
    -
    -    'W0211': ('Static method with %r as first argument',
    -              'bad-staticmethod-argument',
    -              'Used when a static method has "self" or a value specified in '
    -              'valid-classmethod-first-arg option or '
    -              'valid-metaclass-classmethod-first-arg option as first argument.'
    -             ),
    -    'R0201': ('Method could be a function',
    -              'no-self-use',
    -              'Used when a method doesn\'t use its bound instance, and so could '
    -              'be written as a function.'
    -             ),
    -
    -    'E0221': ('Interface resolved to %s is not a class',
    -              'interface-is-not-class',
    -              'Used when a class claims to implement an interface which is not '
    -              'a class.'),
    -    'E0222': ('Missing method %r from %s interface',
    -              'missing-interface-method',
    -              'Used when a method declared in an interface is missing from a '
    -              'class implementing this interface'),
    -    'W0221': ('Arguments number differs from %s %r method',
    -              'arguments-differ',
    -              'Used when a method has a different number of arguments than in '
    -              'the implemented interface or in an overridden method.'),
    -    'W0222': ('Signature differs from %s %r method',
    -              'signature-differs',
    -              'Used when a method signature is different than in the '
    -              'implemented interface or in an overridden method.'),
    -    'W0223': ('Method %r is abstract in class %r but is not overridden',
    -              'abstract-method',
    -              'Used when an abstract method (i.e. raise NotImplementedError) is '
    -              'not overridden in concrete class.'
    -             ),
    -    'F0220': ('failed to resolve interfaces implemented by %s (%s)',
    -              'unresolved-interface',
    -              'Used when a Pylint as failed to find interfaces implemented by '
    -              ' a class'),
    -
    -
    -    'W0231': ('__init__ method from base class %r is not called',
    -              'super-init-not-called',
    -              'Used when an ancestor class method has an __init__ method '
    -              'which is not called by a derived class.'),
    -    'W0232': ('Class has no __init__ method',
    -              'no-init',
    -              'Used when a class has no __init__ method, neither its parent '
    -              'classes.'),
    -    'W0233': ('__init__ method from a non direct base class %r is called',
    -              'non-parent-init-called',
    -              'Used when an __init__ method is called on a class which is not '
    -              'in the direct ancestors for the analysed class.'),
    -    'W0234': ('__iter__ returns non-iterator',
    -              'non-iterator-returned',
    -              'Used when an __iter__ method returns something which is not an '
    -               'iterable (i.e. has no `%s` method)' % NEXT_METHOD),
    -    'E0235': ('__exit__ must accept 3 arguments: type, value, traceback',
    -              'bad-context-manager',
    -              'Used when the __exit__ special method, belonging to a '
    -              'context manager, does not accept 3 arguments '
    -              '(type, value, traceback).'),
    -    'E0236': ('Invalid object %r in __slots__, must contain '
    -              'only non empty strings',
    -              'invalid-slots-object',
    -              'Used when an invalid (non-string) object occurs in __slots__.'),
    -    'E0237': ('Assigning to attribute %r not defined in class slots',
    -              'assigning-non-slot',
    -              'Used when assigning to an attribute not defined '
    -              'in the class slots.'),
    -    'E0238': ('Invalid __slots__ object',
    -              'invalid-slots',
    -              'Used when an invalid __slots__ is found in class. '
    -              'Only a string, an iterable or a sequence is permitted.'),
    -    'E0239': ('Inheriting %r, which is not a class.',
    -              'inherit-non-class',
    -              'Used when a class inherits from something which is not a '
    -              'class.'),
    -
    -
    -    }
    -
    -
    -class ClassChecker(BaseChecker):
    -    """checks for :
    -    * methods without self as first argument
    -    * overridden methods signature
    -    * access only to existent members via self
    -    * attributes not defined in the __init__ method
    -    * supported interfaces implementation
    -    * unreachable code
    -    """
    -
    -    __implements__ = (IAstroidChecker,)
    -
    -    # configuration section name
    -    name = 'classes'
    -    # messages
    -    msgs = MSGS
    -    priority = -2
    -    # configuration options
    -    options = (('ignore-iface-methods',
    -                {'default' : (#zope interface
    -                    'isImplementedBy', 'deferred', 'extends', 'names',
    -                    'namesAndDescriptions', 'queryDescriptionFor', 'getBases',
    -                    'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue',
    -                    'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue',
    -                    'isImplementedByInstancesOf',
    -                    # twisted
    -                    'adaptWith',
    -                    # logilab.common interface
    -                    'is_implemented_by'),
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of interface methods to ignore, \
    -separated by a comma. This is used for instance to not check methods defines \
    -in Zope\'s Interface base class.'}
    -               ),
    -               ('defining-attr-methods',
    -                {'default' : ('__init__', '__new__', 'setUp'),
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of method names used to declare (i.e. assign) \
    -instance attributes.'}
    -               ),
    -               ('valid-classmethod-first-arg',
    -                {'default' : ('cls',),
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of valid names for the first argument in \
    -a class method.'}
    -               ),
    -               ('valid-metaclass-classmethod-first-arg',
    -                {'default' : ('mcs',),
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of valid names for the first argument in \
    -a metaclass class method.'}
    -               ),
    -               ('exclude-protected',
    -                {
    -                    'default': (
    -                        # namedtuple public API.
    -                        '_asdict', '_fields', '_replace', '_source', '_make'),
    -                    'type': 'csv',
    -                    'metavar': '',
    -                    'help': ('List of member names, which should be excluded '
    -                             'from the protected access warning.')}
    -               ))
    -
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        self._accessed = []
    -        self._first_attrs = []
    -        self._meth_could_be_func = None
    -
    -    def visit_class(self, node):
    -        """init visit variable _accessed and check interfaces
    -        """
    -        self._accessed.append(defaultdict(list))
    -        self._check_bases_classes(node)
    -        self._check_interfaces(node)
    -        # if not an interface, exception, metaclass
    -        if node.type == 'class':
    -            try:
    -                node.local_attr('__init__')
    -            except astroid.NotFoundError:
    -                self.add_message('no-init', args=node, node=node)
    -        self._check_slots(node)
    -        self._check_proper_bases(node)
    -
    -    @check_messages('inherit-non-class')
    -    def _check_proper_bases(self, node):
    -        """
    -        Detect that a class inherits something which is not
    -        a class or a type.
    -        """
    -        for base in node.bases:
    -            ancestor = safe_infer(base)
    -            if ancestor in (YES, None):
    -                continue
    -            if (isinstance(ancestor, astroid.Instance) and
    -                    ancestor.is_subtype_of('%s.type' % (BUILTINS,))):
    -                continue
    -            if not isinstance(ancestor, astroid.Class):
    -                self.add_message('inherit-non-class',
    -                                 args=base.as_string(), node=node)
    -
    -    @check_messages('access-member-before-definition',
    -                    'attribute-defined-outside-init')
    -    def leave_class(self, cnode):
    -        """close a class node:
    -        check that instance attributes are defined in __init__ and check
    -        access to existent members
    -        """
    -        # check access to existent members on non metaclass classes
    -        accessed = self._accessed.pop()
    -        if cnode.type != 'metaclass':
    -            self._check_accessed_members(cnode, accessed)
    -        # checks attributes are defined in an allowed method such as __init__
    -        if not self.linter.is_message_enabled('attribute-defined-outside-init'):
    -            return
    -        defining_methods = self.config.defining_attr_methods
    -        current_module = cnode.root()
    -        for attr, nodes in six.iteritems(cnode.instance_attrs):
    -            # skip nodes which are not in the current module and it may screw up
    -            # the output, while it's not worth it
    -            nodes = [n for n in nodes if not
    -                     isinstance(n.statement(), (astroid.Delete, astroid.AugAssign))
    -                     and n.root() is current_module]
    -            if not nodes:
    -                continue # error detected by typechecking
    -            # check if any method attr is defined in is a defining method
    -            if any(node.frame().name in defining_methods
    -                   for node in nodes):
    -                continue
    -
    -            # check attribute is defined in a parent's __init__
    -            for parent in cnode.instance_attr_ancestors(attr):
    -                attr_defined = False
    -                # check if any parent method attr is defined in is a defining method
    -                for node in parent.instance_attrs[attr]:
    -                    if node.frame().name in defining_methods:
    -                        attr_defined = True
    -                if attr_defined:
    -                    # we're done :)
    -                    break
    -            else:
    -                # check attribute is defined as a class attribute
    -                try:
    -                    cnode.local_attr(attr)
    -                except astroid.NotFoundError:
    -                    for node in nodes:
    -                        if node.frame().name not in defining_methods:
    -                            # If the attribute was set by a callfunc in any
    -                            # of the defining methods, then don't emit
    -                            # the warning.
    -                            if _called_in_methods(node.frame(), cnode,
    -                                                  defining_methods):
    -                                continue
    -                            self.add_message('attribute-defined-outside-init',
    -                                             args=attr, node=node)
    -
    -    def visit_function(self, node):
    -        """check method arguments, overriding"""
    -        # ignore actual functions
    -        if not node.is_method():
    -            return
    -        klass = node.parent.frame()
    -        self._meth_could_be_func = True
    -        # check first argument is self if this is actually a method
    -        self._check_first_arg_for_type(node, klass.type == 'metaclass')
    -        if node.name == '__init__':
    -            self._check_init(node)
    -            return
    -        # check signature if the method overloads inherited method
    -        for overridden in klass.local_attr_ancestors(node.name):
    -            # get astroid for the searched method
    -            try:
    -                meth_node = overridden[node.name]
    -            except KeyError:
    -                # we have found the method but it's not in the local
    -                # dictionary.
    -                # This may happen with astroid build from living objects
    -                continue
    -            if not isinstance(meth_node, astroid.Function):
    -                continue
    -            self._check_signature(node, meth_node, 'overridden')
    -            break
    -        if node.decorators:
    -            for decorator in node.decorators.nodes:
    -                if isinstance(decorator, astroid.Getattr) and \
    -                        decorator.attrname in ('getter', 'setter', 'deleter'):
    -                    # attribute affectation will call this method, not hiding it
    -                    return
    -                if isinstance(decorator, astroid.Name) and decorator.name == 'property':
    -                    # attribute affectation will either call a setter or raise
    -                    # an attribute error, anyway not hiding the function
    -                    return
    -        # check if the method is hidden by an attribute
    -        try:
    -            overridden = klass.instance_attr(node.name)[0] # XXX
    -            overridden_frame = overridden.frame()
    -            if (isinstance(overridden_frame, astroid.Function)
    -                    and overridden_frame.type == 'method'):
    -                overridden_frame = overridden_frame.parent.frame()
    -            if (isinstance(overridden_frame, Class)
    -                    and klass.is_subtype_of(overridden_frame.qname())):
    -                args = (overridden.root().name, overridden.fromlineno)
    -                self.add_message('method-hidden', args=args, node=node)
    -        except astroid.NotFoundError:
    -            pass
    -
    -        # check non-iterators in __iter__
    -        if node.name == '__iter__':
    -            self._check_iter(node)
    -        elif node.name == '__exit__':
    -            self._check_exit(node)
    -
    -    def _check_slots(self, node):
    -        if '__slots__' not in node.locals:
    -            return
    -        for slots in node.igetattr('__slots__'):
    -            # check if __slots__ is a valid type
    -            for meth in ITER_METHODS:
    -                try:
    -                    slots.getattr(meth)
    -                    break
    -                except astroid.NotFoundError:
    -                    continue
    -            else:
    -                self.add_message('invalid-slots', node=node)
    -                continue
    -
    -            if isinstance(slots, astroid.Const):
    -                # a string, ignore the following checks
    -                continue
    -            if not hasattr(slots, 'itered'):
    -                # we can't obtain the values, maybe a .deque?
    -                continue
    -
    -            if isinstance(slots, astroid.Dict):
    -                values = [item[0] for item in slots.items]
    -            else:
    -                values = slots.itered()
    -            if values is YES:
    -                return
    -
    -            for elt in values:
    -                try:
    -                    self._check_slots_elt(elt)
    -                except astroid.InferenceError:
    -                    continue
    -
    -    def _check_slots_elt(self, elt):
    -        for infered in elt.infer():
    -            if infered is YES:
    -                continue
    -            if (not isinstance(infered, astroid.Const) or
    -                    not isinstance(infered.value, six.string_types)):
    -                self.add_message('invalid-slots-object',
    -                                 args=infered.as_string(),
    -                                 node=elt)
    -                continue
    -            if not infered.value:
    -                self.add_message('invalid-slots-object',
    -                                 args=infered.as_string(),
    -                                 node=elt)
    -
    -    def _check_iter(self, node):
    -        try:
    -            infered = node.infer_call_result(node)
    -        except astroid.InferenceError:
    -            return
    -
    -        for infered_node in infered:
    -            if (infered_node is YES
    -                    or isinstance(infered_node, Generator)):
    -                continue
    -            if isinstance(infered_node, astroid.Instance):
    -                try:
    -                    infered_node.local_attr(NEXT_METHOD)
    -                except astroid.NotFoundError:
    -                    self.add_message('non-iterator-returned',
    -                                     node=node)
    -                    break
    -
    -    def _check_exit(self, node):
    -        positional = sum(1 for arg in node.args.args if arg.name != 'self')
    -        if positional < 3 and not node.args.vararg:
    -            self.add_message('bad-context-manager',
    -                             node=node)
    -        elif positional > 3:
    -            self.add_message('bad-context-manager',
    -                             node=node)
    -
    -    def leave_function(self, node):
    -        """on method node, check if this method couldn't be a function
    -
    -        ignore class, static and abstract methods, initializer,
    -        methods overridden from a parent class and any
    -        kind of method defined in an interface for this warning
    -        """
    -        if node.is_method():
    -            if node.args.args is not None:
    -                self._first_attrs.pop()
    -            if not self.linter.is_message_enabled('no-self-use'):
    -                return
    -            class_node = node.parent.frame()
    -            if (self._meth_could_be_func and node.type == 'method'
    -                    and not node.name in PYMETHODS
    -                    and not (node.is_abstract() or
    -                             overrides_a_method(class_node, node.name))
    -                    and class_node.type != 'interface'):
    -                self.add_message('no-self-use', node=node)
    -
    -    def visit_getattr(self, node):
    -        """check if the getattr is an access to a class member
    -        if so, register it. Also check for access to protected
    -        class member from outside its class (but ignore __special__
    -        methods)
    -        """
    -        attrname = node.attrname
    -        # Check self
    -        if self.is_first_attr(node):
    -            self._accessed[-1][attrname].append(node)
    -            return
    -        if not self.linter.is_message_enabled('protected-access'):
    -            return
    -
    -        self._check_protected_attribute_access(node)
    -
    -    def visit_assattr(self, node):
    -        if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr(node):
    -            self._accessed[-1][node.attrname].append(node)
    -        self._check_in_slots(node)
    -
    -    def _check_in_slots(self, node):
    -        """ Check that the given assattr node
    -        is defined in the class slots.
    -        """
    -        infered = safe_infer(node.expr)
    -        if infered and isinstance(infered, Instance):
    -            klass = infered._proxied
    -            if '__slots__' not in klass.locals or not klass.newstyle:
    -                return
    -
    -            slots = klass.slots()
    -            if slots is None:
    -                return
    -            # If any ancestor doesn't use slots, the slots
    -            # defined for this class are superfluous.
    -            if any('__slots__' not in ancestor.locals and
    -                   ancestor.name != 'object'
    -                   for ancestor in klass.ancestors()):
    -                return
    -
    -            if not any(slot.value == node.attrname for slot in slots):
    -                # If we have a '__dict__' in slots, then
    -                # assigning any name is valid.
    -                if not any(slot.value == '__dict__' for slot in slots):
    -                    if _is_attribute_property(node.attrname, klass):
    -                        # Properties circumvent the slots mechanism,
    -                        # so we should not emit a warning for them.
    -                        return
    -                    self.add_message('assigning-non-slot',
    -                                     args=(node.attrname, ), node=node)
    -
    -    @check_messages('protected-access')
    -    def visit_assign(self, assign_node):
    -        node = assign_node.targets[0]
    -        if not isinstance(node, AssAttr):
    -            return
    -
    -        if self.is_first_attr(node):
    -            return
    -
    -        self._check_protected_attribute_access(node)
    -
    -    def _check_protected_attribute_access(self, node):
    -        '''Given an attribute access node (set or get), check if attribute
    -        access is legitimate. Call _check_first_attr with node before calling
    -        this method. Valid cases are:
    -        * self._attr in a method or cls._attr in a classmethod. Checked by
    -        _check_first_attr.
    -        * Klass._attr inside "Klass" class.
    -        * Klass2._attr inside "Klass" class when Klass2 is a base class of
    -            Klass.
    -        '''
    -        attrname = node.attrname
    -
    -        if (is_attr_protected(attrname) and
    -                attrname not in self.config.exclude_protected):
    -
    -            klass = node_frame_class(node)
    -
    -            # XXX infer to be more safe and less dirty ??
    -            # in classes, check we are not getting a parent method
    -            # through the class object or through super
    -            callee = node.expr.as_string()
    -
    -            # We are not in a class, no remaining valid case
    -            if klass is None:
    -                self.add_message('protected-access', node=node, args=attrname)
    -                return
    -
    -            # If the expression begins with a call to super, that's ok.
    -            if isinstance(node.expr, astroid.CallFunc) and \
    -               isinstance(node.expr.func, astroid.Name) and \
    -               node.expr.func.name == 'super':
    -                return
    -
    -            # We are in a class, one remaining valid cases, Klass._attr inside
    -            # Klass
    -            if not (callee == klass.name or callee in klass.basenames):
    -                # Detect property assignments in the body of the class.
    -                # This is acceptable:
    -                #
    -                # class A:
    -                #     b = property(lambda: self._b)
    -
    -                stmt = node.parent.statement()
    -                try:
    -                    if (isinstance(stmt, astroid.Assign) and
    -                            (stmt in klass.body or klass.parent_of(stmt)) and
    -                            isinstance(stmt.value, astroid.CallFunc) and
    -                            isinstance(stmt.value.func, astroid.Name) and
    -                            stmt.value.func.name == 'property' and
    -                            is_builtin_object(next(stmt.value.func.infer(), None))):
    -                        return
    -                except astroid.InferenceError:
    -                    pass
    -                self.add_message('protected-access', node=node, args=attrname)
    -
    -    def visit_name(self, node):
    -        """check if the name handle an access to a class member
    -        if so, register it
    -        """
    -        if self._first_attrs and (node.name == self._first_attrs[-1] or
    -                                  not self._first_attrs[-1]):
    -            self._meth_could_be_func = False
    -
    -    def _check_accessed_members(self, node, accessed):
    -        """check that accessed members are defined"""
    -        # XXX refactor, probably much simpler now that E0201 is in type checker
    -        for attr, nodes in six.iteritems(accessed):
    -            # deactivate "except doesn't do anything", that's expected
    -            # pylint: disable=W0704
    -            try:
    -                # is it a class attribute ?
    -                node.local_attr(attr)
    -                # yes, stop here
    -                continue
    -            except astroid.NotFoundError:
    -                pass
    -            # is it an instance attribute of a parent class ?
    -            try:
    -                next(node.instance_attr_ancestors(attr))
    -                # yes, stop here
    -                continue
    -            except StopIteration:
    -                pass
    -            # is it an instance attribute ?
    -            try:
    -                defstmts = node.instance_attr(attr)
    -            except astroid.NotFoundError:
    -                pass
    -            else:
    -                # filter out augment assignment nodes
    -                defstmts = [stmt for stmt in defstmts if stmt not in nodes]
    -                if not defstmts:
    -                    # only augment assignment for this node, no-member should be
    -                    # triggered by the typecheck checker
    -                    continue
    -                # filter defstmts to only pick the first one when there are
    -                # several assignments in the same scope
    -                scope = defstmts[0].scope()
    -                defstmts = [stmt for i, stmt in enumerate(defstmts)
    -                            if i == 0 or stmt.scope() is not scope]
    -                # if there are still more than one, don't attempt to be smarter
    -                # than we can be
    -                if len(defstmts) == 1:
    -                    defstmt = defstmts[0]
    -                    # check that if the node is accessed in the same method as
    -                    # it's defined, it's accessed after the initial assignment
    -                    frame = defstmt.frame()
    -                    lno = defstmt.fromlineno
    -                    for _node in nodes:
    -                        if _node.frame() is frame and _node.fromlineno < lno \
    -                           and not are_exclusive(_node.statement(), defstmt,
    -                                                 ('AttributeError', 'Exception', 'BaseException')):
    -                            self.add_message('access-member-before-definition',
    -                                             node=_node, args=(attr, lno))
    -
    -    def _check_first_arg_for_type(self, node, metaclass=0):
    -        """check the name of first argument, expect:
    -
    -        * 'self' for a regular method
    -        * 'cls' for a class method or a metaclass regular method (actually
    -          valid-classmethod-first-arg value)
    -        * 'mcs' for a metaclass class method (actually
    -          valid-metaclass-classmethod-first-arg)
    -        * not one of the above for a static method
    -        """
    -        # don't care about functions with unknown argument (builtins)
    -        if node.args.args is None:
    -            return
    -        first_arg = node.args.args and node.argnames()[0]
    -        self._first_attrs.append(first_arg)
    -        first = self._first_attrs[-1]
    -        # static method
    -        if node.type == 'staticmethod':
    -            if (first_arg == 'self' or
    -                    first_arg in self.config.valid_classmethod_first_arg or
    -                    first_arg in self.config.valid_metaclass_classmethod_first_arg):
    -                self.add_message('bad-staticmethod-argument', args=first, node=node)
    -                return
    -            self._first_attrs[-1] = None
    -        # class / regular method with no args
    -        elif not node.args.args:
    -            self.add_message('no-method-argument', node=node)
    -        # metaclass
    -        elif metaclass:
    -            # metaclass __new__ or classmethod
    -            if node.type == 'classmethod':
    -                self._check_first_arg_config(
    -                    first,
    -                    self.config.valid_metaclass_classmethod_first_arg, node,
    -                    'bad-mcs-classmethod-argument', node.name)
    -            # metaclass regular method
    -            else:
    -                self._check_first_arg_config(
    -                    first,
    -                    self.config.valid_classmethod_first_arg, node,
    -                    'bad-mcs-method-argument',
    -                    node.name)
    -        # regular class
    -        else:
    -            # class method
    -            if node.type == 'classmethod':
    -                self._check_first_arg_config(
    -                    first,
    -                    self.config.valid_classmethod_first_arg, node,
    -                    'bad-classmethod-argument',
    -                    node.name)
    -            # regular method without self as argument
    -            elif first != 'self':
    -                self.add_message('no-self-argument', node=node)
    -
    -    def _check_first_arg_config(self, first, config, node, message,
    -                                method_name):
    -        if first not in config:
    -            if len(config) == 1:
    -                valid = repr(config[0])
    -            else:
    -                valid = ', '.join(repr(v) for v in config[:-1])
    -                valid = '%s or %r' % (valid, config[-1])
    -            self.add_message(message, args=(method_name, valid), node=node)
    -
    -    def _check_bases_classes(self, node):
    -        """check that the given class node implements abstract methods from
    -        base classes
    -        """
    -        def is_abstract(method):
    -            return method.is_abstract(pass_is_abstract=False)
    -
    -        # check if this class abstract
    -        if class_is_abstract(node):
    -            return
    -
    -        methods = sorted(
    -            unimplemented_abstract_methods(node, is_abstract).items(),
    -            key=lambda item: item[0],
    -        )
    -        for name, method in methods:
    -            owner = method.parent.frame()
    -            if owner is node:
    -                continue
    -            # owner is not this class, it must be a parent class
    -            # check that the ancestor's method is not abstract
    -            if name in node.locals:
    -                # it is redefined as an attribute or with a descriptor
    -                continue
    -            self.add_message('abstract-method', node=node,
    -                             args=(name, owner.name))
    -
    -    def _check_interfaces(self, node):
    -        """check that the given class node really implements declared
    -        interfaces
    -        """
    -        e0221_hack = [False]
    -        def iface_handler(obj):
    -            """filter interface objects, it should be classes"""
    -            if not isinstance(obj, astroid.Class):
    -                e0221_hack[0] = True
    -                self.add_message('interface-is-not-class', node=node,
    -                                 args=(obj.as_string(),))
    -                return False
    -            return True
    -        ignore_iface_methods = self.config.ignore_iface_methods
    -        try:
    -            for iface in node.interfaces(handler_func=iface_handler):
    -                for imethod in iface.methods():
    -                    name = imethod.name
    -                    if name.startswith('_') or name in ignore_iface_methods:
    -                        # don't check method beginning with an underscore,
    -                        # usually belonging to the interface implementation
    -                        continue
    -                    # get class method astroid
    -                    try:
    -                        method = node_method(node, name)
    -                    except astroid.NotFoundError:
    -                        self.add_message('missing-interface-method',
    -                                         args=(name, iface.name),
    -                                         node=node)
    -                        continue
    -                    # ignore inherited methods
    -                    if method.parent.frame() is not node:
    -                        continue
    -                    # check signature
    -                    self._check_signature(method, imethod,
    -                                          '%s interface' % iface.name)
    -        except astroid.InferenceError:
    -            if e0221_hack[0]:
    -                return
    -            implements = Instance(node).getattr('__implements__')[0]
    -            assignment = implements.parent
    -            assert isinstance(assignment, astroid.Assign)
    -            # assignment.expr can be a Name or a Tuple or whatever.
    -            # Use as_string() for the message
    -            # FIXME: in case of multiple interfaces, find which one could not
    -            #        be resolved
    -            self.add_message('unresolved-interface', node=implements,
    -                             args=(node.name, assignment.value.as_string()))
    -
    -    def _check_init(self, node):
    -        """check that the __init__ method call super or ancestors'__init__
    -        method
    -        """
    -        if (not self.linter.is_message_enabled('super-init-not-called') and
    -                not self.linter.is_message_enabled('non-parent-init-called')):
    -            return
    -        klass_node = node.parent.frame()
    -        to_call = _ancestors_to_call(klass_node)
    -        not_called_yet = dict(to_call)
    -        for stmt in node.nodes_of_class(astroid.CallFunc):
    -            expr = stmt.func
    -            if not isinstance(expr, astroid.Getattr) \
    -                   or expr.attrname != '__init__':
    -                continue
    -            # skip the test if using super
    -            if isinstance(expr.expr, astroid.CallFunc) and \
    -                   isinstance(expr.expr.func, astroid.Name) and \
    -               expr.expr.func.name == 'super':
    -                return
    -            try:
    -                for klass in expr.expr.infer():
    -                    if klass is YES:
    -                        continue
    -                    # The infered klass can be super(), which was
    -                    # assigned to a variable and the `__init__`
    -                    # was called later.
    -                    #
    -                    # base = super()
    -                    # base.__init__(...)
    -
    -                    if (isinstance(klass, astroid.Instance) and
    -                            isinstance(klass._proxied, astroid.Class) and
    -                            is_builtin_object(klass._proxied) and
    -                            klass._proxied.name == 'super'):
    -                        return
    -                    try:
    -                        del not_called_yet[klass]
    -                    except KeyError:
    -                        if klass not in to_call:
    -                            self.add_message('non-parent-init-called',
    -                                             node=expr, args=klass.name)
    -            except astroid.InferenceError:
    -                continue
    -        for klass, method in six.iteritems(not_called_yet):
    -            if klass.name == 'object' or method.parent.name == 'object':
    -                continue
    -            self.add_message('super-init-not-called', args=klass.name, node=node)
    -
    -    def _check_signature(self, method1, refmethod, class_type):
    -        """check that the signature of the two given methods match
    -
    -        class_type is in 'class', 'interface'
    -        """
    -        if not (isinstance(method1, astroid.Function)
    -                and isinstance(refmethod, astroid.Function)):
    -            self.add_message('method-check-failed',
    -                             args=(method1, refmethod), node=method1)
    -            return
    -        # don't care about functions with unknown argument (builtins)
    -        if method1.args.args is None or refmethod.args.args is None:
    -            return
    -        # if we use *args, **kwargs, skip the below checks
    -        if method1.args.vararg or method1.args.kwarg:
    -            return
    -        if is_attr_private(method1.name):
    -            return
    -        if len(method1.args.args) != len(refmethod.args.args):
    -            self.add_message('arguments-differ',
    -                             args=(class_type, method1.name),
    -                             node=method1)
    -        elif len(method1.args.defaults) < len(refmethod.args.defaults):
    -            self.add_message('signature-differs',
    -                             args=(class_type, method1.name),
    -                             node=method1)
    -
    -    def is_first_attr(self, node):
    -        """Check that attribute lookup name use first attribute variable name
    -        (self for method, cls for classmethod and mcs for metaclass).
    -        """
    -        return self._first_attrs and isinstance(node.expr, astroid.Name) and \
    -                   node.expr.name == self._first_attrs[-1]
    -
    -def _ancestors_to_call(klass_node, method='__init__'):
    -    """return a dictionary where keys are the list of base classes providing
    -    the queried method, and so that should/may be called from the method node
    -    """
    -    to_call = {}
    -    for base_node in klass_node.ancestors(recurs=False):
    -        try:
    -            to_call[base_node] = next(base_node.igetattr(method))
    -        except astroid.InferenceError:
    -            continue
    -    return to_call
    -
    -
    -def node_method(node, method_name):
    -    """get astroid for  on the given class node, ensuring it
    -    is a Function node
    -    """
    -    for n in node.local_attr(method_name):
    -        if isinstance(n, astroid.Function):
    -            return n
    -    raise astroid.NotFoundError(method_name)
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(ClassChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/design_analysis.py b/pymode/libs/pylint/checkers/design_analysis.py
    deleted file mode 100644
    index 9ff10bf3..00000000
    --- a/pymode/libs/pylint/checkers/design_analysis.py
    +++ /dev/null
    @@ -1,331 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""check for signs of poor design"""
    -
    -import re
    -from collections import defaultdict
    -
    -from astroid import If, InferenceError
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import check_messages
    -
    -# regexp for ignored argument name
    -IGNORED_ARGUMENT_NAMES = re.compile('_.*')
    -
    -
    -MSGS = {
    -    'R0901': ('Too many ancestors (%s/%s)',
    -              'too-many-ancestors',
    -              'Used when class has too many parent classes, try to reduce \
    -              this to get a simpler (and so easier to use) class.'),
    -    'R0902': ('Too many instance attributes (%s/%s)',
    -              'too-many-instance-attributes',
    -              'Used when class has too many instance attributes, try to reduce \
    -              this to get a simpler (and so easier to use) class.'),
    -    'R0903': ('Too few public methods (%s/%s)',
    -              'too-few-public-methods',
    -              'Used when class has too few public methods, so be sure it\'s \
    -              really worth it.'),
    -    'R0904': ('Too many public methods (%s/%s)',
    -              'too-many-public-methods',
    -              'Used when class has too many public methods, try to reduce \
    -              this to get a simpler (and so easier to use) class.'),
    -
    -    'R0911': ('Too many return statements (%s/%s)',
    -              'too-many-return-statements',
    -              'Used when a function or method has too many return statement, \
    -              making it hard to follow.'),
    -    'R0912': ('Too many branches (%s/%s)',
    -              'too-many-branches',
    -              'Used when a function or method has too many branches, \
    -              making it hard to follow.'),
    -    'R0913': ('Too many arguments (%s/%s)',
    -              'too-many-arguments',
    -              'Used when a function or method takes too many arguments.'),
    -    'R0914': ('Too many local variables (%s/%s)',
    -              'too-many-locals',
    -              'Used when a function or method has too many local variables.'),
    -    'R0915': ('Too many statements (%s/%s)',
    -              'too-many-statements',
    -              'Used when a function or method has too many statements. You \
    -              should then split it in smaller functions / methods.'),
    -    'R0923': ('Interface not implemented',
    -              'interface-not-implemented',
    -              'Used when an interface class is not implemented anywhere.'),
    -    }
    -
    -
    -class MisdesignChecker(BaseChecker):
    -    """checks for sign of poor/misdesign:
    -    * number of methods, attributes, local variables...
    -    * size, complexity of functions, methods
    -    """
    -
    -    __implements__ = (IAstroidChecker,)
    -
    -    # configuration section name
    -    name = 'design'
    -    # messages
    -    msgs = MSGS
    -    priority = -2
    -    # configuration options
    -    options = (('max-args',
    -                {'default' : 5, 'type' : 'int', 'metavar' : '',
    -                 'help': 'Maximum number of arguments for function / method'}
    -               ),
    -               ('ignored-argument-names',
    -                {'default' : IGNORED_ARGUMENT_NAMES,
    -                 'type' :'regexp', 'metavar' : '',
    -                 'help' : 'Argument names that match this expression will be '
    -                          'ignored. Default to name with leading underscore'}
    -               ),
    -               ('max-locals',
    -                {'default' : 15, 'type' : 'int', 'metavar' : '',
    -                 'help': 'Maximum number of locals for function / method body'}
    -               ),
    -               ('max-returns',
    -                {'default' : 6, 'type' : 'int', 'metavar' : '',
    -                 'help': 'Maximum number of return / yield for function / '
    -                         'method body'}
    -               ),
    -               ('max-branches',
    -                {'default' : 12, 'type' : 'int', 'metavar' : '',
    -                 'help': 'Maximum number of branch for function / method body'}
    -               ),
    -               ('max-statements',
    -                {'default' : 50, 'type' : 'int', 'metavar' : '',
    -                 'help': 'Maximum number of statements in function / method '
    -                         'body'}
    -               ),
    -               ('max-parents',
    -                {'default' : 7,
    -                 'type' : 'int',
    -                 'metavar' : '',
    -                 'help' : 'Maximum number of parents for a class (see R0901).'}
    -               ),
    -               ('max-attributes',
    -                {'default' : 7,
    -                 'type' : 'int',
    -                 'metavar' : '',
    -                 'help' : 'Maximum number of attributes for a class \
    -(see R0902).'}
    -               ),
    -               ('min-public-methods',
    -                {'default' : 2,
    -                 'type' : 'int',
    -                 'metavar' : '',
    -                 'help' : 'Minimum number of public methods for a class \
    -(see R0903).'}
    -               ),
    -               ('max-public-methods',
    -                {'default' : 20,
    -                 'type' : 'int',
    -                 'metavar' : '',
    -                 'help' : 'Maximum number of public methods for a class \
    -(see R0904).'}
    -               ),
    -              )
    -
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        self.stats = None
    -        self._returns = None
    -        self._branches = None
    -        self._used_ifaces = None
    -        self._ifaces = None
    -        self._stmts = 0
    -
    -    def open(self):
    -        """initialize visit variables"""
    -        self.stats = self.linter.add_stats()
    -        self._returns = []
    -        self._branches = defaultdict(int)
    -        self._used_ifaces = {}
    -        self._ifaces = []
    -
    -    def close(self):
    -        """check that interface classes are used"""
    -        for iface in self._ifaces:
    -            if not iface in self._used_ifaces:
    -                self.add_message('interface-not-implemented', node=iface)
    -
    -    @check_messages('too-many-ancestors', 'too-many-instance-attributes',
    -                    'too-few-public-methods', 'too-many-public-methods',
    -                    'interface-not-implemented')
    -    def visit_class(self, node):
    -        """check size of inheritance hierarchy and number of instance attributes
    -        """
    -        # Is the total inheritance hierarchy is 7 or less?
    -        nb_parents = len(list(node.ancestors()))
    -        if nb_parents > self.config.max_parents:
    -            self.add_message('too-many-ancestors', node=node,
    -                             args=(nb_parents, self.config.max_parents))
    -        # Does the class contain less than 20 attributes for
    -        # non-GUI classes (40 for GUI)?
    -        # FIXME detect gui classes
    -        if len(node.instance_attrs) > self.config.max_attributes:
    -            self.add_message('too-many-instance-attributes', node=node,
    -                             args=(len(node.instance_attrs),
    -                                   self.config.max_attributes))
    -        # update interface classes structures
    -        if node.type == 'interface' and node.name != 'Interface':
    -            self._ifaces.append(node)
    -            for parent in node.ancestors(False):
    -                if parent.name == 'Interface':
    -                    continue
    -                self._used_ifaces[parent] = 1
    -        try:
    -            for iface in node.interfaces():
    -                self._used_ifaces[iface] = 1
    -        except InferenceError:
    -            # XXX log ?
    -            pass
    -
    -    @check_messages('too-few-public-methods', 'too-many-public-methods')
    -    def leave_class(self, node):
    -        """check number of public methods"""
    -        my_methods = sum(1 for method in node.mymethods()
    -                         if not method.name.startswith('_'))
    -        all_methods = sum(1 for method in node.methods()
    -                          if not method.name.startswith('_'))
    -
    -        # Does the class contain less than n public methods ?
    -        # This checks only the methods defined in the current class,
    -        # since the user might not have control over the classes
    -        # from the ancestors. It avoids some false positives
    -        # for classes such as unittest.TestCase, which provides
    -        # a lot of assert methods. It doesn't make sense to warn
    -        # when the user subclasses TestCase to add his own tests.
    -        if my_methods > self.config.max_public_methods:
    -            self.add_message('too-many-public-methods', node=node,
    -                             args=(my_methods,
    -                                   self.config.max_public_methods))
    -        # stop here for exception, metaclass and interface classes
    -        if node.type != 'class':
    -            return
    -
    -        # Does the class contain more than n public methods ?
    -        # This checks all the methods defined by ancestors and
    -        # by the current class.
    -        if all_methods < self.config.min_public_methods:
    -            self.add_message('too-few-public-methods', node=node,
    -                             args=(all_methods,
    -                                   self.config.min_public_methods))
    -
    -    @check_messages('too-many-return-statements', 'too-many-branches',
    -                    'too-many-arguments', 'too-many-locals',
    -                    'too-many-statements')
    -    def visit_function(self, node):
    -        """check function name, docstring, arguments, redefinition,
    -        variable names, max locals
    -        """
    -        # init branch and returns counters
    -        self._returns.append(0)
    -        # check number of arguments
    -        args = node.args.args
    -        if args is not None:
    -            ignored_args_num = len(
    -                [arg for arg in args
    -                 if self.config.ignored_argument_names.match(arg.name)])
    -            argnum = len(args) - ignored_args_num
    -            if  argnum > self.config.max_args:
    -                self.add_message('too-many-arguments', node=node,
    -                                 args=(len(args), self.config.max_args))
    -        else:
    -            ignored_args_num = 0
    -        # check number of local variables
    -        locnum = len(node.locals) - ignored_args_num
    -        if locnum > self.config.max_locals:
    -            self.add_message('too-many-locals', node=node,
    -                             args=(locnum, self.config.max_locals))
    -        # init statements counter
    -        self._stmts = 1
    -
    -    @check_messages('too-many-return-statements', 'too-many-branches',
    -                    'too-many-arguments', 'too-many-locals',
    -                    'too-many-statements')
    -    def leave_function(self, node):
    -        """most of the work is done here on close:
    -        checks for max returns, branch, return in __init__
    -        """
    -        returns = self._returns.pop()
    -        if returns > self.config.max_returns:
    -            self.add_message('too-many-return-statements', node=node,
    -                             args=(returns, self.config.max_returns))
    -        branches = self._branches[node]
    -        if branches > self.config.max_branches:
    -            self.add_message('too-many-branches', node=node,
    -                             args=(branches, self.config.max_branches))
    -        # check number of statements
    -        if self._stmts > self.config.max_statements:
    -            self.add_message('too-many-statements', node=node,
    -                             args=(self._stmts, self.config.max_statements))
    -
    -    def visit_return(self, _):
    -        """count number of returns"""
    -        if not self._returns:
    -            return # return outside function, reported by the base checker
    -        self._returns[-1] += 1
    -
    -    def visit_default(self, node):
    -        """default visit method -> increments the statements counter if
    -        necessary
    -        """
    -        if node.is_statement:
    -            self._stmts += 1
    -
    -    def visit_tryexcept(self, node):
    -        """increments the branches counter"""
    -        branches = len(node.handlers)
    -        if node.orelse:
    -            branches += 1
    -        self._inc_branch(node, branches)
    -        self._stmts += branches
    -
    -    def visit_tryfinally(self, node):
    -        """increments the branches counter"""
    -        self._inc_branch(node, 2)
    -        self._stmts += 2
    -
    -    def visit_if(self, node):
    -        """increments the branches counter"""
    -        branches = 1
    -        # don't double count If nodes coming from some 'elif'
    -        if node.orelse and (len(node.orelse) > 1 or
    -                            not isinstance(node.orelse[0], If)):
    -            branches += 1
    -        self._inc_branch(node, branches)
    -        self._stmts += branches
    -
    -    def visit_while(self, node):
    -        """increments the branches counter"""
    -        branches = 1
    -        if node.orelse:
    -            branches += 1
    -        self._inc_branch(node, branches)
    -
    -    visit_for = visit_while
    -
    -    def _inc_branch(self, node, branchesnum=1):
    -        """increments the branches counter"""
    -        self._branches[node.scope()] += branchesnum
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(MisdesignChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/exceptions.py b/pymode/libs/pylint/checkers/exceptions.py
    deleted file mode 100644
    index 88a8f225..00000000
    --- a/pymode/libs/pylint/checkers/exceptions.py
    +++ /dev/null
    @@ -1,332 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""exceptions handling (raising, catching, exceptions classes) checker
    -"""
    -import sys
    -
    -import astroid
    -from astroid import YES, Instance, unpack_infer, List, Tuple
    -from logilab.common.compat import builtins
    -
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    is_empty,
    -    is_raising,
    -    check_messages,
    -    inherit_from_std_ex,
    -    EXCEPTIONS_MODULE,
    -    has_known_bases,
    -    safe_infer)
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
    -
    -
    -def _annotated_unpack_infer(stmt, context=None):
    -    """
    -    Recursively generate nodes inferred by the given statement.
    -    If the inferred value is a list or a tuple, recurse on the elements.
    -    Returns an iterator which yields tuples in the format
    -    ('original node', 'infered node').
    -    """
    -    if isinstance(stmt, (List, Tuple)):
    -        for elt in stmt.elts:
    -            inferred = safe_infer(elt)
    -            if inferred and inferred is not YES:
    -                yield elt, inferred
    -        return
    -    for infered in stmt.infer(context):
    -        if infered is YES:
    -            continue
    -        yield stmt, infered
    -
    -
    -PY3K = sys.version_info >= (3, 0)
    -OVERGENERAL_EXCEPTIONS = ('Exception',)
    -BUILTINS_NAME = builtins.__name__
    -MSGS = {
    -    'E0701': ('Bad except clauses order (%s)',
    -              'bad-except-order',
    -              'Used when except clauses are not in the correct order (from the '
    -              'more specific to the more generic). If you don\'t fix the order, '
    -              'some exceptions may not be catched by the most specific handler.'),
    -    'E0702': ('Raising %s while only classes or instances are allowed',
    -              'raising-bad-type',
    -              'Used when something which is neither a class, an instance or a \
    -              string is raised (i.e. a `TypeError` will be raised).'),
    -    'E0703': ('Exception context set to something which is not an '
    -              'exception, nor None',
    -              'bad-exception-context',
    -              'Used when using the syntax "raise ... from ...", '
    -              'where the exception context is not an exception, '
    -              'nor None.',
    -              {'minversion': (3, 0)}),
    -    'E0710': ('Raising a new style class which doesn\'t inherit from BaseException',
    -              'raising-non-exception',
    -              'Used when a new style class which doesn\'t inherit from \
    -               BaseException is raised.'),
    -    'E0711': ('NotImplemented raised - should raise NotImplementedError',
    -              'notimplemented-raised',
    -              'Used when NotImplemented is raised instead of \
    -              NotImplementedError'),
    -    'E0712': ('Catching an exception which doesn\'t inherit from BaseException: %s',
    -              'catching-non-exception',
    -              'Used when a class which doesn\'t inherit from \
    -               BaseException is used as an exception in an except clause.'),
    -    'W0702': ('No exception type(s) specified',
    -              'bare-except',
    -              'Used when an except clause doesn\'t specify exceptions type to \
    -              catch.'),
    -    'W0703': ('Catching too general exception %s',
    -              'broad-except',
    -              'Used when an except catches a too general exception, \
    -              possibly burying unrelated errors.'),
    -    'W0704': ('Except doesn\'t do anything',
    -              'pointless-except',
    -              'Used when an except clause does nothing but "pass" and there is\
    -              no "else" clause.'),
    -    'W0710': ('Exception doesn\'t inherit from standard "Exception" class',
    -              'nonstandard-exception',
    -              'Used when a custom exception class is raised but doesn\'t \
    -              inherit from the builtin "Exception" class.',
    -              {'maxversion': (3, 0)}),
    -    'W0711': ('Exception to catch is the result of a binary "%s" operation',
    -              'binary-op-exception',
    -              'Used when the exception to catch is of the form \
    -              "except A or B:".  If intending to catch multiple, \
    -              rewrite as "except (A, B):"'),
    -    }
    -
    -
    -class ExceptionsChecker(BaseChecker):
    -    """checks for
    -    * excepts without exception filter
    -    * type of raise argument : string, Exceptions, other values
    -    """
    -
    -    __implements__ = IAstroidChecker
    -
    -    name = 'exceptions'
    -    msgs = MSGS
    -    priority = -4
    -    options = (('overgeneral-exceptions',
    -                {'default' : OVERGENERAL_EXCEPTIONS,
    -                 'type' :'csv', 'metavar' : '',
    -                 'help' : 'Exceptions that will emit a warning '
    -                          'when being caught. Defaults to "%s"' % (
    -                              ', '.join(OVERGENERAL_EXCEPTIONS),)}
    -               ),
    -              )
    -
    -    @check_messages('nonstandard-exception',
    -                    'raising-bad-type', 'raising-non-exception',
    -                    'notimplemented-raised', 'bad-exception-context')
    -    def visit_raise(self, node):
    -        """visit raise possibly inferring value"""
    -        # ignore empty raise
    -        if node.exc is None:
    -            return
    -        if PY3K and node.cause:
    -            self._check_bad_exception_context(node)
    -
    -        expr = node.exc
    -        if self._check_raise_value(node, expr):
    -            return
    -        else:
    -            try:
    -                value = next(unpack_infer(expr))
    -            except astroid.InferenceError:
    -                return
    -            self._check_raise_value(node, value)
    -
    -    def _check_bad_exception_context(self, node):
    -        """Verify that the exception context is properly set.
    -
    -        An exception context can be only `None` or an exception.
    -        """
    -        cause = safe_infer(node.cause)
    -        if cause in (YES, None):
    -            return
    -        if isinstance(cause, astroid.Const):
    -            if cause.value is not None:
    -                self.add_message('bad-exception-context',
    -                                 node=node)
    -        elif (not isinstance(cause, astroid.Class) and
    -              not inherit_from_std_ex(cause)):
    -            self.add_message('bad-exception-context',
    -                             node=node)
    -
    -    def _check_raise_value(self, node, expr):
    -        """check for bad values, string exception and class inheritance
    -        """
    -        value_found = True
    -        if isinstance(expr, astroid.Const):
    -            value = expr.value
    -            if not isinstance(value, str):
    -                # raising-string will be emitted from python3 porting checker.
    -                self.add_message('raising-bad-type', node=node,
    -                                 args=value.__class__.__name__)
    -        elif ((isinstance(expr, astroid.Name) and
    -               expr.name in ('None', 'True', 'False')) or
    -              isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple,
    -                                astroid.Module, astroid.Function))):
    -            emit = True
    -            if not PY3K and isinstance(expr, astroid.Tuple):
    -                # On Python 2, using the following is not an error:
    -                #    raise (ZeroDivisionError, None)
    -                #    raise (ZeroDivisionError, )
    -                # What's left to do is to check that the first
    -                # argument is indeed an exception.
    -                # Verifying the other arguments is not
    -                # the scope of this check.
    -                first = expr.elts[0]
    -                inferred = safe_infer(first)
    -                if isinstance(inferred, Instance):
    -                    # pylint: disable=protected-access
    -                    inferred = inferred._proxied
    -                if (inferred is YES or
    -                        isinstance(inferred, astroid.Class)
    -                        and inherit_from_std_ex(inferred)):
    -                    emit = False
    -            if emit:
    -                self.add_message('raising-bad-type',
    -                                 node=node,
    -                                 args=expr.name)
    -        elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented')
    -              or (isinstance(expr, astroid.CallFunc) and
    -                  isinstance(expr.func, astroid.Name) and
    -                  expr.func.name == 'NotImplemented')):
    -            self.add_message('notimplemented-raised', node=node)
    -        elif isinstance(expr, (Instance, astroid.Class)):
    -            if isinstance(expr, Instance):
    -                # pylint: disable=protected-access
    -                expr = expr._proxied
    -            if (isinstance(expr, astroid.Class) and
    -                    not inherit_from_std_ex(expr)):
    -                if expr.newstyle:
    -                    self.add_message('raising-non-exception', node=node)
    -                else:
    -                    if has_known_bases(expr):
    -                        confidence = INFERENCE
    -                    else:
    -                        confidence = INFERENCE_FAILURE
    -                    self.add_message(
    -                        'nonstandard-exception', node=node,
    -                        confidence=confidence)
    -            else:
    -                value_found = False
    -        else:
    -            value_found = False
    -        return value_found
    -
    -    def _check_catching_non_exception(self, handler, exc, part):
    -        if isinstance(exc, astroid.Tuple):
    -            # Check if it is a tuple of exceptions.
    -            inferred = [safe_infer(elt) for elt in exc.elts]
    -            if any(node is astroid.YES for node in inferred):
    -                # Don't emit if we don't know every component.
    -                return
    -            if all(node and inherit_from_std_ex(node)
    -                   for node in inferred):
    -                return
    -
    -        if not isinstance(exc, astroid.Class):
    -            # Don't emit the warning if the infered stmt
    -            # is None, but the exception handler is something else,
    -            # maybe it was redefined.
    -            if (isinstance(exc, astroid.Const) and
    -                    exc.value is None):
    -                if ((isinstance(handler.type, astroid.Const) and
    -                     handler.type.value is None) or
    -                        handler.type.parent_of(exc)):
    -                    # If the exception handler catches None or
    -                    # the exception component, which is None, is
    -                    # defined by the entire exception handler, then
    -                    # emit a warning.
    -                    self.add_message('catching-non-exception',
    -                                     node=handler.type,
    -                                     args=(part.as_string(), ))
    -            else:
    -                self.add_message('catching-non-exception',
    -                                 node=handler.type,
    -                                 args=(part.as_string(), ))
    -            return
    -        if (not inherit_from_std_ex(exc) and
    -                exc.root().name != BUILTINS_NAME):
    -            if has_known_bases(exc):
    -                self.add_message('catching-non-exception',
    -                                 node=handler.type,
    -                                 args=(exc.name, ))
    -
    -    @check_messages('bare-except', 'broad-except', 'pointless-except',
    -                    'binary-op-exception', 'bad-except-order',
    -                    'catching-non-exception')
    -    def visit_tryexcept(self, node):
    -        """check for empty except"""
    -        exceptions_classes = []
    -        nb_handlers = len(node.handlers)
    -        for index, handler in enumerate(node.handlers):
    -            # single except doing nothing but "pass" without else clause
    -            if is_empty(handler.body) and not node.orelse:
    -                self.add_message('pointless-except',
    -                                 node=handler.type or handler.body[0])
    -            if handler.type is None:
    -                if not is_raising(handler.body):
    -                    self.add_message('bare-except', node=handler)
    -                # check if a "except:" is followed by some other
    -                # except
    -                if index < (nb_handlers - 1):
    -                    msg = 'empty except clause should always appear last'
    -                    self.add_message('bad-except-order', node=node, args=msg)
    -
    -            elif isinstance(handler.type, astroid.BoolOp):
    -                self.add_message('binary-op-exception',
    -                                 node=handler, args=handler.type.op)
    -            else:
    -                try:
    -                    excs = list(_annotated_unpack_infer(handler.type))
    -                except astroid.InferenceError:
    -                    continue
    -                for part, exc in excs:
    -                    if exc is YES:
    -                        continue
    -                    if (isinstance(exc, astroid.Instance)
    -                            and inherit_from_std_ex(exc)):
    -                        # pylint: disable=protected-access
    -                        exc = exc._proxied
    -
    -                    self._check_catching_non_exception(handler, exc, part)
    -
    -                    if not isinstance(exc, astroid.Class):
    -                        continue
    -
    -                    exc_ancestors = [anc for anc in exc.ancestors()
    -                                     if isinstance(anc, astroid.Class)]
    -                    for previous_exc in exceptions_classes:
    -                        if previous_exc in exc_ancestors:
    -                            msg = '%s is an ancestor class of %s' % (
    -                                previous_exc.name, exc.name)
    -                            self.add_message('bad-except-order',
    -                                             node=handler.type, args=msg)
    -                    if (exc.name in self.config.overgeneral_exceptions
    -                            and exc.root().name == EXCEPTIONS_MODULE
    -                            and not is_raising(handler.body)):
    -                        self.add_message('broad-except',
    -                                         args=exc.name, node=handler.type)
    -
    -                exceptions_classes += [exc for _, exc in excs]
    -
    -
    -def register(linter):
    -    """required method to auto register this checker"""
    -    linter.register_checker(ExceptionsChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/format.py b/pymode/libs/pylint/checkers/format.py
    deleted file mode 100644
    index 8c496ac1..00000000
    --- a/pymode/libs/pylint/checkers/format.py
    +++ /dev/null
    @@ -1,968 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Python code format's checker.
    -
    -By default try to follow Guido's style guide :
    -
    -http://www.python.org/doc/essays/styleguide.html
    -
    -Some parts of the process_token method is based from The Tab Nanny std module.
    -"""
    -
    -import keyword
    -import sys
    -import tokenize
    -from functools import reduce # pylint: disable=redefined-builtin
    -
    -import six
    -from six.moves import zip, map, filter # pylint: disable=redefined-builtin
    -
    -from astroid import nodes
    -
    -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker
    -from pylint.checkers import BaseTokenChecker
    -from pylint.checkers.utils import check_messages
    -from pylint.utils import WarningScope, OPTION_RGX
    -
    -_CONTINUATION_BLOCK_OPENERS = ['elif', 'except', 'for', 'if', 'while', 'def', 'class']
    -_KEYWORD_TOKENS = ['assert', 'del', 'elif', 'except', 'for', 'if', 'in', 'not',
    -                   'raise', 'return', 'while', 'yield']
    -if sys.version_info < (3, 0):
    -    _KEYWORD_TOKENS.append('print')
    -
    -_SPACED_OPERATORS = ['==', '<', '>', '!=', '<>', '<=', '>=',
    -                     '+=', '-=', '*=', '**=', '/=', '//=', '&=', '|=', '^=',
    -                     '%=', '>>=', '<<=']
    -_OPENING_BRACKETS = ['(', '[', '{']
    -_CLOSING_BRACKETS = [')', ']', '}']
    -_TAB_LENGTH = 8
    -
    -_EOL = frozenset([tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT])
    -_JUNK_TOKENS = (tokenize.COMMENT, tokenize.NL)
    -
    -# Whitespace checking policy constants
    -_MUST = 0
    -_MUST_NOT = 1
    -_IGNORE = 2
    -
    -# Whitespace checking config constants
    -_DICT_SEPARATOR = 'dict-separator'
    -_TRAILING_COMMA = 'trailing-comma'
    -_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR]
    -
    -MSGS = {
    -    'C0301': ('Line too long (%s/%s)',
    -              'line-too-long',
    -              'Used when a line is longer than a given number of characters.'),
    -    'C0302': ('Too many lines in module (%s/%s)', # was W0302
    -              'too-many-lines',
    -              'Used when a module has too much lines, reducing its readability.'
    -             ),
    -    'C0303': ('Trailing whitespace',
    -              'trailing-whitespace',
    -              'Used when there is whitespace between the end of a line and the '
    -              'newline.'),
    -    'C0304': ('Final newline missing',
    -              'missing-final-newline',
    -              'Used when the last line in a file is missing a newline.'),
    -    'W0311': ('Bad indentation. Found %s %s, expected %s',
    -              'bad-indentation',
    -              'Used when an unexpected number of indentation\'s tabulations or '
    -              'spaces has been found.'),
    -    'C0330': ('Wrong %s indentation%s.\n%s%s',
    -              'bad-continuation',
    -              'TODO'),
    -    'W0312': ('Found indentation with %ss instead of %ss',
    -              'mixed-indentation',
    -              'Used when there are some mixed tabs and spaces in a module.'),
    -    'W0301': ('Unnecessary semicolon', # was W0106
    -              'unnecessary-semicolon',
    -              'Used when a statement is ended by a semi-colon (";"), which \
    -              isn\'t necessary (that\'s python, not C ;).'),
    -    'C0321': ('More than one statement on a single line',
    -              'multiple-statements',
    -              'Used when more than on statement are found on the same line.',
    -              {'scope': WarningScope.NODE}),
    -    'C0325' : ('Unnecessary parens after %r keyword',
    -               'superfluous-parens',
    -               'Used when a single item in parentheses follows an if, for, or '
    -               'other keyword.'),
    -    'C0326': ('%s space %s %s %s\n%s',
    -              'bad-whitespace',
    -              ('Used when a wrong number of spaces is used around an operator, '
    -               'bracket or block opener.'),
    -              {'old_names': [('C0323', 'no-space-after-operator'),
    -                             ('C0324', 'no-space-after-comma'),
    -                             ('C0322', 'no-space-before-operator')]}),
    -    'W0332': ('Use of "l" as long integer identifier',
    -              'lowercase-l-suffix',
    -              'Used when a lower case "l" is used to mark a long integer. You '
    -              'should use a upper case "L" since the letter "l" looks too much '
    -              'like the digit "1"',
    -              {'maxversion': (3, 0)}),
    -    'C0327': ('Mixed line endings LF and CRLF',
    -              'mixed-line-endings',
    -              'Used when there are mixed (LF and CRLF) newline signs in a file.'),
    -    'C0328': ('Unexpected line ending format. There is \'%s\' while it should be \'%s\'.',
    -              'unexpected-line-ending-format',
    -              'Used when there is different newline than expected.'),
    -    }
    -
    -
    -def _underline_token(token):
    -    length = token[3][1] - token[2][1]
    -    offset = token[2][1]
    -    return token[4] + (' ' * offset) + ('^' * length)
    -
    -
    -def _column_distance(token1, token2):
    -    if token1 == token2:
    -        return 0
    -    if token2[3] < token1[3]:
    -        token1, token2 = token2, token1
    -    if token1[3][0] != token2[2][0]:
    -        return None
    -    return token2[2][1] - token1[3][1]
    -
    -
    -def _last_token_on_line_is(tokens, line_end, token):
    -    return (line_end > 0 and tokens.token(line_end-1) == token or
    -            line_end > 1 and tokens.token(line_end-2) == token
    -            and tokens.type(line_end-1) == tokenize.COMMENT)
    -
    -
    -def _token_followed_by_eol(tokens, position):
    -    return (tokens.type(position+1) == tokenize.NL or
    -            tokens.type(position+1) == tokenize.COMMENT and
    -            tokens.type(position+2) == tokenize.NL)
    -
    -
    -def _get_indent_length(line):
    -    """Return the length of the indentation on the given token's line."""
    -    result = 0
    -    for char in line:
    -        if char == ' ':
    -            result += 1
    -        elif char == '\t':
    -            result += _TAB_LENGTH
    -        else:
    -            break
    -    return result
    -
    -
    -def _get_indent_hint_line(bar_positions, bad_position):
    -    """Return a line with |s for each of the positions in the given lists."""
    -    if not bar_positions:
    -        return ''
    -    markers = [(pos, '|') for pos in bar_positions]
    -    markers.append((bad_position, '^'))
    -    markers.sort()
    -    line = [' '] * (markers[-1][0] + 1)
    -    for position, marker in markers:
    -        line[position] = marker
    -    return ''.join(line)
    -
    -
    -class _ContinuedIndent(object):
    -    __slots__ = ('valid_outdent_offsets',
    -                 'valid_continuation_offsets',
    -                 'context_type',
    -                 'token',
    -                 'position')
    -
    -    def __init__(self,
    -                 context_type,
    -                 token,
    -                 position,
    -                 valid_outdent_offsets,
    -                 valid_continuation_offsets):
    -        self.valid_outdent_offsets = valid_outdent_offsets
    -        self.valid_continuation_offsets = valid_continuation_offsets
    -        self.context_type = context_type
    -        self.position = position
    -        self.token = token
    -
    -
    -# The contexts for hanging indents.
    -# A hanging indented dictionary value after :
    -HANGING_DICT_VALUE = 'dict-value'
    -# Hanging indentation in an expression.
    -HANGING = 'hanging'
    -# Hanging indentation in a block header.
    -HANGING_BLOCK = 'hanging-block'
    -# Continued indentation inside an expression.
    -CONTINUED = 'continued'
    -# Continued indentation in a block header.
    -CONTINUED_BLOCK = 'continued-block'
    -
    -SINGLE_LINE = 'single'
    -WITH_BODY = 'multi'
    -
    -_CONTINUATION_MSG_PARTS = {
    -    HANGING_DICT_VALUE: ('hanging', ' in dict value'),
    -    HANGING: ('hanging', ''),
    -    HANGING_BLOCK: ('hanging', ' before block'),
    -    CONTINUED: ('continued', ''),
    -    CONTINUED_BLOCK: ('continued', ' before block'),
    -}
    -
    -
    -def _Offsets(*args):
    -    """Valid indentation offsets for a continued line."""
    -    return dict((a, None) for a in args)
    -
    -
    -def _BeforeBlockOffsets(single, with_body):
    -    """Valid alternative indent offsets for continued lines before blocks.
    -
    -    :param single: Valid offset for statements on a single logical line.
    -    :param with_body: Valid offset for statements on several lines.
    -    """
    -    return {single: SINGLE_LINE, with_body: WITH_BODY}
    -
    -
    -class TokenWrapper(object):
    -    """A wrapper for readable access to token information."""
    -
    -    def __init__(self, tokens):
    -        self._tokens = tokens
    -
    -    def token(self, idx):
    -        return self._tokens[idx][1]
    -
    -    def type(self, idx):
    -        return self._tokens[idx][0]
    -
    -    def start_line(self, idx):
    -        return self._tokens[idx][2][0]
    -
    -    def start_col(self, idx):
    -        return self._tokens[idx][2][1]
    -
    -    def line(self, idx):
    -        return self._tokens[idx][4]
    -
    -
    -class ContinuedLineState(object):
    -    """Tracker for continued indentation inside a logical line."""
    -
    -    def __init__(self, tokens, config):
    -        self._line_start = -1
    -        self._cont_stack = []
    -        self._is_block_opener = False
    -        self.retained_warnings = []
    -        self._config = config
    -        self._tokens = TokenWrapper(tokens)
    -
    -    @property
    -    def has_content(self):
    -        return bool(self._cont_stack)
    -
    -    @property
    -    def _block_indent_size(self):
    -        return len(self._config.indent_string.replace('\t', ' ' * _TAB_LENGTH))
    -
    -    @property
    -    def _continuation_size(self):
    -        return self._config.indent_after_paren
    -
    -    def handle_line_start(self, pos):
    -        """Record the first non-junk token at the start of a line."""
    -        if self._line_start > -1:
    -            return
    -        self._is_block_opener = self._tokens.token(pos) in _CONTINUATION_BLOCK_OPENERS
    -        self._line_start = pos
    -
    -    def next_physical_line(self):
    -        """Prepares the tracker for a new physical line (NL)."""
    -        self._line_start = -1
    -        self._is_block_opener = False
    -
    -    def next_logical_line(self):
    -        """Prepares the tracker for a new logical line (NEWLINE).
    -
    -        A new logical line only starts with block indentation.
    -        """
    -        self.next_physical_line()
    -        self.retained_warnings = []
    -        self._cont_stack = []
    -
    -    def add_block_warning(self, token_position, state, valid_offsets):
    -        self.retained_warnings.append((token_position, state, valid_offsets))
    -
    -    def get_valid_offsets(self, idx):
    -        """Returns the valid offsets for the token at the given position."""
    -        # The closing brace on a dict or the 'for' in a dict comprehension may
    -        # reset two indent levels because the dict value is ended implicitly
    -        stack_top = -1
    -        if self._tokens.token(idx) in ('}', 'for') and self._cont_stack[-1].token == ':':
    -            stack_top = -2
    -        indent = self._cont_stack[stack_top]
    -        if self._tokens.token(idx) in _CLOSING_BRACKETS:
    -            valid_offsets = indent.valid_outdent_offsets
    -        else:
    -            valid_offsets = indent.valid_continuation_offsets
    -        return indent, valid_offsets.copy()
    -
    -    def _hanging_indent_after_bracket(self, bracket, position):
    -        """Extracts indentation information for a hanging indent."""
    -        indentation = _get_indent_length(self._tokens.line(position))
    -        if self._is_block_opener and self._continuation_size == self._block_indent_size:
    -            return _ContinuedIndent(
    -                HANGING_BLOCK,
    -                bracket,
    -                position,
    -                _Offsets(indentation + self._continuation_size, indentation),
    -                _BeforeBlockOffsets(indentation + self._continuation_size,
    -                                    indentation + self._continuation_size * 2))
    -        elif bracket == ':':
    -            # If the dict key was on the same line as the open brace, the new
    -            # correct indent should be relative to the key instead of the
    -            # current indent level
    -            paren_align = self._cont_stack[-1].valid_outdent_offsets
    -            next_align = self._cont_stack[-1].valid_continuation_offsets.copy()
    -            next_align_keys = list(next_align.keys())
    -            next_align[next_align_keys[0] + self._continuation_size] = True
    -            # Note that the continuation of
    -            # d = {
    -            #       'a': 'b'
    -            #            'c'
    -            # }
    -            # is handled by the special-casing for hanging continued string indents.
    -            return _ContinuedIndent(HANGING_DICT_VALUE, bracket, position, paren_align, next_align)
    -        else:
    -            return _ContinuedIndent(
    -                HANGING,
    -                bracket,
    -                position,
    -                _Offsets(indentation, indentation + self._continuation_size),
    -                _Offsets(indentation + self._continuation_size))
    -
    -    def _continuation_inside_bracket(self, bracket, pos):
    -        """Extracts indentation information for a continued indent."""
    -        indentation = _get_indent_length(self._tokens.line(pos))
    -        token_start = self._tokens.start_col(pos)
    -        next_token_start = self._tokens.start_col(pos + 1)
    -        if self._is_block_opener and next_token_start - indentation == self._block_indent_size:
    -            return _ContinuedIndent(
    -                CONTINUED_BLOCK,
    -                bracket,
    -                pos,
    -                _Offsets(token_start),
    -                _BeforeBlockOffsets(next_token_start, next_token_start + self._continuation_size))
    -        else:
    -            return _ContinuedIndent(
    -                CONTINUED,
    -                bracket,
    -                pos,
    -                _Offsets(token_start),
    -                _Offsets(next_token_start))
    -
    -    def pop_token(self):
    -        self._cont_stack.pop()
    -
    -    def push_token(self, token, position):
    -        """Pushes a new token for continued indentation on the stack.
    -
    -        Tokens that can modify continued indentation offsets are:
    -          * opening brackets
    -          * 'lambda'
    -          * : inside dictionaries
    -
    -        push_token relies on the caller to filter out those
    -        interesting tokens.
    -
    -        :param token: The concrete token
    -        :param position: The position of the token in the stream.
    -        """
    -        if _token_followed_by_eol(self._tokens, position):
    -            self._cont_stack.append(
    -                self._hanging_indent_after_bracket(token, position))
    -        else:
    -            self._cont_stack.append(
    -                self._continuation_inside_bracket(token, position))
    -
    -
    -class FormatChecker(BaseTokenChecker):
    -    """checks for :
    -    * unauthorized constructions
    -    * strict indentation
    -    * line length
    -    """
    -
    -    __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker)
    -
    -    # configuration section name
    -    name = 'format'
    -    # messages
    -    msgs = MSGS
    -    # configuration options
    -    # for available dict keys/values see the optik parser 'add_option' method
    -    options = (('max-line-length',
    -                {'default' : 100, 'type' : "int", 'metavar' : '',
    -                 'help' : 'Maximum number of characters on a single line.'}),
    -               ('ignore-long-lines',
    -                {'type': 'regexp', 'metavar': '',
    -                 'default': r'^\s*(# )??$',
    -                 'help': ('Regexp for a line that is allowed to be longer than '
    -                          'the limit.')}),
    -               ('single-line-if-stmt',
    -                {'default': False, 'type' : 'yn', 'metavar' : '',
    -                 'help' : ('Allow the body of an if to be on the same '
    -                           'line as the test if there is no else.')}),
    -               ('no-space-check',
    -                {'default': ','.join(_NO_SPACE_CHECK_CHOICES),
    -                 'type': 'multiple_choice',
    -                 'choices': _NO_SPACE_CHECK_CHOICES,
    -                 'help': ('List of optional constructs for which whitespace '
    -                          'checking is disabled')}),
    -               ('max-module-lines',
    -                {'default' : 1000, 'type' : 'int', 'metavar' : '',
    -                 'help': 'Maximum number of lines in a module'}
    -               ),
    -               ('indent-string',
    -                {'default' : '    ', 'type' : "string", 'metavar' : '',
    -                 'help' : 'String used as indentation unit. This is usually '
    -                          '"    " (4 spaces) or "\\t" (1 tab).'}),
    -               ('indent-after-paren',
    -                {'type': 'int', 'metavar': '', 'default': 4,
    -                 'help': 'Number of spaces of indent required inside a hanging '
    -                         ' or continued line.'}),
    -               ('expected-line-ending-format',
    -                {'type': 'choice', 'metavar': '', 'default': '',
    -                 'choices': ['', 'LF', 'CRLF'],
    -                 'help': ('Expected format of line ending, '
    -                          'e.g. empty (any line ending), LF or CRLF.')}),
    -              )
    -
    -    def __init__(self, linter=None):
    -        BaseTokenChecker.__init__(self, linter)
    -        self._lines = None
    -        self._visited_lines = None
    -        self._bracket_stack = [None]
    -
    -    def _pop_token(self):
    -        self._bracket_stack.pop()
    -        self._current_line.pop_token()
    -
    -    def _push_token(self, token, idx):
    -        self._bracket_stack.append(token)
    -        self._current_line.push_token(token, idx)
    -
    -    def new_line(self, tokens, line_end, line_start):
    -        """a new line has been encountered, process it if necessary"""
    -        if _last_token_on_line_is(tokens, line_end, ';'):
    -            self.add_message('unnecessary-semicolon', line=tokens.start_line(line_end))
    -
    -        line_num = tokens.start_line(line_start)
    -        line = tokens.line(line_start)
    -        if tokens.type(line_start) not in _JUNK_TOKENS:
    -            self._lines[line_num] = line.split('\n')[0]
    -        self.check_lines(line, line_num)
    -
    -    def process_module(self, module):
    -        self._keywords_with_parens = set()
    -        if 'print_function' in module.future_imports:
    -            self._keywords_with_parens.add('print')
    -
    -    def _check_keyword_parentheses(self, tokens, start):
    -        """Check that there are not unnecessary parens after a keyword.
    -
    -        Parens are unnecessary if there is exactly one balanced outer pair on a
    -        line, and it is followed by a colon, and contains no commas (i.e. is not a
    -        tuple).
    -
    -        Args:
    -        tokens: list of Tokens; the entire list of Tokens.
    -        start: int; the position of the keyword in the token list.
    -        """
    -        # If the next token is not a paren, we're fine.
    -        if self._inside_brackets(':') and tokens[start][1] == 'for':
    -            self._pop_token()
    -        if tokens[start+1][1] != '(':
    -            return
    -
    -        found_and_or = False
    -        depth = 0
    -        keyword_token = tokens[start][1]
    -        line_num = tokens[start][2][0]
    -
    -        for i in range(start, len(tokens) - 1):
    -            token = tokens[i]
    -
    -            # If we hit a newline, then assume any parens were for continuation.
    -            if token[0] == tokenize.NL:
    -                return
    -
    -            if token[1] == '(':
    -                depth += 1
    -            elif token[1] == ')':
    -                depth -= 1
    -                if not depth:
    -                    # ')' can't happen after if (foo), since it would be a syntax error.
    -                    if (tokens[i+1][1] in (':', ')', ']', '}', 'in') or
    -                            tokens[i+1][0] in (tokenize.NEWLINE,
    -                                               tokenize.ENDMARKER,
    -                                               tokenize.COMMENT)):
    -                        # The empty tuple () is always accepted.
    -                        if i == start + 2:
    -                            return
    -                        if keyword_token == 'not':
    -                            if not found_and_or:
    -                                self.add_message('superfluous-parens', line=line_num,
    -                                                 args=keyword_token)
    -                        elif keyword_token in ('return', 'yield'):
    -                            self.add_message('superfluous-parens', line=line_num,
    -                                             args=keyword_token)
    -                        elif keyword_token not in self._keywords_with_parens:
    -                            if not (tokens[i+1][1] == 'in' and found_and_or):
    -                                self.add_message('superfluous-parens', line=line_num,
    -                                                 args=keyword_token)
    -                    return
    -            elif depth == 1:
    -                # This is a tuple, which is always acceptable.
    -                if token[1] == ',':
    -                    return
    -                # 'and' and 'or' are the only boolean operators with lower precedence
    -                # than 'not', so parens are only required when they are found.
    -                elif token[1] in ('and', 'or'):
    -                    found_and_or = True
    -                # A yield inside an expression must always be in parentheses,
    -                # quit early without error.
    -                elif token[1] == 'yield':
    -                    return
    -                # A generator expression always has a 'for' token in it, and
    -                # the 'for' token is only legal inside parens when it is in a
    -                # generator expression.  The parens are necessary here, so bail
    -                # without an error.
    -                elif token[1] == 'for':
    -                    return
    -
    -    def _opening_bracket(self, tokens, i):
    -        self._push_token(tokens[i][1], i)
    -        # Special case: ignore slices
    -        if tokens[i][1] == '[' and tokens[i+1][1] == ':':
    -            return
    -
    -        if (i > 0 and (tokens[i-1][0] == tokenize.NAME and
    -                       not (keyword.iskeyword(tokens[i-1][1]))
    -                       or tokens[i-1][1] in _CLOSING_BRACKETS)):
    -            self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
    -        else:
    -            self._check_space(tokens, i, (_IGNORE, _MUST_NOT))
    -
    -    def _closing_bracket(self, tokens, i):
    -        if self._inside_brackets(':'):
    -            self._pop_token()
    -        self._pop_token()
    -        # Special case: ignore slices
    -        if tokens[i-1][1] == ':' and tokens[i][1] == ']':
    -            return
    -        policy_before = _MUST_NOT
    -        if tokens[i][1] in _CLOSING_BRACKETS and tokens[i-1][1] == ',':
    -            if _TRAILING_COMMA in self.config.no_space_check:
    -                policy_before = _IGNORE
    -
    -        self._check_space(tokens, i, (policy_before, _IGNORE))
    -
    -    def _check_equals_spacing(self, tokens, i):
    -        """Check the spacing of a single equals sign."""
    -        if self._inside_brackets('(') or self._inside_brackets('lambda'):
    -            self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
    -        else:
    -            self._check_space(tokens, i, (_MUST, _MUST))
    -
    -    def _open_lambda(self, tokens, i): # pylint:disable=unused-argument
    -        self._push_token('lambda', i)
    -
    -    def _handle_colon(self, tokens, i):
    -        # Special case: ignore slices
    -        if self._inside_brackets('['):
    -            return
    -        if (self._inside_brackets('{') and
    -                _DICT_SEPARATOR in self.config.no_space_check):
    -            policy = (_IGNORE, _IGNORE)
    -        else:
    -            policy = (_MUST_NOT, _MUST)
    -        self._check_space(tokens, i, policy)
    -
    -        if self._inside_brackets('lambda'):
    -            self._pop_token()
    -        elif self._inside_brackets('{'):
    -            self._push_token(':', i)
    -
    -    def _handle_comma(self, tokens, i):
    -        # Only require a following whitespace if this is
    -        # not a hanging comma before a closing bracket.
    -        if tokens[i+1][1] in _CLOSING_BRACKETS:
    -            self._check_space(tokens, i, (_MUST_NOT, _IGNORE))
    -        else:
    -            self._check_space(tokens, i, (_MUST_NOT, _MUST))
    -        if self._inside_brackets(':'):
    -            self._pop_token()
    -
    -    def _check_surrounded_by_space(self, tokens, i):
    -        """Check that a binary operator is surrounded by exactly one space."""
    -        self._check_space(tokens, i, (_MUST, _MUST))
    -
    -    def _check_space(self, tokens, i, policies):
    -        def _policy_string(policy):
    -            if policy == _MUST:
    -                return 'Exactly one', 'required'
    -            else:
    -                return 'No', 'allowed'
    -
    -        def _name_construct(token):
    -            if token[1] == ',':
    -                return 'comma'
    -            elif token[1] == ':':
    -                return ':'
    -            elif token[1] in '()[]{}':
    -                return 'bracket'
    -            elif token[1] in ('<', '>', '<=', '>=', '!=', '=='):
    -                return 'comparison'
    -            else:
    -                if self._inside_brackets('('):
    -                    return 'keyword argument assignment'
    -                else:
    -                    return 'assignment'
    -
    -        good_space = [True, True]
    -        token = tokens[i]
    -        pairs = [(tokens[i-1], token), (token, tokens[i+1])]
    -
    -        for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)):
    -            if token_pair[other_idx][0] in _EOL or policy == _IGNORE:
    -                continue
    -
    -            distance = _column_distance(*token_pair)
    -            if distance is None:
    -                continue
    -            good_space[other_idx] = (
    -                (policy == _MUST and distance == 1) or
    -                (policy == _MUST_NOT and distance == 0))
    -
    -        warnings = []
    -        if not any(good_space) and policies[0] == policies[1]:
    -            warnings.append((policies[0], 'around'))
    -        else:
    -            for ok, policy, position in zip(good_space, policies, ('before', 'after')):
    -                if not ok:
    -                    warnings.append((policy, position))
    -        for policy, position in warnings:
    -            construct = _name_construct(token)
    -            count, state = _policy_string(policy)
    -            self.add_message('bad-whitespace', line=token[2][0],
    -                             args=(count, state, position, construct,
    -                                   _underline_token(token)))
    -
    -    def _inside_brackets(self, left):
    -        return self._bracket_stack[-1] == left
    -
    -    def _prepare_token_dispatcher(self):
    -        raw = [
    -            (_KEYWORD_TOKENS,
    -             self._check_keyword_parentheses),
    -
    -            (_OPENING_BRACKETS, self._opening_bracket),
    -
    -            (_CLOSING_BRACKETS, self._closing_bracket),
    -
    -            (['='], self._check_equals_spacing),
    -
    -            (_SPACED_OPERATORS, self._check_surrounded_by_space),
    -
    -            ([','], self._handle_comma),
    -
    -            ([':'], self._handle_colon),
    -
    -            (['lambda'], self._open_lambda),
    -
    -            ]
    -
    -        dispatch = {}
    -        for tokens, handler in raw:
    -            for token in tokens:
    -                dispatch[token] = handler
    -        return dispatch
    -
    -    def process_tokens(self, tokens):
    -        """process tokens and search for :
    -
    -         _ non strict indentation (i.e. not always using the  parameter as
    -           indent unit)
    -         _ too long lines (i.e. longer than )
    -         _ optionally bad construct (if given, bad_construct must be a compiled
    -           regular expression).
    -        """
    -        self._bracket_stack = [None]
    -        indents = [0]
    -        check_equal = False
    -        line_num = 0
    -        self._lines = {}
    -        self._visited_lines = {}
    -        token_handlers = self._prepare_token_dispatcher()
    -        self._last_line_ending = None
    -
    -        self._current_line = ContinuedLineState(tokens, self.config)
    -        for idx, (tok_type, token, start, _, line) in enumerate(tokens):
    -            if start[0] != line_num:
    -                line_num = start[0]
    -                # A tokenizer oddity: if an indented line contains a multi-line
    -                # docstring, the line member of the INDENT token does not contain
    -                # the full line; therefore we check the next token on the line.
    -                if tok_type == tokenize.INDENT:
    -                    self.new_line(TokenWrapper(tokens), idx-1, idx+1)
    -                else:
    -                    self.new_line(TokenWrapper(tokens), idx-1, idx)
    -
    -            if tok_type == tokenize.NEWLINE:
    -                # a program statement, or ENDMARKER, will eventually follow,
    -                # after some (possibly empty) run of tokens of the form
    -                #     (NL | COMMENT)* (INDENT | DEDENT+)?
    -                # If an INDENT appears, setting check_equal is wrong, and will
    -                # be undone when we see the INDENT.
    -                check_equal = True
    -                self._process_retained_warnings(TokenWrapper(tokens), idx)
    -                self._current_line.next_logical_line()
    -                self._check_line_ending(token, line_num)
    -            elif tok_type == tokenize.INDENT:
    -                check_equal = False
    -                self.check_indent_level(token, indents[-1]+1, line_num)
    -                indents.append(indents[-1]+1)
    -            elif tok_type == tokenize.DEDENT:
    -                # there's nothing we need to check here!  what's important is
    -                # that when the run of DEDENTs ends, the indentation of the
    -                # program statement (or ENDMARKER) that triggered the run is
    -                # equal to what's left at the top of the indents stack
    -                check_equal = True
    -                if len(indents) > 1:
    -                    del indents[-1]
    -            elif tok_type == tokenize.NL:
    -                self._check_continued_indentation(TokenWrapper(tokens), idx+1)
    -                self._current_line.next_physical_line()
    -            elif tok_type != tokenize.COMMENT:
    -                self._current_line.handle_line_start(idx)
    -                # This is the first concrete token following a NEWLINE, so it
    -                # must be the first token of the next program statement, or an
    -                # ENDMARKER; the "line" argument exposes the leading whitespace
    -                # for this statement; in the case of ENDMARKER, line is an empty
    -                # string, so will properly match the empty string with which the
    -                # "indents" stack was seeded
    -                if check_equal:
    -                    check_equal = False
    -                    self.check_indent_level(line, indents[-1], line_num)
    -
    -            if tok_type == tokenize.NUMBER and token.endswith('l'):
    -                self.add_message('lowercase-l-suffix', line=line_num)
    -
    -            try:
    -                handler = token_handlers[token]
    -            except KeyError:
    -                pass
    -            else:
    -                handler(tokens, idx)
    -
    -        line_num -= 1 # to be ok with "wc -l"
    -        if line_num > self.config.max_module_lines:
    -            # Get the line where the too-many-lines (or its message id)
    -            # was disabled or default to 1.
    -            symbol = self.linter.msgs_store.check_message_id('too-many-lines')
    -            names = (symbol.msgid, 'too-many-lines')
    -            line = next(filter(None,
    -                               map(self.linter._pragma_lineno.get, names)), 1)
    -            self.add_message('too-many-lines',
    -                             args=(line_num, self.config.max_module_lines),
    -                             line=line)
    -
    -    def _check_line_ending(self, line_ending, line_num):
    -        # check if line endings are mixed
    -        if self._last_line_ending is not None:
    -            if line_ending != self._last_line_ending:
    -                self.add_message('mixed-line-endings', line=line_num)
    -
    -        self._last_line_ending = line_ending
    -
    -        # check if line ending is as expected
    -        expected = self.config.expected_line_ending_format
    -        if expected:
    -            # reduce multiple \n\n\n\n to one \n
    -            line_ending = reduce(lambda x, y: x + y if x != y else x, line_ending, "")
    -            line_ending = 'LF' if line_ending == '\n' else 'CRLF'
    -            if line_ending != expected:
    -                self.add_message('unexpected-line-ending-format', args=(line_ending, expected),
    -                                 line=line_num)
    -
    -
    -    def _process_retained_warnings(self, tokens, current_pos):
    -        single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos, ':')
    -
    -        for indent_pos, state, offsets in self._current_line.retained_warnings:
    -            block_type = offsets[tokens.start_col(indent_pos)]
    -            hints = dict((k, v) for k, v in six.iteritems(offsets)
    -                         if v != block_type)
    -            if single_line_block_stmt and block_type == WITH_BODY:
    -                self._add_continuation_message(state, hints, tokens, indent_pos)
    -            elif not single_line_block_stmt and block_type == SINGLE_LINE:
    -                self._add_continuation_message(state, hints, tokens, indent_pos)
    -
    -    def _check_continued_indentation(self, tokens, next_idx):
    -        def same_token_around_nl(token_type):
    -            return (tokens.type(next_idx) == token_type and
    -                    tokens.type(next_idx-2) == token_type)
    -
    -        # Do not issue any warnings if the next line is empty.
    -        if not self._current_line.has_content or tokens.type(next_idx) == tokenize.NL:
    -            return
    -
    -        state, valid_offsets = self._current_line.get_valid_offsets(next_idx)
    -        # Special handling for hanging comments and strings. If the last line ended
    -        # with a comment (string) and the new line contains only a comment, the line
    -        # may also be indented to the start of the previous token.
    -        if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl(tokenize.STRING):
    -            valid_offsets[tokens.start_col(next_idx-2)] = True
    -
    -        # We can only decide if the indentation of a continued line before opening
    -        # a new block is valid once we know of the body of the block is on the
    -        # same line as the block opener. Since the token processing is single-pass,
    -        # emitting those warnings is delayed until the block opener is processed.
    -        if (state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK)
    -                and tokens.start_col(next_idx) in valid_offsets):
    -            self._current_line.add_block_warning(next_idx, state, valid_offsets)
    -        elif tokens.start_col(next_idx) not in valid_offsets:
    -            self._add_continuation_message(state, valid_offsets, tokens, next_idx)
    -
    -    def _add_continuation_message(self, state, offsets, tokens, position):
    -        readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context_type]
    -        hint_line = _get_indent_hint_line(offsets, tokens.start_col(position))
    -        self.add_message(
    -            'bad-continuation',
    -            line=tokens.start_line(position),
    -            args=(readable_type, readable_position, tokens.line(position), hint_line))
    -
    -    @check_messages('multiple-statements')
    -    def visit_default(self, node):
    -        """check the node line number and check it if not yet done"""
    -        if not node.is_statement:
    -            return
    -        if not node.root().pure_python:
    -            return # XXX block visit of child nodes
    -        prev_sibl = node.previous_sibling()
    -        if prev_sibl is not None:
    -            prev_line = prev_sibl.fromlineno
    -        else:
    -            # The line on which a finally: occurs in a try/finally
    -            # is not directly represented in the AST. We infer it
    -            # by taking the last line of the body and adding 1, which
    -            # should be the line of finally:
    -            if (isinstance(node.parent, nodes.TryFinally)
    -                    and node in node.parent.finalbody):
    -                prev_line = node.parent.body[0].tolineno + 1
    -            else:
    -                prev_line = node.parent.statement().fromlineno
    -        line = node.fromlineno
    -        assert line, node
    -        if prev_line == line and self._visited_lines.get(line) != 2:
    -            self._check_multi_statement_line(node, line)
    -            return
    -        if line in self._visited_lines:
    -            return
    -        try:
    -            tolineno = node.blockstart_tolineno
    -        except AttributeError:
    -            tolineno = node.tolineno
    -        assert tolineno, node
    -        lines = []
    -        for line in range(line, tolineno + 1):
    -            self._visited_lines[line] = 1
    -            try:
    -                lines.append(self._lines[line].rstrip())
    -            except KeyError:
    -                lines.append('')
    -
    -    def _check_multi_statement_line(self, node, line):
    -        """Check for lines containing multiple statements."""
    -        # Do not warn about multiple nested context managers
    -        # in with statements.
    -        if isinstance(node, nodes.With):
    -            return
    -        # For try... except... finally..., the two nodes
    -        # appear to be on the same line due to how the AST is built.
    -        if (isinstance(node, nodes.TryExcept) and
    -                isinstance(node.parent, nodes.TryFinally)):
    -            return
    -        if (isinstance(node.parent, nodes.If) and not node.parent.orelse
    -                and self.config.single_line_if_stmt):
    -            return
    -        self.add_message('multiple-statements', node=node)
    -        self._visited_lines[line] = 2
    -
    -    def check_lines(self, lines, i):
    -        """check lines have less than a maximum number of characters
    -        """
    -        max_chars = self.config.max_line_length
    -        ignore_long_line = self.config.ignore_long_lines
    -
    -        for line in lines.splitlines(True):
    -            if not line.endswith('\n'):
    -                self.add_message('missing-final-newline', line=i)
    -            else:
    -                stripped_line = line.rstrip()
    -                if line[len(stripped_line):] not in ('\n', '\r\n'):
    -                    self.add_message('trailing-whitespace', line=i)
    -                # Don't count excess whitespace in the line length.
    -                line = stripped_line
    -            mobj = OPTION_RGX.search(line)
    -            if mobj and mobj.group(1).split('=', 1)[0].strip() == 'disable':
    -                line = line.split('#')[0].rstrip()
    -
    -            if len(line) > max_chars and not ignore_long_line.search(line):
    -                self.add_message('line-too-long', line=i, args=(len(line), max_chars))
    -            i += 1
    -
    -    def check_indent_level(self, string, expected, line_num):
    -        """return the indent level of the string
    -        """
    -        indent = self.config.indent_string
    -        if indent == '\\t': # \t is not interpreted in the configuration file
    -            indent = '\t'
    -        level = 0
    -        unit_size = len(indent)
    -        while string[:unit_size] == indent:
    -            string = string[unit_size:]
    -            level += 1
    -        suppl = ''
    -        while string and string[0] in ' \t':
    -            if string[0] != indent[0]:
    -                if string[0] == '\t':
    -                    args = ('tab', 'space')
    -                else:
    -                    args = ('space', 'tab')
    -                self.add_message('mixed-indentation', args=args, line=line_num)
    -                return level
    -            suppl += string[0]
    -            string = string[1:]
    -        if level != expected or suppl:
    -            i_type = 'spaces'
    -            if indent[0] == '\t':
    -                i_type = 'tabs'
    -            self.add_message('bad-indentation', line=line_num,
    -                             args=(level * unit_size + len(suppl), i_type,
    -                                   expected * unit_size))
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(FormatChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/imports.py b/pymode/libs/pylint/checkers/imports.py
    deleted file mode 100644
    index 1969eeb1..00000000
    --- a/pymode/libs/pylint/checkers/imports.py
    +++ /dev/null
    @@ -1,413 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""imports checkers for Python code"""
    -
    -import sys
    -from collections import defaultdict
    -
    -import six
    -from six.moves import map # pylint: disable=redefined-builtin
    -
    -from logilab.common.graph import get_cycles, DotBackend
    -from logilab.common.ureports import VerbatimText, Paragraph
    -
    -import astroid
    -from astroid import are_exclusive
    -from astroid.modutils import get_module_part, is_standard_module
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.utils import EmptyReport
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import check_messages, is_import_error
    -
    -def _except_import_error(node):
    -    """
    -    Check if the try-except node has an ImportError handler.
    -    Return True if an ImportError handler was infered, False otherwise.
    -    """
    -    if not isinstance(node, astroid.TryExcept):
    -        return
    -    return any(map(is_import_error, node.handlers))
    -
    -def get_first_import(node, context, name, base, level):
    -    """return the node where [base.] is imported or None if not found
    -    """
    -    fullname = '%s.%s' % (base, name) if base else name
    -
    -    first = None
    -    found = False
    -    for first in context.body:
    -        if first is node:
    -            continue
    -        if first.scope() is node.scope() and first.fromlineno > node.fromlineno:
    -            continue
    -        if isinstance(first, astroid.Import):
    -            if any(fullname == iname[0] for iname in first.names):
    -                found = True
    -                break
    -        elif isinstance(first, astroid.From):
    -            if level == first.level and any(
    -                    fullname == '%s.%s' % (first.modname, iname[0])
    -                    for iname in first.names):
    -                found = True
    -                break
    -    if found and not are_exclusive(first, node):
    -        return first
    -
    -# utilities to represents import dependencies as tree and dot graph ###########
    -
    -def make_tree_defs(mod_files_list):
    -    """get a list of 2-uple (module, list_of_files_which_import_this_module),
    -    it will return a dictionary to represent this as a tree
    -    """
    -    tree_defs = {}
    -    for mod, files in mod_files_list:
    -        node = (tree_defs, ())
    -        for prefix in mod.split('.'):
    -            node = node[0].setdefault(prefix, [{}, []])
    -        node[1] += files
    -    return tree_defs
    -
    -def repr_tree_defs(data, indent_str=None):
    -    """return a string which represents imports as a tree"""
    -    lines = []
    -    nodes = data.items()
    -    for i, (mod, (sub, files)) in enumerate(sorted(nodes, key=lambda x: x[0])):
    -        if not files:
    -            files = ''
    -        else:
    -            files = '(%s)' % ','.join(files)
    -        if indent_str is None:
    -            lines.append('%s %s' % (mod, files))
    -            sub_indent_str = '  '
    -        else:
    -            lines.append(r'%s\-%s %s' % (indent_str, mod, files))
    -            if i == len(nodes)-1:
    -                sub_indent_str = '%s  ' % indent_str
    -            else:
    -                sub_indent_str = '%s| ' % indent_str
    -        if sub:
    -            lines.append(repr_tree_defs(sub, sub_indent_str))
    -    return '\n'.join(lines)
    -
    -
    -def dependencies_graph(filename, dep_info):
    -    """write dependencies as a dot (graphviz) file
    -    """
    -    done = {}
    -    printer = DotBackend(filename[:-4], rankdir='LR')
    -    printer.emit('URL="." node[shape="box"]')
    -    for modname, dependencies in sorted(six.iteritems(dep_info)):
    -        done[modname] = 1
    -        printer.emit_node(modname)
    -        for modname in dependencies:
    -            if modname not in done:
    -                done[modname] = 1
    -                printer.emit_node(modname)
    -    for depmodname, dependencies in sorted(six.iteritems(dep_info)):
    -        for modname in dependencies:
    -            printer.emit_edge(modname, depmodname)
    -    printer.generate(filename)
    -
    -
    -def make_graph(filename, dep_info, sect, gtype):
    -    """generate a dependencies graph and add some information about it in the
    -    report's section
    -    """
    -    dependencies_graph(filename, dep_info)
    -    sect.append(Paragraph('%simports graph has been written to %s'
    -                          % (gtype, filename)))
    -
    -
    -# the import checker itself ###################################################
    -
    -MSGS = {
    -    'F0401': ('Unable to import %s',
    -              'import-error',
    -              'Used when pylint has been unable to import a module.'),
    -    'R0401': ('Cyclic import (%s)',
    -              'cyclic-import',
    -              'Used when a cyclic import between two or more modules is \
    -              detected.'),
    -
    -    'W0401': ('Wildcard import %s',
    -              'wildcard-import',
    -              'Used when `from module import *` is detected.'),
    -    'W0402': ('Uses of a deprecated module %r',
    -              'deprecated-module',
    -              'Used a module marked as deprecated is imported.'),
    -    'W0403': ('Relative import %r, should be %r',
    -              'relative-import',
    -              'Used when an import relative to the package directory is '
    -              'detected.',
    -              {'maxversion': (3, 0)}),
    -    'W0404': ('Reimport %r (imported line %s)',
    -              'reimported',
    -              'Used when a module is reimported multiple times.'),
    -    'W0406': ('Module import itself',
    -              'import-self',
    -              'Used when a module is importing itself.'),
    -
    -    'W0410': ('__future__ import is not the first non docstring statement',
    -              'misplaced-future',
    -              'Python 2.5 and greater require __future__ import to be the \
    -              first non docstring statement in the module.',
    -              {'maxversion': (3, 0)}),
    -    }
    -
    -class ImportsChecker(BaseChecker):
    -    """checks for
    -    * external modules dependencies
    -    * relative / wildcard imports
    -    * cyclic imports
    -    * uses of deprecated modules
    -    """
    -
    -    __implements__ = IAstroidChecker
    -
    -    name = 'imports'
    -    msgs = MSGS
    -    priority = -2
    -
    -    if sys.version_info < (3,):
    -        deprecated_modules = ('regsub', 'TERMIOS', 'Bastion', 'rexec')
    -    else:
    -        deprecated_modules = ('stringprep', 'optparse')
    -    options = (('deprecated-modules',
    -                {'default' : deprecated_modules,
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'Deprecated modules which should not be used, \
    -separated by a comma'}
    -               ),
    -               ('import-graph',
    -                {'default' : '',
    -                 'type' : 'string',
    -                 'metavar' : '',
    -                 'help' : 'Create a graph of every (i.e. internal and \
    -external) dependencies in the given file (report RP0402 must not be disabled)'}
    -               ),
    -               ('ext-import-graph',
    -                {'default' : '',
    -                 'type' : 'string',
    -                 'metavar' : '',
    -                 'help' : 'Create a graph of external dependencies in the \
    -given file (report RP0402 must not be disabled)'}
    -               ),
    -               ('int-import-graph',
    -                {'default' : '',
    -                 'type' : 'string',
    -                 'metavar' : '',
    -                 'help' : 'Create a graph of internal dependencies in the \
    -given file (report RP0402 must not be disabled)'}
    -               ),
    -              )
    -
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        self.stats = None
    -        self.import_graph = None
    -        self.__int_dep_info = self.__ext_dep_info = None
    -        self.reports = (('RP0401', 'External dependencies',
    -                         self.report_external_dependencies),
    -                        ('RP0402', 'Modules dependencies graph',
    -                         self.report_dependencies_graph),
    -                       )
    -
    -    def open(self):
    -        """called before visiting project (i.e set of modules)"""
    -        self.linter.add_stats(dependencies={})
    -        self.linter.add_stats(cycles=[])
    -        self.stats = self.linter.stats
    -        self.import_graph = defaultdict(set)
    -
    -    def close(self):
    -        """called before visiting project (i.e set of modules)"""
    -        # don't try to compute cycles if the associated message is disabled
    -        if self.linter.is_message_enabled('cyclic-import'):
    -            vertices = list(self.import_graph)
    -            for cycle in get_cycles(self.import_graph, vertices=vertices):
    -                self.add_message('cyclic-import', args=' -> '.join(cycle))
    -
    -    def visit_import(self, node):
    -        """triggered when an import statement is seen"""
    -        modnode = node.root()
    -        for name, _ in node.names:
    -            importedmodnode = self.get_imported_module(node, name)
    -            if importedmodnode is None:
    -                continue
    -            self._check_relative_import(modnode, node, importedmodnode, name)
    -            self._add_imported_module(node, importedmodnode.name)
    -            self._check_deprecated_module(node, name)
    -            self._check_reimport(node, name)
    -
    -    # TODO This appears to be the list of all messages of the checker...
    -    # @check_messages('W0410', 'W0401', 'W0403', 'W0402', 'W0404', 'W0406', 'F0401')
    -    @check_messages(*(MSGS.keys()))
    -    def visit_from(self, node):
    -        """triggered when a from statement is seen"""
    -        basename = node.modname
    -        if basename == '__future__':
    -            # check if this is the first non-docstring statement in the module
    -            prev = node.previous_sibling()
    -            if prev:
    -                # consecutive future statements are possible
    -                if not (isinstance(prev, astroid.From)
    -                        and prev.modname == '__future__'):
    -                    self.add_message('misplaced-future', node=node)
    -            return
    -        for name, _ in node.names:
    -            if name == '*':
    -                self.add_message('wildcard-import', args=basename, node=node)
    -        modnode = node.root()
    -        importedmodnode = self.get_imported_module(node, basename)
    -        if importedmodnode is None:
    -            return
    -        self._check_relative_import(modnode, node, importedmodnode, basename)
    -        self._check_deprecated_module(node, basename)
    -        for name, _ in node.names:
    -            if name != '*':
    -                self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name))
    -                self._check_reimport(node, name, basename, node.level)
    -
    -    def get_imported_module(self, importnode, modname):
    -        try:
    -            return importnode.do_import_module(modname)
    -        except astroid.InferenceError as ex:
    -            if str(ex) != modname:
    -                args = '%r (%s)' % (modname, ex)
    -            else:
    -                args = repr(modname)
    -            if not _except_import_error(importnode.parent):
    -                self.add_message("import-error", args=args, node=importnode)
    -
    -    def _check_relative_import(self, modnode, importnode, importedmodnode,
    -                               importedasname):
    -        """check relative import. node is either an Import or From node, modname
    -        the imported module name.
    -        """
    -        if not self.linter.is_message_enabled('relative-import'):
    -            return
    -        if importedmodnode.file is None:
    -            return False # built-in module
    -        if modnode is importedmodnode:
    -            return False # module importing itself
    -        if modnode.absolute_import_activated() or getattr(importnode, 'level', None):
    -            return False
    -        if importedmodnode.name != importedasname:
    -            # this must be a relative import...
    -            self.add_message('relative-import',
    -                             args=(importedasname, importedmodnode.name),
    -                             node=importnode)
    -
    -    def _add_imported_module(self, node, importedmodname):
    -        """notify an imported module, used to analyze dependencies"""
    -        try:
    -            importedmodname = get_module_part(importedmodname)
    -        except ImportError:
    -            pass
    -        context_name = node.root().name
    -        if context_name == importedmodname:
    -            # module importing itself !
    -            self.add_message('import-self', node=node)
    -        elif not is_standard_module(importedmodname):
    -            # handle dependencies
    -            importedmodnames = self.stats['dependencies'].setdefault(
    -                importedmodname, set())
    -            if not context_name in importedmodnames:
    -                importedmodnames.add(context_name)
    -            # update import graph
    -            mgraph = self.import_graph[context_name]
    -            if importedmodname not in mgraph:
    -                mgraph.add(importedmodname)
    -
    -    def _check_deprecated_module(self, node, mod_path):
    -        """check if the module is deprecated"""
    -        for mod_name in self.config.deprecated_modules:
    -            if mod_path == mod_name or mod_path.startswith(mod_name + '.'):
    -                self.add_message('deprecated-module', node=node, args=mod_path)
    -
    -    def _check_reimport(self, node, name, basename=None, level=None):
    -        """check if the import is necessary (i.e. not already done)"""
    -        if not self.linter.is_message_enabled('reimported'):
    -            return
    -        frame = node.frame()
    -        root = node.root()
    -        contexts = [(frame, level)]
    -        if root is not frame:
    -            contexts.append((root, None))
    -        for context, level in contexts:
    -            first = get_first_import(node, context, name, basename, level)
    -            if first is not None:
    -                self.add_message('reimported', node=node,
    -                                 args=(name, first.fromlineno))
    -
    -
    -    def report_external_dependencies(self, sect, _, dummy):
    -        """return a verbatim layout for displaying dependencies"""
    -        dep_info = make_tree_defs(six.iteritems(self._external_dependencies_info()))
    -        if not dep_info:
    -            raise EmptyReport()
    -        tree_str = repr_tree_defs(dep_info)
    -        sect.append(VerbatimText(tree_str))
    -
    -    def report_dependencies_graph(self, sect, _, dummy):
    -        """write dependencies as a dot (graphviz) file"""
    -        dep_info = self.stats['dependencies']
    -        if not dep_info or not (self.config.import_graph
    -                                or self.config.ext_import_graph
    -                                or self.config.int_import_graph):
    -            raise EmptyReport()
    -        filename = self.config.import_graph
    -        if filename:
    -            make_graph(filename, dep_info, sect, '')
    -        filename = self.config.ext_import_graph
    -        if filename:
    -            make_graph(filename, self._external_dependencies_info(),
    -                       sect, 'external ')
    -        filename = self.config.int_import_graph
    -        if filename:
    -            make_graph(filename, self._internal_dependencies_info(),
    -                       sect, 'internal ')
    -
    -    def _external_dependencies_info(self):
    -        """return cached external dependencies information or build and
    -        cache them
    -        """
    -        if self.__ext_dep_info is None:
    -            package = self.linter.current_name
    -            self.__ext_dep_info = result = {}
    -            for importee, importers in six.iteritems(self.stats['dependencies']):
    -                if not importee.startswith(package):
    -                    result[importee] = importers
    -        return self.__ext_dep_info
    -
    -    def _internal_dependencies_info(self):
    -        """return cached internal dependencies information or build and
    -        cache them
    -        """
    -        if self.__int_dep_info is None:
    -            package = self.linter.current_name
    -            self.__int_dep_info = result = {}
    -            for importee, importers in six.iteritems(self.stats['dependencies']):
    -                if importee.startswith(package):
    -                    result[importee] = importers
    -        return self.__int_dep_info
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(ImportsChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/logging.py b/pymode/libs/pylint/checkers/logging.py
    deleted file mode 100644
    index 897c1c7f..00000000
    --- a/pymode/libs/pylint/checkers/logging.py
    +++ /dev/null
    @@ -1,256 +0,0 @@
    -# Copyright (c) 2009-2010 Google, Inc.
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""checker for use of Python logging
    -"""
    -
    -import astroid
    -from pylint import checkers
    -from pylint import interfaces
    -from pylint.checkers import utils
    -from pylint.checkers.utils import check_messages
    -
    -import six
    -
    -
    -MSGS = {
    -    'W1201': ('Specify string format arguments as logging function parameters',
    -              'logging-not-lazy',
    -              'Used when a logging statement has a call form of '
    -              '"logging.(format_string % (format_args...))". '
    -              'Such calls should leave string interpolation to the logging '
    -              'method itself and be written '
    -              '"logging.(format_string, format_args...)" '
    -              'so that the program may avoid incurring the cost of the '
    -              'interpolation in those cases in which no message will be '
    -              'logged. For more, see '
    -              'http://www.python.org/dev/peps/pep-0282/.'),
    -    'W1202': ('Use % formatting in logging functions but pass the % '
    -              'parameters as arguments',
    -              'logging-format-interpolation',
    -              'Used when a logging statement has a call form of '
    -              '"logging.(format_string.format(format_args...))"'
    -              '. Such calls should use % formatting instead, but leave '
    -              'interpolation to the logging function by passing the parameters '
    -              'as arguments.'),
    -    'E1200': ('Unsupported logging format character %r (%#02x) at index %d',
    -              'logging-unsupported-format',
    -              'Used when an unsupported format character is used in a logging\
    -              statement format string.'),
    -    'E1201': ('Logging format string ends in middle of conversion specifier',
    -              'logging-format-truncated',
    -              'Used when a logging statement format string terminates before\
    -              the end of a conversion specifier.'),
    -    'E1205': ('Too many arguments for logging format string',
    -              'logging-too-many-args',
    -              'Used when a logging format string is given too few arguments.'),
    -    'E1206': ('Not enough arguments for logging format string',
    -              'logging-too-few-args',
    -              'Used when a logging format string is given too many arguments'),
    -    }
    -
    -
    -CHECKED_CONVENIENCE_FUNCTIONS = set([
    -    'critical', 'debug', 'error', 'exception', 'fatal', 'info', 'warn',
    -    'warning'])
    -
    -def is_method_call(callfunc_node, types=(), methods=()):
    -    """Determines if a CallFunc node represents a method call.
    -
    -    Args:
    -      callfunc_node: The CallFunc AST node to check.
    -      types: Optional sequence of caller type names to restrict check.
    -      methods: Optional sequence of method names to restrict check.
    -
    -    Returns:
    -      True, if the node represents a method call for the given type and
    -      method names, False otherwise.
    -    """
    -    if not isinstance(callfunc_node, astroid.CallFunc):
    -        return False
    -    func = utils.safe_infer(callfunc_node.func)
    -    return (isinstance(func, astroid.BoundMethod)
    -            and isinstance(func.bound, astroid.Instance)
    -            and (func.bound.name in types if types else True)
    -            and (func.name in methods if methods else True))
    -
    -
    -
    -class LoggingChecker(checkers.BaseChecker):
    -    """Checks use of the logging module."""
    -
    -    __implements__ = interfaces.IAstroidChecker
    -    name = 'logging'
    -    msgs = MSGS
    -
    -    options = (('logging-modules',
    -                {'default': ('logging',),
    -                 'type': 'csv',
    -                 'metavar': '',
    -                 'help': 'Logging modules to check that the string format '
    -                         'arguments are in logging function parameter format'}
    -               ),
    -              )
    -
    -    def visit_module(self, node): # pylint: disable=unused-argument
    -        """Clears any state left in this checker from last module checked."""
    -        # The code being checked can just as easily "import logging as foo",
    -        # so it is necessary to process the imports and store in this field
    -        # what name the logging module is actually given.
    -        self._logging_names = set()
    -        logging_mods = self.config.logging_modules
    -
    -        self._logging_modules = set(logging_mods)
    -        self._from_imports = {}
    -        for logging_mod in logging_mods:
    -            parts = logging_mod.rsplit('.', 1)
    -            if len(parts) > 1:
    -                self._from_imports[parts[0]] = parts[1]
    -
    -    def visit_from(self, node):
    -        """Checks to see if a module uses a non-Python logging module."""
    -        try:
    -            logging_name = self._from_imports[node.modname]
    -            for module, as_name in node.names:
    -                if module == logging_name:
    -                    self._logging_names.add(as_name or module)
    -        except KeyError:
    -            pass
    -
    -    def visit_import(self, node):
    -        """Checks to see if this module uses Python's built-in logging."""
    -        for module, as_name in node.names:
    -            if module in self._logging_modules:
    -                self._logging_names.add(as_name or module)
    -
    -    @check_messages(*(MSGS.keys()))
    -    def visit_callfunc(self, node):
    -        """Checks calls to logging methods."""
    -        def is_logging_name():
    -            return (isinstance(node.func, astroid.Getattr) and
    -                    isinstance(node.func.expr, astroid.Name) and
    -                    node.func.expr.name in self._logging_names)
    -
    -        def is_logger_class():
    -            try:
    -                for inferred in node.func.infer():
    -                    if isinstance(inferred, astroid.BoundMethod):
    -                        parent = inferred._proxied.parent
    -                        if (isinstance(parent, astroid.Class) and
    -                                (parent.qname() == 'logging.Logger' or
    -                                 any(ancestor.qname() == 'logging.Logger'
    -                                     for ancestor in parent.ancestors()))):
    -                            return True, inferred._proxied.name
    -            except astroid.exceptions.InferenceError:
    -                pass
    -            return False, None
    -
    -        if is_logging_name():
    -            name = node.func.attrname
    -        else:
    -            result, name = is_logger_class()
    -            if not result:
    -                return
    -        self._check_log_method(node, name)
    -
    -    def _check_log_method(self, node, name):
    -        """Checks calls to logging.log(level, format, *format_args)."""
    -        if name == 'log':
    -            if node.starargs or node.kwargs or len(node.args) < 2:
    -                # Either a malformed call, star args, or double-star args. Beyond
    -                # the scope of this checker.
    -                return
    -            format_pos = 1
    -        elif name in CHECKED_CONVENIENCE_FUNCTIONS:
    -            if node.starargs or node.kwargs or not node.args:
    -                # Either no args, star args, or double-star args. Beyond the
    -                # scope of this checker.
    -                return
    -            format_pos = 0
    -        else:
    -            return
    -
    -        if isinstance(node.args[format_pos], astroid.BinOp) and node.args[format_pos].op == '%':
    -            self.add_message('logging-not-lazy', node=node)
    -        elif isinstance(node.args[format_pos], astroid.CallFunc):
    -            self._check_call_func(node.args[format_pos])
    -        elif isinstance(node.args[format_pos], astroid.Const):
    -            self._check_format_string(node, format_pos)
    -
    -    def _check_call_func(self, callfunc_node):
    -        """Checks that function call is not format_string.format().
    -
    -        Args:
    -          callfunc_node: CallFunc AST node to be checked.
    -        """
    -        if is_method_call(callfunc_node, ('str', 'unicode'), ('format',)):
    -            self.add_message('logging-format-interpolation', node=callfunc_node)
    -
    -    def _check_format_string(self, node, format_arg):
    -        """Checks that format string tokens match the supplied arguments.
    -
    -        Args:
    -          node: AST node to be checked.
    -          format_arg: Index of the format string in the node arguments.
    -        """
    -        num_args = _count_supplied_tokens(node.args[format_arg + 1:])
    -        if not num_args:
    -            # If no args were supplied, then all format strings are valid -
    -            # don't check any further.
    -            return
    -        format_string = node.args[format_arg].value
    -        if not isinstance(format_string, six.string_types):
    -            # If the log format is constant non-string (e.g. logging.debug(5)),
    -            # ensure there are no arguments.
    -            required_num_args = 0
    -        else:
    -            try:
    -                keyword_args, required_num_args = \
    -                    utils.parse_format_string(format_string)
    -                if keyword_args:
    -                    # Keyword checking on logging strings is complicated by
    -                    # special keywords - out of scope.
    -                    return
    -            except utils.UnsupportedFormatCharacter as ex:
    -                char = format_string[ex.index]
    -                self.add_message('logging-unsupported-format', node=node,
    -                                 args=(char, ord(char), ex.index))
    -                return
    -            except utils.IncompleteFormatString:
    -                self.add_message('logging-format-truncated', node=node)
    -                return
    -        if num_args > required_num_args:
    -            self.add_message('logging-too-many-args', node=node)
    -        elif num_args < required_num_args:
    -            self.add_message('logging-too-few-args', node=node)
    -
    -
    -def _count_supplied_tokens(args):
    -    """Counts the number of tokens in an args list.
    -
    -    The Python log functions allow for special keyword arguments: func,
    -    exc_info and extra. To handle these cases correctly, we only count
    -    arguments that aren't keywords.
    -
    -    Args:
    -      args: List of AST nodes that are arguments for a log format string.
    -
    -    Returns:
    -      Number of AST nodes that aren't keywords.
    -    """
    -    return sum(1 for arg in args if not isinstance(arg, astroid.Keyword))
    -
    -
    -def register(linter):
    -    """Required method to auto-register this checker."""
    -    linter.register_checker(LoggingChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/misc.py b/pymode/libs/pylint/checkers/misc.py
    deleted file mode 100644
    index 7fbe70bf..00000000
    --- a/pymode/libs/pylint/checkers/misc.py
    +++ /dev/null
    @@ -1,104 +0,0 @@
    -# pylint: disable=W0511
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -""" Copyright (c) 2000-2010 LOGILAB S.A. (Paris, FRANCE).
    - http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -
    -Check source code is ascii only or has an encoding declaration (PEP 263)
    -"""
    -
    -import re
    -
    -from pylint.interfaces import IRawChecker
    -from pylint.checkers import BaseChecker
    -import six
    -
    -
    -MSGS = {
    -    'W0511': ('%s',
    -              'fixme',
    -              'Used when a warning note as FIXME or XXX is detected.'),
    -    'W0512': ('Cannot decode using encoding "%s", unexpected byte at position %d',
    -              'invalid-encoded-data',
    -              'Used when a source line cannot be decoded using the specified '
    -              'source file encoding.',
    -              {'maxversion': (3, 0)}),
    -}
    -
    -
    -class EncodingChecker(BaseChecker):
    -
    -    """checks for:
    -    * warning notes in the code like FIXME, XXX
    -    * encoding issues.
    -    """
    -    __implements__ = IRawChecker
    -
    -    # configuration section name
    -    name = 'miscellaneous'
    -    msgs = MSGS
    -
    -    options = (('notes',
    -                {'type': 'csv', 'metavar': '',
    -                 'default': ('FIXME', 'XXX', 'TODO'),
    -                 'help': ('List of note tags to take in consideration, '
    -                          'separated by a comma.')}),)
    -
    -    def _check_note(self, notes, lineno, line):
    -        # First, simply check if the notes are in the line at all. This is an
    -        # optimisation to prevent using the regular expression on every line,
    -        # but rather only on lines which may actually contain one of the notes.
    -        # This prevents a pathological problem with lines that are hundreds
    -        # of thousands of characters long.
    -        for note in self.config.notes:
    -            if note in line:
    -                break
    -        else:
    -            return
    -
    -        match = notes.search(line)
    -        if not match:
    -            return
    -        self.add_message('fixme', args=line[match.start(1):-1], line=lineno)
    -
    -    def _check_encoding(self, lineno, line, file_encoding):
    -        try:
    -            return six.text_type(line, file_encoding)
    -        except UnicodeDecodeError as ex:
    -            self.add_message('invalid-encoded-data', line=lineno,
    -                             args=(file_encoding, ex.args[2]))
    -
    -    def process_module(self, module):
    -        """inspect the source file to find encoding problem or fixmes like
    -        notes
    -        """
    -        if self.config.notes:
    -            notes = re.compile(
    -                r'.*?#\s*(%s)(:*\s*.+)' % "|".join(self.config.notes))
    -        else:
    -            notes = None
    -        if module.file_encoding:
    -            encoding = module.file_encoding
    -        else:
    -            encoding = 'ascii'
    -
    -        with module.stream() as stream:
    -            for lineno, line in enumerate(stream):
    -                line = self._check_encoding(lineno + 1, line, encoding)
    -                if line is not None and notes:
    -                    self._check_note(notes, lineno + 1, line)
    -
    -
    -def register(linter):
    -    """required method to auto register this checker"""
    -    linter.register_checker(EncodingChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/newstyle.py b/pymode/libs/pylint/checkers/newstyle.py
    deleted file mode 100644
    index f74e7f15..00000000
    --- a/pymode/libs/pylint/checkers/newstyle.py
    +++ /dev/null
    @@ -1,172 +0,0 @@
    -# Copyright (c) 2005-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""check for new / old style related problems
    -"""
    -import sys
    -
    -import astroid
    -
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    check_messages,
    -    has_known_bases,
    -    node_frame_class,
    -)
    -
    -MSGS = {
    -    'E1001': ('Use of __slots__ on an old style class',
    -              'slots-on-old-class',
    -              'Used when an old style class uses the __slots__ attribute.',
    -              {'maxversion': (3, 0)}),
    -    'E1002': ('Use of super on an old style class',
    -              'super-on-old-class',
    -              'Used when an old style class uses the super builtin.',
    -              {'maxversion': (3, 0)}),
    -    'E1003': ('Bad first argument %r given to super()',
    -              'bad-super-call',
    -              'Used when another argument than the current class is given as \
    -              first argument of the super builtin.'),
    -    'E1004': ('Missing argument to super()',
    -              'missing-super-argument',
    -              'Used when the super builtin didn\'t receive an \
    -               argument.',
    -              {'maxversion': (3, 0)}),
    -    'W1001': ('Use of "property" on an old style class',
    -              'property-on-old-class',
    -              'Used when Pylint detect the use of the builtin "property" \
    -              on an old style class while this is relying on new style \
    -              classes features.',
    -              {'maxversion': (3, 0)}),
    -    'C1001': ('Old-style class defined.',
    -              'old-style-class',
    -              'Used when a class is defined that does not inherit from another'
    -              'class and does not inherit explicitly from "object".',
    -              {'maxversion': (3, 0)})
    -    }
    -
    -
    -class NewStyleConflictChecker(BaseChecker):
    -    """checks for usage of new style capabilities on old style classes and
    -    other new/old styles conflicts problems
    -    * use of property, __slots__, super
    -    * "super" usage
    -    """
    -
    -    __implements__ = (IAstroidChecker,)
    -
    -    # configuration section name
    -    name = 'newstyle'
    -    # messages
    -    msgs = MSGS
    -    priority = -2
    -    # configuration options
    -    options = ()
    -
    -    @check_messages('slots-on-old-class', 'old-style-class')
    -    def visit_class(self, node):
    -        """ Check __slots__ in old style classes and old
    -        style class definition.
    -        """
    -        if '__slots__' in node and not node.newstyle:
    -            confidence = (INFERENCE if has_known_bases(node)
    -                          else INFERENCE_FAILURE)
    -            self.add_message('slots-on-old-class', node=node,
    -                             confidence=confidence)
    -        # The node type could be class, exception, metaclass, or
    -        # interface.  Presumably, the non-class-type nodes would always
    -        # have an explicit base class anyway.
    -        if not node.bases and node.type == 'class' and not node.metaclass():
    -            # We use confidence HIGH here because this message should only ever
    -            # be emitted for classes at the root of the inheritance hierarchyself.
    -            self.add_message('old-style-class', node=node, confidence=HIGH)
    -
    -    @check_messages('property-on-old-class')
    -    def visit_callfunc(self, node):
    -        """check property usage"""
    -        parent = node.parent.frame()
    -        if (isinstance(parent, astroid.Class) and
    -                not parent.newstyle and
    -                isinstance(node.func, astroid.Name)):
    -            confidence = (INFERENCE if has_known_bases(parent)
    -                          else INFERENCE_FAILURE)
    -            name = node.func.name
    -            if name == 'property':
    -                self.add_message('property-on-old-class', node=node,
    -                                 confidence=confidence)
    -
    -    @check_messages('super-on-old-class', 'bad-super-call', 'missing-super-argument')
    -    def visit_function(self, node):
    -        """check use of super"""
    -        # ignore actual functions or method within a new style class
    -        if not node.is_method():
    -            return
    -        klass = node.parent.frame()
    -        for stmt in node.nodes_of_class(astroid.CallFunc):
    -            if node_frame_class(stmt) != node_frame_class(node):
    -                # Don't look down in other scopes.
    -                continue
    -            expr = stmt.func
    -            if not isinstance(expr, astroid.Getattr):
    -                continue
    -            call = expr.expr
    -            # skip the test if using super
    -            if isinstance(call, astroid.CallFunc) and \
    -               isinstance(call.func, astroid.Name) and \
    -               call.func.name == 'super':
    -                confidence = (INFERENCE if has_known_bases(klass)
    -                              else INFERENCE_FAILURE)
    -                if not klass.newstyle:
    -                    # super should not be used on an old style class
    -                    self.add_message('super-on-old-class', node=node,
    -                                     confidence=confidence)
    -                else:
    -                    # super first arg should be the class
    -                    if not call.args and sys.version_info[0] == 3:
    -                        # unless Python 3
    -                        continue
    -
    -                    try:
    -                        supcls = (call.args and next(call.args[0].infer())
    -                                  or None)
    -                    except astroid.InferenceError:
    -                        continue
    -
    -                    if supcls is None:
    -                        self.add_message('missing-super-argument', node=call,
    -                                         confidence=confidence)
    -                        continue
    -
    -                    if klass is not supcls:
    -                        name = None
    -                        # if supcls is not YES, then supcls was infered
    -                        # and use its name. Otherwise, try to look
    -                        # for call.args[0].name
    -                        if supcls is not astroid.YES:
    -                            name = supcls.name
    -                        else:
    -                            if hasattr(call.args[0], 'name'):
    -                                name = call.args[0].name
    -                        if name is not None:
    -                            self.add_message('bad-super-call',
    -                                             node=call,
    -                                             args=(name, ),
    -                                             confidence=confidence)
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(NewStyleConflictChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/python3.py b/pymode/libs/pylint/checkers/python3.py
    deleted file mode 100644
    index 837cbef1..00000000
    --- a/pymode/libs/pylint/checkers/python3.py
    +++ /dev/null
    @@ -1,581 +0,0 @@
    -# Copyright 2014 Google Inc.
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Check Python 2 code for Python 2/3 source-compatible issues."""
    -from __future__ import absolute_import, print_function
    -
    -import re
    -import tokenize
    -
    -import astroid
    -from astroid import bases
    -from pylint import checkers, interfaces
    -from pylint.utils import WarningScope
    -from pylint.checkers import utils
    -
    -
    -_ZERO = re.compile("^0+$")
    -
    -def _is_old_octal(literal):
    -    if _ZERO.match(literal):
    -        return False
    -    if re.match('0\d+', literal):
    -        try:
    -            int(literal, 8)
    -        except ValueError:
    -            return False
    -        return True
    -
    -def _check_dict_node(node):
    -    inferred_types = set()
    -    try:
    -        inferred = node.infer()
    -        for inferred_node in inferred:
    -            inferred_types.add(inferred_node)
    -    except (astroid.InferenceError, astroid.UnresolvableName):
    -        pass
    -    return (not inferred_types
    -            or any(isinstance(x, astroid.Dict) for x in inferred_types))
    -
    -def _is_builtin(node):
    -    return getattr(node, 'name', None) in ('__builtin__', 'builtins')
    -
    -_accepts_iterator = {'iter', 'list', 'tuple', 'sorted', 'set', 'sum', 'any',
    -                     'all', 'enumerate', 'dict'}
    -
    -def _in_iterating_context(node):
    -    """Check if the node is being used as an iterator.
    -
    -    Definition is taken from lib2to3.fixer_util.in_special_context().
    -    """
    -    parent = node.parent
    -    # Since a call can't be the loop variant we only need to know if the node's
    -    # parent is a 'for' loop to know it's being used as the iterator for the
    -    # loop.
    -    if isinstance(parent, astroid.For):
    -        return True
    -    # Need to make sure the use of the node is in the iterator part of the
    -    # comprehension.
    -    elif isinstance(parent, astroid.Comprehension):
    -        if parent.iter == node:
    -            return True
    -    # Various built-ins can take in an iterable or list and lead to the same
    -    # value.
    -    elif isinstance(parent, astroid.CallFunc):
    -        if isinstance(parent.func, astroid.Name):
    -            parent_scope = parent.func.lookup(parent.func.name)[0]
    -            if _is_builtin(parent_scope) and parent.func.name in _accepts_iterator:
    -                return True
    -        elif isinstance(parent.func, astroid.Getattr):
    -            if parent.func.attrname == 'join':
    -                return True
    -    # If the call is in an unpacking, there's no need to warn,
    -    # since it can be considered iterating.
    -    elif (isinstance(parent, astroid.Assign) and
    -          isinstance(parent.targets[0], (astroid.List, astroid.Tuple))):
    -        if len(parent.targets[0].elts) > 1:
    -            return True
    -    return False
    -
    -
    -class Python3Checker(checkers.BaseChecker):
    -
    -    __implements__ = interfaces.IAstroidChecker
    -    enabled = False
    -    name = 'python3'
    -
    -    msgs = {
    -        # Errors for what will syntactically break in Python 3, warnings for
    -        # everything else.
    -        'E1601': ('print statement used',
    -                  'print-statement',
    -                  'Used when a print statement is used '
    -                  '(`print` is a function in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'E1602': ('Parameter unpacking specified',
    -                  'parameter-unpacking',
    -                  'Used when parameter unpacking is specified for a function'
    -                  "(Python 3 doesn't allow it)",
    -                  {'maxversion': (3, 0)}),
    -        'E1603': ('Implicit unpacking of exceptions is not supported '
    -                  'in Python 3',
    -                  'unpacking-in-except',
    -                  'Python3 will not allow implicit unpacking of '
    -                  'exceptions in except clauses. '
    -                  'See http://www.python.org/dev/peps/pep-3110/',
    -                  {'maxversion': (3, 0),
    -                   'old_names': [('W0712', 'unpacking-in-except')]}),
    -        'E1604': ('Use raise ErrorClass(args) instead of '
    -                  'raise ErrorClass, args.',
    -                  'old-raise-syntax',
    -                  "Used when the alternate raise syntax "
    -                  "'raise foo, bar' is used "
    -                  "instead of 'raise foo(bar)'.",
    -                  {'maxversion': (3, 0),
    -                   'old_names': [('W0121', 'old-raise-syntax')]}),
    -        'E1605': ('Use of the `` operator',
    -                  'backtick',
    -                  'Used when the deprecated "``" (backtick) operator is used '
    -                  'instead  of the str() function.',
    -                  {'scope': WarningScope.NODE,
    -                   'maxversion': (3, 0),
    -                   'old_names': [('W0333', 'backtick')]}),
    -        'W1601': ('apply built-in referenced',
    -                  'apply-builtin',
    -                  'Used when the apply built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1602': ('basestring built-in referenced',
    -                  'basestring-builtin',
    -                  'Used when the basestring built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1603': ('buffer built-in referenced',
    -                  'buffer-builtin',
    -                  'Used when the buffer built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1604': ('cmp built-in referenced',
    -                  'cmp-builtin',
    -                  'Used when the cmp built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1605': ('coerce built-in referenced',
    -                  'coerce-builtin',
    -                  'Used when the coerce built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1606': ('execfile built-in referenced',
    -                  'execfile-builtin',
    -                  'Used when the execfile built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1607': ('file built-in referenced',
    -                  'file-builtin',
    -                  'Used when the file built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1608': ('long built-in referenced',
    -                  'long-builtin',
    -                  'Used when the long built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1609': ('raw_input built-in referenced',
    -                  'raw_input-builtin',
    -                  'Used when the raw_input built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1610': ('reduce built-in referenced',
    -                  'reduce-builtin',
    -                  'Used when the reduce built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1611': ('StandardError built-in referenced',
    -                  'standarderror-builtin',
    -                  'Used when the StandardError built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1612': ('unicode built-in referenced',
    -                  'unicode-builtin',
    -                  'Used when the unicode built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1613': ('xrange built-in referenced',
    -                  'xrange-builtin',
    -                  'Used when the xrange built-in function is referenced '
    -                  '(missing from Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1614': ('__coerce__ method defined',
    -                  'coerce-method',
    -                  'Used when a __coerce__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1615': ('__delslice__ method defined',
    -                  'delslice-method',
    -                  'Used when a __delslice__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1616': ('__getslice__ method defined',
    -                  'getslice-method',
    -                  'Used when a __getslice__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1617': ('__setslice__ method defined',
    -                  'setslice-method',
    -                  'Used when a __setslice__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1618': ('import missing `from __future__ import absolute_import`',
    -                  'no-absolute-import',
    -                  'Used when an import is not accompanied by '
    -                  '``from __future__ import absolute_import`` '
    -                  '(default behaviour in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1619': ('division w/o __future__ statement',
    -                  'old-division',
    -                  'Used for non-floor division w/o a float literal or '
    -                  '``from __future__ import division`` '
    -                  '(Python 3 returns a float for int division unconditionally)',
    -                  {'maxversion': (3, 0)}),
    -        'W1620': ('Calling a dict.iter*() method',
    -                  'dict-iter-method',
    -                  'Used for calls to dict.iterkeys(), itervalues() or iteritems() '
    -                  '(Python 3 lacks these methods)',
    -                  {'maxversion': (3, 0)}),
    -        'W1621': ('Calling a dict.view*() method',
    -                  'dict-view-method',
    -                  'Used for calls to dict.viewkeys(), viewvalues() or viewitems() '
    -                  '(Python 3 lacks these methods)',
    -                  {'maxversion': (3, 0)}),
    -        'W1622': ('Called a next() method on an object',
    -                  'next-method-called',
    -                  "Used when an object's next() method is called "
    -                  '(Python 3 uses the next() built-in function)',
    -                  {'maxversion': (3, 0)}),
    -        'W1623': ("Assigning to a class' __metaclass__ attribute",
    -                  'metaclass-assignment',
    -                  "Used when a metaclass is specified by assigning to __metaclass__ "
    -                  '(Python 3 specifies the metaclass as a class statement argument)',
    -                  {'maxversion': (3, 0)}),
    -        'W1624': ('Indexing exceptions will not work on Python 3',
    -                  'indexing-exception',
    -                  'Indexing exceptions will not work on Python 3. Use '
    -                  '`exception.args[index]` instead.',
    -                  {'maxversion': (3, 0),
    -                   'old_names': [('W0713', 'indexing-exception')]}),
    -        'W1625': ('Raising a string exception',
    -                  'raising-string',
    -                  'Used when a string exception is raised. This will not '
    -                  'work on Python 3.',
    -                  {'maxversion': (3, 0),
    -                   'old_names': [('W0701', 'raising-string')]}),
    -        'W1626': ('reload built-in referenced',
    -                  'reload-builtin',
    -                  'Used when the reload built-in function is referenced '
    -                  '(missing from Python 3). You can use instead imp.reload '
    -                  'or importlib.reload.',
    -                  {'maxversion': (3, 0)}),
    -        'W1627': ('__oct__ method defined',
    -                  'oct-method',
    -                  'Used when a __oct__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1628': ('__hex__ method defined',
    -                  'hex-method',
    -                  'Used when a __hex__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1629': ('__nonzero__ method defined',
    -                  'nonzero-method',
    -                  'Used when a __nonzero__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1630': ('__cmp__ method defined',
    -                  'cmp-method',
    -                  'Used when a __cmp__ method is defined '
    -                  '(method is not used by Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        # 'W1631': replaced by W1636
    -        'W1632': ('input built-in referenced',
    -                  'input-builtin',
    -                  'Used when the input built-in is referenced '
    -                  '(backwards-incompatible semantics in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1633': ('round built-in referenced',
    -                  'round-builtin',
    -                  'Used when the round built-in is referenced '
    -                  '(backwards-incompatible semantics in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1634': ('intern built-in referenced',
    -                  'intern-builtin',
    -                  'Used when the intern built-in is referenced '
    -                  '(Moved to sys.intern in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1635': ('unichr built-in referenced',
    -                  'unichr-builtin',
    -                  'Used when the unichr built-in is referenced '
    -                  '(Use chr in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1636': ('map built-in referenced when not iterating',
    -                  'map-builtin-not-iterating',
    -                  'Used when the map built-in is referenced in a non-iterating '
    -                  'context (returns an iterator in Python 3)',
    -                  {'maxversion': (3, 0),
    -                   'old_names': [('W1631', 'implicit-map-evaluation')]}),
    -        'W1637': ('zip built-in referenced when not iterating',
    -                  'zip-builtin-not-iterating',
    -                  'Used when the zip built-in is referenced in a non-iterating '
    -                  'context (returns an iterator in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1638': ('range built-in referenced when not iterating',
    -                  'range-builtin-not-iterating',
    -                  'Used when the range built-in is referenced in a non-iterating '
    -                  'context (returns an iterator in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1639': ('filter built-in referenced when not iterating',
    -                  'filter-builtin-not-iterating',
    -                  'Used when the filter built-in is referenced in a non-iterating '
    -                  'context (returns an iterator in Python 3)',
    -                  {'maxversion': (3, 0)}),
    -        'W1640': ('Using the cmp argument for list.sort / sorted',
    -                  'using-cmp-argument',
    -                  'Using the cmp argument for list.sort or the sorted '
    -                  'builtin should be avoided, since it was removed in '
    -                  'Python 3. Using either `key` or `functools.cmp_to_key` '
    -                  'should be preferred.',
    -                  {'maxversion': (3, 0)}),
    -    }
    -
    -    _bad_builtins = frozenset([
    -        'apply',
    -        'basestring',
    -        'buffer',
    -        'cmp',
    -        'coerce',
    -        'execfile',
    -        'file',
    -        'input',  # Not missing, but incompatible semantics
    -        'intern',
    -        'long',
    -        'raw_input',
    -        'reduce',
    -        'round',  # Not missing, but incompatible semantics
    -        'StandardError',
    -        'unichr',
    -        'unicode',
    -        'xrange',
    -        'reload',
    -    ])
    -
    -    _unused_magic_methods = frozenset([
    -        '__coerce__',
    -        '__delslice__',
    -        '__getslice__',
    -        '__setslice__',
    -        '__oct__',
    -        '__hex__',
    -        '__nonzero__',
    -        '__cmp__',
    -    ])
    -
    -    def __init__(self, *args, **kwargs):
    -        self._future_division = False
    -        self._future_absolute_import = False
    -        super(Python3Checker, self).__init__(*args, **kwargs)
    -
    -    def visit_module(self, node): # pylint: disable=unused-argument
    -        """Clear checker state after previous module."""
    -        self._future_division = False
    -        self._future_absolute_import = False
    -
    -    def visit_function(self, node):
    -        if node.is_method() and node.name in self._unused_magic_methods:
    -            method_name = node.name
    -            if node.name.startswith('__'):
    -                method_name = node.name[2:-2]
    -            self.add_message(method_name + '-method', node=node)
    -
    -    @utils.check_messages('parameter-unpacking')
    -    def visit_arguments(self, node):
    -        for arg in node.args:
    -            if isinstance(arg, astroid.Tuple):
    -                self.add_message('parameter-unpacking', node=arg)
    -
    -    def visit_name(self, node):
    -        """Detect when a "bad" built-in is referenced."""
    -        found_node = node.lookup(node.name)[0]
    -        if _is_builtin(found_node):
    -            if node.name in self._bad_builtins:
    -                message = node.name.lower() + '-builtin'
    -                self.add_message(message, node=node)
    -
    -    @utils.check_messages('print-statement')
    -    def visit_print(self, node):
    -        self.add_message('print-statement', node=node)
    -
    -    @utils.check_messages('no-absolute-import')
    -    def visit_from(self, node):
    -        if node.modname == '__future__':
    -            for name, _ in node.names:
    -                if name == 'division':
    -                    self._future_division = True
    -                elif name == 'absolute_import':
    -                    self._future_absolute_import = True
    -        elif not self._future_absolute_import:
    -            self.add_message('no-absolute-import', node=node)
    -
    -    @utils.check_messages('no-absolute-import')
    -    def visit_import(self, node):
    -        if not self._future_absolute_import:
    -            self.add_message('no-absolute-import', node=node)
    -
    -    @utils.check_messages('metaclass-assignment')
    -    def visit_class(self, node):
    -        if '__metaclass__' in node.locals:
    -            self.add_message('metaclass-assignment', node=node)
    -
    -    @utils.check_messages('old-division')
    -    def visit_binop(self, node):
    -        if not self._future_division and node.op == '/':
    -            for arg in (node.left, node.right):
    -                if isinstance(arg, astroid.Const) and isinstance(arg.value, float):
    -                    break
    -            else:
    -                self.add_message('old-division', node=node)
    -
    -    def _check_cmp_argument(self, node):
    -        # Check that the `cmp` argument is used
    -        args = []
    -        if (isinstance(node.func, astroid.Getattr)
    -                and node.func.attrname == 'sort'):
    -            inferred = utils.safe_infer(node.func.expr)
    -            if not inferred:
    -                return
    -
    -            builtins_list = "{}.list".format(bases.BUILTINS)
    -            if (isinstance(inferred, astroid.List)
    -                    or inferred.qname() == builtins_list):
    -                args = node.args
    -
    -        elif (isinstance(node.func, astroid.Name)
    -                and node.func.name == 'sorted'):
    -            inferred = utils.safe_infer(node.func)
    -            if not inferred:
    -                return
    -
    -            builtins_sorted = "{}.sorted".format(bases.BUILTINS)
    -            if inferred.qname() == builtins_sorted:
    -                args = node.args
    -
    -        for arg in args:
    -            if isinstance(arg, astroid.Keyword) and arg.arg == 'cmp':
    -                self.add_message('using-cmp-argument', node=node)
    -                return
    -
    -    def visit_callfunc(self, node):
    -        self._check_cmp_argument(node)
    -
    -        if isinstance(node.func, astroid.Getattr):
    -            if any([node.args, node.starargs, node.kwargs]):
    -                return
    -            if node.func.attrname == 'next':
    -                self.add_message('next-method-called', node=node)
    -            else:
    -                if _check_dict_node(node.func.expr):
    -                    if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems'):
    -                        self.add_message('dict-iter-method', node=node)
    -                    elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems'):
    -                        self.add_message('dict-view-method', node=node)
    -        elif isinstance(node.func, astroid.Name):
    -            found_node = node.func.lookup(node.func.name)[0]
    -            if _is_builtin(found_node):
    -                if node.func.name in ('filter', 'map', 'range', 'zip'):
    -                    if not _in_iterating_context(node):
    -                        checker = '{}-builtin-not-iterating'.format(node.func.name)
    -                        self.add_message(checker, node=node)
    -
    -
    -    @utils.check_messages('indexing-exception')
    -    def visit_subscript(self, node):
    -        """ Look for indexing exceptions. """
    -        try:
    -            for infered in node.value.infer():
    -                if not isinstance(infered, astroid.Instance):
    -                    continue
    -                if utils.inherit_from_std_ex(infered):
    -                    self.add_message('indexing-exception', node=node)
    -        except astroid.InferenceError:
    -            return
    -
    -    @utils.check_messages('unpacking-in-except')
    -    def visit_excepthandler(self, node):
    -        """Visit an except handler block and check for exception unpacking."""
    -        if isinstance(node.name, (astroid.Tuple, astroid.List)):
    -            self.add_message('unpacking-in-except', node=node)
    -
    -    @utils.check_messages('backtick')
    -    def visit_backquote(self, node):
    -        self.add_message('backtick', node=node)
    -
    -    @utils.check_messages('raising-string', 'old-raise-syntax')
    -    def visit_raise(self, node):
    -        """Visit a raise statement and check for raising
    -        strings or old-raise-syntax.
    -        """
    -        if (node.exc is not None and
    -                node.inst is not None and
    -                node.tback is None):
    -            self.add_message('old-raise-syntax', node=node)
    -
    -        # Ignore empty raise.
    -        if node.exc is None:
    -            return
    -        expr = node.exc
    -        if self._check_raise_value(node, expr):
    -            return
    -        else:
    -            try:
    -                value = next(astroid.unpack_infer(expr))
    -            except astroid.InferenceError:
    -                return
    -            self._check_raise_value(node, value)
    -
    -    def _check_raise_value(self, node, expr):
    -        if isinstance(expr, astroid.Const):
    -            value = expr.value
    -            if isinstance(value, str):
    -                self.add_message('raising-string', node=node)
    -                return True
    -
    -
    -class Python3TokenChecker(checkers.BaseTokenChecker):
    -    __implements__ = interfaces.ITokenChecker
    -    name = 'python3'
    -    enabled = False
    -
    -    msgs = {
    -        'E1606': ('Use of long suffix',
    -                  'long-suffix',
    -                  'Used when "l" or "L" is used to mark a long integer. '
    -                  'This will not work in Python 3, since `int` and `long` '
    -                  'types have merged.',
    -                  {'maxversion': (3, 0)}),
    -        'E1607': ('Use of the <> operator',
    -                  'old-ne-operator',
    -                  'Used when the deprecated "<>" operator is used instead '
    -                  'of "!=". This is removed in Python 3.',
    -                  {'maxversion': (3, 0),
    -                   'old_names': [('W0331', 'old-ne-operator')]}),
    -        'E1608': ('Use of old octal literal',
    -                  'old-octal-literal',
    -                  'Usen when encountering the old octal syntax, '
    -                  'removed in Python 3. To use the new syntax, '
    -                  'prepend 0o on the number.',
    -                  {'maxversion': (3, 0)}),
    -    }
    -
    -    def process_tokens(self, tokens):
    -        for idx, (tok_type, token, start, _, _) in enumerate(tokens):
    -            if tok_type == tokenize.NUMBER:
    -                if token.lower().endswith('l'):
    -                    # This has a different semantic than lowercase-l-suffix.
    -                    self.add_message('long-suffix', line=start[0])
    -                elif _is_old_octal(token):
    -                    self.add_message('old-octal-literal', line=start[0])
    -            if tokens[idx][1] == '<>':
    -                self.add_message('old-ne-operator', line=tokens[idx][2][0])
    -
    -
    -def register(linter):
    -    linter.register_checker(Python3Checker(linter))
    -    linter.register_checker(Python3TokenChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/raw_metrics.py b/pymode/libs/pylint/checkers/raw_metrics.py
    deleted file mode 100644
    index 71fecf68..00000000
    --- a/pymode/libs/pylint/checkers/raw_metrics.py
    +++ /dev/null
    @@ -1,129 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
    - http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -
    -Raw metrics checker
    -"""
    -
    -import tokenize
    -
    -# pylint now requires pylint >= 2.2, so this is no longer necessary
    -#if not hasattr(tokenize, 'NL'):
    -#    raise ValueError("tokenize.NL doesn't exist -- tokenize module too old")
    -
    -from logilab.common.ureports import Table
    -
    -from pylint.interfaces import ITokenChecker
    -from pylint.utils import EmptyReport
    -from pylint.checkers import BaseTokenChecker
    -from pylint.reporters import diff_string
    -
    -def report_raw_stats(sect, stats, old_stats):
    -    """calculate percentage of code / doc / comment / empty
    -    """
    -    total_lines = stats['total_lines']
    -    if not total_lines:
    -        raise EmptyReport()
    -    sect.description = '%s lines have been analyzed' % total_lines
    -    lines = ('type', 'number', '%', 'previous', 'difference')
    -    for node_type in ('code', 'docstring', 'comment', 'empty'):
    -        key = node_type + '_lines'
    -        total = stats[key]
    -        percent = float(total * 100) / total_lines
    -        old = old_stats.get(key, None)
    -        if old is not None:
    -            diff_str = diff_string(old, total)
    -        else:
    -            old, diff_str = 'NC', 'NC'
    -        lines += (node_type, str(total), '%.2f' % percent,
    -                  str(old), diff_str)
    -    sect.append(Table(children=lines, cols=5, rheaders=1))
    -
    -
    -class RawMetricsChecker(BaseTokenChecker):
    -    """does not check anything but gives some raw metrics :
    -    * total number of lines
    -    * total number of code lines
    -    * total number of docstring lines
    -    * total number of comments lines
    -    * total number of empty lines
    -    """
    -
    -    __implements__ = (ITokenChecker,)
    -
    -    # configuration section name
    -    name = 'metrics'
    -    # configuration options
    -    options = ()
    -    # messages
    -    msgs = {}
    -    # reports
    -    reports = (('RP0701', 'Raw metrics', report_raw_stats),)
    -
    -    def __init__(self, linter):
    -        BaseTokenChecker.__init__(self, linter)
    -        self.stats = None
    -
    -    def open(self):
    -        """init statistics"""
    -        self.stats = self.linter.add_stats(total_lines=0, code_lines=0,
    -                                           empty_lines=0, docstring_lines=0,
    -                                           comment_lines=0)
    -
    -    def process_tokens(self, tokens):
    -        """update stats"""
    -        i = 0
    -        tokens = list(tokens)
    -        while i < len(tokens):
    -            i, lines_number, line_type = get_type(tokens, i)
    -            self.stats['total_lines'] += lines_number
    -            self.stats[line_type] += lines_number
    -
    -
    -JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER)
    -
    -def get_type(tokens, start_index):
    -    """return the line type : docstring, comment, code, empty"""
    -    i = start_index
    -    tok_type = tokens[i][0]
    -    start = tokens[i][2]
    -    pos = start
    -    line_type = None
    -    while i < len(tokens) and tokens[i][2][0] == start[0]:
    -        tok_type = tokens[i][0]
    -        pos = tokens[i][3]
    -        if line_type is None:
    -            if tok_type == tokenize.STRING:
    -                line_type = 'docstring_lines'
    -            elif tok_type == tokenize.COMMENT:
    -                line_type = 'comment_lines'
    -            elif tok_type in JUNK:
    -                pass
    -            else:
    -                line_type = 'code_lines'
    -        i += 1
    -    if line_type is None:
    -        line_type = 'empty_lines'
    -    elif i < len(tokens) and tok_type == tokenize.NEWLINE:
    -        i += 1
    -    return i, pos[0] - start[0] + 1, line_type
    -
    -
    -def register(linter):
    -    """ required method to auto register this checker """
    -    linter.register_checker(RawMetricsChecker(linter))
    -
    diff --git a/pymode/libs/pylint/checkers/similar.py b/pymode/libs/pylint/checkers/similar.py
    deleted file mode 100644
    index 95420776..00000000
    --- a/pymode/libs/pylint/checkers/similar.py
    +++ /dev/null
    @@ -1,372 +0,0 @@
    -# pylint: disable=W0622
    -# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""a similarities / code duplication command line tool and pylint checker
    -"""
    -from __future__ import print_function
    -import sys
    -from collections import defaultdict
    -
    -from logilab.common.ureports import Table
    -
    -from pylint.interfaces import IRawChecker
    -from pylint.checkers import BaseChecker, table_lines_from_stats
    -
    -import six
    -from six.moves import zip
    -
    -
    -class Similar(object):
    -    """finds copy-pasted lines of code in a project"""
    -
    -    def __init__(self, min_lines=4, ignore_comments=False,
    -                 ignore_docstrings=False, ignore_imports=False):
    -        self.min_lines = min_lines
    -        self.ignore_comments = ignore_comments
    -        self.ignore_docstrings = ignore_docstrings
    -        self.ignore_imports = ignore_imports
    -        self.linesets = []
    -
    -    def append_stream(self, streamid, stream, encoding=None):
    -        """append a file to search for similarities"""
    -        if encoding is None:
    -            readlines = stream.readlines
    -        else:
    -            readlines = lambda: [line.decode(encoding) for line in stream]
    -        try:
    -            self.linesets.append(LineSet(streamid,
    -                                         readlines(),
    -                                         self.ignore_comments,
    -                                         self.ignore_docstrings,
    -                                         self.ignore_imports))
    -        except UnicodeDecodeError:
    -            pass
    -
    -    def run(self):
    -        """start looking for similarities and display results on stdout"""
    -        self._display_sims(self._compute_sims())
    -
    -    def _compute_sims(self):
    -        """compute similarities in appended files"""
    -        no_duplicates = defaultdict(list)
    -        for num, lineset1, idx1, lineset2, idx2 in self._iter_sims():
    -            duplicate = no_duplicates[num]
    -            for couples in duplicate:
    -                if (lineset1, idx1) in couples or (lineset2, idx2) in couples:
    -                    couples.add((lineset1, idx1))
    -                    couples.add((lineset2, idx2))
    -                    break
    -            else:
    -                duplicate.append(set([(lineset1, idx1), (lineset2, idx2)]))
    -        sims = []
    -        for num, ensembles in six.iteritems(no_duplicates):
    -            for couples in ensembles:
    -                sims.append((num, couples))
    -        sims.sort()
    -        sims.reverse()
    -        return sims
    -
    -    def _display_sims(self, sims):
    -        """display computed similarities on stdout"""
    -        nb_lignes_dupliquees = 0
    -        for num, couples in sims:
    -            print()
    -            print(num, "similar lines in", len(couples), "files")
    -            couples = sorted(couples)
    -            for lineset, idx in couples:
    -                print("==%s:%s" % (lineset.name, idx))
    -            # pylint: disable=W0631
    -            for line in lineset._real_lines[idx:idx+num]:
    -                print("  ", line.rstrip())
    -            nb_lignes_dupliquees += num * (len(couples)-1)
    -        nb_total_lignes = sum([len(lineset) for lineset in self.linesets])
    -        print("TOTAL lines=%s duplicates=%s percent=%.2f" \
    -            % (nb_total_lignes, nb_lignes_dupliquees,
    -               nb_lignes_dupliquees*100. / nb_total_lignes))
    -
    -    def _find_common(self, lineset1, lineset2):
    -        """find similarities in the two given linesets"""
    -        lines1 = lineset1.enumerate_stripped
    -        lines2 = lineset2.enumerate_stripped
    -        find = lineset2.find
    -        index1 = 0
    -        min_lines = self.min_lines
    -        while index1 < len(lineset1):
    -            skip = 1
    -            num = 0
    -            for index2 in find(lineset1[index1]):
    -                non_blank = 0
    -                for num, ((_, line1), (_, line2)) in enumerate(
    -                        zip(lines1(index1), lines2(index2))):
    -                    if line1 != line2:
    -                        if non_blank > min_lines:
    -                            yield num, lineset1, index1, lineset2, index2
    -                        skip = max(skip, num)
    -                        break
    -                    if line1:
    -                        non_blank += 1
    -                else:
    -                    # we may have reach the end
    -                    num += 1
    -                    if non_blank > min_lines:
    -                        yield num, lineset1, index1, lineset2, index2
    -                    skip = max(skip, num)
    -            index1 += skip
    -
    -    def _iter_sims(self):
    -        """iterate on similarities among all files, by making a cartesian
    -        product
    -        """
    -        for idx, lineset in enumerate(self.linesets[:-1]):
    -            for lineset2 in self.linesets[idx+1:]:
    -                for sim in self._find_common(lineset, lineset2):
    -                    yield sim
    -
    -def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports):
    -    """return lines with leading/trailing whitespace and any ignored code
    -    features removed
    -    """
    -
    -    strippedlines = []
    -    docstring = None
    -    for line in lines:
    -        line = line.strip()
    -        if ignore_docstrings:
    -            if not docstring and \
    -                   (line.startswith('"""') or line.startswith("'''")):
    -                docstring = line[:3]
    -                line = line[3:]
    -            if docstring:
    -                if line.endswith(docstring):
    -                    docstring = None
    -                line = ''
    -        if ignore_imports:
    -            if line.startswith("import ") or line.startswith("from "):
    -                line = ''
    -        if ignore_comments:
    -            # XXX should use regex in checkers/format to avoid cutting
    -            # at a "#" in a string
    -            line = line.split('#', 1)[0].strip()
    -        strippedlines.append(line)
    -    return strippedlines
    -
    -
    -class LineSet(object):
    -    """Holds and indexes all the lines of a single source file"""
    -    def __init__(self, name, lines, ignore_comments=False,
    -                 ignore_docstrings=False, ignore_imports=False):
    -        self.name = name
    -        self._real_lines = lines
    -        self._stripped_lines = stripped_lines(lines, ignore_comments,
    -                                              ignore_docstrings,
    -                                              ignore_imports)
    -        self._index = self._mk_index()
    -
    -    def __str__(self):
    -        return '' % self.name
    -
    -    def __len__(self):
    -        return len(self._real_lines)
    -
    -    def __getitem__(self, index):
    -        return self._stripped_lines[index]
    -
    -    def __lt__(self, other):
    -        return self.name < other.name
    -
    -    def __hash__(self):
    -        return id(self)
    -
    -    def enumerate_stripped(self, start_at=0):
    -        """return an iterator on stripped lines, starting from a given index
    -        if specified, else 0
    -        """
    -        idx = start_at
    -        if start_at:
    -            lines = self._stripped_lines[start_at:]
    -        else:
    -            lines = self._stripped_lines
    -        for line in lines:
    -            #if line:
    -            yield idx, line
    -            idx += 1
    -
    -    def find(self, stripped_line):
    -        """return positions of the given stripped line in this set"""
    -        return self._index.get(stripped_line, ())
    -
    -    def _mk_index(self):
    -        """create the index for this set"""
    -        index = defaultdict(list)
    -        for line_no, line in enumerate(self._stripped_lines):
    -            if line:
    -                index[line].append(line_no)
    -        return index
    -
    -
    -MSGS = {'R0801': ('Similar lines in %s files\n%s',
    -                  'duplicate-code',
    -                  'Indicates that a set of similar lines has been detected \
    -                  among multiple file. This usually means that the code should \
    -                  be refactored to avoid this duplication.')}
    -
    -def report_similarities(sect, stats, old_stats):
    -    """make a layout with some stats about duplication"""
    -    lines = ['', 'now', 'previous', 'difference']
    -    lines += table_lines_from_stats(stats, old_stats,
    -                                    ('nb_duplicated_lines',
    -                                     'percent_duplicated_lines'))
    -    sect.append(Table(children=lines, cols=4, rheaders=1, cheaders=1))
    -
    -
    -# wrapper to get a pylint checker from the similar class
    -class SimilarChecker(BaseChecker, Similar):
    -    """checks for similarities and duplicated code. This computation may be
    -    memory / CPU intensive, so you should disable it if you experiment some
    -    problems.
    -    """
    -
    -    __implements__ = (IRawChecker,)
    -    # configuration section name
    -    name = 'similarities'
    -    # messages
    -    msgs = MSGS
    -    # configuration options
    -    # for available dict keys/values see the optik parser 'add_option' method
    -    options = (('min-similarity-lines',
    -                {'default' : 4, 'type' : "int", 'metavar' : '',
    -                 'help' : 'Minimum lines number of a similarity.'}),
    -               ('ignore-comments',
    -                {'default' : True, 'type' : 'yn', 'metavar' : '',
    -                 'help': 'Ignore comments when computing similarities.'}
    -               ),
    -               ('ignore-docstrings',
    -                {'default' : True, 'type' : 'yn', 'metavar' : '',
    -                 'help': 'Ignore docstrings when computing similarities.'}
    -               ),
    -               ('ignore-imports',
    -                {'default' : False, 'type' : 'yn', 'metavar' : '',
    -                 'help': 'Ignore imports when computing similarities.'}
    -               ),
    -              )
    -    # reports
    -    reports = (('RP0801', 'Duplication', report_similarities),)
    -
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        Similar.__init__(self, min_lines=4,
    -                         ignore_comments=True, ignore_docstrings=True)
    -        self.stats = None
    -
    -    def set_option(self, optname, value, action=None, optdict=None):
    -        """method called to set an option (registered in the options list)
    -
    -        overridden to report options setting to Similar
    -        """
    -        BaseChecker.set_option(self, optname, value, action, optdict)
    -        if optname == 'min-similarity-lines':
    -            self.min_lines = self.config.min_similarity_lines
    -        elif optname == 'ignore-comments':
    -            self.ignore_comments = self.config.ignore_comments
    -        elif optname == 'ignore-docstrings':
    -            self.ignore_docstrings = self.config.ignore_docstrings
    -        elif optname == 'ignore-imports':
    -            self.ignore_imports = self.config.ignore_imports
    -
    -    def open(self):
    -        """init the checkers: reset linesets and statistics information"""
    -        self.linesets = []
    -        self.stats = self.linter.add_stats(nb_duplicated_lines=0,
    -                                           percent_duplicated_lines=0)
    -
    -    def process_module(self, node):
    -        """process a module
    -
    -        the module's content is accessible via the stream object
    -
    -        stream must implement the readlines method
    -        """
    -        with node.stream() as stream:
    -            self.append_stream(self.linter.current_name,
    -                               stream,
    -                               node.file_encoding)
    -
    -    def close(self):
    -        """compute and display similarities on closing (i.e. end of parsing)"""
    -        total = sum([len(lineset) for lineset in self.linesets])
    -        duplicated = 0
    -        stats = self.stats
    -        for num, couples in self._compute_sims():
    -            msg = []
    -            for lineset, idx in couples:
    -                msg.append("==%s:%s" % (lineset.name, idx))
    -            msg.sort()
    -            # pylint: disable=W0631
    -            for line in lineset._real_lines[idx:idx+num]:
    -                msg.append(line.rstrip())
    -            self.add_message('R0801', args=(len(couples), '\n'.join(msg)))
    -            duplicated += num * (len(couples) - 1)
    -        stats['nb_duplicated_lines'] = duplicated
    -        stats['percent_duplicated_lines'] = total and duplicated * 100. / total
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(SimilarChecker(linter))
    -
    -def usage(status=0):
    -    """display command line usage information"""
    -    print("finds copy pasted blocks in a set of files")
    -    print()
    -    print('Usage: symilar [-d|--duplicates min_duplicated_lines] \
    -[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1...')
    -    sys.exit(status)
    -
    -def Run(argv=None):
    -    """standalone command line access point"""
    -    if argv is None:
    -        argv = sys.argv[1:]
    -    from getopt import getopt
    -    s_opts = 'hdi'
    -    l_opts = ('help', 'duplicates=', 'ignore-comments', 'ignore-imports',
    -              'ignore-docstrings')
    -    min_lines = 4
    -    ignore_comments = False
    -    ignore_docstrings = False
    -    ignore_imports = False
    -    opts, args = getopt(argv, s_opts, l_opts)
    -    for opt, val in opts:
    -        if opt in ('-d', '--duplicates'):
    -            min_lines = int(val)
    -        elif opt in ('-h', '--help'):
    -            usage()
    -        elif opt in ('-i', '--ignore-comments'):
    -            ignore_comments = True
    -        elif opt in ('--ignore-docstrings',):
    -            ignore_docstrings = True
    -        elif opt in ('--ignore-imports',):
    -            ignore_imports = True
    -    if not args:
    -        usage(1)
    -    sim = Similar(min_lines, ignore_comments, ignore_docstrings, ignore_imports)
    -    for filename in args:
    -        with open(filename) as stream:
    -            sim.append_stream(filename, stream)
    -    sim.run()
    -    sys.exit(0)
    -
    -if __name__ == '__main__':
    -    Run()
    diff --git a/pymode/libs/pylint/checkers/spelling.py b/pymode/libs/pylint/checkers/spelling.py
    deleted file mode 100644
    index f6edd5db..00000000
    --- a/pymode/libs/pylint/checkers/spelling.py
    +++ /dev/null
    @@ -1,250 +0,0 @@
    -# Copyright 2014 Michal Nowikowski.
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Checker for spelling errors in comments and docstrings.
    -"""
    -
    -import sys
    -import tokenize
    -import string
    -import re
    -
    -if sys.version_info[0] >= 3:
    -    maketrans = str.maketrans
    -else:
    -    maketrans = string.maketrans
    -
    -from pylint.interfaces import ITokenChecker, IAstroidChecker
    -from pylint.checkers import BaseTokenChecker
    -from pylint.checkers.utils import check_messages
    -
    -try:
    -    import enchant
    -except ImportError:
    -    enchant = None
    -
    -if enchant is not None:
    -    br = enchant.Broker()
    -    dicts = br.list_dicts()
    -    dict_choices = [''] + [d[0] for d in dicts]
    -    dicts = ["%s (%s)" % (d[0], d[1].name) for d in dicts]
    -    dicts = ", ".join(dicts)
    -    instr = ""
    -else:
    -    dicts = "none"
    -    dict_choices = ['']
    -    instr = " To make it working install python-enchant package."
    -
    -table = maketrans("", "")
    -
    -class SpellingChecker(BaseTokenChecker):
    -    """Check spelling in comments and docstrings"""
    -    __implements__ = (ITokenChecker, IAstroidChecker)
    -    name = 'spelling'
    -    msgs = {
    -        'C0401': ('Wrong spelling of a word \'%s\' in a comment:\n%s\n'
    -                  '%s\nDid you mean: \'%s\'?',
    -                  'wrong-spelling-in-comment',
    -                  'Used when a word in comment is not spelled correctly.'),
    -        'C0402': ('Wrong spelling of a word \'%s\' in a docstring:\n%s\n'
    -                  '%s\nDid you mean: \'%s\'?',
    -                  'wrong-spelling-in-docstring',
    -                  'Used when a word in docstring is not spelled correctly.'),
    -        'C0403': ('Invalid characters %r in a docstring',
    -                  'invalid-characters-in-docstring',
    -                  'Used when a word in docstring cannot be checked by enchant.'),
    -        }
    -    options = (('spelling-dict',
    -                {'default' : '', 'type' : 'choice', 'metavar' : '',
    -                 'choices': dict_choices,
    -                 'help' : 'Spelling dictionary name. '
    -                          'Available dictionaries: %s.%s' % (dicts, instr)}),
    -               ('spelling-ignore-words',
    -                {'default' : '',
    -                 'type' : 'string',
    -                 'metavar' : '',
    -                 'help' : 'List of comma separated words that '
    -                          'should not be checked.'}),
    -               ('spelling-private-dict-file',
    -                {'default' : '',
    -                 'type' : 'string',
    -                 'metavar' : '',
    -                 'help' : 'A path to a file that contains private '
    -                          'dictionary; one word per line.'}),
    -               ('spelling-store-unknown-words',
    -                {'default' : 'n', 'type' : 'yn', 'metavar' : '',
    -                 'help' : 'Tells whether to store unknown words to '
    -                          'indicated private dictionary in '
    -                          '--spelling-private-dict-file option instead of '
    -                          'raising a message.'}),
    -              )
    -
    -    def open(self):
    -        self.initialized = False
    -        self.private_dict_file = None
    -
    -        if enchant is None:
    -            return
    -        dict_name = self.config.spelling_dict
    -        if not dict_name:
    -            return
    -
    -        self.ignore_list = [w.strip() for w in self.config.spelling_ignore_words.split(",")]
    -        # "param" appears in docstring in param description and
    -        # "pylint" appears in comments in pylint pragmas.
    -        self.ignore_list.extend(["param", "pylint"])
    -
    -        if self.config.spelling_private_dict_file:
    -            self.spelling_dict = enchant.DictWithPWL(
    -                dict_name, self.config.spelling_private_dict_file)
    -            self.private_dict_file = open(
    -                self.config.spelling_private_dict_file, "a")
    -        else:
    -            self.spelling_dict = enchant.Dict(dict_name)
    -
    -        if self.config.spelling_store_unknown_words:
    -            self.unknown_words = set()
    -
    -        # Prepare regex for stripping punctuation signs from text.
    -        # ' and _ are treated in a special way.
    -        puncts = string.punctuation.replace("'", "").replace("_", "")
    -        self.punctuation_regex = re.compile('[%s]' % re.escape(puncts))
    -        self.initialized = True
    -
    -    def close(self):
    -        if self.private_dict_file:
    -            self.private_dict_file.close()
    -
    -    def _check_spelling(self, msgid, line, line_num):
    -        line2 = line.strip()
    -        # Replace ['afadf with afadf (but preserve don't)
    -        line2 = re.sub("'([^a-zA-Z]|$)", " ", line2)
    -        # Replace afadf'] with afadf (but preserve don't)
    -        line2 = re.sub("([^a-zA-Z]|^)'", " ", line2)
    -        # Replace punctuation signs with space e.g. and/or -> and or
    -        line2 = self.punctuation_regex.sub(' ', line2)
    -
    -        words = []
    -        for word in line2.split():
    -            # Skip words with digits.
    -            if len(re.findall(r"\d", word)) > 0:
    -                continue
    -
    -            # Skip words with mixed big and small letters,
    -            # they are probaly class names.
    -            if (len(re.findall("[A-Z]", word)) > 0 and
    -                    len(re.findall("[a-z]", word)) > 0 and
    -                    len(word) > 2):
    -                continue
    -
    -            # Skip words with _ - they are probably function parameter names.
    -            if word.count('_') > 0:
    -                continue
    -
    -            words.append(word)
    -
    -        # Go through words and check them.
    -        for word in words:
    -            # Skip words from ignore list.
    -            if word in self.ignore_list:
    -                continue
    -
    -            orig_word = word
    -            word = word.lower()
    -
    -            # Strip starting u' from unicode literals and r' from raw strings.
    -            if (word.startswith("u'") or
    -                    word.startswith('u"') or
    -                    word.startswith("r'") or
    -                    word.startswith('r"')) and len(word) > 2:
    -                word = word[2:]
    -
    -            # If it is a known word, then continue.
    -            try:
    -                if self.spelling_dict.check(word):
    -                    continue
    -            except enchant.errors.Error:
    -                # this can only happen in docstrings, not comments
    -                self.add_message('invalid-characters-in-docstring',
    -                                 line=line_num, args=(word,))
    -                continue
    -
    -            # Store word to private dict or raise a message.
    -            if self.config.spelling_store_unknown_words:
    -                if word not in self.unknown_words:
    -                    self.private_dict_file.write("%s\n" % word)
    -                    self.unknown_words.add(word)
    -            else:
    -                # Present up to 4 suggestions.
    -                # TODO: add support for customising this.
    -                suggestions = self.spelling_dict.suggest(word)[:4]
    -
    -                m = re.search(r"(\W|^)(%s)(\W|$)" % word, line.lower())
    -                if m:
    -                    # Start position of second group in regex.
    -                    col = m.regs[2][0]
    -                else:
    -                    col = line.lower().index(word)
    -                indicator = (" " * col) + ("^" * len(word))
    -
    -                self.add_message(msgid, line=line_num,
    -                                 args=(orig_word, line,
    -                                       indicator,
    -                                       "' or '".join(suggestions)))
    -
    -    def process_tokens(self, tokens):
    -        if not self.initialized:
    -            return
    -
    -        # Process tokens and look for comments.
    -        for (tok_type, token, (start_row, _), _, _) in tokens:
    -            if tok_type == tokenize.COMMENT:
    -                self._check_spelling('wrong-spelling-in-comment',
    -                                     token, start_row)
    -
    -    @check_messages('wrong-spelling-in-docstring')
    -    def visit_module(self, node):
    -        if not self.initialized:
    -            return
    -        self._check_docstring(node)
    -
    -    @check_messages('wrong-spelling-in-docstring')
    -    def visit_class(self, node):
    -        if not self.initialized:
    -            return
    -        self._check_docstring(node)
    -
    -    @check_messages('wrong-spelling-in-docstring')
    -    def visit_function(self, node):
    -        if not self.initialized:
    -            return
    -        self._check_docstring(node)
    -
    -    def _check_docstring(self, node):
    -        """check the node has any spelling errors"""
    -        docstring = node.doc
    -        if not docstring:
    -            return
    -
    -        start_line = node.lineno + 1
    -
    -        # Go through lines of docstring
    -        for idx, line in enumerate(docstring.splitlines()):
    -            self._check_spelling('wrong-spelling-in-docstring',
    -                                 line, start_line + idx)
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(SpellingChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/stdlib.py b/pymode/libs/pylint/checkers/stdlib.py
    deleted file mode 100644
    index a3a61063..00000000
    --- a/pymode/libs/pylint/checkers/stdlib.py
    +++ /dev/null
    @@ -1,216 +0,0 @@
    -# Copyright 2012 Google Inc.
    -#
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Checkers for various standard library functions."""
    -
    -import six
    -import sys
    -
    -import astroid
    -from astroid.bases import Instance
    -
    -from pylint.interfaces import IAstroidChecker
    -from pylint.checkers import BaseChecker
    -from pylint.checkers import utils
    -
    -
    -TYPECHECK_COMPARISON_OPERATORS = frozenset(('is', 'is not', '==', '!=', 'in', 'not in'))
    -LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set)
    -
    -if sys.version_info >= (3, 0):
    -    OPEN_MODULE = '_io'
    -    TYPE_QNAME = 'builtins.type'
    -else:
    -    OPEN_MODULE = '__builtin__'
    -    TYPE_QNAME = '__builtin__.type'
    -
    -
    -def _check_mode_str(mode):
    -    # check type
    -    if not isinstance(mode, six.string_types):
    -        return False
    -    # check syntax
    -    modes = set(mode)
    -    _mode = "rwatb+U"
    -    creating = False
    -    if six.PY3:
    -        _mode += "x"
    -        creating = "x" in modes
    -    if modes - set(_mode) or len(mode) > len(modes):
    -        return False
    -    # check logic
    -    reading = "r" in modes
    -    writing = "w" in modes
    -    appending = "a" in modes
    -    text = "t" in modes
    -    binary = "b" in modes
    -    if "U" in modes:
    -        if writing or appending or creating and six.PY3:
    -            return False
    -        reading = True
    -        if not six.PY3:
    -            binary = True
    -    if text and binary:
    -        return False
    -    total = reading + writing + appending + (creating if six.PY3 else 0)
    -    if total > 1:
    -        return False
    -    if not (reading or writing or appending or creating and six.PY3):
    -        return False
    -    # other 2.x constraints
    -    if not six.PY3:
    -        if "U" in mode:
    -            mode = mode.replace("U", "")
    -            if "r" not in mode:
    -                mode = "r" + mode
    -        return mode[0] in ("r", "w", "a", "U")
    -    return True
    -
    -
    -def _is_one_arg_pos_call(call):
    -    """Is this a call with exactly 1 argument,
    -    where that argument is positional?
    -    """
    -    return (isinstance(call, astroid.CallFunc)
    -            and len(call.args) == 1
    -            and not isinstance(call.args[0], astroid.Keyword))
    -
    -
    -class StdlibChecker(BaseChecker):
    -    __implements__ = (IAstroidChecker,)
    -    name = 'stdlib'
    -
    -    msgs = {
    -        'W1501': ('"%s" is not a valid mode for open.',
    -                  'bad-open-mode',
    -                  'Python supports: r, w, a[, x] modes with b, +, '
    -                  'and U (only with r) options. '
    -                  'See http://docs.python.org/2/library/functions.html#open'),
    -        'W1502': ('Using datetime.time in a boolean context.',
    -                  'boolean-datetime',
    -                  'Using datetime.time in a boolean context can hide '
    -                  'subtle bugs when the time they represent matches '
    -                  'midnight UTC. This behaviour was fixed in Python 3.5. '
    -                  'See http://bugs.python.org/issue13936 for reference.',
    -                  {'maxversion': (3, 5)}),
    -        'W1503': ('Redundant use of %s with constant '
    -                  'value %r',
    -                  'redundant-unittest-assert',
    -                  'The first argument of assertTrue and assertFalse is '
    -                  'a condition. If a constant is passed as parameter, that '
    -                  'condition will be always true. In this case a warning '
    -                  'should be emitted.'),
    -        'W1504': ('Using type() instead of isinstance() for a typecheck.',
    -                  'unidiomatic-typecheck',
    -                  'The idiomatic way to perform an explicit typecheck in '
    -                  'Python is to use isinstance(x, Y) rather than '
    -                  'type(x) == Y, type(x) is Y. Though there are unusual '
    -                  'situations where these give different results.')
    -    }
    -
    -    @utils.check_messages('bad-open-mode', 'redundant-unittest-assert')
    -    def visit_callfunc(self, node):
    -        """Visit a CallFunc node."""
    -        if hasattr(node, 'func'):
    -            infer = utils.safe_infer(node.func)
    -            if infer:
    -                if infer.root().name == OPEN_MODULE:
    -                    if getattr(node.func, 'name', None) in ('open', 'file'):
    -                        self._check_open_mode(node)
    -                if infer.root().name == 'unittest.case':
    -                    self._check_redundant_assert(node, infer)
    -
    -    @utils.check_messages('boolean-datetime')
    -    def visit_unaryop(self, node):
    -        if node.op == 'not':
    -            self._check_datetime(node.operand)
    -
    -    @utils.check_messages('boolean-datetime')
    -    def visit_if(self, node):
    -        self._check_datetime(node.test)
    -
    -    @utils.check_messages('boolean-datetime')
    -    def visit_ifexp(self, node):
    -        self._check_datetime(node.test)
    -
    -    @utils.check_messages('boolean-datetime')
    -    def visit_boolop(self, node):
    -        for value in node.values:
    -            self._check_datetime(value)
    -
    -    @utils.check_messages('unidiomatic-typecheck')
    -    def visit_compare(self, node):
    -        operator, right = node.ops[0]
    -        if operator in TYPECHECK_COMPARISON_OPERATORS:
    -            left = node.left
    -            if _is_one_arg_pos_call(left):
    -                self._check_type_x_is_y(node, left, operator, right)
    -
    -    def _check_redundant_assert(self, node, infer):
    -        if (isinstance(infer, astroid.BoundMethod) and
    -                node.args and isinstance(node.args[0], astroid.Const) and
    -                infer.name in ['assertTrue', 'assertFalse']):
    -            self.add_message('redundant-unittest-assert',
    -                             args=(infer.name, node.args[0].value, ),
    -                             node=node)
    -
    -    def _check_datetime(self, node):
    -        """ Check that a datetime was infered.
    -        If so, emit boolean-datetime warning.
    -        """
    -        try:
    -            infered = next(node.infer())
    -        except astroid.InferenceError:
    -            return
    -        if (isinstance(infered, Instance) and
    -                infered.qname() == 'datetime.time'):
    -            self.add_message('boolean-datetime', node=node)
    -
    -    def _check_open_mode(self, node):
    -        """Check that the mode argument of an open or file call is valid."""
    -        try:
    -            mode_arg = utils.get_argument_from_call(node, position=1,
    -                                                    keyword='mode')
    -        except utils.NoSuchArgumentError:
    -            return
    -        if mode_arg:
    -            mode_arg = utils.safe_infer(mode_arg)
    -            if (isinstance(mode_arg, astroid.Const)
    -                    and not _check_mode_str(mode_arg.value)):
    -                self.add_message('bad-open-mode', node=node,
    -                                 args=mode_arg.value)
    -
    -    def _check_type_x_is_y(self, node, left, operator, right):
    -        """Check for expressions like type(x) == Y."""
    -        left_func = utils.safe_infer(left.func)
    -        if not (isinstance(left_func, astroid.Class)
    -                and left_func.qname() == TYPE_QNAME):
    -            return
    -
    -        if operator in ('is', 'is not') and _is_one_arg_pos_call(right):
    -            right_func = utils.safe_infer(right.func)
    -            if (isinstance(right_func, astroid.Class)
    -                    and right_func.qname() == TYPE_QNAME):
    -                # type(x) == type(a)
    -                right_arg = utils.safe_infer(right.args[0])
    -                if not isinstance(right_arg, LITERAL_NODE_TYPES):
    -                    # not e.g. type(x) == type([])
    -                    return
    -        self.add_message('unidiomatic-typecheck', node=node)
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(StdlibChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/strings.py b/pymode/libs/pylint/checkers/strings.py
    deleted file mode 100644
    index 8892c2cc..00000000
    --- a/pymode/libs/pylint/checkers/strings.py
    +++ /dev/null
    @@ -1,615 +0,0 @@
    -# Copyright (c) 2009-2010 Arista Networks, Inc. - James Lingard
    -# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE).
    -# Copyright 2012 Google Inc.
    -#
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Checker for string formatting operations.
    -"""
    -
    -import sys
    -import tokenize
    -import string
    -import numbers
    -
    -import astroid
    -
    -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker
    -from pylint.checkers import BaseChecker, BaseTokenChecker
    -from pylint.checkers import utils
    -from pylint.checkers.utils import check_messages
    -
    -import six
    -
    -
    -_PY3K = sys.version_info[:2] >= (3, 0)
    -_PY27 = sys.version_info[:2] == (2, 7)
    -
    -MSGS = {
    -    'E1300': ("Unsupported format character %r (%#02x) at index %d",
    -              "bad-format-character",
    -              "Used when a unsupported format character is used in a format\
    -              string."),
    -    'E1301': ("Format string ends in middle of conversion specifier",
    -              "truncated-format-string",
    -              "Used when a format string terminates before the end of a \
    -              conversion specifier."),
    -    'E1302': ("Mixing named and unnamed conversion specifiers in format string",
    -              "mixed-format-string",
    -              "Used when a format string contains both named (e.g. '%(foo)d') \
    -              and unnamed (e.g. '%d') conversion specifiers.  This is also \
    -              used when a named conversion specifier contains * for the \
    -              minimum field width and/or precision."),
    -    'E1303': ("Expected mapping for format string, not %s",
    -              "format-needs-mapping",
    -              "Used when a format string that uses named conversion specifiers \
    -              is used with an argument that is not a mapping."),
    -    'W1300': ("Format string dictionary key should be a string, not %s",
    -              "bad-format-string-key",
    -              "Used when a format string that uses named conversion specifiers \
    -              is used with a dictionary whose keys are not all strings."),
    -    'W1301': ("Unused key %r in format string dictionary",
    -              "unused-format-string-key",
    -              "Used when a format string that uses named conversion specifiers \
    -              is used with a dictionary that conWtains keys not required by the \
    -              format string."),
    -    'E1304': ("Missing key %r in format string dictionary",
    -              "missing-format-string-key",
    -              "Used when a format string that uses named conversion specifiers \
    -              is used with a dictionary that doesn't contain all the keys \
    -              required by the format string."),
    -    'E1305': ("Too many arguments for format string",
    -              "too-many-format-args",
    -              "Used when a format string that uses unnamed conversion \
    -              specifiers is given too many arguments."),
    -    'E1306': ("Not enough arguments for format string",
    -              "too-few-format-args",
    -              "Used when a format string that uses unnamed conversion \
    -              specifiers is given too few arguments"),
    -
    -    'W1302': ("Invalid format string",
    -              "bad-format-string",
    -              "Used when a PEP 3101 format string is invalid.",
    -              {'minversion': (2, 7)}),
    -    'W1303': ("Missing keyword argument %r for format string",
    -              "missing-format-argument-key",
    -              "Used when a PEP 3101 format string that uses named fields "
    -              "doesn't receive one or more required keywords.",
    -              {'minversion': (2, 7)}),
    -    'W1304': ("Unused format argument %r",
    -              "unused-format-string-argument",
    -              "Used when a PEP 3101 format string that uses named "
    -              "fields is used with an argument that "
    -              "is not required by the format string.",
    -              {'minversion': (2, 7)}),
    -    'W1305': ("Format string contains both automatic field numbering "
    -              "and manual field specification",
    -              "format-combined-specification",
    -              "Usen when a PEP 3101 format string contains both automatic "
    -              "field numbering (e.g. '{}') and manual field "
    -              "specification (e.g. '{0}').",
    -              {'minversion': (2, 7)}),
    -    'W1306': ("Missing format attribute %r in format specifier %r",
    -              "missing-format-attribute",
    -              "Used when a PEP 3101 format string uses an "
    -              "attribute specifier ({0.length}), but the argument "
    -              "passed for formatting doesn't have that attribute.",
    -              {'minversion': (2, 7)}),
    -    'W1307': ("Using invalid lookup key %r in format specifier %r",
    -              "invalid-format-index",
    -              "Used when a PEP 3101 format string uses a lookup specifier "
    -              "({a[1]}), but the argument passed for formatting "
    -              "doesn't contain or doesn't have that key as an attribute.",
    -              {'minversion': (2, 7)})
    -    }
    -
    -OTHER_NODES = (astroid.Const, astroid.List, astroid.Backquote,
    -               astroid.Lambda, astroid.Function,
    -               astroid.ListComp, astroid.SetComp, astroid.GenExpr)
    -
    -if _PY3K:
    -    import _string
    -
    -    def split_format_field_names(format_string):
    -        return _string.formatter_field_name_split(format_string)
    -else:
    -    def _field_iterator_convertor(iterator):
    -        for is_attr, key in iterator:
    -            if isinstance(key, numbers.Number):
    -                yield is_attr, int(key)
    -            else:
    -                yield is_attr, key
    -
    -    def split_format_field_names(format_string):
    -        keyname, fielditerator = format_string._formatter_field_name_split()
    -        # it will return longs, instead of ints, which will complicate
    -        # the output
    -        return keyname, _field_iterator_convertor(fielditerator)
    -
    -
    -def collect_string_fields(format_string):
    -    """ Given a format string, return an iterator
    -    of all the valid format fields. It handles nested fields
    -    as well.
    -    """
    -
    -    formatter = string.Formatter()
    -    try:
    -        parseiterator = formatter.parse(format_string)
    -        for result in parseiterator:
    -            if all(item is None for item in result[1:]):
    -                # not a replacement format
    -                continue
    -            name = result[1]
    -            nested = result[2]
    -            yield name
    -            if nested:
    -                for field in collect_string_fields(nested):
    -                    yield field
    -    except ValueError:
    -        # probably the format string is invalid
    -        # should we check the argument of the ValueError?
    -        raise utils.IncompleteFormatString(format_string)
    -
    -def parse_format_method_string(format_string):
    -    """
    -    Parses a PEP 3101 format string, returning a tuple of
    -    (keys, num_args, manual_pos_arg),
    -    where keys is the set of mapping keys in the format string, num_args
    -    is the number of arguments required by the format string and
    -    manual_pos_arg is the number of arguments passed with the position.
    -    """
    -    keys = []
    -    num_args = 0
    -    manual_pos_arg = set()
    -    for name in collect_string_fields(format_string):
    -        if name and str(name).isdigit():
    -            manual_pos_arg.add(str(name))
    -        elif name:
    -            keyname, fielditerator = split_format_field_names(name)
    -            if isinstance(keyname, numbers.Number):
    -                # In Python 2 it will return long which will lead
    -                # to different output between 2 and 3
    -                manual_pos_arg.add(str(keyname))
    -                keyname = int(keyname)
    -            keys.append((keyname, list(fielditerator)))
    -        else:
    -            num_args += 1
    -    return keys, num_args, len(manual_pos_arg)
    -
    -def get_args(callfunc):
    -    """ Get the arguments from the given `CallFunc` node.
    -    Return a tuple, where the first element is the
    -    number of positional arguments and the second element
    -    is the keyword arguments in a dict.
    -    """
    -    positional = 0
    -    named = {}
    -
    -    for arg in callfunc.args:
    -        if isinstance(arg, astroid.Keyword):
    -            named[arg.arg] = utils.safe_infer(arg.value)
    -        else:
    -            positional += 1
    -    return positional, named
    -
    -def get_access_path(key, parts):
    -    """ Given a list of format specifiers, returns
    -    the final access path (e.g. a.b.c[0][1]).
    -    """
    -    path = []
    -    for is_attribute, specifier in parts:
    -        if is_attribute:
    -            path.append(".{}".format(specifier))
    -        else:
    -            path.append("[{!r}]".format(specifier))
    -    return str(key) + "".join(path)
    -
    -
    -class StringFormatChecker(BaseChecker):
    -    """Checks string formatting operations to ensure that the format string
    -    is valid and the arguments match the format string.
    -    """
    -
    -    __implements__ = (IAstroidChecker,)
    -    name = 'string'
    -    msgs = MSGS
    -
    -    @check_messages(*(MSGS.keys()))
    -    def visit_binop(self, node):
    -        if node.op != '%':
    -            return
    -        left = node.left
    -        args = node.right
    -
    -        if not (isinstance(left, astroid.Const)
    -                and isinstance(left.value, six.string_types)):
    -            return
    -        format_string = left.value
    -        try:
    -            required_keys, required_num_args = \
    -                utils.parse_format_string(format_string)
    -        except utils.UnsupportedFormatCharacter as e:
    -            c = format_string[e.index]
    -            self.add_message('bad-format-character',
    -                             node=node, args=(c, ord(c), e.index))
    -            return
    -        except utils.IncompleteFormatString:
    -            self.add_message('truncated-format-string', node=node)
    -            return
    -        if required_keys and required_num_args:
    -            # The format string uses both named and unnamed format
    -            # specifiers.
    -            self.add_message('mixed-format-string', node=node)
    -        elif required_keys:
    -            # The format string uses only named format specifiers.
    -            # Check that the RHS of the % operator is a mapping object
    -            # that contains precisely the set of keys required by the
    -            # format string.
    -            if isinstance(args, astroid.Dict):
    -                keys = set()
    -                unknown_keys = False
    -                for k, _ in args.items:
    -                    if isinstance(k, astroid.Const):
    -                        key = k.value
    -                        if isinstance(key, six.string_types):
    -                            keys.add(key)
    -                        else:
    -                            self.add_message('bad-format-string-key',
    -                                             node=node, args=key)
    -                    else:
    -                        # One of the keys was something other than a
    -                        # constant.  Since we can't tell what it is,
    -                        # supress checks for missing keys in the
    -                        # dictionary.
    -                        unknown_keys = True
    -                if not unknown_keys:
    -                    for key in required_keys:
    -                        if key not in keys:
    -                            self.add_message('missing-format-string-key',
    -                                             node=node, args=key)
    -                for key in keys:
    -                    if key not in required_keys:
    -                        self.add_message('unused-format-string-key',
    -                                         node=node, args=key)
    -            elif isinstance(args, OTHER_NODES + (astroid.Tuple,)):
    -                type_name = type(args).__name__
    -                self.add_message('format-needs-mapping',
    -                                 node=node, args=type_name)
    -            # else:
    -                # The RHS of the format specifier is a name or
    -                # expression.  It may be a mapping object, so
    -                # there's nothing we can check.
    -        else:
    -            # The format string uses only unnamed format specifiers.
    -            # Check that the number of arguments passed to the RHS of
    -            # the % operator matches the number required by the format
    -            # string.
    -            if isinstance(args, astroid.Tuple):
    -                num_args = len(args.elts)
    -            elif isinstance(args, OTHER_NODES + (astroid.Dict, astroid.DictComp)):
    -                num_args = 1
    -            else:
    -                # The RHS of the format specifier is a name or
    -                # expression.  It could be a tuple of unknown size, so
    -                # there's nothing we can check.
    -                num_args = None
    -            if num_args is not None:
    -                if num_args > required_num_args:
    -                    self.add_message('too-many-format-args', node=node)
    -                elif num_args < required_num_args:
    -                    self.add_message('too-few-format-args', node=node)
    -
    -
    -class StringMethodsChecker(BaseChecker):
    -    __implements__ = (IAstroidChecker,)
    -    name = 'string'
    -    msgs = {
    -        'E1310': ("Suspicious argument in %s.%s call",
    -                  "bad-str-strip-call",
    -                  "The argument to a str.{l,r,}strip call contains a"
    -                  " duplicate character, "),
    -        }
    -
    -    @check_messages(*(MSGS.keys()))
    -    def visit_callfunc(self, node):
    -        func = utils.safe_infer(node.func)
    -        if (isinstance(func, astroid.BoundMethod)
    -                and isinstance(func.bound, astroid.Instance)
    -                and func.bound.name in ('str', 'unicode', 'bytes')):
    -            if func.name in ('strip', 'lstrip', 'rstrip') and node.args:
    -                arg = utils.safe_infer(node.args[0])
    -                if not isinstance(arg, astroid.Const):
    -                    return
    -                if len(arg.value) != len(set(arg.value)):
    -                    self.add_message('bad-str-strip-call', node=node,
    -                                     args=(func.bound.name, func.name))
    -            elif func.name == 'format':
    -                if _PY27 or _PY3K:
    -                    self._check_new_format(node, func)
    -
    -    def _check_new_format(self, node, func):
    -        """ Check the new string formatting. """
    -        # TODO: skip (for now) format nodes which don't have
    -        #       an explicit string on the left side of the format operation.
    -        #       We do this because our inference engine can't properly handle
    -        #       redefinitions of the original string.
    -        #       For more details, see issue 287.
    -        #
    -        # Note that there may not be any left side at all, if the format method
    -        # has been assigned to another variable. See issue 351. For example:
    -        #
    -        #    fmt = 'some string {}'.format
    -        #    fmt('arg')
    -        if (isinstance(node.func, astroid.Getattr)
    -                and not isinstance(node.func.expr, astroid.Const)):
    -            return
    -        try:
    -            strnode = next(func.bound.infer())
    -        except astroid.InferenceError:
    -            return
    -        if not isinstance(strnode, astroid.Const):
    -            return
    -        if node.starargs or node.kwargs:
    -            # TODO: Don't complicate the logic, skip these for now.
    -            return
    -        try:
    -            positional, named = get_args(node)
    -        except astroid.InferenceError:
    -            return
    -        try:
    -            fields, num_args, manual_pos = parse_format_method_string(strnode.value)
    -        except utils.IncompleteFormatString:
    -            self.add_message('bad-format-string', node=node)
    -            return
    -
    -        named_fields = set(field[0] for field in fields
    -                           if isinstance(field[0], six.string_types))
    -        if num_args and manual_pos:
    -            self.add_message('format-combined-specification',
    -                             node=node)
    -            return
    -
    -        check_args = False
    -        # Consider "{[0]} {[1]}" as num_args.
    -        num_args += sum(1 for field in named_fields
    -                        if field == '')
    -        if named_fields:
    -            for field in named_fields:
    -                if field not in named and field:
    -                    self.add_message('missing-format-argument-key',
    -                                     node=node,
    -                                     args=(field, ))
    -            for field in named:
    -                if field not in named_fields:
    -                    self.add_message('unused-format-string-argument',
    -                                     node=node,
    -                                     args=(field, ))
    -            # num_args can be 0 if manual_pos is not.
    -            num_args = num_args or manual_pos
    -            if positional or num_args:
    -                empty = any(True for field in named_fields
    -                            if field == '')
    -                if named or empty:
    -                    # Verify the required number of positional arguments
    -                    # only if the .format got at least one keyword argument.
    -                    # This means that the format strings accepts both
    -                    # positional and named fields and we should warn
    -                    # when one of the them is missing or is extra.
    -                    check_args = True
    -        else:
    -            check_args = True
    -        if check_args:
    -            # num_args can be 0 if manual_pos is not.
    -            num_args = num_args or manual_pos
    -            if positional > num_args:
    -                self.add_message('too-many-format-args', node=node)
    -            elif positional < num_args:
    -                self.add_message('too-few-format-args', node=node)
    -
    -        self._check_new_format_specifiers(node, fields, named)
    -
    -    def _check_new_format_specifiers(self, node, fields, named):
    -        """
    -        Check attribute and index access in the format
    -        string ("{0.a}" and "{0[a]}").
    -        """
    -        for key, specifiers in fields:
    -            # Obtain the argument. If it can't be obtained
    -            # or infered, skip this check.
    -            if key == '':
    -                # {[0]} will have an unnamed argument, defaulting
    -                # to 0. It will not be present in `named`, so use the value
    -                # 0 for it.
    -                key = 0
    -            if isinstance(key, numbers.Number):
    -                try:
    -                    argname = utils.get_argument_from_call(node, key)
    -                except utils.NoSuchArgumentError:
    -                    continue
    -            else:
    -                if key not in named:
    -                    continue
    -                argname = named[key]
    -            if argname in (astroid.YES, None):
    -                continue
    -            try:
    -                argument = next(argname.infer())
    -            except astroid.InferenceError:
    -                continue
    -            if not specifiers or argument is astroid.YES:
    -                # No need to check this key if it doesn't
    -                # use attribute / item access
    -                continue
    -            if argument.parent and isinstance(argument.parent, astroid.Arguments):
    -                # Ignore any object coming from an argument,
    -                # because we can't infer its value properly.
    -                continue
    -            previous = argument
    -            parsed = []
    -            for is_attribute, specifier in specifiers:
    -                if previous is astroid.YES:
    -                    break
    -                parsed.append((is_attribute, specifier))
    -                if is_attribute:
    -                    try:
    -                        previous = previous.getattr(specifier)[0]
    -                    except astroid.NotFoundError:
    -                        if (hasattr(previous, 'has_dynamic_getattr') and
    -                                previous.has_dynamic_getattr()):
    -                            # Don't warn if the object has a custom __getattr__
    -                            break
    -                        path = get_access_path(key, parsed)
    -                        self.add_message('missing-format-attribute',
    -                                         args=(specifier, path),
    -                                         node=node)
    -                        break
    -                else:
    -                    warn_error = False
    -                    if hasattr(previous, 'getitem'):
    -                        try:
    -                            previous = previous.getitem(specifier)
    -                        except (IndexError, TypeError):
    -                            warn_error = True
    -                    else:
    -                        try:
    -                            # Lookup __getitem__ in the current node,
    -                            # but skip further checks, because we can't
    -                            # retrieve the looked object
    -                            previous.getattr('__getitem__')
    -                            break
    -                        except astroid.NotFoundError:
    -                            warn_error = True
    -                    if warn_error:
    -                        path = get_access_path(key, parsed)
    -                        self.add_message('invalid-format-index',
    -                                         args=(specifier, path),
    -                                         node=node)
    -                        break
    -
    -                try:
    -                    previous = next(previous.infer())
    -                except astroid.InferenceError:
    -                    # can't check further if we can't infer it
    -                    break
    -
    -
    -
    -class StringConstantChecker(BaseTokenChecker):
    -    """Check string literals"""
    -    __implements__ = (ITokenChecker, IRawChecker)
    -    name = 'string_constant'
    -    msgs = {
    -        'W1401': ('Anomalous backslash in string: \'%s\'. '
    -                  'String constant might be missing an r prefix.',
    -                  'anomalous-backslash-in-string',
    -                  'Used when a backslash is in a literal string but not as an '
    -                  'escape.'),
    -        'W1402': ('Anomalous Unicode escape in byte string: \'%s\'. '
    -                  'String constant might be missing an r or u prefix.',
    -                  'anomalous-unicode-escape-in-string',
    -                  'Used when an escape like \\u is encountered in a byte '
    -                  'string where it has no effect.'),
    -        }
    -
    -    # Characters that have a special meaning after a backslash in either
    -    # Unicode or byte strings.
    -    ESCAPE_CHARACTERS = 'abfnrtvx\n\r\t\\\'\"01234567'
    -
    -    # TODO(mbp): Octal characters are quite an edge case today; people may
    -    # prefer a separate warning where they occur.  \0 should be allowed.
    -
    -    # Characters that have a special meaning after a backslash but only in
    -    # Unicode strings.
    -    UNICODE_ESCAPE_CHARACTERS = 'uUN'
    -
    -    def process_module(self, module):
    -        self._unicode_literals = 'unicode_literals' in module.future_imports
    -
    -    def process_tokens(self, tokens):
    -        for (tok_type, token, (start_row, _), _, _) in tokens:
    -            if tok_type == tokenize.STRING:
    -                # 'token' is the whole un-parsed token; we can look at the start
    -                # of it to see whether it's a raw or unicode string etc.
    -                self.process_string_token(token, start_row)
    -
    -    def process_string_token(self, token, start_row):
    -        for i, c in enumerate(token):
    -            if c in '\'\"':
    -                quote_char = c
    -                break
    -        # pylint: disable=undefined-loop-variable
    -        prefix = token[:i].lower() #  markers like u, b, r.
    -        after_prefix = token[i:]
    -        if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char:
    -            string_body = after_prefix[3:-3]
    -        else:
    -            string_body = after_prefix[1:-1]  # Chop off quotes
    -        # No special checks on raw strings at the moment.
    -        if 'r' not in prefix:
    -            self.process_non_raw_string_token(prefix, string_body, start_row)
    -
    -    def process_non_raw_string_token(self, prefix, string_body, start_row):
    -        """check for bad escapes in a non-raw string.
    -
    -        prefix: lowercase string of eg 'ur' string prefix markers.
    -        string_body: the un-parsed body of the string, not including the quote
    -        marks.
    -        start_row: integer line number in the source.
    -        """
    -        # Walk through the string; if we see a backslash then escape the next
    -        # character, and skip over it.  If we see a non-escaped character,
    -        # alert, and continue.
    -        #
    -        # Accept a backslash when it escapes a backslash, or a quote, or
    -        # end-of-line, or one of the letters that introduce a special escape
    -        # sequence 
    -        #
    -        # TODO(mbp): Maybe give a separate warning about the rarely-used
    -        # \a \b \v \f?
    -        #
    -        # TODO(mbp): We could give the column of the problem character, but
    -        # add_message doesn't seem to have a way to pass it through at present.
    -        i = 0
    -        while True:
    -            i = string_body.find('\\', i)
    -            if i == -1:
    -                break
    -            # There must be a next character; having a backslash at the end
    -            # of the string would be a SyntaxError.
    -            next_char = string_body[i+1]
    -            match = string_body[i:i+2]
    -            if next_char in self.UNICODE_ESCAPE_CHARACTERS:
    -                if 'u' in prefix:
    -                    pass
    -                elif (_PY3K or self._unicode_literals) and 'b' not in prefix:
    -                    pass  # unicode by default
    -                else:
    -                    self.add_message('anomalous-unicode-escape-in-string',
    -                                     line=start_row, args=(match, ))
    -            elif next_char not in self.ESCAPE_CHARACTERS:
    -                self.add_message('anomalous-backslash-in-string',
    -                                 line=start_row, args=(match, ))
    -            # Whether it was a valid escape or not, backslash followed by
    -            # another character can always be consumed whole: the second
    -            # character can never be the start of a new backslash escape.
    -            i += 2
    -
    -
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(StringFormatChecker(linter))
    -    linter.register_checker(StringMethodsChecker(linter))
    -    linter.register_checker(StringConstantChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/typecheck.py b/pymode/libs/pylint/checkers/typecheck.py
    deleted file mode 100644
    index 9f074ae0..00000000
    --- a/pymode/libs/pylint/checkers/typecheck.py
    +++ /dev/null
    @@ -1,627 +0,0 @@
    -# Copyright (c) 2006-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""try to find more bugs in the code using astroid inference capabilities
    -"""
    -
    -import re
    -import shlex
    -
    -import astroid
    -from astroid import InferenceError, NotFoundError, YES, Instance
    -from astroid.bases import BUILTINS
    -
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    safe_infer, is_super,
    -    check_messages, decorated_with_property)
    -
    -MSGS = {
    -    'E1101': ('%s %r has no %r member',
    -              'no-member',
    -              'Used when a variable is accessed for an unexistent member.',
    -              {'old_names': [('E1103', 'maybe-no-member')]}),
    -    'E1102': ('%s is not callable',
    -              'not-callable',
    -              'Used when an object being called has been inferred to a non \
    -              callable object'),
    -    'E1111': ('Assigning to function call which doesn\'t return',
    -              'assignment-from-no-return',
    -              'Used when an assignment is done on a function call but the \
    -              inferred function doesn\'t return anything.'),
    -    'W1111': ('Assigning to function call which only returns None',
    -              'assignment-from-none',
    -              'Used when an assignment is done on a function call but the \
    -              inferred function returns nothing but None.'),
    -
    -    'E1120': ('No value for argument %s in %s call',
    -              'no-value-for-parameter',
    -              'Used when a function call passes too few arguments.'),
    -    'E1121': ('Too many positional arguments for %s call',
    -              'too-many-function-args',
    -              'Used when a function call passes too many positional \
    -              arguments.'),
    -    'E1123': ('Unexpected keyword argument %r in %s call',
    -              'unexpected-keyword-arg',
    -              'Used when a function call passes a keyword argument that \
    -              doesn\'t correspond to one of the function\'s parameter names.'),
    -    'E1124': ('Argument %r passed by position and keyword in %s call',
    -              'redundant-keyword-arg',
    -              'Used when a function call would result in assigning multiple \
    -              values to a function parameter, one value from a positional \
    -              argument and one from a keyword argument.'),
    -    'E1125': ('Missing mandatory keyword argument %r in %s call',
    -              'missing-kwoa',
    -              ('Used when a function call does not pass a mandatory'
    -               ' keyword-only argument.'),
    -              {'minversion': (3, 0)}),
    -    'E1126': ('Sequence index is not an int, slice, or instance with __index__',
    -              'invalid-sequence-index',
    -              'Used when a sequence type is indexed with an invalid type. '
    -              'Valid types are ints, slices, and objects with an __index__ '
    -              'method.'),
    -    'E1127': ('Slice index is not an int, None, or instance with __index__',
    -              'invalid-slice-index',
    -              'Used when a slice index is not an integer, None, or an object \
    -               with an __index__ method.'),
    -    }
    -
    -# builtin sequence types in Python 2 and 3.
    -SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray',
    -                      'xrange', 'range', 'bytes', 'memoryview'])
    -
    -def _determine_callable(callable_obj):
    -    # Ordering is important, since BoundMethod is a subclass of UnboundMethod,
    -    # and Function inherits Lambda.
    -    if isinstance(callable_obj, astroid.BoundMethod):
    -        # Bound methods have an extra implicit 'self' argument.
    -        return callable_obj, 1, callable_obj.type
    -    elif isinstance(callable_obj, astroid.UnboundMethod):
    -        return callable_obj, 0, 'unbound method'
    -    elif isinstance(callable_obj, astroid.Function):
    -        return callable_obj, 0, callable_obj.type
    -    elif isinstance(callable_obj, astroid.Lambda):
    -        return callable_obj, 0, 'lambda'
    -    elif isinstance(callable_obj, astroid.Class):
    -        # Class instantiation, lookup __new__ instead.
    -        # If we only find object.__new__, we can safely check __init__
    -        # instead.
    -        try:
    -            # Use the last definition of __new__.
    -            new = callable_obj.local_attr('__new__')[-1]
    -        except astroid.NotFoundError:
    -            new = None
    -
    -        if not new or new.parent.scope().name == 'object':
    -            try:
    -                # Use the last definition of __init__.
    -                callable_obj = callable_obj.local_attr('__init__')[-1]
    -            except astroid.NotFoundError:
    -                # do nothing, covered by no-init.
    -                raise ValueError
    -        else:
    -            callable_obj = new
    -
    -        if not isinstance(callable_obj, astroid.Function):
    -            raise ValueError
    -        # both have an extra implicit 'cls'/'self' argument.
    -        return callable_obj, 1, 'constructor'
    -    else:
    -        raise ValueError
    -
    -class TypeChecker(BaseChecker):
    -    """try to find bugs in the code using type inference
    -    """
    -
    -    __implements__ = (IAstroidChecker,)
    -
    -    # configuration section name
    -    name = 'typecheck'
    -    # messages
    -    msgs = MSGS
    -    priority = -1
    -    # configuration options
    -    options = (('ignore-mixin-members',
    -                {'default' : True, 'type' : 'yn', 'metavar': '',
    -                 'help' : 'Tells whether missing members accessed in mixin \
    -class should be ignored. A mixin class is detected if its name ends with \
    -"mixin" (case insensitive).'}
    -               ),
    -               ('ignored-modules',
    -                {'default': (),
    -                 'type': 'csv',
    -                 'metavar': '',
    -                 'help': 'List of module names for which member attributes \
    -should not be checked (useful for modules/projects where namespaces are \
    -manipulated during runtime and thus existing member attributes cannot be \
    -deduced by static analysis'},
    -               ),
    -               ('ignored-classes',
    -                {'default' : ('SQLObject',),
    -                 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of classes names for which member attributes \
    -should not be checked (useful for classes with attributes dynamically set).'}
    -               ),
    -
    -               ('zope',
    -                {'default' : False, 'type' : 'yn', 'metavar': '',
    -                 'help' : 'When zope mode is activated, add a predefined set \
    -of Zope acquired attributes to generated-members.'}
    -               ),
    -               ('generated-members',
    -                {'default' : ('REQUEST', 'acl_users', 'aq_parent'),
    -                 'type' : 'string',
    -                 'metavar' : '',
    -                 'help' : 'List of members which are set dynamically and \
    -missed by pylint inference system, and so shouldn\'t trigger E0201 when \
    -accessed. Python regular expressions are accepted.'}
    -               ),
    -              )
    -
    -    def open(self):
    -        # do this in open since config not fully initialized in __init__
    -        self.generated_members = list(self.config.generated_members)
    -        if self.config.zope:
    -            self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent'))
    -
    -    def visit_assattr(self, node):
    -        if isinstance(node.ass_type(), astroid.AugAssign):
    -            self.visit_getattr(node)
    -
    -    def visit_delattr(self, node):
    -        self.visit_getattr(node)
    -
    -    @check_messages('no-member')
    -    def visit_getattr(self, node):
    -        """check that the accessed attribute exists
    -
    -        to avoid to much false positives for now, we'll consider the code as
    -        correct if a single of the inferred nodes has the accessed attribute.
    -
    -        function/method, super call and metaclasses are ignored
    -        """
    -        # generated_members may containt regular expressions
    -        # (surrounded by quote `"` and followed by a comma `,`)
    -        # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' =>
    -        # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}')
    -        if isinstance(self.config.generated_members, str):
    -            gen = shlex.shlex(self.config.generated_members)
    -            gen.whitespace += ','
    -            gen.wordchars += '[]-+'
    -            self.config.generated_members = tuple(tok.strip('"') for tok in gen)
    -        for pattern in self.config.generated_members:
    -            # attribute is marked as generated, stop here
    -            if re.match(pattern, node.attrname):
    -                return
    -        try:
    -            infered = list(node.expr.infer())
    -        except InferenceError:
    -            return
    -        # list of (node, nodename) which are missing the attribute
    -        missingattr = set()
    -        ignoremim = self.config.ignore_mixin_members
    -        inference_failure = False
    -        for owner in infered:
    -            # skip yes object
    -            if owner is YES:
    -                inference_failure = True
    -                continue
    -            # skip None anyway
    -            if isinstance(owner, astroid.Const) and owner.value is None:
    -                continue
    -            # XXX "super" / metaclass call
    -            if is_super(owner) or getattr(owner, 'type', None) == 'metaclass':
    -                continue
    -            name = getattr(owner, 'name', 'None')
    -            if name in self.config.ignored_classes:
    -                continue
    -            if ignoremim and name[-5:].lower() == 'mixin':
    -                continue
    -            try:
    -                if not [n for n in owner.getattr(node.attrname)
    -                        if not isinstance(n.statement(), astroid.AugAssign)]:
    -                    missingattr.add((owner, name))
    -                    continue
    -            except AttributeError:
    -                # XXX method / function
    -                continue
    -            except NotFoundError:
    -                if isinstance(owner, astroid.Function) and owner.decorators:
    -                    continue
    -                if isinstance(owner, Instance) and owner.has_dynamic_getattr():
    -                    continue
    -                # explicit skipping of module member access
    -                if owner.root().name in self.config.ignored_modules:
    -                    continue
    -                if isinstance(owner, astroid.Class):
    -                    # Look up in the metaclass only if the owner is itself
    -                    # a class.
    -                    # TODO: getattr doesn't return by default members
    -                    # from the metaclass, because handling various cases
    -                    # of methods accessible from the metaclass itself
    -                    # and/or subclasses only is too complicated for little to
    -                    # no benefit.
    -                    metaclass = owner.metaclass()
    -                    try:
    -                        if metaclass and metaclass.getattr(node.attrname):
    -                            continue
    -                    except NotFoundError:
    -                        pass
    -                missingattr.add((owner, name))
    -                continue
    -            # stop on the first found
    -            break
    -        else:
    -            # we have not found any node with the attributes, display the
    -            # message for infered nodes
    -            done = set()
    -            for owner, name in missingattr:
    -                if isinstance(owner, Instance):
    -                    actual = owner._proxied
    -                else:
    -                    actual = owner
    -                if actual in done:
    -                    continue
    -                done.add(actual)
    -                confidence = INFERENCE if not inference_failure else INFERENCE_FAILURE
    -                self.add_message('no-member', node=node,
    -                                 args=(owner.display_type(), name,
    -                                       node.attrname),
    -                                 confidence=confidence)
    -
    -    @check_messages('assignment-from-no-return', 'assignment-from-none')
    -    def visit_assign(self, node):
    -        """check that if assigning to a function call, the function is
    -        possibly returning something valuable
    -        """
    -        if not isinstance(node.value, astroid.CallFunc):
    -            return
    -        function_node = safe_infer(node.value.func)
    -        # skip class, generator and incomplete function definition
    -        if not (isinstance(function_node, astroid.Function) and
    -                function_node.root().fully_defined()):
    -            return
    -        if function_node.is_generator() \
    -               or function_node.is_abstract(pass_is_abstract=False):
    -            return
    -        returns = list(function_node.nodes_of_class(astroid.Return,
    -                                                    skip_klass=astroid.Function))
    -        if len(returns) == 0:
    -            self.add_message('assignment-from-no-return', node=node)
    -        else:
    -            for rnode in returns:
    -                if not (isinstance(rnode.value, astroid.Const)
    -                        and rnode.value.value is None
    -                        or rnode.value is None):
    -                    break
    -            else:
    -                self.add_message('assignment-from-none', node=node)
    -
    -    def _check_uninferable_callfunc(self, node):
    -        """
    -        Check that the given uninferable CallFunc node does not
    -        call an actual function.
    -        """
    -        if not isinstance(node.func, astroid.Getattr):
    -            return
    -
    -        # Look for properties. First, obtain
    -        # the lhs of the Getattr node and search the attribute
    -        # there. If that attribute is a property or a subclass of properties,
    -        # then most likely it's not callable.
    -
    -        # TODO: since astroid doesn't understand descriptors very well
    -        # we will not handle them here, right now.
    -
    -        expr = node.func.expr
    -        klass = safe_infer(expr)
    -        if (klass is None or klass is astroid.YES or
    -                not isinstance(klass, astroid.Instance)):
    -            return
    -
    -        try:
    -            attrs = klass._proxied.getattr(node.func.attrname)
    -        except astroid.NotFoundError:
    -            return
    -
    -        for attr in attrs:
    -            if attr is astroid.YES:
    -                continue
    -            if not isinstance(attr, astroid.Function):
    -                continue
    -
    -            # Decorated, see if it is decorated with a property.
    -            # Also, check the returns and see if they are callable.
    -            if decorated_with_property(attr):
    -                if all(return_node.callable()
    -                       for return_node in attr.infer_call_result(node)):
    -                    continue
    -                else:
    -                    self.add_message('not-callable', node=node,
    -                                     args=node.func.as_string())
    -                    break
    -
    -    @check_messages(*(list(MSGS.keys())))
    -    def visit_callfunc(self, node):
    -        """check that called functions/methods are inferred to callable objects,
    -        and that the arguments passed to the function match the parameters in
    -        the inferred function's definition
    -        """
    -        # Build the set of keyword arguments, checking for duplicate keywords,
    -        # and count the positional arguments.
    -        keyword_args = set()
    -        num_positional_args = 0
    -        for arg in node.args:
    -            if isinstance(arg, astroid.Keyword):
    -                keyword_args.add(arg.arg)
    -            else:
    -                num_positional_args += 1
    -
    -        called = safe_infer(node.func)
    -        # only function, generator and object defining __call__ are allowed
    -        if called is not None and not called.callable():
    -            self.add_message('not-callable', node=node,
    -                             args=node.func.as_string())
    -
    -        self._check_uninferable_callfunc(node)
    -
    -        try:
    -            called, implicit_args, callable_name = _determine_callable(called)
    -        except ValueError:
    -            # Any error occurred during determining the function type, most of
    -            # those errors are handled by different warnings.
    -            return
    -        num_positional_args += implicit_args
    -        if called.args.args is None:
    -            # Built-in functions have no argument information.
    -            return
    -
    -        if len(called.argnames()) != len(set(called.argnames())):
    -            # Duplicate parameter name (see E9801).  We can't really make sense
    -            # of the function call in this case, so just return.
    -            return
    -
    -        # Analyze the list of formal parameters.
    -        num_mandatory_parameters = len(called.args.args) - len(called.args.defaults)
    -        parameters = []
    -        parameter_name_to_index = {}
    -        for i, arg in enumerate(called.args.args):
    -            if isinstance(arg, astroid.Tuple):
    -                name = None
    -                # Don't store any parameter names within the tuple, since those
    -                # are not assignable from keyword arguments.
    -            else:
    -                if isinstance(arg, astroid.Keyword):
    -                    name = arg.arg
    -                else:
    -                    assert isinstance(arg, astroid.AssName)
    -                    # This occurs with:
    -                    #    def f( (a), (b) ): pass
    -                    name = arg.name
    -                parameter_name_to_index[name] = i
    -            if i >= num_mandatory_parameters:
    -                defval = called.args.defaults[i - num_mandatory_parameters]
    -            else:
    -                defval = None
    -            parameters.append([(name, defval), False])
    -
    -        kwparams = {}
    -        for i, arg in enumerate(called.args.kwonlyargs):
    -            if isinstance(arg, astroid.Keyword):
    -                name = arg.arg
    -            else:
    -                assert isinstance(arg, astroid.AssName)
    -                name = arg.name
    -            kwparams[name] = [called.args.kw_defaults[i], False]
    -
    -        # Match the supplied arguments against the function parameters.
    -
    -        # 1. Match the positional arguments.
    -        for i in range(num_positional_args):
    -            if i < len(parameters):
    -                parameters[i][1] = True
    -            elif called.args.vararg is not None:
    -                # The remaining positional arguments get assigned to the *args
    -                # parameter.
    -                break
    -            else:
    -                # Too many positional arguments.
    -                self.add_message('too-many-function-args',
    -                                 node=node, args=(callable_name,))
    -                break
    -
    -        # 2. Match the keyword arguments.
    -        for keyword in keyword_args:
    -            if keyword in parameter_name_to_index:
    -                i = parameter_name_to_index[keyword]
    -                if parameters[i][1]:
    -                    # Duplicate definition of function parameter.
    -                    self.add_message('redundant-keyword-arg',
    -                                     node=node, args=(keyword, callable_name))
    -                else:
    -                    parameters[i][1] = True
    -            elif keyword in kwparams:
    -                if kwparams[keyword][1]:  # XXX is that even possible?
    -                    # Duplicate definition of function parameter.
    -                    self.add_message('redundant-keyword-arg', node=node,
    -                                     args=(keyword, callable_name))
    -                else:
    -                    kwparams[keyword][1] = True
    -            elif called.args.kwarg is not None:
    -                # The keyword argument gets assigned to the **kwargs parameter.
    -                pass
    -            else:
    -                # Unexpected keyword argument.
    -                self.add_message('unexpected-keyword-arg', node=node,
    -                                 args=(keyword, callable_name))
    -
    -        # 3. Match the *args, if any.  Note that Python actually processes
    -        #    *args _before_ any keyword arguments, but we wait until after
    -        #    looking at the keyword arguments so as to make a more conservative
    -        #    guess at how many values are in the *args sequence.
    -        if node.starargs is not None:
    -            for i in range(num_positional_args, len(parameters)):
    -                [(name, defval), assigned] = parameters[i]
    -                # Assume that *args provides just enough values for all
    -                # non-default parameters after the last parameter assigned by
    -                # the positional arguments but before the first parameter
    -                # assigned by the keyword arguments.  This is the best we can
    -                # get without generating any false positives.
    -                if (defval is not None) or assigned:
    -                    break
    -                parameters[i][1] = True
    -
    -        # 4. Match the **kwargs, if any.
    -        if node.kwargs is not None:
    -            for i, [(name, defval), assigned] in enumerate(parameters):
    -                # Assume that *kwargs provides values for all remaining
    -                # unassigned named parameters.
    -                if name is not None:
    -                    parameters[i][1] = True
    -                else:
    -                    # **kwargs can't assign to tuples.
    -                    pass
    -
    -        # Check that any parameters without a default have been assigned
    -        # values.
    -        for [(name, defval), assigned] in parameters:
    -            if (defval is None) and not assigned:
    -                if name is None:
    -                    display_name = ''
    -                else:
    -                    display_name = repr(name)
    -                self.add_message('no-value-for-parameter', node=node,
    -                                 args=(display_name, callable_name))
    -
    -        for name in kwparams:
    -            defval, assigned = kwparams[name]
    -            if defval is None and not assigned:
    -                self.add_message('missing-kwoa', node=node,
    -                                 args=(name, callable_name))
    -
    -    @check_messages('invalid-sequence-index')
    -    def visit_extslice(self, node):
    -        # Check extended slice objects as if they were used as a sequence
    -        # index to check if the object being sliced can support them
    -        return self.visit_index(node)
    -
    -    @check_messages('invalid-sequence-index')
    -    def visit_index(self, node):
    -        if not node.parent or not hasattr(node.parent, "value"):
    -            return
    -
    -        # Look for index operations where the parent is a sequence type.
    -        # If the types can be determined, only allow indices to be int,
    -        # slice or instances with __index__.
    -
    -        parent_type = safe_infer(node.parent.value)
    -        if not isinstance(parent_type, (astroid.Class, astroid.Instance)):
    -            return
    -
    -        # Determine what method on the parent this index will use
    -        # The parent of this node will be a Subscript, and the parent of that
    -        # node determines if the Subscript is a get, set, or delete operation.
    -        operation = node.parent.parent
    -        if isinstance(operation, astroid.Assign):
    -            methodname = '__setitem__'
    -        elif isinstance(operation, astroid.Delete):
    -            methodname = '__delitem__'
    -        else:
    -            methodname = '__getitem__'
    -
    -        # Check if this instance's __getitem__, __setitem__, or __delitem__, as
    -        # appropriate to the statement, is implemented in a builtin sequence
    -        # type. This way we catch subclasses of sequence types but skip classes
    -        # that override __getitem__ and which may allow non-integer indices.
    -        try:
    -            methods = parent_type.getattr(methodname)
    -            if methods is astroid.YES:
    -                return
    -            itemmethod = methods[0]
    -        except (astroid.NotFoundError, IndexError):
    -            return
    -
    -        if not isinstance(itemmethod, astroid.Function):
    -            return
    -        if itemmethod.root().name != BUILTINS:
    -            return
    -        if not itemmethod.parent:
    -            return
    -        if itemmethod.parent.name not in SEQUENCE_TYPES:
    -            return
    -
    -        # For ExtSlice objects coming from visit_extslice, no further
    -        # inference is necessary, since if we got this far the ExtSlice
    -        # is an error.
    -        if isinstance(node, astroid.ExtSlice):
    -            index_type = node
    -        else:
    -            index_type = safe_infer(node)
    -        if index_type is None or index_type is astroid.YES:
    -            return
    -
    -        # Constants must be of type int
    -        if isinstance(index_type, astroid.Const):
    -            if isinstance(index_type.value, int):
    -                return
    -        # Instance values must be int, slice, or have an __index__ method
    -        elif isinstance(index_type, astroid.Instance):
    -            if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
    -                return
    -            try:
    -                index_type.getattr('__index__')
    -                return
    -            except astroid.NotFoundError:
    -                pass
    -
    -        # Anything else is an error
    -        self.add_message('invalid-sequence-index', node=node)
    -
    -    @check_messages('invalid-slice-index')
    -    def visit_slice(self, node):
    -        # Check the type of each part of the slice
    -        for index in (node.lower, node.upper, node.step):
    -            if index is None:
    -                continue
    -
    -            index_type = safe_infer(index)
    -            if index_type is None or index_type is astroid.YES:
    -                continue
    -
    -            # Constants must of type int or None
    -            if isinstance(index_type, astroid.Const):
    -                if isinstance(index_type.value, (int, type(None))):
    -                    continue
    -            # Instance values must be of type int, None or an object
    -            # with __index__
    -            elif isinstance(index_type, astroid.Instance):
    -                if index_type.pytype() in (BUILTINS + '.int',
    -                                           BUILTINS + '.NoneType'):
    -                    continue
    -
    -                try:
    -                    index_type.getattr('__index__')
    -                    return
    -                except astroid.NotFoundError:
    -                    pass
    -
    -            # Anything else is an error
    -            self.add_message('invalid-slice-index', node=node)
    -
    -def register(linter):
    -    """required method to auto register this checker """
    -    linter.register_checker(TypeChecker(linter))
    diff --git a/pymode/libs/pylint/checkers/utils.py b/pymode/libs/pylint/checkers/utils.py
    deleted file mode 100644
    index 2cb01d55..00000000
    --- a/pymode/libs/pylint/checkers/utils.py
    +++ /dev/null
    @@ -1,564 +0,0 @@
    -# pylint: disable=W0611
    -#
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""some functions that may be useful for various checkers
    -"""
    -
    -import re
    -import sys
    -import string
    -
    -import astroid
    -from astroid import scoped_nodes
    -from logilab.common.compat import builtins
    -
    -BUILTINS_NAME = builtins.__name__
    -COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.GenExpr
    -PY3K = sys.version_info[0] == 3
    -
    -if not PY3K:
    -    EXCEPTIONS_MODULE = "exceptions"
    -else:
    -    EXCEPTIONS_MODULE = "builtins"
    -ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod',
    -                   'abc.abstractclassmethod', 'abc.abstractstaticmethod'))
    -
    -
    -class NoSuchArgumentError(Exception):
    -    pass
    -
    -def is_inside_except(node):
    -    """Returns true if node is inside the name of an except handler."""
    -    current = node
    -    while current and not isinstance(current.parent, astroid.ExceptHandler):
    -        current = current.parent
    -
    -    return current and current is current.parent.name
    -
    -
    -def get_all_elements(node):
    -    """Recursively returns all atoms in nested lists and tuples."""
    -    if isinstance(node, (astroid.Tuple, astroid.List)):
    -        for child in node.elts:
    -            for e in get_all_elements(child):
    -                yield e
    -    else:
    -        yield node
    -
    -
    -def clobber_in_except(node):
    -    """Checks if an assignment node in an except handler clobbers an existing
    -    variable.
    -
    -    Returns (True, args for W0623) if assignment clobbers an existing variable,
    -    (False, None) otherwise.
    -    """
    -    if isinstance(node, astroid.AssAttr):
    -        return (True, (node.attrname, 'object %r' % (node.expr.as_string(),)))
    -    elif isinstance(node, astroid.AssName):
    -        name = node.name
    -        if is_builtin(name):
    -            return (True, (name, 'builtins'))
    -        else:
    -            stmts = node.lookup(name)[1]
    -            if (stmts and not isinstance(stmts[0].ass_type(),
    -                                         (astroid.Assign, astroid.AugAssign,
    -                                          astroid.ExceptHandler))):
    -                return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno))
    -    return (False, None)
    -
    -
    -def safe_infer(node):
    -    """return the inferred value for the given node.
    -    Return None if inference failed or if there is some ambiguity (more than
    -    one node has been inferred)
    -    """
    -    try:
    -        inferit = node.infer()
    -        value = next(inferit)
    -    except astroid.InferenceError:
    -        return
    -    try:
    -        next(inferit)
    -        return # None if there is ambiguity on the inferred node
    -    except astroid.InferenceError:
    -        return # there is some kind of ambiguity
    -    except StopIteration:
    -        return value
    -
    -def is_super(node):
    -    """return True if the node is referencing the "super" builtin function
    -    """
    -    if getattr(node, 'name', None) == 'super' and \
    -           node.root().name == BUILTINS_NAME:
    -        return True
    -    return False
    -
    -def is_error(node):
    -    """return true if the function does nothing but raising an exception"""
    -    for child_node in node.get_children():
    -        if isinstance(child_node, astroid.Raise):
    -            return True
    -        return False
    -
    -def is_raising(body):
    -    """return true if the given statement node raise an exception"""
    -    for node in body:
    -        if isinstance(node, astroid.Raise):
    -            return True
    -    return False
    -
    -def is_empty(body):
    -    """return true if the given node does nothing but 'pass'"""
    -    return len(body) == 1 and isinstance(body[0], astroid.Pass)
    -
    -builtins = builtins.__dict__.copy()
    -SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__')
    -
    -def is_builtin_object(node):
    -    """Returns True if the given node is an object from the __builtin__ module."""
    -    return node and node.root().name == BUILTINS_NAME
    -
    -def is_builtin(name): # was is_native_builtin
    -    """return true if  could be considered as a builtin defined by python
    -    """
    -    if name in builtins:
    -        return True
    -    if name in SPECIAL_BUILTINS:
    -        return True
    -    return False
    -
    -def is_defined_before(var_node):
    -    """return True if the variable node is defined by a parent node (list,
    -    set, dict, or generator comprehension, lambda) or in a previous sibling
    -    node on the same line (statement_defining ; statement_using)
    -    """
    -    varname = var_node.name
    -    _node = var_node.parent
    -    while _node:
    -        if isinstance(_node, COMP_NODE_TYPES):
    -            for ass_node in _node.nodes_of_class(astroid.AssName):
    -                if ass_node.name == varname:
    -                    return True
    -        elif isinstance(_node, astroid.For):
    -            for ass_node in _node.target.nodes_of_class(astroid.AssName):
    -                if ass_node.name == varname:
    -                    return True
    -        elif isinstance(_node, astroid.With):
    -            for expr, ids in _node.items:
    -                if expr.parent_of(var_node):
    -                    break
    -                if (ids and
    -                        isinstance(ids, astroid.AssName) and
    -                        ids.name == varname):
    -                    return True
    -        elif isinstance(_node, (astroid.Lambda, astroid.Function)):
    -            if _node.args.is_argument(varname):
    -                return True
    -            if getattr(_node, 'name', None) == varname:
    -                return True
    -            break
    -        elif isinstance(_node, astroid.ExceptHandler):
    -            if isinstance(_node.name, astroid.AssName):
    -                ass_node = _node.name
    -                if ass_node.name == varname:
    -                    return True
    -        _node = _node.parent
    -    # possibly multiple statements on the same line using semi colon separator
    -    stmt = var_node.statement()
    -    _node = stmt.previous_sibling()
    -    lineno = stmt.fromlineno
    -    while _node and _node.fromlineno == lineno:
    -        for ass_node in _node.nodes_of_class(astroid.AssName):
    -            if ass_node.name == varname:
    -                return True
    -        for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)):
    -            if varname in [name[1] or name[0] for name in imp_node.names]:
    -                return True
    -        _node = _node.previous_sibling()
    -    return False
    -
    -def is_func_default(node):
    -    """return true if the given Name node is used in function default argument's
    -    value
    -    """
    -    parent = node.scope()
    -    if isinstance(parent, astroid.Function):
    -        for default_node in parent.args.defaults:
    -            for default_name_node in default_node.nodes_of_class(astroid.Name):
    -                if default_name_node is node:
    -                    return True
    -    return False
    -
    -def is_func_decorator(node):
    -    """return true if the name is used in function decorator"""
    -    parent = node.parent
    -    while parent is not None:
    -        if isinstance(parent, astroid.Decorators):
    -            return True
    -        if (parent.is_statement or
    -                isinstance(parent, astroid.Lambda) or
    -                isinstance(parent, (scoped_nodes.ComprehensionScope,
    -                                    scoped_nodes.ListComp))):
    -            break
    -        parent = parent.parent
    -    return False
    -
    -def is_ancestor_name(frame, node):
    -    """return True if `frame` is a astroid.Class node with `node` in the
    -    subtree of its bases attribute
    -    """
    -    try:
    -        bases = frame.bases
    -    except AttributeError:
    -        return False
    -    for base in bases:
    -        if node in base.nodes_of_class(astroid.Name):
    -            return True
    -    return False
    -
    -def assign_parent(node):
    -    """return the higher parent which is not an AssName, Tuple or List node
    -    """
    -    while node and isinstance(node, (astroid.AssName,
    -                                     astroid.Tuple,
    -                                     astroid.List)):
    -        node = node.parent
    -    return node
    -
    -def overrides_an_abstract_method(class_node, name):
    -    """return True if pnode is a parent of node"""
    -    for ancestor in class_node.ancestors():
    -        if name in ancestor and isinstance(ancestor[name], astroid.Function) and \
    -               ancestor[name].is_abstract(pass_is_abstract=False):
    -            return True
    -    return False
    -
    -def overrides_a_method(class_node, name):
    -    """return True if  is a method overridden from an ancestor"""
    -    for ancestor in class_node.ancestors():
    -        if name in ancestor and isinstance(ancestor[name], astroid.Function):
    -            return True
    -    return False
    -
    -PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__',
    -                 '__str__', '__repr__',
    -                 '__len__', '__iter__',
    -                 '__delete__', '__get__', '__set__',
    -                 '__getitem__', '__setitem__', '__delitem__', '__contains__',
    -                 '__getattribute__', '__getattr__', '__setattr__', '__delattr__',
    -                 '__call__',
    -                 '__enter__', '__exit__',
    -                 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__',
    -                 '__nonzero__', '__neg__', '__invert__',
    -                 '__mul__', '__imul__', '__rmul__',
    -                 '__div__', '__idiv__', '__rdiv__',
    -                 '__add__', '__iadd__', '__radd__',
    -                 '__sub__', '__isub__', '__rsub__',
    -                 '__pow__', '__ipow__', '__rpow__',
    -                 '__mod__', '__imod__', '__rmod__',
    -                 '__and__', '__iand__', '__rand__',
    -                 '__or__', '__ior__', '__ror__',
    -                 '__xor__', '__ixor__', '__rxor__',
    -                 # XXX To be continued
    -                ))
    -
    -def check_messages(*messages):
    -    """decorator to store messages that are handled by a checker method"""
    -
    -    def store_messages(func):
    -        func.checks_msgs = messages
    -        return func
    -    return store_messages
    -
    -class IncompleteFormatString(Exception):
    -    """A format string ended in the middle of a format specifier."""
    -    pass
    -
    -class UnsupportedFormatCharacter(Exception):
    -    """A format character in a format string is not one of the supported
    -    format characters."""
    -    def __init__(self, index):
    -        Exception.__init__(self, index)
    -        self.index = index
    -
    -def parse_format_string(format_string):
    -    """Parses a format string, returning a tuple of (keys, num_args), where keys
    -    is the set of mapping keys in the format string, and num_args is the number
    -    of arguments required by the format string.  Raises
    -    IncompleteFormatString or UnsupportedFormatCharacter if a
    -    parse error occurs."""
    -    keys = set()
    -    num_args = 0
    -    def next_char(i):
    -        i += 1
    -        if i == len(format_string):
    -            raise IncompleteFormatString
    -        return (i, format_string[i])
    -    i = 0
    -    while i < len(format_string):
    -        char = format_string[i]
    -        if char == '%':
    -            i, char = next_char(i)
    -            # Parse the mapping key (optional).
    -            key = None
    -            if char == '(':
    -                depth = 1
    -                i, char = next_char(i)
    -                key_start = i
    -                while depth != 0:
    -                    if char == '(':
    -                        depth += 1
    -                    elif char == ')':
    -                        depth -= 1
    -                    i, char = next_char(i)
    -                key_end = i - 1
    -                key = format_string[key_start:key_end]
    -
    -            # Parse the conversion flags (optional).
    -            while char in '#0- +':
    -                i, char = next_char(i)
    -            # Parse the minimum field width (optional).
    -            if char == '*':
    -                num_args += 1
    -                i, char = next_char(i)
    -            else:
    -                while char in string.digits:
    -                    i, char = next_char(i)
    -            # Parse the precision (optional).
    -            if char == '.':
    -                i, char = next_char(i)
    -                if char == '*':
    -                    num_args += 1
    -                    i, char = next_char(i)
    -                else:
    -                    while char in string.digits:
    -                        i, char = next_char(i)
    -            # Parse the length modifier (optional).
    -            if char in 'hlL':
    -                i, char = next_char(i)
    -            # Parse the conversion type (mandatory).
    -            if PY3K:
    -                flags = 'diouxXeEfFgGcrs%a'
    -            else:
    -                flags = 'diouxXeEfFgGcrs%'
    -            if char not in flags:
    -                raise UnsupportedFormatCharacter(i)
    -            if key:
    -                keys.add(key)
    -            elif char != '%':
    -                num_args += 1
    -        i += 1
    -    return keys, num_args
    -
    -
    -def is_attr_protected(attrname):
    -    """return True if attribute name is protected (start with _ and some other
    -    details), False otherwise.
    -    """
    -    return attrname[0] == '_' and not attrname == '_' and not (
    -        attrname.startswith('__') and attrname.endswith('__'))
    -
    -def node_frame_class(node):
    -    """return klass node for a method node (or a staticmethod or a
    -    classmethod), return null otherwise
    -    """
    -    klass = node.frame()
    -
    -    while klass is not None and not isinstance(klass, astroid.Class):
    -        if klass.parent is None:
    -            klass = None
    -        else:
    -            klass = klass.parent.frame()
    -
    -    return klass
    -
    -def is_super_call(expr):
    -    """return True if expression node is a function call and if function name
    -    is super. Check before that you're in a method.
    -    """
    -    return (isinstance(expr, astroid.CallFunc) and
    -            isinstance(expr.func, astroid.Name) and
    -            expr.func.name == 'super')
    -
    -def is_attr_private(attrname):
    -    """Check that attribute name is private (at least two leading underscores,
    -    at most one trailing underscore)
    -    """
    -    regex = re.compile('^_{2,}.*[^_]+_?$')
    -    return regex.match(attrname)
    -
    -def get_argument_from_call(callfunc_node, position=None, keyword=None):
    -    """Returns the specified argument from a function call.
    -
    -    :param callfunc_node: Node representing a function call to check.
    -    :param int position: position of the argument.
    -    :param str keyword: the keyword of the argument.
    -
    -    :returns: The node representing the argument, None if the argument is not found.
    -    :raises ValueError: if both position and keyword are None.
    -    :raises NoSuchArgumentError: if no argument at the provided position or with
    -    the provided keyword.
    -    """
    -    if position is None and keyword is None:
    -        raise ValueError('Must specify at least one of: position or keyword.')
    -    try:
    -        if position is not None and not isinstance(callfunc_node.args[position], astroid.Keyword):
    -            return callfunc_node.args[position]
    -    except IndexError as error:
    -        raise NoSuchArgumentError(error)
    -    if keyword:
    -        for arg in callfunc_node.args:
    -            if isinstance(arg, astroid.Keyword) and arg.arg == keyword:
    -                return arg.value
    -    raise NoSuchArgumentError
    -
    -def inherit_from_std_ex(node):
    -    """
    -    Return true if the given class node is subclass of
    -    exceptions.Exception.
    -    """
    -    if node.name in ('Exception', 'BaseException') \
    -            and node.root().name == EXCEPTIONS_MODULE:
    -        return True
    -    return any(inherit_from_std_ex(parent)
    -               for parent in node.ancestors(recurs=False))
    -
    -def is_import_error(handler):
    -    """
    -    Check if the given exception handler catches
    -    ImportError.
    -
    -    :param handler: A node, representing an ExceptHandler node.
    -    :returns: True if the handler catches ImportError, False otherwise.
    -    """
    -    names = None
    -    if isinstance(handler.type, astroid.Tuple):
    -        names = [name for name in handler.type.elts
    -                 if isinstance(name, astroid.Name)]
    -    elif isinstance(handler.type, astroid.Name):
    -        names = [handler.type]
    -    else:
    -        # Don't try to infer that.
    -        return
    -    for name in names:
    -        try:
    -            for infered in name.infer():
    -                if (isinstance(infered, astroid.Class) and
    -                        inherit_from_std_ex(infered) and
    -                        infered.name == 'ImportError'):
    -                    return True
    -        except astroid.InferenceError:
    -            continue
    -
    -def has_known_bases(klass):
    -    """Returns true if all base classes of a class could be inferred."""
    -    try:
    -        return klass._all_bases_known
    -    except AttributeError:
    -        pass
    -    for base in klass.bases:
    -        result = safe_infer(base)
    -        # TODO: check for A->B->A->B pattern in class structure too?
    -        if (not isinstance(result, astroid.Class) or
    -                result is klass or
    -                not has_known_bases(result)):
    -            klass._all_bases_known = False
    -            return False
    -    klass._all_bases_known = True
    -    return True
    -
    -def decorated_with_property(node):
    -    """ Detect if the given function node is decorated with a property. """
    -    if not node.decorators:
    -        return False
    -    for decorator in node.decorators.nodes:
    -        if not isinstance(decorator, astroid.Name):
    -            continue
    -        try:
    -            for infered in decorator.infer():
    -                if isinstance(infered, astroid.Class):
    -                    if (infered.root().name == BUILTINS_NAME and
    -                            infered.name == 'property'):
    -                        return True
    -                    for ancestor in infered.ancestors():
    -                        if (ancestor.name == 'property' and
    -                                ancestor.root().name == BUILTINS_NAME):
    -                            return True
    -        except astroid.InferenceError:
    -            pass
    -
    -
    -def decorated_with_abc(func):
    -    """Determine if the `func` node is decorated with `abc` decorators."""
    -    if func.decorators:
    -        for node in func.decorators.nodes:
    -            try:
    -                infered = next(node.infer())
    -            except astroid.InferenceError:
    -                continue
    -            if infered and infered.qname() in ABC_METHODS:
    -                return True
    -
    -
    -def unimplemented_abstract_methods(node, is_abstract_cb=decorated_with_abc):
    -    """
    -    Get the unimplemented abstract methods for the given *node*.
    -
    -    A method can be considered abstract if the callback *is_abstract_cb*
    -    returns a ``True`` value. The check defaults to verifying that
    -    a method is decorated with abstract methods.
    -    The function will work only for new-style classes. For old-style
    -    classes, it will simply return an empty dictionary.
    -    For the rest of them, it will return a dictionary of abstract method
    -    names and their inferred objects.
    -    """
    -    visited = {}
    -    try:
    -        mro = reversed(node.mro())
    -    except NotImplementedError:
    -        # Old style class, it will not have a mro.
    -        return {}
    -    except astroid.ResolveError:
    -        # Probably inconsistent hierarchy, don'try
    -        # to figure this out here.
    -        return {}
    -    for ancestor in mro:
    -        for obj in ancestor.values():
    -            infered = obj
    -            if isinstance(obj, astroid.AssName):
    -                infered = safe_infer(obj)
    -                if not infered:
    -                    continue
    -                if not isinstance(infered, astroid.Function):
    -                    if obj.name in visited:
    -                        del visited[obj.name]
    -            if isinstance(infered, astroid.Function):
    -                # It's critical to use the original name,
    -                # since after inferring, an object can be something
    -                # else than expected, as in the case of the
    -                # following assignment.
    -                #
    -                # class A:
    -                #     def keys(self): pass
    -                #     __iter__ = keys
    -                abstract = is_abstract_cb(infered)
    -                if abstract:
    -                    visited[obj.name] = infered
    -                elif not abstract and obj.name in visited:
    -                    del visited[obj.name]
    -    return visited
    diff --git a/pymode/libs/pylint/checkers/variables.py b/pymode/libs/pylint/checkers/variables.py
    deleted file mode 100644
    index 8f6f9574..00000000
    --- a/pymode/libs/pylint/checkers/variables.py
    +++ /dev/null
    @@ -1,1069 +0,0 @@
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""variables checkers for Python code
    -"""
    -import os
    -import sys
    -import re
    -from copy import copy
    -
    -import astroid
    -from astroid import are_exclusive, builtin_lookup
    -from astroid import modutils
    -
    -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
    -from pylint.utils import get_global_option
    -from pylint.checkers import BaseChecker
    -from pylint.checkers.utils import (
    -    PYMETHODS, is_ancestor_name, is_builtin,
    -    is_defined_before, is_error, is_func_default, is_func_decorator,
    -    assign_parent, check_messages, is_inside_except, clobber_in_except,
    -    get_all_elements, has_known_bases)
    -import six
    -
    -SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
    -
    -PY3K = sys.version_info >= (3, 0)
    -
    -def in_for_else_branch(parent, stmt):
    -    """Returns True if stmt in inside the else branch for a parent For stmt."""
    -    return (isinstance(parent, astroid.For) and
    -            any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse))
    -
    -def overridden_method(klass, name):
    -    """get overridden method if any"""
    -    try:
    -        parent = next(klass.local_attr_ancestors(name))
    -    except (StopIteration, KeyError):
    -        return None
    -    try:
    -        meth_node = parent[name]
    -    except KeyError:
    -        # We have found an ancestor defining  but it's not in the local
    -        # dictionary. This may happen with astroid built from living objects.
    -        return None
    -    if isinstance(meth_node, astroid.Function):
    -        return meth_node
    -    return None
    -
    -def _get_unpacking_extra_info(node, infered):
    -    """return extra information to add to the message for unpacking-non-sequence
    -    and unbalanced-tuple-unpacking errors
    -    """
    -    more = ''
    -    infered_module = infered.root().name
    -    if node.root().name == infered_module:
    -        if node.lineno == infered.lineno:
    -            more = ' %s' % infered.as_string()
    -        elif infered.lineno:
    -            more = ' defined at line %s' % infered.lineno
    -    elif infered.lineno:
    -        more = ' defined at line %s of %s' % (infered.lineno, infered_module)
    -    return more
    -
    -def _detect_global_scope(node, frame, defframe):
    -    """ Detect that the given frames shares a global
    -    scope.
    -
    -    Two frames shares a global scope when neither
    -    of them are hidden under a function scope, as well
    -    as any of parent scope of them, until the root scope.
    -    In this case, depending from something defined later on
    -    will not work, because it is still undefined.
    -
    -    Example:
    -        class A:
    -            # B has the same global scope as `C`, leading to a NameError.
    -            class B(C): ...
    -        class C: ...
    -
    -    """
    -    def_scope = scope = None
    -    if frame and frame.parent:
    -        scope = frame.parent.scope()
    -    if defframe and defframe.parent:
    -        def_scope = defframe.parent.scope()
    -    if isinstance(frame, astroid.Function):
    -        # If the parent of the current node is a
    -        # function, then it can be under its scope
    -        # (defined in, which doesn't concern us) or
    -        # the `->` part of annotations. The same goes
    -        # for annotations of function arguments, they'll have
    -        # their parent the Arguments node.
    -        if not isinstance(node.parent,
    -                          (astroid.Function, astroid.Arguments)):
    -            return False
    -    elif any(not isinstance(f, (astroid.Class, astroid.Module))
    -             for f in (frame, defframe)):
    -        # Not interested in other frames, since they are already
    -        # not in a global scope.
    -        return False
    -
    -    break_scopes = []
    -    for s in (scope, def_scope):
    -        # Look for parent scopes. If there is anything different
    -        # than a module or a class scope, then they frames don't
    -        # share a global scope.
    -        parent_scope = s
    -        while parent_scope:
    -            if not isinstance(parent_scope, (astroid.Class, astroid.Module)):
    -                break_scopes.append(parent_scope)
    -                break
    -            if parent_scope.parent:
    -                parent_scope = parent_scope.parent.scope()
    -            else:
    -                break
    -    if break_scopes and len(set(break_scopes)) != 1:
    -        # Store different scopes than expected.
    -        # If the stored scopes are, in fact, the very same, then it means
    -        # that the two frames (frame and defframe) shares the same scope,
    -        # and we could apply our lineno analysis over them.
    -        # For instance, this works when they are inside a function, the node
    -        # that uses a definition and the definition itself.
    -        return False
    -    # At this point, we are certain that frame and defframe shares a scope
    -    # and the definition of the first depends on the second.
    -    return frame.lineno < defframe.lineno
    -
    -def _fix_dot_imports(not_consumed):
    -    """ Try to fix imports with multiple dots, by returning a dictionary
    -    with the import names expanded. The function unflattens root imports,
    -    like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree'
    -    and 'xml.sax' respectively.
    -    """
    -    # TODO: this should be improved in issue astroid #46
    -    names = {}
    -    for name, stmts in six.iteritems(not_consumed):
    -        if any(isinstance(stmt, astroid.AssName)
    -               and isinstance(stmt.ass_type(), astroid.AugAssign)
    -               for stmt in stmts):
    -            continue
    -        for stmt in stmts:
    -            if not isinstance(stmt, (astroid.From, astroid.Import)):
    -                continue
    -            for imports in stmt.names:
    -                second_name = None
    -                if imports[0] == "*":
    -                    # In case of wildcard imports,
    -                    # pick the name from inside the imported module.
    -                    second_name = name
    -                else:
    -                    if imports[0].find(".") > -1 or name in imports:
    -                        # Most likely something like 'xml.etree',
    -                        # which will appear in the .locals as 'xml'.
    -                        # Only pick the name if it wasn't consumed.
    -                        second_name = imports[0]
    -                if second_name and second_name not in names:
    -                    names[second_name] = stmt
    -    return sorted(names.items(), key=lambda a: a[1].fromlineno)
    -
    -def _find_frame_imports(name, frame):
    -    """
    -    Detect imports in the frame, with the required
    -    *name*. Such imports can be considered assignments.
    -    Returns True if an import for the given name was found.
    -    """
    -    imports = frame.nodes_of_class((astroid.Import, astroid.From))
    -    for import_node in imports:
    -        for import_name, import_alias in import_node.names:
    -            # If the import uses an alias, check only that.
    -            # Otherwise, check only the import name.
    -            if import_alias:
    -                if import_alias == name:
    -                    return True
    -            elif import_name and import_name == name:
    -                return True
    -
    -
    -MSGS = {
    -    'E0601': ('Using variable %r before assignment',
    -              'used-before-assignment',
    -              'Used when a local variable is accessed before it\'s \
    -              assignment.'),
    -    'E0602': ('Undefined variable %r',
    -              'undefined-variable',
    -              'Used when an undefined variable is accessed.'),
    -    'E0603': ('Undefined variable name %r in __all__',
    -              'undefined-all-variable',
    -              'Used when an undefined variable name is referenced in __all__.'),
    -    'E0604': ('Invalid object %r in __all__, must contain only strings',
    -              'invalid-all-object',
    -              'Used when an invalid (non-string) object occurs in __all__.'),
    -    'E0611': ('No name %r in module %r',
    -              'no-name-in-module',
    -              'Used when a name cannot be found in a module.'),
    -
    -    'W0601': ('Global variable %r undefined at the module level',
    -              'global-variable-undefined',
    -              'Used when a variable is defined through the "global" statement \
    -              but the variable is not defined in the module scope.'),
    -    'W0602': ('Using global for %r but no assignment is done',
    -              'global-variable-not-assigned',
    -              'Used when a variable is defined through the "global" statement \
    -              but no assignment to this variable is done.'),
    -    'W0603': ('Using the global statement', # W0121
    -              'global-statement',
    -              'Used when you use the "global" statement to update a global \
    -              variable. Pylint just try to discourage this \
    -              usage. That doesn\'t mean you can not use it !'),
    -    'W0604': ('Using the global statement at the module level', # W0103
    -              'global-at-module-level',
    -              'Used when you use the "global" statement at the module level \
    -              since it has no effect'),
    -    'W0611': ('Unused %s',
    -              'unused-import',
    -              'Used when an imported module or variable is not used.'),
    -    'W0612': ('Unused variable %r',
    -              'unused-variable',
    -              'Used when a variable is defined but not used.'),
    -    'W0613': ('Unused argument %r',
    -              'unused-argument',
    -              'Used when a function or method argument is not used.'),
    -    'W0614': ('Unused import %s from wildcard import',
    -              'unused-wildcard-import',
    -              'Used when an imported module or variable is not used from a \
    -              \'from X import *\' style import.'),
    -
    -    'W0621': ('Redefining name %r from outer scope (line %s)',
    -              'redefined-outer-name',
    -              'Used when a variable\'s name hide a name defined in the outer \
    -              scope.'),
    -    'W0622': ('Redefining built-in %r',
    -              'redefined-builtin',
    -              'Used when a variable or function override a built-in.'),
    -    'W0623': ('Redefining name %r from %s in exception handler',
    -              'redefine-in-handler',
    -              'Used when an exception handler assigns the exception \
    -               to an existing name'),
    -
    -    'W0631': ('Using possibly undefined loop variable %r',
    -              'undefined-loop-variable',
    -              'Used when an loop variable (i.e. defined by a for loop or \
    -              a list comprehension or a generator expression) is used outside \
    -              the loop.'),
    -
    -    'W0632': ('Possible unbalanced tuple unpacking with '
    -              'sequence%s: '
    -              'left side has %d label(s), right side has %d value(s)',
    -              'unbalanced-tuple-unpacking',
    -              'Used when there is an unbalanced tuple unpacking in assignment'),
    -
    -    'W0633': ('Attempting to unpack a non-sequence%s',
    -              'unpacking-non-sequence',
    -              'Used when something which is not '
    -              'a sequence is used in an unpack assignment'),
    -
    -    'W0640': ('Cell variable %s defined in loop',
    -              'cell-var-from-loop',
    -              'A variable used in a closure is defined in a loop. '
    -              'This will result in all closures using the same value for '
    -              'the closed-over variable.'),
    -
    -    }
    -
    -class VariablesChecker(BaseChecker):
    -    """checks for
    -    * unused variables / imports
    -    * undefined variables
    -    * redefinition of variable from builtins or from an outer scope
    -    * use of variable before assignment
    -    * __all__ consistency
    -    """
    -
    -    __implements__ = IAstroidChecker
    -
    -    name = 'variables'
    -    msgs = MSGS
    -    priority = -1
    -    options = (("init-import",
    -                {'default': 0, 'type' : 'yn', 'metavar' : '',
    -                 'help' : 'Tells whether we should check for unused import in \
    -__init__ files.'}),
    -               ("dummy-variables-rgx",
    -                {'default': ('_$|dummy'),
    -                 'type' :'regexp', 'metavar' : '',
    -                 'help' : 'A regular expression matching the name of dummy \
    -variables (i.e. expectedly not used).'}),
    -               ("additional-builtins",
    -                {'default': (), 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of additional names supposed to be defined in \
    -builtins. Remember that you should avoid to define new builtins when possible.'
    -                }),
    -               ("callbacks",
    -                {'default' : ('cb_', '_cb'), 'type' : 'csv',
    -                 'metavar' : '',
    -                 'help' : 'List of strings which can identify a callback '
    -                          'function by name. A callback name must start or '
    -                          'end with one of those strings.'}
    -               )
    -              )
    -    def __init__(self, linter=None):
    -        BaseChecker.__init__(self, linter)
    -        self._to_consume = None
    -        self._checking_mod_attr = None
    -
    -    def visit_module(self, node):
    -        """visit module : update consumption analysis variable
    -        checks globals doesn't overrides builtins
    -        """
    -        self._to_consume = [(copy(node.locals), {}, 'module')]
    -        for name, stmts in six.iteritems(node.locals):
    -            if is_builtin(name) and not is_inside_except(stmts[0]):
    -                # do not print Redefining builtin for additional builtins
    -                self.add_message('redefined-builtin', args=name, node=stmts[0])
    -
    -    @check_messages('unused-import', 'unused-wildcard-import',
    -                    'redefined-builtin', 'undefined-all-variable',
    -                    'invalid-all-object')
    -    def leave_module(self, node):
    -        """leave module: check globals
    -        """
    -        assert len(self._to_consume) == 1
    -        not_consumed = self._to_consume.pop()[0]
    -        # attempt to check for __all__ if defined
    -        if '__all__' in node.locals:
    -            assigned = next(node.igetattr('__all__'))
    -            if assigned is not astroid.YES:
    -                for elt in getattr(assigned, 'elts', ()):
    -                    try:
    -                        elt_name = next(elt.infer())
    -                    except astroid.InferenceError:
    -                        continue
    -
    -                    if not isinstance(elt_name, astroid.Const) \
    -                             or not isinstance(elt_name.value, six.string_types):
    -                        self.add_message('invalid-all-object',
    -                                         args=elt.as_string(), node=elt)
    -                        continue
    -                    elt_name = elt_name.value
    -                    # If elt is in not_consumed, remove it from not_consumed
    -                    if elt_name in not_consumed:
    -                        del not_consumed[elt_name]
    -                        continue
    -                    if elt_name not in node.locals:
    -                        if not node.package:
    -                            self.add_message('undefined-all-variable',
    -                                             args=elt_name,
    -                                             node=elt)
    -                        else:
    -                            basename = os.path.splitext(node.file)[0]
    -                            if os.path.basename(basename) == '__init__':
    -                                name = node.name + "." + elt_name
    -                                try:
    -                                    modutils.file_from_modpath(name.split("."))
    -                                except ImportError:
    -                                    self.add_message('undefined-all-variable',
    -                                                     args=elt_name,
    -                                                     node=elt)
    -                                except SyntaxError:
    -                                    # don't yield an syntax-error warning,
    -                                    # because it will be later yielded
    -                                    # when the file will be checked
    -                                    pass
    -        # don't check unused imports in __init__ files
    -        if not self.config.init_import and node.package:
    -            return
    -
    -        self._check_imports(not_consumed)
    -
    -    def _check_imports(self, not_consumed):
    -        local_names = _fix_dot_imports(not_consumed)
    -        checked = set()
    -        for name, stmt in local_names:
    -            for imports in stmt.names:
    -                real_name = imported_name = imports[0]
    -                if imported_name == "*":
    -                    real_name = name
    -                as_name = imports[1]
    -                if real_name in checked:
    -                    continue
    -                if name not in (real_name, as_name):
    -                    continue
    -                checked.add(real_name)
    -
    -                if (isinstance(stmt, astroid.Import) or
    -                        (isinstance(stmt, astroid.From) and
    -                         not stmt.modname)):
    -                    if (isinstance(stmt, astroid.From) and
    -                            SPECIAL_OBJ.search(imported_name)):
    -                        # Filter special objects (__doc__, __all__) etc.,
    -                        # because they can be imported for exporting.
    -                        continue
    -                    if as_name is None:
    -                        msg = "import %s" % imported_name
    -                    else:
    -                        msg = "%s imported as %s" % (imported_name, as_name)
    -                    self.add_message('unused-import', args=msg, node=stmt)
    -                elif isinstance(stmt, astroid.From) and stmt.modname != '__future__':
    -                    if SPECIAL_OBJ.search(imported_name):
    -                        # Filter special objects (__doc__, __all__) etc.,
    -                        # because they can be imported for exporting.
    -                        continue
    -                    if imported_name == '*':
    -                        self.add_message('unused-wildcard-import',
    -                                         args=name, node=stmt)
    -                    else:
    -                        if as_name is None:
    -                            msg = "%s imported from %s" % (imported_name, stmt.modname)
    -                        else:
    -                            fields = (imported_name, stmt.modname, as_name)
    -                            msg = "%s imported from %s as %s" % fields
    -                        self.add_message('unused-import', args=msg, node=stmt)
    -        del self._to_consume
    -
    -    def visit_class(self, node):
    -        """visit class: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'class'))
    -
    -    def leave_class(self, _):
    -        """leave class: update consumption analysis variable
    -        """
    -        # do not check for not used locals here (no sense)
    -        self._to_consume.pop()
    -
    -    def visit_lambda(self, node):
    -        """visit lambda: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'lambda'))
    -
    -    def leave_lambda(self, _):
    -        """leave lambda: update consumption analysis variable
    -        """
    -        # do not check for not used locals here
    -        self._to_consume.pop()
    -
    -    def visit_genexpr(self, node):
    -        """visit genexpr: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
    -
    -    def leave_genexpr(self, _):
    -        """leave genexpr: update consumption analysis variable
    -        """
    -        # do not check for not used locals here
    -        self._to_consume.pop()
    -
    -    def visit_dictcomp(self, node):
    -        """visit dictcomp: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
    -
    -    def leave_dictcomp(self, _):
    -        """leave dictcomp: update consumption analysis variable
    -        """
    -        # do not check for not used locals here
    -        self._to_consume.pop()
    -
    -    def visit_setcomp(self, node):
    -        """visit setcomp: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
    -
    -    def leave_setcomp(self, _):
    -        """leave setcomp: update consumption analysis variable
    -        """
    -        # do not check for not used locals here
    -        self._to_consume.pop()
    -
    -    def visit_function(self, node):
    -        """visit function: update consumption analysis variable and check locals
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'function'))
    -        if not (self.linter.is_message_enabled('redefined-outer-name') or
    -                self.linter.is_message_enabled('redefined-builtin')):
    -            return
    -        globs = node.root().globals
    -        for name, stmt in node.items():
    -            if is_inside_except(stmt):
    -                continue
    -            if name in globs and not isinstance(stmt, astroid.Global):
    -                line = globs[name][0].fromlineno
    -                dummy_rgx = self.config.dummy_variables_rgx
    -                if not dummy_rgx.match(name):
    -                    self.add_message('redefined-outer-name', args=(name, line), node=stmt)
    -            elif is_builtin(name):
    -                # do not print Redefining builtin for additional builtins
    -                self.add_message('redefined-builtin', args=name, node=stmt)
    -
    -    def leave_function(self, node):
    -        """leave function: check function's locals are consumed"""
    -        not_consumed = self._to_consume.pop()[0]
    -        if not (self.linter.is_message_enabled('unused-variable') or
    -                self.linter.is_message_enabled('unused-argument')):
    -            return
    -        # don't check arguments of function which are only raising an exception
    -        if is_error(node):
    -            return
    -        # don't check arguments of abstract methods or within an interface
    -        is_method = node.is_method()
    -        klass = node.parent.frame()
    -        if is_method and (klass.type == 'interface' or node.is_abstract()):
    -            return
    -        if is_method and isinstance(klass, astroid.Class):
    -            confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE
    -        else:
    -            confidence = HIGH
    -        authorized_rgx = self.config.dummy_variables_rgx
    -        called_overridden = False
    -        argnames = node.argnames()
    -        global_names = set()
    -        nonlocal_names = set()
    -        for global_stmt in node.nodes_of_class(astroid.Global):
    -            global_names.update(set(global_stmt.names))
    -        for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal):
    -            nonlocal_names.update(set(nonlocal_stmt.names))
    -
    -        for name, stmts in six.iteritems(not_consumed):
    -            # ignore some special names specified by user configuration
    -            if authorized_rgx.match(name):
    -                continue
    -            # ignore names imported by the global statement
    -            # FIXME: should only ignore them if it's assigned latter
    -            stmt = stmts[0]
    -            if isinstance(stmt, astroid.Global):
    -                continue
    -            if isinstance(stmt, (astroid.Import, astroid.From)):
    -                # Detect imports, assigned to global statements.
    -                if global_names:
    -                    skip = False
    -                    for import_name, import_alias in stmt.names:
    -                        # If the import uses an alias, check only that.
    -                        # Otherwise, check only the import name.
    -                        if import_alias:
    -                            if import_alias in global_names:
    -                                skip = True
    -                                break
    -                        elif import_name in global_names:
    -                            skip = True
    -                            break
    -                    if skip:
    -                        continue
    -
    -            # care about functions with unknown argument (builtins)
    -            if name in argnames:
    -                if is_method:
    -                    # don't warn for the first argument of a (non static) method
    -                    if node.type != 'staticmethod' and name == argnames[0]:
    -                        continue
    -                    # don't warn for argument of an overridden method
    -                    if not called_overridden:
    -                        overridden = overridden_method(klass, node.name)
    -                        called_overridden = True
    -                    if overridden is not None and name in overridden.argnames():
    -                        continue
    -                    if node.name in PYMETHODS and node.name not in ('__init__', '__new__'):
    -                        continue
    -                # don't check callback arguments
    -                if any(node.name.startswith(cb) or node.name.endswith(cb)
    -                       for cb in self.config.callbacks):
    -                    continue
    -                self.add_message('unused-argument', args=name, node=stmt,
    -                                 confidence=confidence)
    -            else:
    -                if stmt.parent and isinstance(stmt.parent, astroid.Assign):
    -                    if name in nonlocal_names:
    -                        continue
    -                self.add_message('unused-variable', args=name, node=stmt)
    -
    -    @check_messages('global-variable-undefined', 'global-variable-not-assigned', 'global-statement',
    -                    'global-at-module-level', 'redefined-builtin')
    -    def visit_global(self, node):
    -        """check names imported exists in the global scope"""
    -        frame = node.frame()
    -        if isinstance(frame, astroid.Module):
    -            self.add_message('global-at-module-level', node=node)
    -            return
    -        module = frame.root()
    -        default_message = True
    -        for name in node.names:
    -            try:
    -                assign_nodes = module.getattr(name)
    -            except astroid.NotFoundError:
    -                # unassigned global, skip
    -                assign_nodes = []
    -            for anode in assign_nodes:
    -                if anode.parent is None:
    -                    # node returned for builtin attribute such as __file__,
    -                    # __doc__, etc...
    -                    continue
    -                if anode.frame() is frame:
    -                    # same scope level assignment
    -                    break
    -            else:
    -                if not _find_frame_imports(name, frame):
    -                    self.add_message('global-variable-not-assigned',
    -                                     args=name, node=node)
    -                default_message = False
    -            if not assign_nodes:
    -                continue
    -            for anode in assign_nodes:
    -                if anode.parent is None:
    -                    self.add_message('redefined-builtin', args=name, node=node)
    -                    break
    -                if anode.frame() is module:
    -                    # module level assignment
    -                    break
    -            else:
    -                # global undefined at the module scope
    -                self.add_message('global-variable-undefined', args=name, node=node)
    -                default_message = False
    -        if default_message:
    -            self.add_message('global-statement', node=node)
    -
    -    def _check_late_binding_closure(self, node, assignment_node):
    -        def _is_direct_lambda_call():
    -            return (isinstance(node_scope.parent, astroid.CallFunc)
    -                    and node_scope.parent.func is node_scope)
    -
    -        node_scope = node.scope()
    -        if not isinstance(node_scope, (astroid.Lambda, astroid.Function)):
    -            return
    -        if isinstance(node.parent, astroid.Arguments):
    -            return
    -
    -        if isinstance(assignment_node, astroid.Comprehension):
    -            if assignment_node.parent.parent_of(node.scope()):
    -                self.add_message('cell-var-from-loop', node=node, args=node.name)
    -        else:
    -            assign_scope = assignment_node.scope()
    -            maybe_for = assignment_node
    -            while not isinstance(maybe_for, astroid.For):
    -                if maybe_for is assign_scope:
    -                    break
    -                maybe_for = maybe_for.parent
    -            else:
    -                if (maybe_for.parent_of(node_scope)
    -                        and not _is_direct_lambda_call()
    -                        and not isinstance(node_scope.statement(), astroid.Return)):
    -                    self.add_message('cell-var-from-loop', node=node, args=node.name)
    -
    -    def _loopvar_name(self, node, name):
    -        # filter variables according to node's scope
    -        # XXX used to filter parents but don't remember why, and removing this
    -        # fixes a W0631 false positive reported by Paul Hachmann on 2008/12 on
    -        # python-projects (added to func_use_for_or_listcomp_var test)
    -        #astmts = [stmt for stmt in node.lookup(name)[1]
    -        #          if hasattr(stmt, 'ass_type')] and
    -        #          not stmt.statement().parent_of(node)]
    -        if not self.linter.is_message_enabled('undefined-loop-variable'):
    -            return
    -        astmts = [stmt for stmt in node.lookup(name)[1]
    -                  if hasattr(stmt, 'ass_type')]
    -        # filter variables according their respective scope test is_statement
    -        # and parent to avoid #74747. This is not a total fix, which would
    -        # introduce a mechanism similar to special attribute lookup in
    -        # modules. Also, in order to get correct inference in this case, the
    -        # scope lookup rules would need to be changed to return the initial
    -        # assignment (which does not exist in code per se) as well as any later
    -        # modifications.
    -        if not astmts or (astmts[0].is_statement or astmts[0].parent) \
    -             and astmts[0].statement().parent_of(node):
    -            _astmts = []
    -        else:
    -            _astmts = astmts[:1]
    -        for i, stmt in enumerate(astmts[1:]):
    -            if (astmts[i].statement().parent_of(stmt)
    -                    and not in_for_else_branch(astmts[i].statement(), stmt)):
    -                continue
    -            _astmts.append(stmt)
    -        astmts = _astmts
    -        if len(astmts) == 1:
    -            ass = astmts[0].ass_type()
    -            if isinstance(ass, (astroid.For, astroid.Comprehension, astroid.GenExpr)) \
    -                   and not ass.statement() is node.statement():
    -                self.add_message('undefined-loop-variable', args=name, node=node)
    -
    -    @check_messages('redefine-in-handler')
    -    def visit_excepthandler(self, node):
    -        for name in get_all_elements(node.name):
    -            clobbering, args = clobber_in_except(name)
    -            if clobbering:
    -                self.add_message('redefine-in-handler', args=args, node=name)
    -
    -    def visit_assname(self, node):
    -        if isinstance(node.ass_type(), astroid.AugAssign):
    -            self.visit_name(node)
    -
    -    def visit_delname(self, node):
    -        self.visit_name(node)
    -
    -    @check_messages(*(MSGS.keys()))
    -    def visit_name(self, node):
    -        """check that a name is defined if the current scope and doesn't
    -        redefine a built-in
    -        """
    -        stmt = node.statement()
    -        if stmt.fromlineno is None:
    -            # name node from a astroid built from live code, skip
    -            assert not stmt.root().file.endswith('.py')
    -            return
    -        name = node.name
    -        frame = stmt.scope()
    -        # if the name node is used as a function default argument's value or as
    -        # a decorator, then start from the parent frame of the function instead
    -        # of the function frame - and thus open an inner class scope
    -        if (is_func_default(node) or is_func_decorator(node)
    -                or is_ancestor_name(frame, node)):
    -            start_index = len(self._to_consume) - 2
    -        else:
    -            start_index = len(self._to_consume) - 1
    -        # iterates through parent scopes, from the inner to the outer
    -        base_scope_type = self._to_consume[start_index][-1]
    -        for i in range(start_index, -1, -1):
    -            to_consume, consumed, scope_type = self._to_consume[i]
    -            # if the current scope is a class scope but it's not the inner
    -            # scope, ignore it. This prevents to access this scope instead of
    -            # the globals one in function members when there are some common
    -            # names. The only exception is when the starting scope is a
    -            # comprehension and its direct outer scope is a class
    -            if scope_type == 'class' and i != start_index and not (
    -                    base_scope_type == 'comprehension' and i == start_index-1):
    -                # Detect if we are in a local class scope, as an assignment.
    -                # For example, the following is fair game.
    -                #
    -                # class A:
    -                #    b = 1
    -                #    c = lambda b=b: b * b
    -                #
    -                # class B:
    -                #    tp = 1
    -                #    def func(self, arg: tp):
    -                #        ...
    -
    -                in_annotation = (
    -                    PY3K and isinstance(frame, astroid.Function)
    -                    and node.statement() is frame and
    -                    (node in frame.args.annotations
    -                     or node is frame.args.varargannotation
    -                     or node is frame.args.kwargannotation))
    -                if in_annotation:
    -                    frame_locals = frame.parent.scope().locals
    -                else:
    -                    frame_locals = frame.locals
    -                if not ((isinstance(frame, astroid.Class) or in_annotation)
    -                        and name in frame_locals):
    -                    continue
    -            # the name has already been consumed, only check it's not a loop
    -            # variable used outside the loop
    -            if name in consumed:
    -                defnode = assign_parent(consumed[name][0])
    -                self._check_late_binding_closure(node, defnode)
    -                self._loopvar_name(node, name)
    -                break
    -            # mark the name as consumed if it's defined in this scope
    -            # (i.e. no KeyError is raised by "to_consume[name]")
    -            try:
    -                consumed[name] = to_consume[name]
    -            except KeyError:
    -                continue
    -            # checks for use before assignment
    -            defnode = assign_parent(to_consume[name][0])
    -            if defnode is not None:
    -                self._check_late_binding_closure(node, defnode)
    -                defstmt = defnode.statement()
    -                defframe = defstmt.frame()
    -                maybee0601 = True
    -                if not frame is defframe:
    -                    maybee0601 = _detect_global_scope(node, frame, defframe)
    -                elif defframe.parent is None:
    -                    # we are at the module level, check the name is not
    -                    # defined in builtins
    -                    if name in defframe.scope_attrs or builtin_lookup(name)[1]:
    -                        maybee0601 = False
    -                else:
    -                    # we are in a local scope, check the name is not
    -                    # defined in global or builtin scope
    -                    if defframe.root().lookup(name)[1]:
    -                        maybee0601 = False
    -                    else:
    -                        # check if we have a nonlocal
    -                        if name in defframe.locals:
    -                            maybee0601 = not any(isinstance(child, astroid.Nonlocal)
    -                                                 and name in child.names
    -                                                 for child in defframe.get_children())
    -
    -                # Handle a couple of class scoping issues.
    -                annotation_return = False
    -                # The class reuses itself in the class scope.
    -                recursive_klass = (frame is defframe and
    -                                   defframe.parent_of(node) and
    -                                   isinstance(defframe, astroid.Class) and
    -                                   node.name == defframe.name)
    -                if (self._to_consume[-1][-1] == 'lambda' and
    -                        isinstance(frame, astroid.Class)
    -                        and name in frame.locals):
    -                    maybee0601 = True
    -                elif (isinstance(defframe, astroid.Class) and
    -                      isinstance(frame, astroid.Function)):
    -                    # Special rule for function return annotations,
    -                    # which uses the same name as the class where
    -                    # the function lives.
    -                    if (PY3K and node is frame.returns and
    -                            defframe.parent_of(frame.returns)):
    -                        maybee0601 = annotation_return = True
    -
    -                    if (maybee0601 and defframe.name in defframe.locals and
    -                            defframe.locals[name][0].lineno < frame.lineno):
    -                        # Detect class assignments with the same
    -                        # name as the class. In this case, no warning
    -                        # should be raised.
    -                        maybee0601 = False
    -                elif recursive_klass:
    -                    maybee0601 = True
    -                else:
    -                    maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno
    -
    -                if (maybee0601
    -                        and not is_defined_before(node)
    -                        and not are_exclusive(stmt, defstmt, ('NameError',
    -                                                              'Exception',
    -                                                              'BaseException'))):
    -                    if recursive_klass or (defstmt is stmt and
    -                                           isinstance(node, (astroid.DelName,
    -                                                             astroid.AssName))):
    -                        self.add_message('undefined-variable', args=name, node=node)
    -                    elif annotation_return:
    -                        self.add_message('undefined-variable', args=name, node=node)
    -                    elif self._to_consume[-1][-1] != 'lambda':
    -                        # E0601 may *not* occurs in lambda scope.
    -                        self.add_message('used-before-assignment', args=name, node=node)
    -                    elif self._to_consume[-1][-1] == 'lambda':
    -                        # E0601 can occur in class-level scope in lambdas, as in
    -                        # the following example:
    -                        #   class A:
    -                        #      x = lambda attr: f + attr
    -                        #      f = 42
    -                        if isinstance(frame, astroid.Class) and name in frame.locals:
    -                            if isinstance(node.parent, astroid.Arguments):
    -                                # Doing the following is fine:
    -                                #   class A:
    -                                #      x = 42
    -                                #      y = lambda attr=x: attr
    -                                if stmt.fromlineno <= defstmt.fromlineno:
    -                                    self.add_message('used-before-assignment',
    -                                                     args=name, node=node)
    -                            else:
    -                                self.add_message('undefined-variable',
    -                                                 args=name, node=node)
    -
    -            if isinstance(node, astroid.AssName): # Aug AssName
    -                del consumed[name]
    -            else:
    -                del to_consume[name]
    -            # check it's not a loop variable used outside the loop
    -            self._loopvar_name(node, name)
    -            break
    -        else:
    -            # we have not found the name, if it isn't a builtin, that's an
    -            # undefined name !
    -            if not (name in astroid.Module.scope_attrs or is_builtin(name)
    -                    or name in self.config.additional_builtins):
    -                self.add_message('undefined-variable', args=name, node=node)
    -
    -    @check_messages('no-name-in-module')
    -    def visit_import(self, node):
    -        """check modules attribute accesses"""
    -        for name, _ in node.names:
    -            parts = name.split('.')
    -            try:
    -                module = next(node.infer_name_module(parts[0]))
    -            except astroid.ResolveError:
    -                continue
    -            self._check_module_attrs(node, module, parts[1:])
    -
    -    @check_messages('no-name-in-module')
    -    def visit_from(self, node):
    -        """check modules attribute accesses"""
    -        name_parts = node.modname.split('.')
    -        level = getattr(node, 'level', None)
    -        try:
    -            module = node.root().import_module(name_parts[0], level=level)
    -        except Exception: # pylint: disable=broad-except
    -            return
    -        module = self._check_module_attrs(node, module, name_parts[1:])
    -        if not module:
    -            return
    -        for name, _ in node.names:
    -            if name == '*':
    -                continue
    -            self._check_module_attrs(node, module, name.split('.'))
    -
    -    @check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence')
    -    def visit_assign(self, node):
    -        """Check unbalanced tuple unpacking for assignments
    -        and unpacking non-sequences.
    -        """
    -        if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)):
    -            return
    -
    -        targets = node.targets[0].itered()
    -        try:
    -            for infered in node.value.infer():
    -                self._check_unpacking(infered, node, targets)
    -        except astroid.InferenceError:
    -            return
    -
    -    def _check_unpacking(self, infered, node, targets):
    -        """ Check for unbalanced tuple unpacking
    -        and unpacking non sequences.
    -        """
    -        if infered is astroid.YES:
    -            return
    -        if (isinstance(infered.parent, astroid.Arguments) and
    -                isinstance(node.value, astroid.Name) and
    -                node.value.name == infered.parent.vararg):
    -            # Variable-length argument, we can't determine the length.
    -            return
    -        if isinstance(infered, (astroid.Tuple, astroid.List)):
    -            # attempt to check unpacking is properly balanced
    -            values = infered.itered()
    -            if len(targets) != len(values):
    -                # Check if we have starred nodes.
    -                if any(isinstance(target, astroid.Starred)
    -                       for target in targets):
    -                    return
    -                self.add_message('unbalanced-tuple-unpacking', node=node,
    -                                 args=(_get_unpacking_extra_info(node, infered),
    -                                       len(targets),
    -                                       len(values)))
    -        # attempt to check unpacking may be possible (ie RHS is iterable)
    -        elif isinstance(infered, astroid.Instance):
    -            for meth in ('__iter__', '__getitem__'):
    -                try:
    -                    infered.getattr(meth)
    -                    break
    -                except astroid.NotFoundError:
    -                    continue
    -            else:
    -                self.add_message('unpacking-non-sequence', node=node,
    -                                 args=(_get_unpacking_extra_info(node, infered),))
    -        else:
    -            self.add_message('unpacking-non-sequence', node=node,
    -                             args=(_get_unpacking_extra_info(node, infered),))
    -
    -
    -    def _check_module_attrs(self, node, module, module_names):
    -        """check that module_names (list of string) are accessible through the
    -        given module
    -        if the latest access name corresponds to a module, return it
    -        """
    -        assert isinstance(module, astroid.Module), module
    -        ignored_modules = get_global_option(self, 'ignored-modules',
    -                                            default=[])
    -        while module_names:
    -            name = module_names.pop(0)
    -            if name == '__dict__':
    -                module = None
    -                break
    -            try:
    -                module = next(module.getattr(name)[0].infer())
    -                if module is astroid.YES:
    -                    return None
    -            except astroid.NotFoundError:
    -                if module.name in ignored_modules:
    -                    return None
    -                self.add_message('no-name-in-module',
    -                                 args=(name, module.name), node=node)
    -                return None
    -            except astroid.InferenceError:
    -                return None
    -        if module_names:
    -            # FIXME: other message if name is not the latest part of
    -            # module_names ?
    -            modname = module and module.name or '__dict__'
    -            self.add_message('no-name-in-module', node=node,
    -                             args=('.'.join(module_names), modname))
    -            return None
    -        if isinstance(module, astroid.Module):
    -            return module
    -        return None
    -
    -
    -class VariablesChecker3k(VariablesChecker):
    -    '''Modified variables checker for 3k'''
    -    # listcomp have now also their scope
    -
    -    def visit_listcomp(self, node):
    -        """visit dictcomp: update consumption analysis variable
    -        """
    -        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
    -
    -    def leave_listcomp(self, _):
    -        """leave dictcomp: update consumption analysis variable
    -        """
    -        # do not check for not used locals here
    -        self._to_consume.pop()
    -
    -    def leave_module(self, node):
    -        """ Update consumption analysis variable
    -        for metaclasses.
    -        """
    -        module_locals = self._to_consume[0][0]
    -        module_imports = self._to_consume[0][1]
    -        consumed = {}
    -
    -        for klass in node.nodes_of_class(astroid.Class):
    -            found = metaclass = name = None
    -            if not klass._metaclass:
    -                # Skip if this class doesn't use
    -                # explictly a metaclass, but inherits it from ancestors
    -                continue
    -
    -            metaclass = klass.metaclass()
    -
    -            # Look the name in the already found locals.
    -            # If it's not found there, look in the module locals
    -            # and in the imported modules.
    -            if isinstance(klass._metaclass, astroid.Name):
    -                name = klass._metaclass.name
    -            elif metaclass:
    -                # if it uses a `metaclass=module.Class`
    -                name = metaclass.root().name
    -
    -            if name:
    -                found = consumed.setdefault(
    -                    name, module_locals.get(name, module_imports.get(name)))
    -
    -            if found is None and not metaclass:
    -                name = None
    -                if isinstance(klass._metaclass, astroid.Name):
    -                    name = klass._metaclass.name
    -                elif isinstance(klass._metaclass, astroid.Getattr):
    -                    name = klass._metaclass.as_string()
    -
    -                if name is not None:
    -                    if not (name in astroid.Module.scope_attrs or
    -                            is_builtin(name) or
    -                            name in self.config.additional_builtins or
    -                            name in node.locals):
    -                        self.add_message('undefined-variable',
    -                                         node=klass,
    -                                         args=(name, ))
    -        # Pop the consumed items, in order to
    -        # avoid having unused-import false positives
    -        for name in consumed:
    -            module_locals.pop(name, None)
    -        super(VariablesChecker3k, self).leave_module(node)
    -
    -if sys.version_info >= (3, 0):
    -    VariablesChecker = VariablesChecker3k
    -
    -
    -def register(linter):
    -    """required method to auto register this checker"""
    -    linter.register_checker(VariablesChecker(linter))
    diff --git a/pymode/libs/pylint/config.py b/pymode/libs/pylint/config.py
    deleted file mode 100644
    index ebfe5789..00000000
    --- a/pymode/libs/pylint/config.py
    +++ /dev/null
    @@ -1,157 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""utilities for Pylint configuration :
    -
    -* pylintrc
    -* pylint.d (PYLINTHOME)
    -"""
    -from __future__ import with_statement
    -from __future__ import print_function
    -
    -import pickle
    -import os
    -import sys
    -from os.path import exists, isfile, join, expanduser, abspath, dirname
    -
    -# pylint home is used to save old runs results ################################
    -
    -USER_HOME = expanduser('~')
    -if 'PYLINTHOME' in os.environ:
    -    PYLINT_HOME = os.environ['PYLINTHOME']
    -    if USER_HOME == '~':
    -        USER_HOME = dirname(PYLINT_HOME)
    -elif USER_HOME == '~':
    -    PYLINT_HOME = ".pylint.d"
    -else:
    -    PYLINT_HOME = join(USER_HOME, '.pylint.d')
    -
    -def get_pdata_path(base_name, recurs):
    -    """return the path of the file which should contain old search data for the
    -    given base_name with the given options values
    -    """
    -    base_name = base_name.replace(os.sep, '_')
    -    return join(PYLINT_HOME, "%s%s%s"%(base_name, recurs, '.stats'))
    -
    -def load_results(base):
    -    """try to unpickle and return data from file if it exists and is not
    -    corrupted
    -
    -    return an empty dictionary if it doesn't exists
    -    """
    -    data_file = get_pdata_path(base, 1)
    -    try:
    -        with open(data_file, _PICK_LOAD) as stream:
    -            return pickle.load(stream)
    -    except Exception: # pylint: disable=broad-except
    -        return {}
    -
    -if sys.version_info < (3, 0):
    -    _PICK_DUMP, _PICK_LOAD = 'w', 'r'
    -else:
    -    _PICK_DUMP, _PICK_LOAD = 'wb', 'rb'
    -
    -def save_results(results, base):
    -    """pickle results"""
    -    if not exists(PYLINT_HOME):
    -        try:
    -            os.mkdir(PYLINT_HOME)
    -        except OSError:
    -            print('Unable to create directory %s' % PYLINT_HOME, file=sys.stderr)
    -    data_file = get_pdata_path(base, 1)
    -    try:
    -        with open(data_file, _PICK_DUMP) as stream:
    -            pickle.dump(results, stream)
    -    except (IOError, OSError) as ex:
    -        print('Unable to create file %s: %s' % (data_file, ex), file=sys.stderr)
    -
    -# location of the configuration file ##########################################
    -
    -
    -def find_pylintrc():
    -    """search the pylint rc file and return its path if it find it, else None
    -    """
    -    # is there a pylint rc file in the current directory ?
    -    if exists('pylintrc'):
    -        return abspath('pylintrc')
    -    if isfile('__init__.py'):
    -        curdir = abspath(os.getcwd())
    -        while isfile(join(curdir, '__init__.py')):
    -            curdir = abspath(join(curdir, '..'))
    -            if isfile(join(curdir, 'pylintrc')):
    -                return join(curdir, 'pylintrc')
    -    if 'PYLINTRC' in os.environ and exists(os.environ['PYLINTRC']):
    -        pylintrc = os.environ['PYLINTRC']
    -    else:
    -        user_home = expanduser('~')
    -        if user_home == '~' or user_home == '/root':
    -            pylintrc = ".pylintrc"
    -        else:
    -            pylintrc = join(user_home, '.pylintrc')
    -            if not isfile(pylintrc):
    -                pylintrc = join(user_home, '.config', 'pylintrc')
    -    if not isfile(pylintrc):
    -        if isfile('/etc/pylintrc'):
    -            pylintrc = '/etc/pylintrc'
    -        else:
    -            pylintrc = None
    -    return pylintrc
    -
    -PYLINTRC = find_pylintrc()
    -
    -ENV_HELP = '''
    -The following environment variables are used:                                   
    -    * PYLINTHOME                                                                
    -    Path to the directory where the persistent for the run will be stored. If 
    -not found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working 
    -directory).                                                                     
    -    * PYLINTRC                                                                  
    -    Path to the configuration file. See the documentation for the method used
    -to search for configuration file.
    -''' % globals()
    -
    -# evaluation messages #########################################################
    -
    -def get_note_message(note):
    -    """return a message according to note
    -    note is a float < 10  (10 is the highest note)
    -    """
    -    assert note <= 10, "Note is %.2f. Either you cheated, or pylint's \
    -broken!" % note
    -    if note < 0:
    -        msg = 'You have to do something quick !'
    -    elif note < 1:
    -        msg = 'Hey! This is really dreadful. Or maybe pylint is buggy?'
    -    elif note < 2:
    -        msg = "Come on! You can't be proud of this code"
    -    elif note < 3:
    -        msg = 'Hum... Needs work.'
    -    elif note < 4:
    -        msg = 'Wouldn\'t you be a bit lazy?'
    -    elif note < 5:
    -        msg = 'A little more work would make it acceptable.'
    -    elif note < 6:
    -        msg = 'Just the bare minimum. Give it a bit more polish. '
    -    elif note < 7:
    -        msg = 'This is okay-ish, but I\'m sure you can do better.'
    -    elif note < 8:
    -        msg = 'If you commit now, people should not be making nasty \
    -comments about you on c.l.py'
    -    elif note < 9:
    -        msg = 'That\'s pretty good. Good work mate.'
    -    elif note < 10:
    -        msg = 'So close to being perfect...'
    -    else:
    -        msg = 'Wow ! Now this deserves our uttermost respect.\nPlease send \
    -your code to python-projects@logilab.org'
    -    return msg
    diff --git a/pymode/libs/pylint/epylint.py b/pymode/libs/pylint/epylint.py
    deleted file mode 100644
    index 3d73ecd3..00000000
    --- a/pymode/libs/pylint/epylint.py
    +++ /dev/null
    @@ -1,177 +0,0 @@
    -# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4
    -# -*- vim:fenc=utf-8:ft=python:et:sw=4:ts=4:sts=4
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Emacs and Flymake compatible Pylint.
    -
    -This script is for integration with emacs and is compatible with flymake mode.
    -
    -epylint walks out of python packages before invoking pylint. This avoids
    -reporting import errors that occur when a module within a package uses the
    -absolute import path to get another module within this package.
    -
    -For example:
    -    - Suppose a package is structured as
    -
    -        a/__init__.py
    -        a/b/x.py
    -        a/c/y.py
    -
    -   - Then if y.py imports x as "from a.b import x" the following produces pylint
    -     errors
    -
    -       cd a/c; pylint y.py
    -
    -   - The following obviously doesn't
    -
    -       pylint a/c/y.py
    -
    -   - As this script will be invoked by emacs within the directory of the file
    -     we are checking we need to go out of it to avoid these false positives.
    -
    -
    -You may also use py_run to run pylint with desired options and get back (or not)
    -its output.
    -"""
    -from __future__ import print_function
    -
    -import sys, os
    -import os.path as osp
    -from subprocess import Popen, PIPE
    -
    -def _get_env():
    -    '''Extracts the environment PYTHONPATH and appends the current sys.path to
    -    those.'''
    -    env = dict(os.environ)
    -    env['PYTHONPATH'] = os.pathsep.join(sys.path)
    -    return env
    -
    -def lint(filename, options=None):
    -    """Pylint the given file.
    -
    -    When run from emacs we will be in the directory of a file, and passed its
    -    filename.  If this file is part of a package and is trying to import other
    -    modules from within its own package or another package rooted in a directory
    -    below it, pylint will classify it as a failed import.
    -
    -    To get around this, we traverse down the directory tree to find the root of
    -    the package this module is in.  We then invoke pylint from this directory.
    -
    -    Finally, we must correct the filenames in the output generated by pylint so
    -    Emacs doesn't become confused (it will expect just the original filename,
    -    while pylint may extend it with extra directories if we've traversed down
    -    the tree)
    -    """
    -    # traverse downwards until we are out of a python package
    -    full_path = osp.abspath(filename)
    -    parent_path = osp.dirname(full_path)
    -    child_path = osp.basename(full_path)
    -
    -    while parent_path != "/" and osp.exists(osp.join(parent_path, '__init__.py')):
    -        child_path = osp.join(osp.basename(parent_path), child_path)
    -        parent_path = osp.dirname(parent_path)
    -
    -    # Start pylint
    -    # Ensure we use the python and pylint associated with the running epylint
    -    from pylint import lint as lint_mod
    -    lint_path = lint_mod.__file__
    -    options = options or ['--disable=C,R,I']
    -    cmd = [sys.executable, lint_path] + options + [
    -        '--msg-template', '{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}',
    -        '-r', 'n', child_path]
    -    process = Popen(cmd, stdout=PIPE, cwd=parent_path, env=_get_env(),
    -                    universal_newlines=True)
    -
    -    for line in process.stdout:
    -        # remove pylintrc warning
    -        if line.startswith("No config file found"):
    -            continue
    -
    -        # modify the file name thats output to reverse the path traversal we made
    -        parts = line.split(":")
    -        if parts and parts[0] == child_path:
    -            line = ":".join([filename] + parts[1:])
    -        print(line, end=' ')
    -
    -    process.wait()
    -    return process.returncode
    -
    -
    -def py_run(command_options='', return_std=False, stdout=None, stderr=None,
    -           script='epylint'):
    -    """Run pylint from python
    -
    -    ``command_options`` is a string containing ``pylint`` command line options;
    -    ``return_std`` (boolean) indicates return of created standard output
    -    and error (see below);
    -    ``stdout`` and ``stderr`` are 'file-like' objects in which standard output
    -    could be written.
    -
    -    Calling agent is responsible for stdout/err management (creation, close).
    -    Default standard output and error are those from sys,
    -    or standalone ones (``subprocess.PIPE``) are used
    -    if they are not set and ``return_std``.
    -
    -    If ``return_std`` is set to ``True``, this function returns a 2-uple
    -    containing standard output and error related to created process,
    -    as follows: ``(stdout, stderr)``.
    -
    -    A trivial usage could be as follows:
    -        >>> py_run( '--version')
    -        No config file found, using default configuration
    -        pylint 0.18.1,
    -            ...
    -
    -    To silently run Pylint on a module, and get its standard output and error:
    -        >>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True)
    -    """
    -    # Create command line to call pylint
    -    if os.name == 'nt':
    -        script += '.bat'
    -    command_line = script + ' ' + command_options
    -    # Providing standard output and/or error if not set
    -    if stdout is None:
    -        if return_std:
    -            stdout = PIPE
    -        else:
    -            stdout = sys.stdout
    -    if stderr is None:
    -        if return_std:
    -            stderr = PIPE
    -        else:
    -            stderr = sys.stderr
    -    # Call pylint in a subprocess
    -    p = Popen(command_line, shell=True, stdout=stdout, stderr=stderr,
    -              env=_get_env(), universal_newlines=True)
    -    p.wait()
    -    # Return standard output and error
    -    if return_std:
    -        return (p.stdout, p.stderr)
    -
    -
    -def Run():
    -    if len(sys.argv) == 1:
    -        print("Usage: %s  [options]" % sys.argv[0])
    -        sys.exit(1)
    -    elif not osp.exists(sys.argv[1]):
    -        print("%s does not exist" % sys.argv[1])
    -        sys.exit(1)
    -    else:
    -        sys.exit(lint(sys.argv[1], sys.argv[2:]))
    -
    -
    -if __name__ == '__main__':
    -    Run()
    diff --git a/pymode/libs/pylint/gui.py b/pymode/libs/pylint/gui.py
    deleted file mode 100644
    index 8327e0ec..00000000
    --- a/pymode/libs/pylint/gui.py
    +++ /dev/null
    @@ -1,531 +0,0 @@
    -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Tkinker gui for pylint"""
    -from __future__ import print_function
    -
    -import os
    -import sys
    -import re
    -from threading import Thread
    -
    -import six
    -
    -from six.moves.tkinter import (
    -    Tk, Frame, Listbox, Entry, Label, Button, Scrollbar,
    -    Checkbutton, Radiobutton, IntVar, StringVar, PanedWindow,
    -    TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W,
    -    HORIZONTAL, DISABLED, NORMAL, W,
    -)
    -from six.moves.tkinter_tkfiledialog import (
    -    askopenfilename, askdirectory,
    -)
    -
    -import pylint.lint
    -from pylint.reporters.guireporter import GUIReporter
    -
    -HOME = os.path.expanduser('~/')
    -HISTORY = '.pylint-gui-history'
    -COLORS = {'(I)':'green',
    -          '(C)':'blue', '(R)':'darkblue',
    -          '(W)':'black', '(E)':'darkred',
    -          '(F)':'red'}
    -
    -
    -def convert_to_string(msg):
    -    """make a string representation of a message"""
    -    module_object = msg.module
    -    if msg.obj:
    -        module_object += ".%s" % msg.obj
    -    return "(%s) %s [%d]: %s" % (msg.C, module_object, msg.line, msg.msg)
    -
    -class BasicStream(object):
    -    '''
    -    used in gui reporter instead of writing to stdout, it is written to
    -    this stream and saved in contents
    -    '''
    -    def __init__(self, gui):
    -        """init"""
    -        self.curline = ""
    -        self.gui = gui
    -        self.contents = []
    -        self.outdict = {}
    -        self.currout = None
    -        self.next_title = None
    -
    -    def write(self, text):
    -        """write text to the stream"""
    -        if re.match('^--+$', text.strip()) or re.match('^==+$', text.strip()):
    -            if self.currout:
    -                self.outdict[self.currout].remove(self.next_title)
    -                self.outdict[self.currout].pop()
    -            self.currout = self.next_title
    -            self.outdict[self.currout] = ['']
    -
    -        if text.strip():
    -            self.next_title = text.strip()
    -
    -        if text.startswith(os.linesep):
    -            self.contents.append('')
    -            if self.currout:
    -                self.outdict[self.currout].append('')
    -        self.contents[-1] += text.strip(os.linesep)
    -        if self.currout:
    -            self.outdict[self.currout][-1] += text.strip(os.linesep)
    -        if text.endswith(os.linesep) and text.strip():
    -            self.contents.append('')
    -            if self.currout:
    -                self.outdict[self.currout].append('')
    -
    -    def fix_contents(self):
    -        """finalize what the contents of the dict should look like before output"""
    -        for item in self.outdict:
    -            num_empty = self.outdict[item].count('')
    -            for _ in range(num_empty):
    -                self.outdict[item].remove('')
    -            if self.outdict[item]:
    -                self.outdict[item].pop(0)
    -
    -    def output_contents(self):
    -        """output contents of dict to the gui, and set the rating"""
    -        self.fix_contents()
    -        self.gui.tabs = self.outdict
    -        try:
    -            self.gui.rating.set(self.outdict['Global evaluation'][0])
    -        except KeyError:
    -            self.gui.rating.set('Error')
    -        self.gui.refresh_results_window()
    -
    -        #reset stream variables for next run
    -        self.contents = []
    -        self.outdict = {}
    -        self.currout = None
    -        self.next_title = None
    -
    -
    -class LintGui(object):
    -    """Build and control a window to interact with pylint"""
    -
    -    def __init__(self, root=None):
    -        """init"""
    -        self.root = root or Tk()
    -        self.root.title('Pylint')
    -        #reporter
    -        self.reporter = None
    -        #message queue for output from reporter
    -        self.msg_queue = six.moves.queue.Queue()
    -        self.msgs = []
    -        self.visible_msgs = []
    -        self.filenames = []
    -        self.rating = StringVar()
    -        self.tabs = {}
    -        self.report_stream = BasicStream(self)
    -        #gui objects
    -        self.lb_messages = None
    -        self.showhistory = None
    -        self.results = None
    -        self.btnRun = None
    -        self.information_box = None
    -        self.convention_box = None
    -        self.refactor_box = None
    -        self.warning_box = None
    -        self.error_box = None
    -        self.fatal_box = None
    -        self.txtModule = None
    -        self.status = None
    -        self.msg_type_dict = None
    -        self.init_gui()
    -
    -    def init_gui(self):
    -        """init helper"""
    -
    -        window = PanedWindow(self.root, orient="vertical")
    -        window.pack(side=TOP, fill=BOTH, expand=True)
    -
    -        top_pane = Frame(window)
    -        window.add(top_pane)
    -        mid_pane = Frame(window)
    -        window.add(mid_pane)
    -        bottom_pane = Frame(window)
    -        window.add(bottom_pane)
    -
    -        #setting up frames
    -        top_frame = Frame(top_pane)
    -        mid_frame = Frame(top_pane)
    -        history_frame = Frame(top_pane)
    -        radio_frame = Frame(mid_pane)
    -        rating_frame = Frame(mid_pane)
    -        res_frame = Frame(mid_pane)
    -        check_frame = Frame(bottom_pane)
    -        msg_frame = Frame(bottom_pane)
    -        btn_frame = Frame(bottom_pane)
    -        top_frame.pack(side=TOP, fill=X)
    -        mid_frame.pack(side=TOP, fill=X)
    -        history_frame.pack(side=TOP, fill=BOTH, expand=True)
    -        radio_frame.pack(side=TOP, fill=X)
    -        rating_frame.pack(side=TOP, fill=X)
    -        res_frame.pack(side=TOP, fill=BOTH, expand=True)
    -        check_frame.pack(side=TOP, fill=X)
    -        msg_frame.pack(side=TOP, fill=BOTH, expand=True)
    -        btn_frame.pack(side=TOP, fill=X)
    -
    -        # Binding F5 application-wide to run lint
    -        self.root.bind('', self.run_lint)
    -
    -        #Message ListBox
    -        rightscrollbar = Scrollbar(msg_frame)
    -        rightscrollbar.pack(side=RIGHT, fill=Y)
    -        bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL)
    -        bottomscrollbar.pack(side=BOTTOM, fill=X)
    -        self.lb_messages = Listbox(
    -            msg_frame,
    -            yscrollcommand=rightscrollbar.set,
    -            xscrollcommand=bottomscrollbar.set,
    -            bg="white")
    -        self.lb_messages.bind("", self.show_sourcefile)
    -        self.lb_messages.pack(expand=True, fill=BOTH)
    -        rightscrollbar.config(command=self.lb_messages.yview)
    -        bottomscrollbar.config(command=self.lb_messages.xview)
    -
    -        #History ListBoxes
    -        rightscrollbar2 = Scrollbar(history_frame)
    -        rightscrollbar2.pack(side=RIGHT, fill=Y)
    -        bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL)
    -        bottomscrollbar2.pack(side=BOTTOM, fill=X)
    -        self.showhistory = Listbox(
    -            history_frame,
    -            yscrollcommand=rightscrollbar2.set,
    -            xscrollcommand=bottomscrollbar2.set,
    -            bg="white")
    -        self.showhistory.pack(expand=True, fill=BOTH)
    -        rightscrollbar2.config(command=self.showhistory.yview)
    -        bottomscrollbar2.config(command=self.showhistory.xview)
    -        self.showhistory.bind('', self.select_recent_file)
    -        self.set_history_window()
    -
    -        #status bar
    -        self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
    -        self.status.pack(side=BOTTOM, fill=X)
    -
    -        #labelbl_ratingls
    -        lbl_rating_label = Label(rating_frame, text='Rating:')
    -        lbl_rating_label.pack(side=LEFT)
    -        lbl_rating = Label(rating_frame, textvariable=self.rating)
    -        lbl_rating.pack(side=LEFT)
    -        Label(mid_frame, text='Recently Used:').pack(side=LEFT)
    -        Label(top_frame, text='Module or package').pack(side=LEFT)
    -
    -        #file textbox
    -        self.txt_module = Entry(top_frame, background='white')
    -        self.txt_module.bind('', self.run_lint)
    -        self.txt_module.pack(side=LEFT, expand=True, fill=X)
    -
    -        #results box
    -        rightscrollbar = Scrollbar(res_frame)
    -        rightscrollbar.pack(side=RIGHT, fill=Y)
    -        bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL)
    -        bottomscrollbar.pack(side=BOTTOM, fill=X)
    -        self.results = Listbox(
    -            res_frame,
    -            yscrollcommand=rightscrollbar.set,
    -            xscrollcommand=bottomscrollbar.set,
    -            bg="white", font="Courier")
    -        self.results.pack(expand=True, fill=BOTH, side=BOTTOM)
    -        rightscrollbar.config(command=self.results.yview)
    -        bottomscrollbar.config(command=self.results.xview)
    -
    -        #buttons
    -        Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT)
    -        Button(top_frame, text='Open Package',
    -               command=(lambda: self.file_open(package=True))).pack(side=LEFT)
    -
    -        self.btnRun = Button(top_frame, text='Run', command=self.run_lint)
    -        self.btnRun.pack(side=LEFT)
    -        Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM)
    -
    -        #radio buttons
    -        self.information_box = IntVar()
    -        self.convention_box = IntVar()
    -        self.refactor_box = IntVar()
    -        self.warning_box = IntVar()
    -        self.error_box = IntVar()
    -        self.fatal_box = IntVar()
    -        i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'],
    -                        variable=self.information_box, command=self.refresh_msg_window)
    -        c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'],
    -                        variable=self.convention_box, command=self.refresh_msg_window)
    -        r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'],
    -                        variable=self.refactor_box, command=self.refresh_msg_window)
    -        w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'],
    -                        variable=self.warning_box, command=self.refresh_msg_window)
    -        e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'],
    -                        variable=self.error_box, command=self.refresh_msg_window)
    -        f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'],
    -                        variable=self.fatal_box, command=self.refresh_msg_window)
    -        i.select()
    -        c.select()
    -        r.select()
    -        w.select()
    -        e.select()
    -        f.select()
    -        i.pack(side=LEFT)
    -        c.pack(side=LEFT)
    -        r.pack(side=LEFT)
    -        w.pack(side=LEFT)
    -        e.pack(side=LEFT)
    -        f.pack(side=LEFT)
    -
    -        #check boxes
    -        self.box = StringVar()
    -        # XXX should be generated
    -        report = Radiobutton(
    -            radio_frame, text="Report", variable=self.box,
    -            value="Report", command=self.refresh_results_window)
    -        raw_met = Radiobutton(
    -            radio_frame, text="Raw metrics", variable=self.box,
    -            value="Raw metrics", command=self.refresh_results_window)
    -        dup = Radiobutton(
    -            radio_frame, text="Duplication", variable=self.box,
    -            value="Duplication", command=self.refresh_results_window)
    -        ext = Radiobutton(
    -            radio_frame, text="External dependencies",
    -            variable=self.box, value="External dependencies",
    -            command=self.refresh_results_window)
    -        stat = Radiobutton(
    -            radio_frame, text="Statistics by type",
    -            variable=self.box, value="Statistics by type",
    -            command=self.refresh_results_window)
    -        msg_cat = Radiobutton(
    -            radio_frame, text="Messages by category",
    -            variable=self.box, value="Messages by category",
    -            command=self.refresh_results_window)
    -        msg = Radiobutton(
    -            radio_frame, text="Messages", variable=self.box,
    -            value="Messages", command=self.refresh_results_window)
    -        source_file = Radiobutton(
    -            radio_frame, text="Source File", variable=self.box,
    -            value="Source File", command=self.refresh_results_window)
    -        report.select()
    -        report.grid(column=0, row=0, sticky=W)
    -        raw_met.grid(column=1, row=0, sticky=W)
    -        dup.grid(column=2, row=0, sticky=W)
    -        msg.grid(column=3, row=0, sticky=W)
    -        stat.grid(column=0, row=1, sticky=W)
    -        msg_cat.grid(column=1, row=1, sticky=W)
    -        ext.grid(column=2, row=1, sticky=W)
    -        source_file.grid(column=3, row=1, sticky=W)
    -
    -        #dictionary for check boxes and associated error term
    -        self.msg_type_dict = {
    -            'I': lambda: self.information_box.get() == 1,
    -            'C': lambda: self.convention_box.get() == 1,
    -            'R': lambda: self.refactor_box.get() == 1,
    -            'E': lambda: self.error_box.get() == 1,
    -            'W': lambda: self.warning_box.get() == 1,
    -            'F': lambda: self.fatal_box.get() == 1
    -        }
    -        self.txt_module.focus_set()
    -
    -
    -    def select_recent_file(self, event): # pylint: disable=unused-argument
    -        """adds the selected file in the history listbox to the Module box"""
    -        if not self.showhistory.size():
    -            return
    -
    -        selected = self.showhistory.curselection()
    -        item = self.showhistory.get(selected)
    -        #update module
    -        self.txt_module.delete(0, END)
    -        self.txt_module.insert(0, item)
    -
    -    def refresh_msg_window(self):
    -        """refresh the message window with current output"""
    -        #clear the window
    -        self.lb_messages.delete(0, END)
    -        self.visible_msgs = []
    -        for msg in self.msgs:
    -            if self.msg_type_dict.get(msg.C)():
    -                self.visible_msgs.append(msg)
    -                msg_str = convert_to_string(msg)
    -                self.lb_messages.insert(END, msg_str)
    -                fg_color = COLORS.get(msg_str[:3], 'black')
    -                self.lb_messages.itemconfigure(END, fg=fg_color)
    -
    -    def refresh_results_window(self):
    -        """refresh the results window with current output"""
    -        #clear the window
    -        self.results.delete(0, END)
    -        try:
    -            for res in self.tabs[self.box.get()]:
    -                self.results.insert(END, res)
    -        except KeyError:
    -            pass
    -
    -    def process_incoming(self):
    -        """process the incoming messages from running pylint"""
    -        while self.msg_queue.qsize():
    -            try:
    -                msg = self.msg_queue.get(0)
    -                if msg == "DONE":
    -                    self.report_stream.output_contents()
    -                    return False
    -
    -                #adding message to list of msgs
    -                self.msgs.append(msg)
    -
    -                #displaying msg if message type is selected in check box
    -                if self.msg_type_dict.get(msg.C)():
    -                    self.visible_msgs.append(msg)
    -                    msg_str = convert_to_string(msg)
    -                    self.lb_messages.insert(END, msg_str)
    -                    fg_color = COLORS.get(msg_str[:3], 'black')
    -                    self.lb_messages.itemconfigure(END, fg=fg_color)
    -
    -            except six.moves.queue.Empty:
    -                pass
    -        return True
    -
    -    def periodic_call(self):
    -        """determine when to unlock the run button"""
    -        if self.process_incoming():
    -            self.root.after(100, self.periodic_call)
    -        else:
    -            #enabling button so it can be run again
    -            self.btnRun.config(state=NORMAL)
    -
    -    def mainloop(self):
    -        """launch the mainloop of the application"""
    -        self.root.mainloop()
    -
    -    def quit(self, _=None):
    -        """quit the application"""
    -        self.root.quit()
    -
    -    def halt(self): # pylint: disable=no-self-use
    -        """program halt placeholder"""
    -        return
    -
    -    def file_open(self, package=False, _=None):
    -        """launch a file browser"""
    -        if not package:
    -            filename = askopenfilename(parent=self.root,
    -                                       filetypes=[('pythonfiles', '*.py'),
    -                                                  ('allfiles', '*')],
    -                                       title='Select Module')
    -        else:
    -            filename = askdirectory(title="Select A Folder", mustexist=1)
    -
    -        if filename == ():
    -            return
    -
    -        self.txt_module.delete(0, END)
    -        self.txt_module.insert(0, filename)
    -
    -    def update_filenames(self):
    -        """update the list of recent filenames"""
    -        filename = self.txt_module.get()
    -        if not filename:
    -            filename = os.getcwd()
    -        if filename+'\n' in self.filenames:
    -            index = self.filenames.index(filename+'\n')
    -            self.filenames.pop(index)
    -
    -        #ensure only 10 most recent are stored
    -        if len(self.filenames) == 10:
    -            self.filenames.pop()
    -        self.filenames.insert(0, filename+'\n')
    -
    -    def set_history_window(self):
    -        """update the history window with info from the history file"""
    -        #clear the window
    -        self.showhistory.delete(0, END)
    -        # keep the last 10 most recent files
    -        try:
    -            view_history = open(HOME+HISTORY, 'r')
    -            for hist in view_history.readlines():
    -                if not hist in self.filenames:
    -                    self.filenames.append(hist)
    -                self.showhistory.insert(END, hist.split('\n')[0])
    -            view_history.close()
    -        except IOError:
    -            # do nothing since history file will be created later
    -            return
    -
    -    def run_lint(self, _=None):
    -        """launches pylint"""
    -        self.update_filenames()
    -        self.root.configure(cursor='watch')
    -        self.reporter = GUIReporter(self, output=self.report_stream)
    -        module = self.txt_module.get()
    -        if not module:
    -            module = os.getcwd()
    -
    -        #cleaning up msgs and windows
    -        self.msgs = []
    -        self.visible_msgs = []
    -        self.lb_messages.delete(0, END)
    -        self.tabs = {}
    -        self.results.delete(0, END)
    -        self.btnRun.config(state=DISABLED)
    -
    -        #setting up a worker thread to run pylint
    -        worker = Thread(target=lint_thread, args=(module, self.reporter, self,))
    -        self.periodic_call()
    -        worker.start()
    -
    -        # Overwrite the .pylint-gui-history file with all the new recently added files
    -        # in order from filenames but only save last 10 files
    -        write_history = open(HOME+HISTORY, 'w')
    -        write_history.writelines(self.filenames)
    -        write_history.close()
    -        self.set_history_window()
    -
    -        self.root.configure(cursor='')
    -
    -    def show_sourcefile(self, event=None):  # pylint: disable=unused-argument
    -        selected = self.lb_messages.curselection()
    -        if not selected:
    -            return
    -
    -        msg = self.visible_msgs[int(selected[0])]
    -        scroll = msg.line - 3
    -        if scroll < 0:
    -            scroll = 0
    -
    -        self.tabs["Source File"] = open(msg.path, "r").readlines()
    -        self.box.set("Source File")
    -        self.refresh_results_window()
    -        self.results.yview(scroll)
    -        self.results.select_set(msg.line - 1)
    -
    -
    -def lint_thread(module, reporter, gui):
    -    """thread for pylint"""
    -    gui.status.text = "processing module(s)"
    -    pylint.lint.Run(args=[module], reporter=reporter, exit=False)
    -    gui.msg_queue.put("DONE")
    -
    -
    -def Run(args):
    -    """launch pylint gui from args"""
    -    if args:
    -        print('USAGE: pylint-gui\n launch a simple pylint gui using Tk')
    -        sys.exit(1)
    -    gui = LintGui()
    -    gui.mainloop()
    -    sys.exit(0)
    -
    -if __name__ == '__main__':
    -    Run(sys.argv[1:])
    diff --git a/pymode/libs/pylint/interfaces.py b/pymode/libs/pylint/interfaces.py
    deleted file mode 100644
    index 64f5a956..00000000
    --- a/pymode/libs/pylint/interfaces.py
    +++ /dev/null
    @@ -1,84 +0,0 @@
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -"""Interfaces for Pylint objects"""
    -from collections import namedtuple
    -
    -from logilab.common.interface import Interface
    -
    -Confidence = namedtuple('Confidence', ['name', 'description'])
    -# Warning Certainties
    -HIGH = Confidence('HIGH', 'No false positive possible.')
    -INFERENCE = Confidence('INFERENCE', 'Warning based on inference result.')
    -INFERENCE_FAILURE = Confidence('INFERENCE_FAILURE',
    -                               'Warning based on inference with failures.')
    -UNDEFINED = Confidence('UNDEFINED',
    -                       'Warning without any associated confidence level.')
    -
    -CONFIDENCE_LEVELS = [HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED]
    -
    -
    -class IChecker(Interface):
    -    """This is an base interface, not designed to be used elsewhere than for
    -    sub interfaces definition.
    -    """
    -
    -    def open(self):
    -        """called before visiting project (i.e set of modules)"""
    -
    -    def close(self):
    -        """called after visiting project (i.e set of modules)"""
    -
    -
    -class IRawChecker(IChecker):
    -    """interface for checker which need to parse the raw file
    -    """
    -
    -    def process_module(self, astroid):
    -        """ process a module
    -
    -        the module's content is accessible via astroid.stream
    -        """
    -
    -
    -class ITokenChecker(IChecker):
    -    """Interface for checkers that need access to the token list."""
    -    def process_tokens(self, tokens):
    -        """Process a module.
    -
    -        tokens is a list of all source code tokens in the file.
    -        """
    -
    -
    -class IAstroidChecker(IChecker):
    -    """ interface for checker which prefers receive events according to
    -    statement type
    -    """
    -
    -
    -class IReporter(Interface):
    -    """ reporter collect messages and display results encapsulated in a layout
    -    """
    -    def add_message(self, msg_id, location, msg):
    -        """add a message of a given type
    -
    -        msg_id is a message identifier
    -        location is a 3-uple (module, object, line)
    -        msg is the actual message
    -        """
    -
    -    def display_results(self, layout):
    -        """display results encapsulated in the layout tree
    -        """
    -
    -
    -__all__ = ('IRawChecker', 'IAstroidChecker', 'ITokenChecker', 'IReporter')
    diff --git a/pymode/libs/pylint/lint.py b/pymode/libs/pylint/lint.py
    deleted file mode 100644
    index 01fc2f5d..00000000
    --- a/pymode/libs/pylint/lint.py
    +++ /dev/null
    @@ -1,1397 +0,0 @@
    -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
    -# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    -#
    -# This program is free software; you can redistribute it and/or modify it under
    -# the terms of the GNU General Public License as published by the Free Software
    -# Foundation; either version 2 of the License, or (at your option) any later
    -# version.
    -#
    -# This program is distributed in the hope that it will be useful, but WITHOUT
    -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    -#
    -# You should have received a copy of the GNU General Public License along with
    -# this program; if not, write to the Free Software Foundation, Inc.,
    -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -""" %prog [options] module_or_package
    -
    -  Check that a module satisfies a coding standard (and more !).
    -
    -    %prog --help
    -
    -  Display this help message and exit.
    -
    -    %prog --help-msg [,]
    -
    -  Display help messages about given message identifiers and exit.
    -"""
    -from __future__ import print_function
    -
    -import collections
    -import contextlib
    -import itertools
    -import operator
    -import os
    -try:
    -    import multiprocessing
    -except ImportError:
    -    multiprocessing = None
    -import sys
    -import tokenize
    -import warnings
    -
    -import astroid
    -from astroid.__pkginfo__ import version as astroid_version
    -from astroid import modutils
    -from logilab.common import configuration
    -from logilab.common import optik_ext
    -from logilab.common import interface
    -from logilab.common import textutils
    -from logilab.common import ureports
    -from logilab.common import __version__ as common_version
    -import six
    -
    -from pylint import checkers
    -from pylint import interfaces
    -from pylint import reporters
    -from pylint import utils
    -from pylint import config
    -from pylint.__pkginfo__ import version
    -
    -
    -MANAGER = astroid.MANAGER
    -INCLUDE_IDS_HELP = ("Deprecated. It was used to include message\'s "
    -                    "id in output. Use --msg-template instead.")
    -SYMBOLS_HELP = ("Deprecated. It was used to include symbolic ids of "
    -                "messages in output. Use --msg-template instead.")
    -
    -def _get_new_args(message):
    -    location = (
    -        message.abspath,
    -        message.path,
    -        message.module,
    -        message.obj,
    -        message.line,
    -        message.column,
    -    )
    -    return (
    -        message.msg_id,
    -        message.symbol,
    -        location,
    -        message.msg,
    -        message.confidence,
    -    )
    -
    -def _get_python_path(filepath):
    -    dirname = os.path.realpath(os.path.expanduser(filepath))
    -    if not os.path.isdir(dirname):
    -        dirname = os.path.dirname(dirname)
    -    while True:
    -        if not os.path.exists(os.path.join(dirname, "__init__.py")):
    -            return dirname
    -        old_dirname = dirname
    -        dirname = os.path.dirname(dirname)
    -        if old_dirname == dirname:
    -            return os.getcwd()
    -
    -
    -def _merge_stats(stats):
    -    merged = {}
    -    for stat in stats:
    -        for key, item in six.iteritems(stat):
    -            if key not in merged:
    -                merged[key] = item
    -            else:
    -                if isinstance(item, dict):
    -                    merged[key].update(item)
    -                else:
    -                    merged[key] = merged[key] + item
    -    return merged
    -
    -
    -@contextlib.contextmanager
    -def _patch_sysmodules():
    -    # Context manager that permits running pylint, on Windows, with -m switch
    -    # and with --jobs, as in 'python -2 -m pylint .. --jobs'.
    -    # For more details why this is needed,
    -    # see Python issue http://bugs.python.org/issue10845.
    -
    -    mock_main = __name__ != '__main__' # -m switch
    -    if mock_main:
    -        sys.modules['__main__'] = sys.modules[__name__]
    -
    -    try:
    -        yield
    -    finally:
    -        if mock_main:
    -            sys.modules.pop('__main__')
    -
    -
    -# Python Linter class #########################################################
    -
    -MSGS = {
    -    'F0001': ('%s',
    -              'fatal',
    -              'Used when an error occurred preventing the analysis of a \
    -              module (unable to find it for instance).'),
    -    'F0002': ('%s: %s',
    -              'astroid-error',
    -              'Used when an unexpected error occurred while building the '
    -              'Astroid  representation. This is usually accompanied by a '
    -              'traceback. Please report such errors !'),
    -    'F0003': ('ignored builtin module %s',
    -              'ignored-builtin-module',
    -              'Used to indicate that the user asked to analyze a builtin '
    -              'module which has been skipped.'),
    -    'F0010': ('error while code parsing: %s',
    -              'parse-error',
    -              'Used when an exception occured while building the Astroid '
    -              'representation which could be handled by astroid.'),
    -
    -    'I0001': ('Unable to run raw checkers on built-in module %s',
    -              'raw-checker-failed',
    -              'Used to inform that a built-in module has not been checked '
    -              'using the raw checkers.'),
    -
    -    'I0010': ('Unable to consider inline option %r',
    -              'bad-inline-option',
    -              'Used when an inline option is either badly formatted or can\'t '
    -              'be used inside modules.'),
    -
    -    'I0011': ('Locally disabling %s (%s)',
    -              'locally-disabled',
    -              'Used when an inline option disables a message or a messages '
    -              'category.'),
    -    'I0012': ('Locally enabling %s (%s)',
    -              'locally-enabled',
    -              'Used when an inline option enables a message or a messages '
    -              'category.'),
    -    'I0013': ('Ignoring entire file',
    -              'file-ignored',
    -              'Used to inform that the file will not be checked'),
    -    'I0020': ('Suppressed %s (from line %d)',
    -              'suppressed-message',
    -              'A message was triggered on a line, but suppressed explicitly '
    -              'by a disable= comment in the file. This message is not '
    -              'generated for messages that are ignored due to configuration '
    -              'settings.'),
    -    'I0021': ('Useless suppression of %s',
    -              'useless-suppression',
    -              'Reported when a message is explicitly disabled for a line or '
    -              'a block of code, but never triggered.'),
    -    'I0022': ('Pragma "%s" is deprecated, use "%s" instead',
    -              'deprecated-pragma',
    -              'Some inline pylint options have been renamed or reworked, '
    -              'only the most recent form should be used. '
    -              'NOTE:skip-all is only available with pylint >= 0.26',
    -              {'old_names': [('I0014', 'deprecated-disable-all')]}),
    -
    -    'E0001': ('%s',
    -              'syntax-error',
    -              'Used when a syntax error is raised for a module.'),
    -
    -    'E0011': ('Unrecognized file option %r',
    -              'unrecognized-inline-option',
    -              'Used when an unknown inline option is encountered.'),
    -    'E0012': ('Bad option value %r',
    -              'bad-option-value',
    -              'Used when a bad value for an inline option is encountered.'),
    -    }
    -
    -
    -def _deprecated_option(shortname, opt_type, help_msg):
    -    def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument
    -        sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,))
    -    return {'short': shortname, 'help': help_msg, 'hide': True,
    -            'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated}
    -
    -
    -if multiprocessing is not None:
    -    class ChildLinter(multiprocessing.Process): # pylint: disable=no-member
    -        def run(self):
    -            tasks_queue, results_queue, self._config = self._args # pylint: disable=no-member
    -
    -            self._config["jobs"] = 1  # Child does not parallelize any further.
    -            self._python3_porting_mode = self._config.pop(
    -                'python3_porting_mode', None)
    -
    -            # Run linter for received files/modules.
    -            for file_or_module in iter(tasks_queue.get, 'STOP'):
    -                result = self._run_linter(file_or_module[0])
    -                try:
    -                    results_queue.put(result)
    -                except Exception as ex:
    -                    print("internal error with sending report for module %s" %
    -                          file_or_module, file=sys.stderr)
    -                    print(ex, file=sys.stderr)
    -                    results_queue.put({})
    -
    -        def _run_linter(self, file_or_module):
    -            linter = PyLinter()
    -
    -            # Register standard checkers.
    -            linter.load_default_plugins()
    -            # Load command line plugins.
    -            # TODO linter.load_plugin_modules(self._plugins)
    -
    -            linter.load_configuration(**self._config)
    -            linter.set_reporter(reporters.CollectingReporter())
    -
    -            # Enable the Python 3 checker mode. This option is
    -            # passed down from the parent linter up to here, since
    -            # the Python 3 porting flag belongs to the Run class,
    -            # instead of the Linter class.
    -            if self._python3_porting_mode:
    -                linter.python3_porting_mode()
    -
    -            # Run the checks.
    -            linter.check(file_or_module)
    -
    -            msgs = [_get_new_args(m) for m in linter.reporter.messages]
    -            return (file_or_module, linter.file_state.base_name, linter.current_name,
    -                    msgs, linter.stats, linter.msg_status)
    -
    -
    -class PyLinter(configuration.OptionsManagerMixIn,
    -               utils.MessagesHandlerMixIn,
    -               utils.ReportsHandlerMixIn,
    -               checkers.BaseTokenChecker):
    -    """lint Python modules using external checkers.
    -
    -    This is the main checker controlling the other ones and the reports
    -    generation. It is itself both a raw checker and an astroid checker in order
    -    to:
    -    * handle message activation / deactivation at the module level
    -    * handle some basic but necessary stats'data (number of classes, methods...)
    -
    -    IDE plugins developpers: you may have to call
    -    `astroid.builder.MANAGER.astroid_cache.clear()` accross run if you want
    -    to ensure the latest code version is actually checked.
    -    """
    -
    -    __implements__ = (interfaces.ITokenChecker, )
    -
    -    name = 'master'
    -    priority = 0
    -    level = 0
    -    msgs = MSGS
    -
    -    @staticmethod
    -    def make_options():
    -        return (('ignore',
    -                 {'type' : 'csv', 'metavar' : '[,...]',
    -                  'dest' : 'black_list', 'default' : ('CVS',),
    -                  'help' : 'Add files or directories to the blacklist. '
    -                           'They should be base names, not paths.'}),
    -                ('persistent',
    -                 {'default': True, 'type' : 'yn', 'metavar' : '',
    -                  'level': 1,
    -                  'help' : 'Pickle collected data for later comparisons.'}),
    -
    -                ('load-plugins',
    -                 {'type' : 'csv', 'metavar' : '', 'default' : (),
    -                  'level': 1,
    -                  'help' : 'List of plugins (as comma separated values of '
    -                           'python modules names) to load, usually to register '
    -                           'additional checkers.'}),
    -
    -                ('output-format',
    -                 {'default': 'text', 'type': 'string', 'metavar' : '',
    -                  'short': 'f',
    -                  'group': 'Reports',
    -                  'help' : 'Set the output format. Available formats are text,'
    -                           ' parseable, colorized, msvs (visual studio) and html. You '
    -                           'can also give a reporter class, eg mypackage.mymodule.'
    -                           'MyReporterClass.'}),
    -
    -                ('files-output',
    -                 {'default': 0, 'type' : 'yn', 'metavar' : '',
    -                  'group': 'Reports', 'level': 1,
    -                  'help' : 'Put messages in a separate file for each module / '
    -                           'package specified on the command line instead of printing '
    -                           'them on stdout. Reports (if any) will be written in a file '
    -                           'name "pylint_global.[txt|html]".'}),
    -
    -                ('reports',
    -                 {'default': 1, 'type' : 'yn', 'metavar' : '',
    -                  'short': 'r',
    -                  'group': 'Reports',
    -                  'help' : 'Tells whether to display a full report or only the '
    -                           'messages'}),
    -
    -                ('evaluation',
    -                 {'type' : 'string', 'metavar' : '',
    -                  'group': 'Reports', 'level': 1,
    -                  'default': '10.0 - ((float(5 * error + warning + refactor + '
    -                             'convention) / statement) * 10)',
    -                  'help' : 'Python expression which should return a note less '
    -                           'than 10 (10 is the highest note). You have access '
    -                           'to the variables errors warning, statement which '
    -                           'respectively contain the number of errors / '
    -                           'warnings messages and the total number of '
    -                           'statements analyzed. This is used by the global '
    -                           'evaluation report (RP0004).'}),
    -
    -                ('comment',
    -                 {'default': 0, 'type' : 'yn', 'metavar' : '',
    -                  'group': 'Reports', 'level': 1,
    -                  'help' : 'Add a comment according to your evaluation note. '
    -                           'This is used by the global evaluation report (RP0004).'}),
    -
    -                ('confidence',
    -                 {'type' : 'multiple_choice', 'metavar': '',
    -                  'default': '',
    -                  'choices': [c.name for c in interfaces.CONFIDENCE_LEVELS],
    -                  'group': 'Messages control',
    -                  'help' : 'Only show warnings with the listed confidence levels.'
    -                           ' Leave empty to show all. Valid levels: %s' % (
    -                               ', '.join(c.name for c in interfaces.CONFIDENCE_LEVELS),)}),
    -
    -                ('enable',
    -                 {'type' : 'csv', 'metavar': '',
    -                  'short': 'e',
    -                  'group': 'Messages control',
    -                  'help' : 'Enable the message, report, category or checker with the '
    -                           'given id(s). You can either give multiple identifier '
    -                           'separated by comma (,) or put this option multiple time. '
    -                           'See also the "--disable" option for examples. '}),
    -
    -                ('disable',
    -                 {'type' : 'csv', 'metavar': '',
    -                  'short': 'd',
    -                  'group': 'Messages control',
    -                  'help' : 'Disable the message, report, category or checker '
    -                           'with the given id(s). You can either give multiple identifiers'
    -                           ' separated by comma (,) or put this option multiple times '
    -                           '(only on the command line, not in the configuration file '
    -                           'where it should appear only once).'
    -                           'You can also use "--disable=all" to disable everything first '
    -                           'and then reenable specific checks. For example, if you want '
    -                           'to run only the similarities checker, you can use '
    -                           '"--disable=all --enable=similarities". '
    -                           'If you want to run only the classes checker, but have no '
    -                           'Warning level messages displayed, use'
    -                           '"--disable=all --enable=classes --disable=W"'}),
    -
    -                ('msg-template',
    -                 {'type' : 'string', 'metavar': '