Adding new stuff

This commit is contained in:
ViktorBarzin 2017-07-31 00:00:01 +03:00
parent 131691e143
commit 6c0a9f5b29
718 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,11 @@
[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}

View file

@ -0,0 +1 @@
vim-flavor

View file

@ -0,0 +1 @@
ruby-1.9.3

View file

@ -0,0 +1,6 @@
language: ruby
python: "2.7"
rvm:
- 1.9.3
script:
- make travis

View file

@ -0,0 +1,64 @@
Maintainers:
* Kirill Klenov <horneds@gmail.com>
* 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);

View file

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View file

@ -0,0 +1,356 @@
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 <C-space> 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_<name> -> pymode_rope_<name>)
* 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
* "<C-c>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

View file

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem 'vim-flavor', '~> 1.1'

View file

@ -0,0 +1,90 @@
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 <horneds@gmail.com>"
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

View file

@ -0,0 +1,367 @@
|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 (``<leader>r``)
- Add/remove breakpoints (``<leader>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 refactoring library> (rope_)
- Strong code completion (rope_)
- Go to definition (``<C-c>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 <https://github.com/tpope/vim-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 <path_to_pymode>/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 <https://github.com/klen/python-mode/issues/new>`_
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 = "<C-]>"
" Override run current python file key shortcut to Ctrl-Shift-e
let g:pymode_run_bind = "<C-S-e>"
" Override view python doc key shortcut to Ctrl-Shift-d
let g:pymode_doc_bind = "<C-S-d>"
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
<https://github.com/klen/python-mode/blob/develop/doc/pymode.txt>`__
-- ``:help pymode``
- `plugin/pymode.vim
<https://github.com/klen/python-mode/blob/develop/plugin/pymode.vim>`__
-- python-mode VIM plugin
- `syntax/python.vim
<https://github.com/klen/python-mode/blob/develop/syntax/python.vim>`__
-- python-mode ``python.vim`` VIM syntax
- `syntax/pyrex.vim
<https://github.com/klen/python-mode/blob/develop/syntax/pyrex.vim>`__
-- ``pyrex.vim`` VIM syntax (pyrex, Cython)
- `t/
<https://github.com/klen/python-mode/tree/develop/t>`__
-- ``*.vim`` more python-mode VIM configuration
- `pymode/
<https://github.com/klen/python-mode/tree/develop/pymode>`__
-- ``*.py`` -- python-mode Python module
- `pymode/libs/
<https://github.com/klen/python-mode/tree/develop/pymode/libs>`__
-- ``*.py`` -- `Python Libraries <#python-libraries>`__
Python Libraries
------------------
Vendored Python modules are located
mostly in
`pymode/libs/ <https://github.com/klen/python-mode/tree/develop/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

View file

@ -0,0 +1,11 @@
#!/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

View file

@ -0,0 +1 @@
runtime after/ftplugin/python.vim

View file

@ -0,0 +1,58 @@
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 <buffer> ]] :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', '')<CR>
nnoremap <buffer> [[ :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', 'b')<CR>
nnoremap <buffer> ]C :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', '')<CR>
nnoremap <buffer> [C :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', 'b')<CR>
nnoremap <buffer> ]M :<C-U>call pymode#motion#move('^<Bslash>s*def<Bslash>s', '')<CR>
nnoremap <buffer> [M :<C-U>call pymode#motion#move('^<Bslash>s*def<Bslash>s', 'b')<CR>
onoremap <buffer> ]] :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', '')<CR>
onoremap <buffer> [[ :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', 'b')<CR>
onoremap <buffer> ]C :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', '')<CR>
onoremap <buffer> [C :<C-U>call pymode#motion#move('<Bslash>v^(class<bar>def)<Bslash>s', 'b')<CR>
onoremap <buffer> ]M :<C-U>call pymode#motion#move('^<Bslash>s*def<Bslash>s', '')<CR>
onoremap <buffer> [M :<C-U>call pymode#motion#move('^<Bslash>s*def<Bslash>s', 'b')<CR>
vnoremap <buffer> ]] :call pymode#motion#vmove('<Bslash>v^(class<bar>def)<Bslash>s', '')<CR>
vnoremap <buffer> [[ :call pymode#motion#vmove('<Bslash>v^(class<bar>def)<Bslash>s', 'b')<CR>
vnoremap <buffer> ]M :call pymode#motion#vmove('^<Bslash>s*def<Bslash>s', '')<CR>
vnoremap <buffer> [M :call pymode#motion#vmove('^<Bslash>s*def<Bslash>s', 'b')<CR>
onoremap <buffer> C :<C-U>call pymode#motion#select('^<Bslash>s*class<Bslash>s', 0)<CR>
onoremap <buffer> aC :<C-U>call pymode#motion#select('^<Bslash>s*class<Bslash>s', 0)<CR>
onoremap <buffer> iC :<C-U>call pymode#motion#select('^<Bslash>s*class<Bslash>s', 1)<CR>
vnoremap <buffer> aC :<C-U>call pymode#motion#select('^<Bslash>s*class<Bslash>s', 0)<CR>
vnoremap <buffer> iC :<C-U>call pymode#motion#select('^<Bslash>s*class<Bslash>s', 1)<CR>
onoremap <buffer> M :<C-U>call pymode#motion#select('^<Bslash>s*def<Bslash>s', 0)<CR>
onoremap <buffer> aM :<C-U>call pymode#motion#select('^<Bslash>s*def<Bslash>s', 0)<CR>
onoremap <buffer> iM :<C-U>call pymode#motion#select('^<Bslash>s*def<Bslash>s', 1)<CR>
vnoremap <buffer> aM :<C-U>call pymode#motion#select('^<Bslash>s*def<Bslash>s', 0)<CR>
vnoremap <buffer> iM :<C-U>call pymode#motion#select('^<Bslash>s*def<Bslash>s', 1)<CR>
endif
if g:pymode_rope && g:pymode_rope_completion
setlocal omnifunc=pymode#rope#completions
if g:pymode_rope_completion_bind != ""
exe "inoremap <silent> <buffer> " . g:pymode_rope_completion_bind . " <C-R>=pymode#rope#complete(0)<CR>"
if tolower(g:pymode_rope_completion_bind) == '<c-space>'
exe "inoremap <silent> <buffer> <Nul> <C-R>=pymode#rope#complete(0)<CR>"
endif
end
end

View file

@ -0,0 +1 @@
runtime after/indent/python.vim

View file

@ -0,0 +1,13 @@
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

View file

@ -0,0 +1,133 @@
" 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 \<Esc>"
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! * <buffer>
augroup END
endfunction "}}}

View file

@ -0,0 +1,51 @@
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 "}}}

View file

@ -0,0 +1,37 @@
" 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 "}}}

View file

@ -0,0 +1,274 @@
" 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

View file

@ -0,0 +1,186 @@
" PEP8 compatible Python indent file
" Language: Python
" Maintainer: Hynek Schlawack <hs@ox.cx>
" Prev Maintainer: Eric Mc Sween <em@tomcom.de> (address invalid)
" Original Author: David Bustos <bustos@caltech.edu> (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 " }}}

View file

@ -0,0 +1,101 @@
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("\<Right>\<Left>", "n")
else
call feedkeys("\<Left>\<Right>", "n")
endif
else
call feedkeys("f\e", "n")
endif
endfunction "}}}
fun! pymode#lint#stop() "{{{
au! pymode CursorHold <buffer>
endfunction "}}}
fun! pymode#lint#start() "{{{
au! pymode CursorHold <buffer> call pymode#lint#tick_queue()
call pymode#lint#tick_queue()
endfunction "}}}

View file

@ -0,0 +1,97 @@
" 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

View file

@ -0,0 +1,185 @@
" 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 "\<C-n>"
end
if a:dot
PymodePython rope.complete(True)
else
PymodePython rope.complete()
end
return pumvisible() ? "\<C-p>\<Down>" : ""
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 "}}}

View file

@ -0,0 +1,99 @@
" 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 "}}}

View file

@ -0,0 +1,81 @@
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 "}}}

View file

@ -0,0 +1,57 @@
let g:PymodeSigns = {}
fun! pymode#tools#signs#init() "{{{
call g:PymodeSigns.setup()
endfunction "}}}
fun! g:PymodeSigns.enabled() "{{{
return (g:pymode_lint_signs && has('signs'))
endfunction "}}}
fun! g:PymodeSigns.setup() "{{{
if self.enabled()
execute 'sign define PymodeW text=' . g:pymode_lint_todo_symbol . " texthl=Todo"
execute 'sign define PymodeD text=' . g:pymode_lint_docs_symbol . " texthl=String"
execute 'sign define PymodeC text=' . g:pymode_lint_comment_symbol . " texthl=Comment"
execute 'sign define PymodeR text=' . g:pymode_lint_visual_symbol . " texthl=Visual"
execute 'sign define PymodeE text=' . g:pymode_lint_error_symbol . " texthl=Error"
execute 'sign define PymodeI text=' . g:pymode_lint_info_symbol . " texthl=Info"
execute 'sign define PymodeF text=' . g:pymode_lint_pyflakes_symbol . " texthl=Info"
endif
let self._sign_ids = []
let self._next_id = 10000
let self._messages = {}
endfunction "}}}
fun! g:PymodeSigns.refresh(loclist) "{{{
if self.enabled()
call self.clear()
call self.place(a:loclist)
endif
endfunction "}}}
fun! g:PymodeSigns.clear() "{{{
let ids = copy(self._sign_ids)
for i in ids
execute "sign unplace " . i
call remove(self._sign_ids, index(self._sign_ids, i))
endfor
endfunction "}}}
fun! g:PymodeSigns.place(loclist) "{{{
let seen = {}
for issue in a:loclist._loclist
if !has_key(seen, issue.lnum)
let seen[issue.lnum] = 1
call add(self._sign_ids, self._next_id)
execute printf('sign place %d line=%d name=%s buffer=%d', self._next_id, issue.lnum, "Pymode".issue.type[0], issue.bufnr)
let self._next_id += 1
endif
endfor
endfunction "}}}

View file

@ -0,0 +1,89 @@
" 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('<sfile>: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 "}}}

View file

@ -0,0 +1,17 @@
" 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 "}}}

View file

@ -0,0 +1,13 @@
" 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('<sfile>:p:h'))
set rtp -=$HOME/.vim
set rtp -=$HOME/.vim/after
set nocp
syntax enable

View file

@ -0,0 +1,796 @@
*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 (``<leader>r``)
- Add/remove breakpoints (``<leader>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 refactoring library> (rope_)
- Strong code completion (rope_)
- Go to definition (``<C-c>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* <args> — 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* <path> -- 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 = '<leader>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 = '<leader>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* [<path>] -- 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 = '<C-c>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 <Ctrl-Space> for autocompletion. The first entry will
be automatically selected and you can press <Return> to insert the entry in
your code. <C-X><C-O> and <C-P>/<C-N> 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 = '<C-Space>'
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 *<C-C>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 = '<C-c>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 = '<C-c>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 = '<C-c>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 = '<C-c>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 = '<C-c>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 = '<C-c>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 = '<C-c>rm'
let g:pymode_rope_extract_variable_bind = '<C-c>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 = '<C-c>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 = '<C-c>rv'
Change function signature ~
>
let g:pymode_rope_change_signature_bind = '<C-c>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 <johann@rocholl.net>
http://github.com/jcrocholl/pep8
autopep8:
Copyright (c) 2012 hhatto <hhatto.jp@gmail.com>
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 <hs@ox.cx>
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:

View file

@ -0,0 +1 @@
runtime ftplugin/python/pymode.vim

View file

@ -0,0 +1,212 @@
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('<sfile>: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(<args>)
" Setup events for pymode
au! pymode BufWritePre <buffer> call pymode#buffer_pre_write()
au! pymode BufWritePost <buffer> call pymode#buffer_post_write()
" Run python code
if g:pymode_run
command! -buffer -nargs=0 -range=% PymodeRun call pymode#run#code_run(<f-line1>, <f-line2>)
exe "nnoremap <silent> <buffer> " g:pymode_run_bind ":PymodeRun<CR>"
exe "vnoremap <silent> <buffer> " g:pymode_run_bind ":PymodeRun<CR>"
endif
" Add/remove breakpoints
if g:pymode_breakpoint
exe "nnoremap <silent> <buffer> " g:pymode_breakpoint_bind ":call pymode#breakpoint#operate(line('.'))<CR>"
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 <buffer> 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 <buffer> 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 <buffer> PymodeLint
endif
if g:pymode_lint_message
au! pymode CursorMoved <buffer>
au! pymode CursorMoved <buffer> 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 <buffer> call pymode#lint#start()
" au! BufLeave <buffer> call pymode#lint#stop()
end
endif
" Show python documentation
if g:pymode_doc
" Set commands
command! -buffer -nargs=1 PymodeDoc call pymode#doc#show("<args>")
" Set keys
exe "nnoremap <silent> <buffer> " g:pymode_doc_bind ":call pymode#doc#find()<CR>"
exe "vnoremap <silent> <buffer> " g:pymode_doc_bind ":<C-U>call pymode#doc#show(@*)<CR>"
end
" Rope support
if g:pymode_rope
if g:pymode_rope_goto_definition_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_goto_definition_bind . " :call pymode#rope#goto_definition()<CR>"
endif
if g:pymode_rope_show_doc_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_show_doc_bind . " :call pymode#rope#show_doc()<CR>"
end
if g:pymode_rope_find_it_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_find_it_bind . " :call pymode#rope#find_it()<CR>"
end
if g:pymode_rope_organize_imports_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_organize_imports_bind . " :call pymode#rope#organize_imports()<CR>"
end
if g:pymode_rope_rename_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_rename_bind . " :call pymode#rope#rename()<CR>"
end
if g:pymode_rope_rename_module_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_rename_module_bind . " :call pymode#rope#rename_module()<CR>"
end
if g:pymode_rope_extract_method_bind != ""
exe "vnoremap <silent> <buffer> " . g:pymode_rope_extract_method_bind . " :call pymode#rope#extract_method()<CR>"
end
if g:pymode_rope_extract_variable_bind != ""
exe "vnoremap <silent> <buffer> " . g:pymode_rope_extract_variable_bind . " :call pymode#rope#extract_variable()<CR>"
end
if g:pymode_rope_inline_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_inline_bind . " :call pymode#rope#inline()<CR>"
end
if g:pymode_rope_move_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_move_bind . " :call pymode#rope#move()<CR>"
end
if g:pymode_rope_change_signature_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_change_signature_bind . " :call pymode#rope#signature()<CR>"
end
if g:pymode_rope_use_function_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_use_function_bind . " :call pymode#rope#use_function()<CR>"
end
if g:pymode_rope_generate_function_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_generate_function_bind . " :call pymode#rope#generate_function()<CR>"
end
if g:pymode_rope_generate_package_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_generate_package_bind . " :call pymode#rope#generate_package()<CR>"
end
if g:pymode_rope_generate_class_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_generate_class_bind . " :call pymode#rope#generate_class()<CR>"
end
if g:pymode_rope_module_to_package_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_module_to_package_bind . " :call pymode#rope#module_to_package()<CR>"
end
if g:pymode_rope_autoimport_bind != ""
exe "noremap <silent> <buffer> " . g:pymode_rope_autoimport_bind . " :PymodeRopeAutoImport<CR>"
end
if g:pymode_rope_completion && g:pymode_rope_complete_on_dot
inoremap <silent> <buffer> . .<C-R>=pymode#rope#complete_on_dot()<CR>
end
command! -buffer -nargs=? PymodeRopeNewProject call pymode#rope#new(<f-args>)
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('<cword>'))
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,316 @@
" 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', '<leader>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', '<leader>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', '<C-c>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', '<C-Space>')
" Bind keys for goto definition (leave empty for disable)
call pymode#default('g:pymode_rope_goto_definition_bind', '<C-c>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', '<C-c>d')
" Bind keys for find occurencies (leave empty for disable)
call pymode#default('g:pymode_rope_find_it_bind', '<C-c>f')
" Bind keys for organize imports (leave empty for disable)
call pymode#default('g:pymode_rope_organize_imports_bind', '<C-c>ro')
" Bind keys for rename variable/method/class in the project (leave empty for disable)
call pymode#default('g:pymode_rope_rename_bind', '<C-c>rr')
" Bind keys for rename module
call pymode#default('g:pymode_rope_rename_module_bind', '<C-c>r1r')
" Bind keys for convert module to package
call pymode#default('g:pymode_rope_module_to_package_bind', '<C-c>r1p')
" Creates a new function or method (depending on the context) from the selected lines
call pymode#default('g:pymode_rope_extract_method_bind', '<C-c>rm')
" Creates a variable from the selected lines
call pymode#default('g:pymode_rope_extract_variable_bind', '<C-c>rl')
" Inline refactoring
call pymode#default('g:pymode_rope_inline_bind', '<C-c>ri')
" Move refactoring
call pymode#default('g:pymode_rope_move_bind', '<C-c>rv')
" Generate function
call pymode#default('g:pymode_rope_generate_function_bind', '<C-c>rnf')
" Generate class
call pymode#default('g:pymode_rope_generate_class_bind', '<C-c>rnc')
" Generate package
call pymode#default('g:pymode_rope_generate_package_bind', '<C-c>rnp')
" Change signature
call pymode#default('g:pymode_rope_change_signature_bind', '<C-c>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', '<C-c>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 <args>
let g:UltiSnipsUsePythonVersion = 2
elseif g:pymode_python == 'python3'
command! -nargs=1 PymodePython python3 <args>
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 <args>
endif
command! PymodeVersion echomsg "Pymode version: " . g:pymode_version . " interpreter: " . g:pymode_python . " lint: " . g:pymode_lint . " rope: " . g:pymode_rope
augroup pymode

View file

@ -0,0 +1,8 @@
[pylama]
linters=pep8,pyflakes,pylint
[pylama:pymode/libs*]
skip=1
[pylama:pylint]
disable=E1120,E1130,E1103,W1401,F0001

View file

@ -0,0 +1,37 @@
""" 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)

View file

@ -0,0 +1,98 @@
""" 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

View file

@ -0,0 +1,6 @@
""" Python-mode async support. """
from ._compat import Queue
RESULTS = Queue()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,249 @@
"""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()

View file

@ -0,0 +1,16 @@
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)()

View file

@ -0,0 +1,119 @@
# -*- 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, '<environment 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)

View file

@ -0,0 +1,131 @@
# 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 <http://www.gnu.org/licenses/>.
"""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])

View file

@ -0,0 +1,42 @@
# 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 <http://www.gnu.org/licenses/>.
"""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",
]

View file

@ -0,0 +1,499 @@
# 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 <http://www.gnu.org/licenses/>.
"""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_<node> 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()

View file

@ -0,0 +1,86 @@
# 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 <http://www.gnu.org/licenses/>.
"""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

View file

@ -0,0 +1,652 @@
# 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 <http://www.gnu.org/licenses/>.
"""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 <stmts>
"""
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 '<Instance of %s.%s at 0x%s>' % (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 '<Generator(%s) l.%s at 0x%s>' % (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]

View file

@ -0,0 +1,245 @@
"""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')

View file

@ -0,0 +1,155 @@
"""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("<flags") or
str(obj).startswith("<enum ") or
str(obj).startswith("<GType ") or
inspect.isdatadescriptor(obj)):
constants[name] = 0
elif callable(obj):
# Fall back to a function for anything callable
functions[name] = obj
else:
# Assume everything else is some manner of constant
constants[name] = 0
ret = ""
if constants:
ret += "# %s contants\n\n" % parent.__name__
for name in sorted(constants):
if name[0].isdigit():
# GDK has some busted constant names like
# Gdk.EventType.2BUTTON_PRESS
continue
val = constants[name]
strval = str(val)
if type(val) is str:
strval = '"%s"' % str(val).replace("\\", "\\\\")
ret += "%s = %s\n" % (name, strval)
if ret:
ret += "\n\n"
if functions:
ret += "# %s functions\n\n" % parent.__name__
for name in sorted(functions):
func = functions[name]
ret += "def %s(*args, **kwargs):\n" % name
ret += " pass\n"
if ret:
ret += "\n\n"
if methods:
ret += "# %s methods\n\n" % parent.__name__
for name in sorted(methods):
func = methods[name]
ret += "def %s(self, *args, **kwargs):\n" % name
ret += " pass\n"
if ret:
ret += "\n\n"
if classes:
ret += "# %s classes\n\n" % parent.__name__
for name in sorted(classes):
ret += "class %s(object):\n" % name
classret = _gi_build_stub(classes[name])
if not classret:
classret = "pass\n"
for line in classret.splitlines():
ret += " " + line + "\n"
ret += "\n"
return ret
def _import_gi_module(modname):
# we only consider gi.repository submodules
if not modname.startswith('gi.repository.'):
raise AstroidBuildingException()
# build astroid representation unless we already tried so
if modname not in _inspected_modules:
modnames = [modname]
optional_modnames = []
# GLib and GObject may have some special case handling
# in pygobject that we need to cope with. However at
# least as of pygobject3-3.13.91 the _glib module doesn't
# exist anymore, so if treat these modules as optional.
if modname == 'gi.repository.GLib':
optional_modnames.append('gi._glib')
elif modname == 'gi.repository.GObject':
optional_modnames.append('gi._gobject')
try:
modcode = ''
for m in itertools.chain(modnames, optional_modnames):
try:
__import__(m)
modcode += _gi_build_stub(sys.modules[m])
except ImportError:
if m not in optional_modnames:
raise
except ImportError:
astng = _inspected_modules[modname] = None
else:
astng = AstroidBuilder(MANAGER).string_build(modcode, modname)
_inspected_modules[modname] = astng
else:
astng = _inspected_modules[modname]
if astng is None:
raise AstroidBuildingException('Failed to import module %r' % modname)
return astng
MANAGER.register_failed_import_hook(_import_gi_module)

View file

@ -0,0 +1,18 @@
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
def mechanize_transform():
return AstroidBuilder(MANAGER).string_build('''
class Browser(object):
def open(self, url, data=None, timeout=None):
return None
def open_novisit(self, url, data=None, timeout=None):
return None
def open_local_file(self, filename):
return None
''')
register_module_extender(MANAGER, 'mechanize', mechanize_transform)

View file

@ -0,0 +1,31 @@
"""Astroid hooks for pytest."""
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
def pytest_transform():
return AstroidBuilder(MANAGER).string_build('''
try:
import _pytest.mark
import _pytest.recwarn
import _pytest.runner
import _pytest.python
except ImportError:
pass
else:
deprecated_call = _pytest.recwarn.deprecated_call
exit = _pytest.runner.exit
fail = _pytest.runner.fail
fixture = _pytest.python.fixture
importorskip = _pytest.runner.importorskip
mark = _pytest.mark.MarkGenerator()
raises = _pytest.python.raises
skip = _pytest.runner.skip
yield_fixture = _pytest.python.yield_fixture
''')
register_module_extender(MANAGER, 'pytest', pytest_transform)
register_module_extender(MANAGER, 'py.test', pytest_transform)

View file

@ -0,0 +1,22 @@
"""Astroid hooks for the Python 2 qt4 module.
Currently help understanding of :
* PyQT4.QtCore
"""
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
def pyqt4_qtcore_transform():
return AstroidBuilder(MANAGER).string_build('''
def SIGNAL(signal_name): pass
class QObject(object):
def emit(self, signal): pass
''')
register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform)

View file

@ -0,0 +1,334 @@
"""Astroid hooks for the Python 2 standard library.
Currently help understanding of :
* hashlib.md5 and hashlib.sha1
"""
import sys
from functools import partial
from textwrap import dedent
from astroid import (
MANAGER, AsStringRegexpPredicate,
UseInferenceDefault, inference_tip,
YES, InferenceError, register_module_extender)
from astroid import exceptions
from astroid import nodes
from astroid.builder import AstroidBuilder
PY3K = sys.version_info > (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)

View file

@ -0,0 +1,79 @@
# 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 <http://www.gnu.org/licenses/>.
"""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')

View file

@ -0,0 +1,261 @@
# 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 <http://www.gnu.org/licenses/>.
"""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)

View file

@ -0,0 +1,240 @@
# 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 <http://www.gnu.org/licenses/>.
"""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, "<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

View file

@ -0,0 +1,51 @@
# 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 <http://www.gnu.org/licenses/>.
"""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
"""

View file

@ -0,0 +1,405 @@
# 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 <http://www.gnu.org/licenses/>.
"""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

View file

@ -0,0 +1,273 @@
# 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 <http://www.gnu.org/licenses/>.
"""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)

View file

@ -0,0 +1,391 @@
# 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 <http://www.gnu.org/licenses/>.
"""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' : "<file>",
'dest' : "black_list", "default" : ('CVS',),
'help' : "add <file> (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' : '<project name>',
'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 '<Project %r at %s (%s modules)>' % (self.name, id(self),
len(self.modules))

View file

@ -0,0 +1,124 @@
# 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 <http://www.gnu.org/licenses/>.
"""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 <modname> imported by <self>
"""
# 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)

View file

@ -0,0 +1,670 @@
# 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 <http://www.gnu.org/licenses/>.
"""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 <http://bugs.python.org/issue1294959>`_
# :see: `FHS <http://www.pathname.com/fhs/pub/fhs-2.3.html#LIBLTQUALGTALTERNATEFORMATESSENTIAL>`_
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

Some files were not shown because too many files have changed in this diff Show more