-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for pydocstyle linter (#2085)
The linter can correctly parse pydocstyle output with any of the following command-line options enabled: --explain, --source, --debug, and/or --verbose
- Loading branch information
1 parent
2760cf7
commit fdd37ac
Showing
6 changed files
with
272 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
" Author: Pablo Acosta <pmasdev@gmail.com> | ||
" Description: pydocstyle for python files | ||
|
||
call ale#Set('python_pydocstyle_executable', 'pydocstyle') | ||
call ale#Set('python_pydocstyle_options', '') | ||
call ale#Set('python_pydocstyle_use_global', get(g:, 'ale_use_global_executables', 0)) | ||
call ale#Set('python_pydocstyle_auto_pipenv', 0) | ||
|
||
function! ale_linters#python#pydocstyle#GetExecutable(buffer) abort | ||
if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pydocstyle_auto_pipenv')) | ||
\ && ale#python#PipenvPresent(a:buffer) | ||
return 'pipenv' | ||
endif | ||
|
||
return ale#python#FindExecutable(a:buffer, 'python_pydocstyle', ['pydocstyle']) | ||
endfunction | ||
|
||
function! ale_linters#python#pydocstyle#GetCommand(buffer) abort | ||
let l:dir = fnamemodify(bufname(a:buffer), ':p:h') | ||
let l:executable = ale_linters#python#pydocstyle#GetExecutable(a:buffer) | ||
|
||
let l:exec_args = l:executable =~? 'pipenv$' | ||
\ ? ' run pydocstyle' | ||
\ : '' | ||
|
||
return ale#path#CdString(l:dir) | ||
\ . ale#Escape(l:executable) . l:exec_args | ||
\ . ' ' . ale#Var(a:buffer, 'python_pydocstyle_options') | ||
\ . ' ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:t')) | ||
endfunction | ||
|
||
function! ale_linters#python#pydocstyle#Handle(buffer, lines) abort | ||
" Matches patterns like the following: | ||
" mydir/myfile.py:33 in public function `myfunction`: | ||
" DXXX: Error description | ||
let l:fname = ale#Escape(fnamemodify(bufname(a:buffer), ':p:t')) | ||
let l:line1_pattern = '\v^' . l:fname . ':\s*(\d+)\s+.*$' | ||
let l:line2_pattern = '\v^.*([a-zA-Z]\d+):\s*(.*)$' | ||
let l:output = [] | ||
|
||
let l:num_lines = len(a:lines) | ||
let l:index = 0 | ||
|
||
while l:index < l:num_lines | ||
let l:lnum = matchlist(a:lines[l:index], l:line1_pattern) | ||
|
||
if !empty(l:lnum) && (l:index + 1 < l:num_lines) | ||
let l:desc = matchlist(a:lines[l:index + 1], l:line2_pattern) | ||
|
||
if !empty(l:desc) | ||
call add(l:output, { | ||
\ 'lnum': l:lnum[1] + 0, | ||
\ 'col': 1, | ||
\ 'type': 'W', | ||
\ 'text': l:desc[2], | ||
\ 'code': l:desc[1], | ||
\}) | ||
endif | ||
|
||
let l:index = l:index + 2 | ||
else | ||
let l:index = l:index + 1 | ||
endif | ||
endwhile | ||
|
||
return l:output | ||
endfunction | ||
|
||
call ale#linter#Define('python', { | ||
\ 'name': 'pydocstyle', | ||
\ 'executable_callback': 'ale_linters#python#pydocstyle#GetExecutable', | ||
\ 'command_callback': 'ale_linters#python#pydocstyle#GetCommand', | ||
\ 'callback': 'ale_linters#python#pydocstyle#Handle', | ||
\}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
test/command_callback/test_pydocstyle_command_callback.vader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
Before: | ||
call ale#assert#SetUpLinterTest('python', 'pydocstyle') | ||
|
||
After: | ||
call ale#assert#TearDownLinterTest() | ||
|
||
Execute(The pydocstyle command callback should return default string): | ||
AssertLinter 'pydocstyle', | ||
\ ale#path#BufferCdString(bufnr('')) | ||
\ . ale#Escape('pydocstyle') . ' ' . ale#Escape('dummy.txt') | ||
|
||
Execute(The pydocstyle command callback should allow options): | ||
let g:ale_python_pydocstyle_options = '--verbose' | ||
|
||
AssertLinter 'pydocstyle', | ||
\ ale#path#BufferCdString(bufnr('')) | ||
\ . ale#Escape('pydocstyle') . ' --verbose ' . ale#Escape('dummy.txt') | ||
|
||
Execute(The pydocstyle executable should be configurable): | ||
let g:ale_python_pydocstyle_executable = '~/.local/bin/pydocstyle' | ||
|
||
AssertLinter '~/.local/bin/pydocstyle', | ||
\ ale#path#BufferCdString(bufnr('')) | ||
\ . ale#Escape('~/.local/bin/pydocstyle') . ' ' . ale#Escape('dummy.txt') | ||
|
||
Execute(Setting executable to 'pipenv' appends 'run pydocstyle'): | ||
let g:ale_python_pydocstyle_executable = 'path/to/pipenv' | ||
|
||
AssertLinter 'path/to/pipenv', | ||
\ ale#path#BufferCdString(bufnr('')) | ||
\ . ale#Escape('path/to/pipenv') . ' run pydocstyle ' . ale#Escape('dummy.txt') | ||
|
||
Execute(Pipenv is detected when python_pydocstyle_auto_pipenv is set): | ||
let g:ale_python_pydocstyle_auto_pipenv = 1 | ||
call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') | ||
|
||
AssertLinter 'pipenv', | ||
\ ale#path#BufferCdString(bufnr('')) | ||
\ . ale#Escape('pipenv') . ' run pydocstyle ' . ale#Escape('whatever.py') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
Before: | ||
Save g:ale_warn_about_trailing_whitespace | ||
|
||
let g:ale_warn_about_trailing_whitespace = 1 | ||
|
||
runtime ale_linters/python/pydocstyle.vim | ||
|
||
After: | ||
Restore | ||
|
||
call ale#linter#Reset() | ||
|
||
silent file something_else.py | ||
|
||
" File sample.py | ||
" # sample.py file | ||
" | ||
" def main(): | ||
" """ | ||
" This is a multi-line description that should produce multiple errors to be | ||
" tested by the handler | ||
" """ | ||
" return Fales | ||
" | ||
" | ||
" if __name__ == '__main__': | ||
" main() | ||
" | ||
" The command to generate the handler input is: | ||
" | ||
" $ python -m pydocstyle --verbose --source --explain sample.py | ||
" [...] | ||
" $ | ||
|
||
Execute(Basic pydocstyle warnings should be handled): | ||
AssertEqual | ||
\ [ | ||
\ { | ||
\ 'lnum': 1, | ||
\ 'col': 1, | ||
\ 'text': 'Missing docstring in public module', | ||
\ 'code': 'D100', | ||
\ 'type': 'W', | ||
\ }, | ||
\ { | ||
\ 'lnum': 4, | ||
\ 'col': 1, | ||
\ 'text': '1 blank line required between summary line and description (found 0)', | ||
\ 'code': 'D205', | ||
\ 'type': 'W', | ||
\ }, | ||
\ { | ||
\ 'lnum': 4, | ||
\ 'col': 1, | ||
\ 'text': 'First line should end with a period (not ''e'')', | ||
\ 'code': 'D400', | ||
\ 'type': 'W', | ||
\ }, | ||
\ { | ||
\ 'lnum': 4, | ||
\ 'col': 1, | ||
\ 'text': 'First line should be in imperative mood; try rephrasing (found ''This'')', | ||
\ 'code': 'D401', | ||
\ 'type': 'W', | ||
\ }, | ||
\ ], | ||
\ ale_linters#python#pydocstyle#Handle(bufnr(''), [ | ||
\ 'Checking file ' . fnamemodify(bufname(bufnr('')), ':p') . '.', | ||
\ ale#Escape(fnamemodify(bufname(bufnr('')), ':t')) . ':1 at module level:', | ||
\ ' D100: Missing docstring in public module', | ||
\ '', | ||
\ ' All modules should normally have docstrings. [...] all functions and', | ||
\ ' classes exported by a module should also have docstrings. Public', | ||
\ ' methods (including the __init__ constructor) should also have', | ||
\ ' docstrings.', | ||
\ ' Note: Public (exported) definitions are either those with names listed', | ||
\ ' in __all__ variable (if present), or those that do not start', | ||
\ ' with a single underscore.', | ||
\ '', | ||
\ ' 1: # 2: 3: s 4: a 5: m 6: p 7: l ...', | ||
\ '', | ||
\ '', | ||
\ ale#Escape(fnamemodify(bufname(bufnr('')), ':t')) . ':4 in public function `main`:', | ||
\ ' D205: 1 blank line required between summary line and description (found 0)', | ||
\ '', | ||
\ ' Multi-line docstrings consist of a summary line just like a one-line', | ||
\ ' docstring, followed by a blank line, followed by a more elaborate', | ||
\ ' description. The summary line may be used by automatic indexing tools;', | ||
\ ' it is important that it fits on one line and is separated from the', | ||
\ ' rest of the docstring by a blank line.', | ||
\ '', | ||
\ ' 3: d 4: e 5: f 6: 7: m 8: a 9: i ...', | ||
\ '', | ||
\ '', | ||
\ ale#Escape(fnamemodify(bufname(bufnr('')), ':t')) . ':4 in public function `main`:', | ||
\ ' D400: First line should end with a period (not ''e'')', | ||
\ '', | ||
\ ' The [first line of a] docstring is a phrase ending in a period.', | ||
\ '', | ||
\ ' 3: d 4: e 5: f 6: 7: m 8: a 9: i ...', | ||
\ '', | ||
\ '', | ||
\ ale#Escape(fnamemodify(bufname(bufnr('')), ':t')) . ':4 in public function `main`:', | ||
\ ' D401: First line should be in imperative mood; try rephrasing (found ''This'')', | ||
\ '', | ||
\ ' [Docstring] prescribes the function or method''s effect as a command:', | ||
\ ' ("Do this", "Return that"), not as a description; e.g. don''t write', | ||
\ ' "Returns the pathname ...".', | ||
\ '', | ||
\ ' 3: d 4: e 5: f 6: 7: m 8: a 9: i ...', | ||
\ ]) | ||
|
||
Execute(Handler should handle empty output): | ||
AssertEqual | ||
\ [], | ||
\ ale_linters#python#pydocstyle#Handle(bufnr(''), []) |