From b6f6d84c25169744f43db6d0ee296ee44eb78d91 Mon Sep 17 00:00:00 2001 From: Yining Date: Fri, 27 Jan 2023 12:24:18 +1100 Subject: add: pycln as a python linter and fixer (#4415) this commit adds pycln as a Python linter and fixer, together with some tests and documentation. It addresses #4340 pycln repo: https://github.com/hadialqattan/pycln --- ale_linters/python/pycln.vim | 86 +++++++++++++ autoload/ale/fix/registry.vim | 5 + autoload/ale/fixers/pycln.vim | 90 +++++++++++++ doc/ale-python.txt | 72 +++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/fixers/test_pycln_fixer_callback.vader | 141 +++++++++++++++++++++ test/linter/test_pycln.vader | 124 ++++++++++++++++++ .../python/with_virtualenv/env/Scripts/pycln.exe | 0 .../python/with_virtualenv/env/bin/pycln | 0 11 files changed, 521 insertions(+) create mode 100644 ale_linters/python/pycln.vim create mode 100644 autoload/ale/fixers/pycln.vim create mode 100644 test/fixers/test_pycln_fixer_callback.vader create mode 100644 test/linter/test_pycln.vader create mode 100755 test/test-files/python/with_virtualenv/env/Scripts/pycln.exe create mode 100755 test/test-files/python/with_virtualenv/env/bin/pycln diff --git a/ale_linters/python/pycln.vim b/ale_linters/python/pycln.vim new file mode 100644 index 00000000..917a9757 --- /dev/null +++ b/ale_linters/python/pycln.vim @@ -0,0 +1,86 @@ +" Author: Yining +" Description: pycln as linter for python files + +call ale#Set('python_pycln_executable', 'pycln') +call ale#Set('python_pycln_options', '') +call ale#Set('python_pycln_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_pycln_change_directory', 1) +call ale#Set('python_pycln_auto_pipenv', 0) +call ale#Set('python_pycln_auto_poetry', 0) +call ale#Set('python_pycln_config_file', '') + +function! ale_linters#python#pycln#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pycln_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + + if (ale#Var(a:buffer, 'python_auto_poetry') || ale#Var(a:buffer, 'python_pycln_auto_poetry')) + \ && ale#python#PoetryPresent(a:buffer) + return 'poetry' + endif + + return ale#python#FindExecutable(a:buffer, 'python_pycln', ['pycln']) +endfunction + +function! ale_linters#python#pycln#GetCwd(buffer) abort + if ale#Var(a:buffer, 'python_pycln_change_directory') + " Run from project root if found, else from buffer dir. + let l:project_root = ale#python#FindProjectRoot(a:buffer) + + return !empty(l:project_root) ? l:project_root : '%s:h' + endif + + return '' +endfunction + +function! ale_linters#python#pycln#GetCommand(buffer, version) abort + let l:executable = ale_linters#python#pycln#GetExecutable(a:buffer) + let l:exec_args = l:executable =~? 'pipenv\|poetry$' + \ ? ' run pycln' + \ : '' + + let l:options = ale#Var(a:buffer, 'python_pycln_options') + let l:config_file = ale#Var(a:buffer, 'python_pycln_config_file') + let l:config_file = l:options !~# '\v(^| )--config ' && !empty(l:config_file) + \ ? ale#Escape(ale#path#Simplify(l:config_file)) + \ : '' + + " NOTE: pycln version `1.3.0` supports liniting input from stdin + return ale#Escape(l:executable) . l:exec_args + \ . ale#Pad(ale#Var(a:buffer, 'python_pycln_options')) + \ . (empty(l:config_file) ? '' : ' --config ' . l:config_file) + \ . ' --check' + \ . (ale#semver#GTE(a:version, [1, 3, 0]) ? ' -' : ' %s') +endfunction + +function! ale_linters#python#pycln#Handle(buffer, lines) abort + " Example: tmp/test.py:3:0 'import os' would be removed! + let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):(\d+):? (.+)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'text': l:match[3], + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('python', { +\ 'name': 'pycln', +\ 'executable': function('ale_linters#python#pycln#GetExecutable'), +\ 'cwd': function('ale_linters#python#pycln#GetCwd'), +\ 'command': {buffer -> ale#semver#RunWithVersionCheck( +\ buffer, +\ ale_linters#python#pycln#GetExecutable(buffer), +\ '%e --version', +\ function('ale_linters#python#pycln#GetCommand'), +\ )}, +\ 'callback': 'ale_linters#python#pycln#Handle', +\ 'output_stream': 'both', +\ 'read_buffer': 1, +\}) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 28e33926..6657fc3c 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -585,6 +585,11 @@ let s:default_registry = { \ 'function': 'ale#fixers#ruff#Fix', \ 'suggested_filetypes': ['python'], \ 'description': 'Fix python files with ruff.', +\ }, +\ 'pycln': { +\ 'function': 'ale#fixers#pycln#Fix', +\ 'suggested_filetypes': ['python'], +\ 'description': 'remove unused python import statements', \ } \} diff --git a/autoload/ale/fixers/pycln.vim b/autoload/ale/fixers/pycln.vim new file mode 100644 index 00000000..1f61d083 --- /dev/null +++ b/autoload/ale/fixers/pycln.vim @@ -0,0 +1,90 @@ +" Author: Yining +" Description: pycln as ALE fixer for python files + +call ale#Set('python_pycln_executable', 'pycln') +call ale#Set('python_pycln_options', '') +call ale#Set('python_pycln_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_pycln_change_directory', 1) +call ale#Set('python_pycln_auto_pipenv', 0) +call ale#Set('python_pycln_auto_poetry', 0) +call ale#Set('python_pycln_config_file', '') + +function! ale#fixers#pycln#GetCwd(buffer) abort + if ale#Var(a:buffer, 'python_pycln_change_directory') + " Run from project root if found, else from buffer dir. + let l:project_root = ale#python#FindProjectRoot(a:buffer) + + return !empty(l:project_root) ? l:project_root : '%s:h' + endif + + return '%s:h' +endfunction + +function! ale#fixers#pycln#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pycln_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + + if (ale#Var(a:buffer, 'python_auto_poetry') || ale#Var(a:buffer, 'python_pycln_auto_poetry')) + \ && ale#python#PoetryPresent(a:buffer) + return 'poetry' + endif + + return ale#python#FindExecutable(a:buffer, 'python_pycln', ['pycln']) +endfunction + +function! ale#fixers#pycln#GetCommand(buffer) abort + let l:executable = ale#fixers#pycln#GetExecutable(a:buffer) + let l:exec_args = l:executable =~? 'pipenv\|poetry$' + \ ? ' run pycln' + \ : '' + + return ale#Escape(l:executable) . l:exec_args +endfunction + +function! ale#fixers#pycln#FixForVersion(buffer, version) abort + let l:executable = ale#fixers#pycln#GetExecutable(a:buffer) + let l:cmd = [ale#Escape(l:executable)] + + if l:executable =~? 'pipenv\|poetry$' + call extend(l:cmd, ['run', 'pycln']) + endif + + let l:options = ale#Var(a:buffer, 'python_pycln_options') + + if !empty(l:options) + call add(l:cmd, l:options) + endif + + let l:config_file = ale#Var(a:buffer, 'python_pycln_config_file') + let l:config_file = l:options !~# '\v(^| )--config ' && !empty(l:config_file) + \ ? ale#Escape(ale#path#Simplify(l:config_file)) + \ : '' + + if !empty(l:config_file) + call add(l:cmd, '--config ' . l:config_file) + endif + + call add(l:cmd, '--silence') + + " NOTE: pycln version `1.3.0` support reading from stdin + call add(l:cmd, ale#semver#GTE(a:version, [1, 3, 0]) ? '-' : '%s') + + return { + \ 'cwd': ale#fixers#pycln#GetCwd(a:buffer), + \ 'command': join(l:cmd, ' '), + \} +endfunction + +function! ale#fixers#pycln#Fix(buffer) abort + let l:executable = ale#fixers#pycln#GetExecutable(a:buffer) + let l:command = ale#fixers#pycln#GetCommand(a:buffer) . ale#Pad('--version') + + return ale#semver#RunWithVersionCheck( + \ a:buffer, + \ l:executable, + \ l:command, + \ function('ale#fixers#pycln#FixForVersion'), + \) +endfunction diff --git a/doc/ale-python.txt b/doc/ale-python.txt index 07f54db8..aad64b1d 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -580,6 +580,78 @@ g:ale_python_prospector_auto_poetry *g:ale_python_prospector_auto_poetry* if true. This is overridden by a manually-set executable. +=============================================================================== +pycln *ale-python-pycln* + +g:ale_python_pycln_change_directory *g:ale_python_pycln_change_directory* + *b:ale_python_pycln_change_directory* + Type: |Number| + Default: `1` + + If set to `1`, `pycln` will be run from a detected project root, per + |ale-python-root|. if set to `0` or no project root detected, + `pycln` will be run from the buffer's directory. + + +g:ale_python_pycln_executable *g:ale_python_pycln_executable* + *b:ale_python_pycln_executable* + Type: |String| + Default: `'pycln'` + + See |ale-integrations-local-executables| + + Set this to `'pipenv'` to invoke `'pipenv` `run` `pycln'`. + Set this to `'poetry'` to invoke `'poetry` `run` `pycln'`. + + +g:ale_python_pycln_options *g:ale_python_pycln_options* + *b:ale_python_pycln_options* + Type: |String| + Default: `''` + + This variable can be changed to add command-line arguments to the pycln + invocation. + + For example, to select/enable and/or disable some error codes, + you may want to set > + let g:ale_python_pycln_options = '--expand-stars' + + +g:ale_python_pycln_config_file *g:ale_python_pycln_config_file* + *b:ale_python_pycln_config_file* + Type: |String| + Default: `''` + + Use this variable to set the configuration file. + If `'--config' ` is found in the |g:ale_python_pycln_options|, then that + option value will override the value in this variable. + +g:ale_python_pycln_use_global *g:ale_python_pycln_use_global* + *b:ale_python_pycln_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + See |ale-integrations-local-executables| + + +g:ale_python_pycln_auto_pipenv *g:ale_python_pycln_auto_pipenv* + *b:ale_python_pycln_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_pycln_auto_poetry *g:ale_python_pycln_auto_poetry* + *b:ale_python_pycln_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. + + =============================================================================== pycodestyle *ale-python-pycodestyle* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 4edc10c0..71b9c12d 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -481,6 +481,7 @@ Notes: * `isort` * `mypy` * `prospector`!! + * `pycln` * `pycodestyle` * `pydocstyle` * `pyflakes` diff --git a/doc/ale.txt b/doc/ale.txt index 949ceae0..2876b235 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3169,6 +3169,7 @@ documented in additional help files. isort.................................|ale-python-isort| mypy..................................|ale-python-mypy| prospector............................|ale-python-prospector| + pycln.................................|ale-python-pycln| pycodestyle...........................|ale-python-pycodestyle| pydocstyle............................|ale-python-pydocstyle| pyflakes..............................|ale-python-pyflakes| diff --git a/supported-tools.md b/supported-tools.md index d6ea92b4..348efe2e 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -490,6 +490,7 @@ formatting. * [isort](https://github.com/timothycrosley/isort) * [mypy](http://mypy-lang.org/) * [prospector](https://github.com/PyCQA/prospector) :warning: :floppy_disk: + * [pycln](https://github.com/hadialqattan/pycln) * [pycodestyle](https://github.com/PyCQA/pycodestyle) :warning: * [pydocstyle](https://www.pydocstyle.org/) :warning: * [pyflakes](https://github.com/PyCQA/pyflakes) diff --git a/test/fixers/test_pycln_fixer_callback.vader b/test/fixers/test_pycln_fixer_callback.vader new file mode 100644 index 00000000..30cfaa3b --- /dev/null +++ b/test/fixers/test_pycln_fixer_callback.vader @@ -0,0 +1,141 @@ +Before: + call ale#assert#SetUpFixerTest('python', 'pycln') + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + + let b:cmd_tail = ' --silence' +After: + call ale#assert#TearDownFixerTest() + + unlet! g:dir + unlet! b:bin_dir + +Execute(The pycln callback should return the correct default values): + let file_path = g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py' + + silent execute 'file ' . fnameescape(file_path) + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir'), + \ 'command': ale#Escape(ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/pycln')) . b:cmd_tail . ' -', + \ } + +Execute(The pycln callback should not use stdin for older versions (< 1.3.0)): + let file_path = g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py' + + silent execute 'file ' . fnameescape(file_path) + + GivenCommandOutput ['pycln, version 1.2.99'] + AssertFixer + \ { + \ 'cwd': ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir'), + \ 'command': ale#Escape(ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/pycln')) . b:cmd_tail . ' %s', + \ } + +Execute(The pycln callback should not change directory if the option is set to 0): + let g:ale_python_pycln_change_directory = 0 + + let file_path = g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py' + + silent execute 'file ' . fnameescape(file_path) + + let fname = ale#Escape(ale#path#Simplify(file_path)) + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': '%s:h', + \ 'command': ale#Escape(ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/pycln')) . b:cmd_tail . ' -', + \ } + +Execute(The pycln callback should respect custom options): + let g:ale_python_pycln_options = '--expand-stars --no-gitignore' + + let file_path = g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py' + + silent execute 'file ' . fnameescape(file_path) + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir'), + \ 'command': ale#Escape(ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/pycln')) + \ . ' --expand-stars --no-gitignore' . b:cmd_tail . ' -', + \ } + +Execute(Pipenv is detected when python_pycln_auto_pipenv is set): + let g:ale_python_pycln_auto_pipenv = 1 + let g:ale_python_pycln_change_directory = 0 + + let file_path = '../test-files/python/pipenv/whatever.py' + + call ale#test#SetFilename(file_path) + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': '%s:h', + \ 'command': ale#Escape('pipenv') . ' run pycln' . b:cmd_tail . ' -' + \ } + +Execute(Poetry is detected when python_pycln_auto_poetry is set): + let g:ale_python_pycln_auto_poetry = 1 + let g:ale_python_pycln_change_directory = 0 + + call ale#test#SetFilename('../test-files/python/poetry/whatever.py') + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': '%s:h', + \ 'command': ale#Escape('poetry') . ' run pycln' . b:cmd_tail . ' -' + \ } + +Execute(Poetry is detected when python_pycln_auto_poetry is set, and cwd respects change_directory option): + let g:ale_python_pycln_auto_poetry = 1 + let g:ale_python_pycln_change_directory = 1 + + call ale#test#SetFilename('../test-files/python/poetry/whatever.py') + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': ale#path#Simplify(g:dir . '/../test-files/python/poetry'), + \ 'command': ale#Escape('poetry') . ' run pycln' . b:cmd_tail . ' -' + \ } + +Execute(configuration files set in _config should be supported): + let g:ale_python_pycln_change_directory = 0 + let g:ale_python_pycln_config_file = ale#path#Simplify(g:dir . '/../test-files/pycln/other_config.xml') + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': '%s:h', + \ 'command': ale#Escape('pycln') + \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/../test-files/pycln/other_config.xml')) + \ . b:cmd_tail . ' -' + \ } + +Execute(configuration file set in _options overrides _config): + let g:ale_python_pycln_change_directory = 0 + let g:ale_python_pycln_config_file = '/foo.xml' + let g:ale_python_pycln_options = '--config /bar.xml' + + GivenCommandOutput ['pycln, version 1.3.0'] + AssertFixer + \ { + \ 'cwd': '%s:h', + \ 'command': ale#Escape('pycln') . ' --config /bar.xml' . b:cmd_tail . ' -' + \ } + + let b:ale_python_pycln_options = '-x --config /bar.xml' + + AssertFixer + \ { + \ 'cwd': '%s:h', + \ 'command': ale#Escape('pycln') . ' -x --config /bar.xml' . b:cmd_tail . ' -' + \ } + diff --git a/test/linter/test_pycln.vader b/test/linter/test_pycln.vader new file mode 100644 index 00000000..860cbbc0 --- /dev/null +++ b/test/linter/test_pycln.vader @@ -0,0 +1,124 @@ +Before: + Save g:ale_python_auto_pipenv + + let g:ale_python_auto_pipenv = 0 + + call ale#assert#SetUpLinterTest('python', 'pycln') + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + let b:cmd_tail = ' --check -' + + GivenCommandOutput ['pycln, version 1.3.0'] + +After: + unlet! b:bin_dir + unlet! b:executable + unlet! b:cmd_tail + + call ale#assert#TearDownLinterTest() + +Execute(The pycln callbacks should return the correct default values): + AssertLinterCwd expand('%:p:h') + AssertLinter 'pycln', ale#Escape('pycln') . b:cmd_tail + +Execute(pycln should run with the file path of buffer in old versions): + " version `1.3.0` supports liniting input from stdin + GivenCommandOutput ['pycln, version 1.2.99'] + + AssertLinterCwd expand('%:p:h') + AssertLinter 'pycln', ale#Escape('pycln') . b:cmd_tail[:-3] . ' %s' + +Execute(pycln should run with the stdin in new enough versions): + GivenCommandOutput ['pycln, version 1.3.0'] + + AssertLinterCwd expand('%:p:h') + AssertLinter 'pycln', ale#Escape('pycln') . b:cmd_tail[:-3] . ' -' + " AssertLinter 'pycln', ale#Escape('pycln') . b:cmd_tail[:-3] . '--check -' + +Execute(The option for disabling changing directories should work): + let g:ale_python_pycln_change_directory = 0 + + AssertLinterCwd '' + AssertLinter 'pycln', ale#Escape('pycln') . b:cmd_tail + +Execute(The pycln executable should be configurable, and escaped properly): + let g:ale_python_pycln_executable = 'executable with spaces' + + AssertLinter 'executable with spaces', ale#Escape('executable with spaces') . b:cmd_tail + +Execute(The pycln command callback should let you set options): + let g:ale_python_pycln_options = '--some-flag' + AssertLinter 'pycln', ale#Escape('pycln') . ' --some-flag' . b:cmd_tail + + let g:ale_python_pycln_options = '--some-option value' + AssertLinter 'pycln', ale#Escape('pycln') . ' --some-option value' . b:cmd_tail + +Execute(The pycln callbacks shouldn't detect virtualenv directories where they don't exist): + call ale#test#SetFilename('../test-files/python/no_virtualenv/subdir/foo/bar.py') + + AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/python/no_virtualenv/subdir') + AssertLinter 'pycln', ale#Escape('pycln') . b:cmd_tail + +Execute(The pycln callbacks should detect virtualenv directories): + call ale#test#SetFilename('../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 . '/pycln' + \) + AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir') + AssertLinter b:executable, ale#Escape(b:executable) . b:cmd_tail + +Execute(You should able able to use the global pycln instead): + call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py') + let g:ale_python_pycln_use_global = 1 + + AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir') + AssertLinter 'pycln', ale#Escape('pycln') . b:cmd_tail + +Execute(Setting executable to 'pipenv' appends 'run pycln'): + let g:ale_python_pycln_executable = 'path/to/pipenv' + let g:ale_python_pycln_use_global = 1 + + AssertLinter 'path/to/pipenv', ale#Escape('path/to/pipenv') . ' run pycln' + \ . b:cmd_tail + +Execute(Pipenv is detected when python_pycln_auto_pipenv is set): + let g:ale_python_pycln_auto_pipenv = 1 + call ale#test#SetFilename('../test-files/python/pipenv/whatever.py') + + AssertLinterCwd expand('%:p:h') + AssertLinter 'pipenv', ale#Escape('pipenv') . ' run pycln' + \ . b:cmd_tail + +Execute(Setting executable to 'poetry' appends 'run pycln'): + let g:ale_python_pycln_executable = 'path/to/poetry' + let g:ale_python_pycln_use_global = 1 + + AssertLinter 'path/to/poetry', ale#Escape('path/to/poetry') . ' run pycln' + \ . b:cmd_tail + +Execute(poetry is detected when python_pycln_auto_poetry is set): + let g:ale_python_pycln_auto_poetry = 1 + call ale#test#SetFilename('../test-files/python/poetry/whatever.py') + + AssertLinterCwd expand('%:p:h') + AssertLinter 'poetry', ale#Escape('poetry') . ' run pycln' + \ . b:cmd_tail + +Execute(configuration files set in _config should be supported): + let g:ale_python_pycln_config_file = ale#path#Simplify(g:dir . '/../test-files/pycln/other_config.xml') + + AssertLinter 'pycln', + \ ale#Escape('pycln') + \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/../test-files/pycln/other_config.xml')) + \ . b:cmd_tail + +Execute(configuration file set in _options overrides _config): + let g:ale_python_pycln_config_file = '/foo.xml' + let g:ale_python_pycln_options = '--config /bar.xml' + + AssertLinter 'pycln', ale#Escape('pycln') . ' --config /bar.xml' . b:cmd_tail + + let b:ale_python_pycln_options = '-x --config /bar.xml' + + AssertLinter 'pycln', ale#Escape('pycln') . ' -x --config /bar.xml' . b:cmd_tail + diff --git a/test/test-files/python/with_virtualenv/env/Scripts/pycln.exe b/test/test-files/python/with_virtualenv/env/Scripts/pycln.exe new file mode 100755 index 00000000..e69de29b diff --git a/test/test-files/python/with_virtualenv/env/bin/pycln b/test/test-files/python/with_virtualenv/env/bin/pycln new file mode 100755 index 00000000..e69de29b -- cgit v1.2.3