From f9deee0e416f626d8597e3e6ea8e89172e5aaa78 Mon Sep 17 00:00:00 2001 From: a666 <19142162+a666@users.noreply.github.com> Date: Sat, 2 Oct 2021 02:37:57 -0500 Subject: Add flakehell python linter (#3295) (#3921) --- ale_linters/python/flakehell.vim | 175 +++++++++++++ doc/ale-python.txt | 63 +++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/handler/test_flakehell_handler.vader | 276 +++++++++++++++++++++ test/linter/test_flakehell.vader | 202 +++++++++++++++ .../with_virtualenv/env/Scripts/flakehell.exe | 0 .../python/with_virtualenv/env/bin/flakehell | 0 9 files changed, 719 insertions(+) create mode 100644 ale_linters/python/flakehell.vim create mode 100644 test/handler/test_flakehell_handler.vader create mode 100644 test/linter/test_flakehell.vader create mode 100755 test/test-files/python/with_virtualenv/env/Scripts/flakehell.exe create mode 100755 test/test-files/python/with_virtualenv/env/bin/flakehell diff --git a/ale_linters/python/flakehell.vim b/ale_linters/python/flakehell.vim new file mode 100644 index 00000000..ffe87e29 --- /dev/null +++ b/ale_linters/python/flakehell.vim @@ -0,0 +1,175 @@ +" Author: w0rp +" Description: flakehell for python files + +call ale#Set('python_flakehell_executable', 'flakehell') +call ale#Set('python_flakehell_options', '') +call ale#Set('python_flakehell_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_flakehell_change_directory', 'project') +call ale#Set('python_flakehell_auto_pipenv', 0) +call ale#Set('python_flakehell_auto_poetry', 0) + +function! s:UsingModule(buffer) abort + return ale#Var(a:buffer, 'python_flakehell_executable') is? 'python' +endfunction + +function! ale_linters#python#flakehell#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_flakehell_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + + if (ale#Var(a:buffer, 'python_auto_poetry') || ale#Var(a:buffer, 'python_flakehell_auto_poetry')) + \ && ale#python#PoetryPresent(a:buffer) + return 'poetry' + endif + + if !s:UsingModule(a:buffer) + return ale#python#FindExecutable(a:buffer, 'python_flakehell', ['flakehell']) + endif + + return ale#Var(a:buffer, 'python_flakehell_executable') +endfunction + +function! ale_linters#python#flakehell#RunWithVersionCheck(buffer) abort + let l:executable = ale_linters#python#flakehell#GetExecutable(a:buffer) + + let l:module_string = s:UsingModule(a:buffer) ? ' -m flakehell' : '' + let l:command = ale#Escape(l:executable) . l:module_string . ' --version' + + return ale#semver#RunWithVersionCheck( + \ a:buffer, + \ l:executable, + \ l:command, + \ function('ale_linters#python#flakehell#GetCommand'), + \) +endfunction + +function! ale_linters#python#flakehell#GetCwd(buffer) abort + let l:change_directory = ale#Var(a:buffer, 'python_flakehell_change_directory') + let l:cwd = '' + + if l:change_directory is# 'project' + let l:project_root = ale#python#FindProjectRootIni(a:buffer) + + if !empty(l:project_root) + let l:cwd = l:project_root + endif + endif + + if (l:change_directory is# 'project' && empty(l:cwd)) + \|| l:change_directory is# 1 + \|| l:change_directory is# 'file' + let l:cwd = '%s:h' + endif + + return l:cwd +endfunction + +function! ale_linters#python#flakehell#GetCommand(buffer, version) abort + let l:executable = ale_linters#python#flakehell#GetExecutable(a:buffer) + + if (l:executable =~? 'pipenv\|poetry$') + let l:exec_args = ' run flakehell' + elseif (l:executable is? 'python') + let l:exec_args = ' -m flakehell' + else + let l:exec_args = '' + endif + + " Only include the --stdin-display-name argument if we can parse the + " flakehell version, and it is recent enough to support it. + let l:display_name_args = ale#semver#GTE(a:version, [0, 8, 0]) + \ ? ' --stdin-display-name %s' + \ : '' + + let l:options = ale#Var(a:buffer, 'python_flakehell_options') + + return ale#Escape(l:executable) + \ . l:exec_args + \ . (!empty(l:options) ? ' lint ' . l:options : ' lint') + \ . ' --format=default' + \ . l:display_name_args . ' -' +endfunction + +let s:end_col_pattern_map = { +\ 'F405': '\(.\+\) may be undefined', +\ 'F821': 'undefined name ''\([^'']\+\)''', +\ 'F999': '^''\([^'']\+\)''', +\ 'F841': 'local variable ''\([^'']\+\)''', +\} + +function! ale_linters#python#flakehell#Handle(buffer, lines) abort + let l:output = ale#python#HandleTraceback(a:lines, 10) + + if !empty(l:output) + return l:output + endif + + " Matches patterns line the following: + " + " Matches patterns line the following: + " + " stdin:6:6: E111 indentation is not a multiple of four + let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):?(\d+)?: ([[:alnum:]]+):? (.*)$' + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:code = l:match[3] + + if (l:code is# 'W291' || l:code is# 'W293') + \ && !ale#Var(a:buffer, 'warn_about_trailing_whitespace') + " Skip warnings for trailing whitespace if the option is off. + continue + endif + + if l:code is# 'W391' + \&& !ale#Var(a:buffer, 'warn_about_trailing_blank_lines') + " Skip warnings for trailing blank lines if the option is off + continue + endif + + let l:item = { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'vcol': 1, + \ 'text': l:match[4], + \ 'code': l:code, + \ 'type': 'W', + \} + + if l:code[:0] is# 'F' + if l:code isnot# 'F401' + let l:item.type = 'E' + endif + elseif l:code[:0] is# 'E' + let l:item.type = 'E' + + if l:code isnot# 'E999' && l:code isnot# 'E112' + let l:item.sub_type = 'style' + endif + elseif l:code[:0] is# 'W' + let l:item.sub_type = 'style' + endif + + let l:end_col_pattern = get(s:end_col_pattern_map, l:code, '') + + if !empty(l:end_col_pattern) + let l:end_col_match = matchlist(l:match[4], l:end_col_pattern) + + if !empty(l:end_col_match) + let l:item.end_col = l:item.col + len(l:end_col_match[1]) - 1 + endif + endif + + call add(l:output, l:item) + endfor + + return l:output +endfunction + +call ale#linter#Define('python', { +\ 'name': 'flakehell', +\ 'executable': function('ale_linters#python#flakehell#GetExecutable'), +\ 'cwd': function('ale_linters#python#flakehell#GetCwd'), +\ 'command': function('ale_linters#python#flakehell#RunWithVersionCheck'), +\ 'callback': 'ale_linters#python#flakehell#Handle', +\}) diff --git a/doc/ale-python.txt b/doc/ale-python.txt index 8f0d8ffa..10cc2897 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -320,6 +320,69 @@ g:ale_python_flake8_auto_poetry *g:ale_python_flake8_auto_poetry* Detect whether the file is inside a poetry, and set the executable to `poetry` if true. This is overridden by a manually-set executable. +=============================================================================== +flakehell *ale-python-flakehell* + +g:ale_python_flakehell_change_directory*g:ale_python_flakehell_change_directory* + *b:ale_python_flakehell_change_directory* + Type: |String| + Default: `project` + + If set to `project`, ALE will switch to the project root before checking file. + If set to `file`, ALE will switch to directory the Python file being + checked with `flakehell` is in before checking it. + You can turn it off with `off` option if you want to control the directory + Python is executed from yourself. + + +g:ale_python_flakehell_executable *g:ale_python_flakehell_executable* + *b:ale_python_flakehell_executable* + Type: |String| + Default: `'flakehell'` + + This variable can be changed to modify the executable used for flakehell. Set + this to `'pipenv'` to invoke `'pipenv` `run` `flakehell'`. Set this to + `'poetry'` to invoke `'poetry` `run` `flakehell'`. Set this to `'python'` to + invoke `'python` `-m` `flakehell'`. + + +g:ale_python_flakehell_options *g:ale_python_flakehell_options* + *b:ale_python_flakehell_options* + Type: |String| + Default: `''` + + This variable can be changed to add command-line arguments to the flakehell + lint invocation. + + +g:ale_python_flakehell_use_global *g:ale_python_flakehell_use_global* + *b:ale_python_flakehell_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + This variable controls whether or not ALE will search for flakehell in a + virtualenv directory first. If this variable is set to `1`, then ALE will + always use |g:ale_python_flakehell_executable| for the executable path. + + Both variables can be set with `b:` buffer variables instead. + + +g:ale_python_flakehell_auto_pipenv *g:ale_python_flakehell_auto_pipenv* + *b:ale_python_flakehell_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + +g:ale_python_flakehell_auto_poetry *g:ale_python_flakehell_auto_poetry* + *b:ale_python_flakehell_auto_poetry* + Type: |Number| + Default: `0` + + Detect whether the file is inside a poetry, and set the executable to `poetry` + if true. This is overridden by a manually-set executable. =============================================================================== isort *ale-python-isort* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index fb244c88..5617b33d 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -419,6 +419,7 @@ Notes: * `bandit` * `black` * `flake8` + * `flakehell` * `isort` * `mypy` * `prospector`!! diff --git a/doc/ale.txt b/doc/ale.txt index 15ebb5ed..f58dba01 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2953,6 +2953,7 @@ documented in additional help files. bandit................................|ale-python-bandit| black.................................|ale-python-black| flake8................................|ale-python-flake8| + flakehell.............................|ale-python-flakehell| isort.................................|ale-python-isort| mypy..................................|ale-python-mypy| prospector............................|ale-python-prospector| diff --git a/supported-tools.md b/supported-tools.md index 49a83d75..f6e7dbc5 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -428,6 +428,7 @@ formatting. * [bandit](https://github.com/PyCQA/bandit) :warning: * [black](https://github.com/ambv/black) * [flake8](http://flake8.pycqa.org/en/latest/) + * [flakehell](https://github.com/flakehell/flakehell) * [isort](https://github.com/timothycrosley/isort) * [mypy](http://mypy-lang.org/) * [prospector](https://github.com/PyCQA/prospector) :warning: :floppy_disk: diff --git a/test/handler/test_flakehell_handler.vader b/test/handler/test_flakehell_handler.vader new file mode 100644 index 00000000..1f77bd96 --- /dev/null +++ b/test/handler/test_flakehell_handler.vader @@ -0,0 +1,276 @@ +Before: + Save g:ale_warn_about_trailing_blank_lines + Save g:ale_warn_about_trailing_whitespace + + let g:ale_warn_about_trailing_blank_lines = 1 + let g:ale_warn_about_trailing_whitespace = 1 + + runtime ale_linters/python/flakehell.vim + +After: + Restore + + unlet! b:ale_warn_about_trailing_blank_lines + unlet! b:ale_warn_about_trailing_whitespace + + call ale#linter#Reset() + +Execute(The flakehell handler should handle basic warnings and syntax errors): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 6, + \ 'vcol': 1, + \ 'type': 'E', + \ 'text': 'indentation is not a multiple of four', + \ 'code': 'E111', + \ 'sub_type': 'style', + \ }, + \ { + \ 'lnum': 7, + \ 'col': 6, + \ 'vcol': 1, + \ 'type': 'W', + \ 'text': 'some warning', + \ 'code': 'W123', + \ 'sub_type': 'style', + \ }, + \ { + \ 'lnum': 8, + \ 'col': 3, + \ 'vcol': 1, + \ 'type': 'E', + \ 'text': 'SyntaxError: invalid syntax', + \ 'code': 'E999', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(1, [ + \ 'stdin:6:6: E111 indentation is not a multiple of four', + \ 'stdin:7:6: W123 some warning', + \ 'stdin:8:3: E999 SyntaxError: invalid syntax', + \ ]) + +Execute(The flakehell handler should set end column indexes for certain errors): + AssertEqual + \ [ + \ { + \ 'lnum': 25, + \ 'col': 1, + \ 'vcol': 1, + \ 'type': 'E', + \ 'end_col': 3, + \ 'text': 'undefined name ''foo''', + \ 'code': 'F821', + \ }, + \ { + \ 'lnum': 28, + \ 'col': 5, + \ 'vcol': 1, + \ 'type': 'E', + \ 'end_col': 9, + \ 'text': 'hello may be undefined, or defined from star imports: x', + \ 'code': 'F405', + \ }, + \ { + \ 'lnum': 104, + \ 'col': 5, + \ 'vcol': 1, + \ 'type': 'E', + \ 'end_col': 12, + \ 'text': '''continue'' not properly in loop', + \ 'code': 'F999', + \ }, + \ { + \ 'lnum': 106, + \ 'col': 5, + \ 'vcol': 1, + \ 'type': 'E', + \ 'end_col': 9, + \ 'text': '''break'' outside loop', + \ 'code': 'F999', + \ }, + \ { + \ 'lnum': 109, + \ 'col': 5, + \ 'vcol': 1, + \ 'type': 'E', + \ 'end_col': 8, + \ 'text': 'local variable ''test'' is assigned to but never used', + \ 'code': 'F841', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(1, [ + \ 'foo.py:25:1: F821 undefined name ''foo''', + \ 'foo.py:28:5: F405 hello may be undefined, or defined from star imports: x', + \ 'foo.py:104:5: F999 ''continue'' not properly in loop', + \ 'foo.py:106:5: F999 ''break'' outside loop', + \ 'foo.py:109:5: F841 local variable ''test'' is assigned to but never used', + \ ]) + +Execute(The flakehell handler should handle stack traces): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'text': 'ImportError: No module named parser (See :ALEDetail)', + \ 'detail': join([ + \ 'Traceback (most recent call last):', + \ ' File "/usr/local/bin/flakehell", line 7, in ', + \ ' from flakehell.main.cli import main', + \ ' File "/usr/local/lib/python2.7/dist-packages/flakehell/main/cli.py", line 2, in ', + \ ' from flakehell.main import application', + \ ' File "/usr/local/lib/python2.7/dist-packages/flakehell/main/application.py", line 17, in ', + \ ' from flakehell.plugins import manager as plugin_manager', + \ ' File "/usr/local/lib/python2.7/dist-packages/flakehell/plugins/manager.py", line 5, in ', + \ ' import pkg_resources', + \ ' File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 35, in ', + \ ' import email.parser', + \ 'ImportError: No module named parser', + \ ], "\n"), + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(42, [ + \ 'Traceback (most recent call last):', + \ ' File "/usr/local/bin/flakehell", line 7, in ', + \ ' from flakehell.main.cli import main', + \ ' File "/usr/local/lib/python2.7/dist-packages/flakehell/main/cli.py", line 2, in ', + \ ' from flakehell.main import application', + \ ' File "/usr/local/lib/python2.7/dist-packages/flakehell/main/application.py", line 17, in ', + \ ' from flakehell.plugins import manager as plugin_manager', + \ ' File "/usr/local/lib/python2.7/dist-packages/flakehell/plugins/manager.py", line 5, in ', + \ ' import pkg_resources', + \ ' File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 35, in ', + \ ' import email.parser', + \ 'ImportError: No module named parser', + \ ]) + +Execute(The flakehell handler should handle names with spaces): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 6, + \ 'vcol': 1, + \ 'type': 'E', + \ 'text': 'indentation is not a multiple of four', + \ 'code': 'E111', + \ 'sub_type': 'style', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(42, [ + \ 'C:\something\with spaces.py:6:6: E111 indentation is not a multiple of four', + \ ]) + +Execute(Warnings about trailing whitespace should be reported by default): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'vcol': 1, + \ 'code': 'W291', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'who cares', + \ }, + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'vcol': 1, + \ 'code': 'W293', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'who cares', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(bufnr(''), [ + \ 'foo.py:6:1: W291 who cares', + \ 'foo.py:6:1: W293 who cares', + \ ]) + +Execute(Disabling trailing whitespace warnings should work): + let b:ale_warn_about_trailing_whitespace = 0 + + AssertEqual + \ [ + \ ], + \ ale_linters#python#flakehell#Handle(bufnr(''), [ + \ 'foo.py:6:1: W291 who cares', + \ 'foo.py:6:1: W293 who cares', + \ ]) + +Execute(Warnings about trailing blank lines should be reported by default): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'vcol': 1, + \ 'code': 'W391', + \ 'type': 'W', + \ 'sub_type': 'style', + \ 'text': 'blank line at end of file', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(bufnr(''), [ + \ 'foo.py:6:1: W391 blank line at end of file', + \ ]) + +Execute(Disabling trailing blank line warnings should work): + let b:ale_warn_about_trailing_blank_lines = 0 + + AssertEqual + \ [ + \ ], + \ ale_linters#python#flakehell#Handle(bufnr(''), [ + \ 'foo.py:6:1: W391 blank line at end of file', + \ ]) + +Execute(F401 should be a warning): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'vcol': 1, + \ 'code': 'F401', + \ 'type': 'W', + \ 'text': 'module imported but unused', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(bufnr(''), [ + \ 'foo.py:6:1: F401 module imported but unused', + \ ]) + +Execute(E112 should be a syntax error): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'vcol': 1, + \ 'code': 'E112', + \ 'type': 'E', + \ 'text': 'expected an indented block', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(bufnr(''), [ + \ 'foo.py:6:1: E112 expected an indented block', + \ ]) + +Execute(Compatibility with hacking which uses older style flakehell): + AssertEqual + \ [ + \ { + \ 'lnum': 6, + \ 'col': 1, + \ 'vcol': 1, + \ 'code': 'H306', + \ 'type': 'W', + \ 'text': 'imports not in alphabetical order (smtplib, io)', + \ }, + \ ], + \ ale_linters#python#flakehell#Handle(bufnr(''), [ + \ 'foo.py:6:1: H306: imports not in alphabetical order (smtplib, io)', + \ ]) diff --git a/test/linter/test_flakehell.vader b/test/linter/test_flakehell.vader new file mode 100644 index 00000000..fa7cb9e3 --- /dev/null +++ b/test/linter/test_flakehell.vader @@ -0,0 +1,202 @@ +Before: + call ale#assert#SetUpLinterTest('python', 'flakehell') + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + + GivenCommandOutput ['0.8.0'] + +After: + unlet! b:executable + unlet! b:bin_dir + call ale#assert#TearDownLinterTest() + +Execute(The flakehell callbacks should return the correct default values): + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' --version', + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + + " The version check should be cached. + GivenCommandOutput [] + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + +Execute(The option for disabling changing directories should work): + let g:ale_python_flakehell_change_directory = 'off' + + AssertLinterCwd ['', ''] + call ale#semver#ResetVersionCache() + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' --version', + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + + let g:ale_python_flakehell_change_directory = 0 + + AssertLinterCwd [''] + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + + " Invalid options should be considered the same as turning the setting off. + let g:ale_python_flakehell_change_directory = 'xxx' + + AssertLinterCwd [''] + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + +Execute(The option for changing directory to project root should work): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/namespace_package_tox/namespace/foo/bar.py') + + AssertLinterCwd ale#python#FindProjectRootIni(bufnr('')) + call ale#semver#ResetVersionCache() + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' --version', + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + +Execute(The option for changing directory to file dir should work): + let g:ale_python_flakehell_change_directory = 'file' + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/namespace_package_tox/namespace/foo/bar.py') + + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' --version', + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + + let g:ale_python_flakehell_change_directory = 1 + + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' lint --format=default --stdin-display-name %s -', + \] + +Execute(The flakehell command callback should let you set options): + let g:ale_python_flakehell_options = '--some-option' + + GivenCommandOutput ['0.8.0'] + AssertLinter 'flakehell', [ + \ ale#Escape('flakehell') . ' --version', + \ ale#Escape('flakehell') . ' lint --some-option' + \ . ' --format=default --stdin-display-name %s -', + \] + +Execute(You should be able to set a custom executable and it should be escaped): + let g:ale_python_flakehell_executable = 'executable with spaces' + + AssertLinterCwd ['%s:h', '%s:h'] + call ale#semver#ResetVersionCache() + AssertLinter 'executable with spaces', [ + \ ale#Escape('executable with spaces') . ' --version', + \ ale#Escape('executable with spaces') + \ . ' lint' + \ . ' --format=default' + \ . ' --stdin-display-name %s -', + \] + +Execute(The flakehell callbacks should detect virtualenv directories): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py') + + let b:executable = ale#path#Simplify( + \ g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/flakehell' + \) + + AssertLinter b:executable, [ + \ ale#Escape(b:executable) . ' --version', + \ ale#Escape(b:executable) + \ . ' lint' + \ . ' --format=default' + \ . ' --stdin-display-name %s -', + \] + +Execute(The FindProjectRoot should detect the project root directory for namespace package via Manifest.in): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/namespace_package_manifest/namespace/foo/bar.py') + + AssertEqual + \ ale#path#Simplify(g:dir . '/../test-files/python/namespace_package_manifest'), + \ ale#python#FindProjectRoot(bufnr('')) + +Execute(The FindProjectRoot should detect the project root directory for namespace package via setup.cf): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/namespace_package_setup/namespace/foo/bar.py') + + AssertEqual + \ ale#path#Simplify(g:dir . '/../test-files/python/namespace_package_setup'), + \ ale#python#FindProjectRoot(bufnr('')) + +Execute(The FindProjectRoot should detect the project root directory for namespace package via pytest.ini): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/namespace_package_pytest/namespace/foo/bar.py') + + AssertEqual + \ ale#path#Simplify(g:dir . '/../test-files/python/namespace_package_pytest'), + \ ale#python#FindProjectRoot(bufnr('')) + +Execute(The FindProjectRoot should detect the project root directory for namespace package via tox.ini): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/namespace_package_tox/namespace/foo/bar.py') + + AssertEqual + \ ale#path#Simplify(g:dir . '/../test-files/python/namespace_package_tox'), + \ ale#python#FindProjectRoot(bufnr('')) + +Execute(The FindProjectRoot should detect the project root directory for non-namespace package): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/no_virtualenv/subdir/foo/bar.py') + + AssertEqual + \ ale#path#Simplify(g:dir . '/../test-files/python/no_virtualenv/subdir'), + \ ale#python#FindProjectRoot(bufnr('')) + +" Some users currently run flakehell this way, so we should support it. +Execute(Using `python -m flakehell` should be supported for running flakehell): + silent execute 'file ' . fnameescape(g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py') + + let g:ale_python_flakehell_executable = 'python' + let g:ale_python_flakehell_options = '--some-option' + + AssertLinter 'python', [ + \ ale#Escape('python') . ' -m flakehell --version', + \ ale#Escape('python') + \ . ' -m flakehell lint --some-option --format=default --stdin-display-name %s -' + \] + + call ale#semver#ResetVersionCache() + + " Leading spaces shouldn't matter + let g:ale_python_flakehell_options = ' --some-option' + + AssertLinter 'python', [ + \ ale#Escape('python') . ' -m flakehell --version', + \ ale#Escape('python') + \ . ' -m flakehell lint --some-option --format=default --stdin-display-name %s -' + \] + +Execute(Setting executable to 'pipenv' should append 'run flakehell'): + let g:ale_python_flakehell_executable = 'path/to/pipenv' + + " FIXME: pipenv should check the version with flakehell. + GivenCommandOutput [] + AssertLinter 'path/to/pipenv', + \ ale#Escape('path/to/pipenv') . ' run flakehell lint --format=default -' + +Execute(Pipenv is detected when python_flakehell_auto_pipenv is set): + let g:ale_python_flakehell_auto_pipenv = 1 + call ale#test#SetFilename('../test-files/python/pipenv/whatever.py') + + AssertLinterCwd ale#python#FindProjectRootIni(bufnr('')) + AssertLinter 'pipenv', + \ ale#Escape('pipenv') . ' run flakehell lint --format=default --stdin-display-name %s -' + +Execute(Setting executable to 'poetry' should append 'run flakehell'): + let g:ale_python_flakehell_executable = 'path/to/poetry' + + " FIXME: poetry should check the version with flakehell. + GivenCommandOutput [] + AssertLinter 'path/to/poetry', + \ ale#Escape('path/to/poetry') . ' run flakehell lint --format=default -' + +Execute(poetry is detected when python_flakehell_auto_poetry is set): + let g:ale_python_flakehell_auto_poetry = 1 + call ale#test#SetFilename('../test-files/python/poetry/whatever.py') + + AssertLinterCwd ale#python#FindProjectRootIni(bufnr('')) + AssertLinter 'poetry', + \ ale#Escape('poetry') . ' run flakehell lint --format=default --stdin-display-name %s -' diff --git a/test/test-files/python/with_virtualenv/env/Scripts/flakehell.exe b/test/test-files/python/with_virtualenv/env/Scripts/flakehell.exe new file mode 100755 index 00000000..e69de29b diff --git a/test/test-files/python/with_virtualenv/env/bin/flakehell b/test/test-files/python/with_virtualenv/env/bin/flakehell new file mode 100755 index 00000000..e69de29b -- cgit v1.2.3