From 8f8d015daeb2070b20c8296dd8488e706332b5b7 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 25 Aug 2017 04:46:56 -0700 Subject: [PATCH] Add pycodestyle Python linter support (#872) Add a pycodestyle linter --- README.md | 2 +- ale_linters/python/pycodestyle.vim | 42 ++++++++++++++++ doc/ale-python.txt | 29 +++++++++++ doc/ale.txt | 1 + .../test_pycodestyle_command_callback.vader | 23 +++++++++ test/handler/test_pycodestyle_handler.vader | 48 +++++++++++++++++++ 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 ale_linters/python/pycodestyle.vim create mode 100644 test/command_callback/test_pycodestyle_command_callback.vader create mode 100644 test/handler/test_pycodestyle_handler.vader diff --git a/README.md b/README.md index 24024d3..cbfef30 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ name. That seems to be the fairest way to arrange this table. | Pod | [proselint](http://proselint.com/)| | Pug | [pug-lint](https://github.com/pugjs/pug-lint) | | Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | -| Python | [autopep8](https://github.com/hhatto/autopep8), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [pylint](https://www.pylint.org/), [yapf](https://github.com/google/yapf) | +| Python | [autopep8](https://github.com/hhatto/autopep8), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pylint](https://www.pylint.org/), [yapf](https://github.com/google/yapf) | | R | [lintr](https://github.com/jimhester/lintr) | | ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-integration-reason-merlin` for configuration instructions | reStructuredText | [proselint](http://proselint.com/)| diff --git a/ale_linters/python/pycodestyle.vim b/ale_linters/python/pycodestyle.vim new file mode 100644 index 0000000..ad89599 --- /dev/null +++ b/ale_linters/python/pycodestyle.vim @@ -0,0 +1,42 @@ +" Author: Michael Thiesen +" Description: pycodestyle linting for python files + +call ale#Set('python_pycodestyle_executable', 'pycodestyle') +call ale#Set('python_pycodestyle_options', '') +call ale#Set('python_pycodestyle_use_global', 0) + +function! ale_linters#python#pycodestyle#GetExecutable(buffer) abort + return ale#python#FindExecutable(a:buffer, 'python_pycodestyle', ['pycodestyle']) +endfunction + +function! ale_linters#python#pycodestyle#GetCommand(buffer) abort + return ale#Escape(ale_linters#python#pycodestyle#GetExecutable(a:buffer)) + \ . ' ' + \ . ale#Var(a:buffer, 'python_pycodestyle_options') + \ . ' -' +endfunction + +function! ale_linters#python#pycodestyle#Handle(buffer, lines) abort + let l:pattern = '\v^(\S*):(\d*):(\d*): ((([EW])\d+) .*)$' + let l:output = [] + + " lines are formatted as follows: + " file.py:21:26: W291 trailing whitespace + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[2] + 0, + \ 'col': l:match[3] + 0, + \ 'type': l:match[6], + \ 'text': l:match[4], + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('python', { +\ 'name': 'pycodestyle', +\ 'executable_callback': 'ale_linters#python#pycodestyle#GetExecutable', +\ 'command_callback': 'ale_linters#python#pycodestyle#GetCommand', +\ 'callback': 'ale_linters#python#pycodestyle#Handle', +\}) diff --git a/doc/ale-python.txt b/doc/ale-python.txt index dabc573..a8d033e 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -122,6 +122,35 @@ g:ale_python_mypy_use_global *g:ale_python_mypy_use_global* See |ale-integrations-local-executables| +=============================================================================== +pycodestyle *ale-python-pycodestyle* + + +g:ale_python_pycodestyle_executable *g:ale_python_pycodestyle_executable* + *b:ale_python_pycodestyle_executable* + Type: |String| + Default: `'pycodestyle'` + + See |ale-integrations-local-executables| + + +g:ale_python_pycodestyle_options *g:ale_python_pycodestyle_options* + *b:ale_python_pycodestyle_options* + Type: |String| + Default: `''` + + This variable can be changed to add command-line arguments to the + pycodestyle invocation. + + +g:ale_python_pycodestyle_use_global *g:ale_python_pycodestyle_use_global* + *b:ale_python_pycodestyle_use_global* + Type: |Number| + Default: `0` + + See |ale-integrations-local-executables| + + =============================================================================== pylint *ale-python-pylint* diff --git a/doc/ale.txt b/doc/ale.txt index 56c5946..6c3be18 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -96,6 +96,7 @@ CONTENTS *ale-contents* flake8..............................|ale-python-flake8| isort...............................|ale-python-isort| mypy................................|ale-python-mypy| + pycodestyle.........................|ale-python-pycodestyle| pylint..............................|ale-python-pylint| yapf................................|ale-python-yapf| ruby..................................|ale-ruby-options| diff --git a/test/command_callback/test_pycodestyle_command_callback.vader b/test/command_callback/test_pycodestyle_command_callback.vader new file mode 100644 index 0000000..a516346 --- /dev/null +++ b/test/command_callback/test_pycodestyle_command_callback.vader @@ -0,0 +1,23 @@ +Before: + runtime ale_linters/python/pycodestyle.vim + Save g:ale_python_pycodestyle_executable, + \ g:ale_python_pycodestyle_options, + \ g:ale_python_pycodestyle_use_global + +After: + call ale#linter#Reset() + Restore + +Execute(The pycodestyle command callback should return default string): + AssertEqual '''pycodestyle'' -', + \ ale_linters#python#pycodestyle#GetCommand(bufnr('')) + +Execute(The pycodestyle command callback should allow options): + let g:ale_python_pycodestyle_options = '--exclude=test*.py' + AssertEqual '''pycodestyle'' --exclude=test*.py -', + \ ale_linters#python#pycodestyle#GetCommand(bufnr('')) + +Execute(The pycodestyle executable should be configurable): + let g:ale_python_pycodestyle_executable = '~/.local/bin/pycodestyle' + AssertEqual '''~/.local/bin/pycodestyle'' -', + \ ale_linters#python#pycodestyle#GetCommand(bufnr('')) diff --git a/test/handler/test_pycodestyle_handler.vader b/test/handler/test_pycodestyle_handler.vader new file mode 100644 index 0000000..cc83fc8 --- /dev/null +++ b/test/handler/test_pycodestyle_handler.vader @@ -0,0 +1,48 @@ +Before: + runtime ale_linters/python/pycodestyle.vim + +After: + call ale#linter#Reset() + silent file something_else.py + +Execute(The pycodestyle handler should parse output): + AssertEqual + \ [ + \ { + \ 'lnum': 69, + \ 'col': 11, + \ 'text': 'E401 multiple imports on one line', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 77, + \ 'col': 1, + \ 'text': 'E302 expected 2 blank lines, found 1', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 88, + \ 'col': 5, + \ 'text': 'E301 expected 1 blank line, found 0', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 222, + \ 'col': 34, + \ 'text': 'W602 deprecated form of raising exception', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 544, + \ 'col': 21, + \ 'text': 'W601 .has_key() is deprecated, use ''in''', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#python#pycodestyle#Handle(bufnr(''), [ + \ 'stdin:69:11: E401 multiple imports on one line', + \ 'stdin:77:1: E302 expected 2 blank lines, found 1', + \ 'stdin:88:5: E301 expected 1 blank line, found 0', + \ 'stdin:222:34: W602 deprecated form of raising exception', + \ 'example.py:544:21: W601 .has_key() is deprecated, use ''in''', + \ ])