#2172 Auto PATH with ale_python_auto_virtualenv

Automatically set `PATH` for some Python linters that seem to need it
when g:ale_python_auto_virtualenv or b:ale_python_auto_virtualenv is
`1`.
This commit is contained in:
w0rp
2023-02-08 09:11:31 +00:00
parent 6ff1f0b200
commit 4c162877e2
10 changed files with 174 additions and 54 deletions

View File

@@ -16,12 +16,16 @@ endfunction
function! ale_linters#python#jedils#GetCommand(buffer) abort
let l:executable = ale_linters#python#jedils#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run jedi-language-server'
\ : ''
let l:env_string = ''
return ale#Escape(l:executable) . l:exec_args
if ale#Var(a:buffer, 'python_auto_virtualenv')
let l:env_string = ale#python#AutoVirtualenvEnvString(a:buffer)
endif
return l:env_string . ale#Escape(l:executable) . l:exec_args
endfunction
call ale#linter#Define('python', {

View File

@@ -37,12 +37,16 @@ endfunction
function! ale_linters#python#pylsp#GetCommand(buffer) abort
let l:executable = ale_linters#python#pylsp#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv\|poetry$'
\ ? ' run pylsp'
\ : ''
let l:env_string = ''
return ale#Escape(l:executable) . l:exec_args . ale#Pad(ale#Var(a:buffer, 'python_pylsp_options'))
if ale#Var(a:buffer, 'python_auto_virtualenv')
let l:env_string = ale#python#AutoVirtualenvEnvString(a:buffer)
endif
return l:env_string . ale#Escape(l:executable) . l:exec_args . ale#Pad(ale#Var(a:buffer, 'python_pylsp_options'))
endfunction
call ale#linter#Define('python', {

View File

@@ -64,12 +64,16 @@ endfunction
function! ale_linters#python#pyright#GetCommand(buffer) abort
let l:executable = ale_linters#python#pyright#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv\|poetry$'
\ ? ' run pyright'
\ : ''
let l:env_string = ''
return ale#Escape(l:executable) . l:exec_args . ' --stdio'
if ale#Var(a:buffer, 'python_auto_virtualenv')
let l:env_string = ale#python#AutoVirtualenvEnvString(a:buffer)
endif
return l:env_string . ale#Escape(l:executable) . l:exec_args . ' --stdio'
endfunction
call ale#linter#Define('python', {

View File

@@ -1,4 +1,4 @@
" Author: w0rp <devw0rp@gmail.com>
" Author: w0rp <dev@w0rp.com>
" Description: Functions for integrating with Python linters.
call ale#Set('python_auto_pipenv', '0')
@@ -96,6 +96,24 @@ function! ale#python#FindVirtualenv(buffer) abort
return $VIRTUAL_ENV
endfunction
" Automatically determine virtualenv environment variables and build
" a string of them to prefix linter commands with.
function! ale#python#AutoVirtualenvEnvString(buffer) abort
let l:venv_dir = ale#python#FindVirtualenv(a:buffer)
let l:sep = has('win32') ? ';' : ':'
if !empty(l:venv_dir)
let l:vars = [
\ ['PATH', ale#path#Simplify(l:venv_dir . '/bin') . l:sep . $PATH],
\]
" We don't need a space between var as ale#Env adds one.
return join(map(l:vars, 'ale#Env(v:val[0], v:val[1])'), '')
endif
return ''
endfunction
" Given a buffer number and a command name, find the path to the executable.
" First search on a virtualenv for Python, if nothing is found, try the global
" command. Returns an empty string if cannot find the executable

View File

@@ -20,6 +20,17 @@ g:ale_python_auto_poetry *g:ale_python_auto_poetry*
if true. This is overridden by a manually-set executable.
g:ale_python_auto_virtualenv *g:ale_python_auto_virtualenv*
*b:ale_python_auto_virtualenv*
Type: |Number|
Default: `0`
If set to `1`, ALE will automatically set environment variables for commands
such as `PATH` to attempt to make the experience of running Python linters
via virtualenv easier, without the need for another plugin or some
specialised setup.
===============================================================================
ALE Python Project Root Behavior *ale-python-root*
@@ -88,24 +99,24 @@ g:ale_python_autoflake_use_global *g:ale_python_autoflake_use_global*
===============================================================================
autoimport *ale-python-autoimport*
g:ale_python_autoimport_executable *g:ale_python_autoimport_executable*
*b:ale_python_autoimport_executable*
g:ale_python_autoimport_executable *g:ale_python_autoimport_executable*
*b:ale_python_autoimport_executable*
Type: |String|
Default: `'autoimport'`
See |ale-integrations-local-executables|
g:ale_python_autoimport_options *g:ale_python_autoimport_options*
*b:ale_python_autoimport_options*
g:ale_python_autoimport_options *g:ale_python_autoimport_options*
*b:ale_python_autoimport_options*
Type: |String|
Default: `''`
This variable can be set to pass extra options to autoimport.
g:ale_python_autoimport_use_global *g:ale_python_autoimport_use_global*
*b:ale_python_autoimport_use_global*
g:ale_python_autoimport_use_global *g:ale_python_autoimport_use_global*
*b:ale_python_autoimport_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
@@ -338,7 +349,7 @@ g:ale_python_flake8_auto_poetry *g:ale_python_flake8_auto_poetry*
flakehell *ale-python-flakehell*
g:ale_python_flakehell_change_directory*g:ale_python_flakehell_change_directory*
*b:ale_python_flakehell_change_directory*
*b:ale_python_flakehell_change_directory*
Type: |String|
Default: `project`
@@ -349,8 +360,8 @@ g:ale_python_flakehell_change_directory*g:ale_python_flakehell_change_directory*
Python is executed from yourself.
g:ale_python_flakehell_executable *g:ale_python_flakehell_executable*
*b:ale_python_flakehell_executable*
g:ale_python_flakehell_executable *g:ale_python_flakehell_executable*
*b:ale_python_flakehell_executable*
Type: |String|
Default: `'flakehell'`
@@ -360,8 +371,8 @@ g:ale_python_flakehell_executable *g:ale_python_flakehell_executable*
invoke `'python` `-m` `flakehell'`.
g:ale_python_flakehell_options *g:ale_python_flakehell_options*
*b:ale_python_flakehell_options*
g:ale_python_flakehell_options *g:ale_python_flakehell_options*
*b:ale_python_flakehell_options*
Type: |String|
Default: `''`
@@ -369,8 +380,8 @@ g:ale_python_flakehell_options *g:ale_python_flakehell_options*
lint invocation.
g:ale_python_flakehell_use_global *g:ale_python_flakehell_use_global*
*b:ale_python_flakehell_use_global*
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)`
@@ -381,8 +392,8 @@ g:ale_python_flakehell_use_global *g:ale_python_flakehell_use_global*
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*
g:ale_python_flakehell_auto_pipenv *g:ale_python_flakehell_auto_pipenv*
*b:ale_python_flakehell_auto_pipenv*
Type: |Number|
Default: `0`
@@ -975,13 +986,13 @@ g:ale_python_pylint_use_msg_id *g:ale_python_pylint_use_msg_id*
===============================================================================
pylsp *ale-python-pylsp*
pylsp *ale-python-pylsp*
`pylsp` will be run from a detected project root, per |ale-python-root|.
g:ale_python_pylsp_executable *g:ale_python_pylsp_executable*
*b:ale_python_pylsp_executable*
g:ale_python_pylsp_executable *g:ale_python_pylsp_executable*
*b:ale_python_pylsp_executable*
Type: |String|
Default: `'pylsp'`
@@ -991,16 +1002,16 @@ g:ale_python_pylsp_executable *g:ale_python_pylsp_executable
Set this to `'poetry'` to invoke `'poetry` `run` `pyls'`.
g:ale_python_pylsp_use_global *g:ale_python_pylsp_use_global*
*b:ale_python_pylsp_use_global*
g:ale_python_pylsp_use_global *g:ale_python_pylsp_use_global*
*b:ale_python_pylsp_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
g:ale_python_pylsp_auto_pipenv *g:ale_python_pylsp_auto_pipenv*
*b:ale_python_pylsp_auto_pipenv*
g:ale_python_pylsp_auto_pipenv *g:ale_python_pylsp_auto_pipenv*
*b:ale_python_pylsp_auto_pipenv*
Type: |Number|
Default: `0`
@@ -1008,8 +1019,8 @@ g:ale_python_pylsp_auto_pipenv *g:ale_python_pylsp_auto_pipenv
if true. This is overridden by a manually-set executable.
g:ale_python_pylsp_auto_poetry *g:ale_python_pylsp_auto_poetry*
*b:ale_python_pylsp_auto_poetry*
g:ale_python_pylsp_auto_poetry *g:ale_python_pylsp_auto_poetry*
*b:ale_python_pylsp_auto_poetry*
Type: |Number|
Default: `0`
@@ -1017,8 +1028,8 @@ g:ale_python_pylsp_auto_poetry *g:ale_python_pylsp_auto_poetry
if true. This is overridden by a manually-set executable.
g:ale_python_pylsp_config *g:ale_python_pylsp_config*
*b:ale_python_pylsp_config*
g:ale_python_pylsp_config *g:ale_python_pylsp_config*
*b:ale_python_pylsp_config*
Type: |Dictionary|
Default: `{}`
@@ -1035,8 +1046,8 @@ g:ale_python_pylsp_config *g:ale_python_pylsp_config
\ }
<
g:ale_python_pylsp_options *g:ale_python_pylsp_options*
*b:ale_python_pylsp_options*
g:ale_python_pylsp_options *g:ale_python_pylsp_options*
*b:ale_python_pylsp_options*
Type: |String|
Default: `''`
@@ -1158,10 +1169,10 @@ g:ale_python_pyright_config *g:ale_python_pyright_config*
<
===============================================================================
refurb *ale-python-refurb*
refurb *ale-python-refurb*
g:ale_python_refurb_change_directory *g:ale_python_refurb_change_directory*
*b:ale_python_refurb_change_directory*
g:ale_python_refurb_change_directory *g:ale_python_refurb_change_directory*
*b:ale_python_refurb_change_directory*
Type: |Number|
Default: `1`
@@ -1170,8 +1181,8 @@ g:ale_python_refurb_change_directory *g:ale_python_refurb_change_directory
`refurb` will be run from the buffer's directory.
g:ale_python_refurb_executable *g:ale_python_refurb_executable*
*b:ale_python_refurb_executable*
g:ale_python_refurb_executable *g:ale_python_refurb_executable*
*b:ale_python_refurb_executable*
Type: |String|
Default: `'refurb'`
@@ -1181,8 +1192,8 @@ g:ale_python_refurb_executable *g:ale_python_refurb_executable
Set this to `'poetry'` to invoke `'poetry` `run` `refurb'`.
g:ale_python_refurb_options *g:ale_python_refurb_options*
*b:ale_python_refurb_options*
g:ale_python_refurb_options *g:ale_python_refurb_options*
*b:ale_python_refurb_options*
Type: |String|
Default: `''`
@@ -1192,16 +1203,16 @@ g:ale_python_refurb_options *g:ale_python_refurb_options
For example, to select/enable and/or disable some error codes,
you may want to set >
let g:ale_python_refurb_options = '--ignore 100'
g:ale_python_refurb_use_global *g:ale_python_refurb_use_global*
*b:ale_python_refurb_use_global*
g:ale_python_refurb_use_global *g:ale_python_refurb_use_global*
*b:ale_python_refurb_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
g:ale_python_refurb_auto_pipenv *g:ale_python_refurb_auto_pipenv*
*b:ale_python_refurb_auto_pipenv*
g:ale_python_refurb_auto_pipenv *g:ale_python_refurb_auto_pipenv*
*b:ale_python_refurb_auto_pipenv*
Type: |Number|
Default: `0`
@@ -1209,8 +1220,8 @@ g:ale_python_refurb_auto_pipenv *g:ale_python_refurb_auto_pipenv
if true. This is overridden by a manually-set executable.
g:ale_python_refurb_auto_poetry *g:ale_python_refurb_auto_poetry*
*b:ale_python_refurb_auto_poetry*
g:ale_python_refurb_auto_poetry *g:ale_python_refurb_auto_poetry*
*b:ale_python_refurb_auto_poetry*
Type: |Number|
Default: `0`
@@ -1335,8 +1346,8 @@ g:ale_python_unimport_auto_poetry *g:ale_python_unimport_auto_poetry*
if true. This is overridden by a manually-set executable.
g:ale_python_unimport_executable *g:ale_python_unimport_executable*
*b:ale_python_unimport_executable*
g:ale_python_unimport_executable *g:ale_python_unimport_executable*
*b:ale_python_unimport_executable*
Type: |String|
Default: `'unimport'`
@@ -1346,8 +1357,8 @@ g:ale_python_unimport_executable *g:ale_python_unimport_executable*
Set this to `'poetry'` to invoke `'poetry` `run` `unimport'`.
g:ale_python_unimport_options *g:ale_python_unimport_options*
*b:ale_python_unimport_options*
g:ale_python_unimport_options *g:ale_python_unimport_options*
*b:ale_python_unimport_options*
Type: |String|
Default: `''`
@@ -1355,8 +1366,8 @@ g:ale_python_unimport_options *g:ale_python_unimport_options*
invocation.
g:ale_python_unimport_use_global *g:ale_python_unimport_use_global*
*b:ale_python_unimport_use_global*
g:ale_python_unimport_use_global *g:ale_python_unimport_use_global*
*b:ale_python_unimport_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`

View File

@@ -178,6 +178,10 @@ let g:ale_python_auto_pipenv = get(g:, 'ale_python_auto_pipenv', 0)
" Enable automatic detection of poetry for Python linters.
let g:ale_python_auto_poetry = get(g:, 'ale_python_auto_poetry', 0)
" Enable automatic adjustment of environment variables for Python linters.
" The variables are set based on ALE's virtualenv detection.
let g:ale_python_auto_virtualenv = get(g:, 'ale_python_auto_virtualenv', 0)
" This variable can be overridden to set the GO111MODULE environment variable.
let g:ale_go_go111module = get(g:, 'ale_go_go111module', '')

View File

@@ -0,0 +1,47 @@
Before:
call ale#assert#SetUpLinterTest('python', 'jedils')
Save b:ale_python_auto_virtualenv
let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
After:
unlet! b:bin_dir
unlet! b:venv_bin
unlet! b:sep
unlet! b:executable
call ale#assert#TearDownLinterTest()
Execute(The jedi-language-server command callback should return default string):
call ale#test#SetFilename('./foo.py')
AssertLinter 'jedi-language-server', ale#Escape('jedi-language-server')
Execute(The jedi-language-server executable should be configurable):
let g:ale_python_jedils_executable = '~/.local/bin/jedi-language-server'
AssertLinter '~/.local/bin/jedi-language-server' , ale#Escape('~/.local/bin/jedi-language-server')
Execute(virtualenv vars should be used when ale_python_auto_virtualenv = 1):
let b:ale_python_auto_virtualenv = 1
call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py')
let b:venv_bin = ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir)
let b:sep = has('win32') ? ';' : ':'
let b:executable = ale#path#Simplify(b:venv_bin . '/jedi-language-server')
AssertLinter b:executable, ale#Env('PATH', b:venv_bin . b:sep . $PATH)
\ . ale#Escape(b:executable)
Execute(You should be able to override the jedi-language-server virtualenv lookup):
call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py')
let g:ale_python_jedils_use_global = 1
AssertLinter 'jedi-language-server', ale#Escape('jedi-language-server')
Execute(Setting executable to 'pipenv' appends 'run jedi-language-server'):
let g:ale_python_jedils_executable = 'path/to/pipenv'
call ale#test#SetFilename('../test-files/dummy')
AssertLinter 'path/to/pipenv', ale#Escape('path/to/pipenv') . ' run jedi-language-server'

View File

@@ -1,10 +1,13 @@
Before:
call ale#assert#SetUpLinterTest('python', 'pylsp')
Save b:ale_python_auto_virtualenv
let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
After:
unlet! b:bin_dir
unlet! b:venv_bin
unlet! b:sep
unlet! b:executable
call ale#assert#TearDownLinterTest()
@@ -40,6 +43,17 @@ Execute(The pylsp executable should be run from the virtualenv path):
AssertEqual ale#Escape(b:executable),
\ ale_linters#python#pylsp#GetCommand(bufnr(''))
Execute(virtualenv vars should be used when ale_python_auto_virtualenv = 1):
let b:ale_python_auto_virtualenv = 1
call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py')
let b:venv_bin = ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir)
let b:sep = has('win32') ? ';' : ':'
let b:executable = ale#path#Simplify(b:venv_bin . '/pylsp')
AssertLinter b:executable, ale#Env('PATH', b:venv_bin . b:sep . $PATH)
\ . ale#Escape(b:executable)
Execute(You should be able to override the pylsp virtualenv lookup):
call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py')

View File

@@ -1,10 +1,13 @@
Before:
call ale#assert#SetUpLinterTest('python', 'pyright')
Save b:ale_python_auto_virtualenv
let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
After:
unlet! b:bin_dir
unlet! b:venv_bin
unlet! b:sep
unlet! b:executable
call ale#assert#TearDownLinterTest()
@@ -132,6 +135,17 @@ Execute(The pyright callbacks should detect virtualenv directories):
AssertLinter b:executable, ale#Escape(b:executable) . ' --stdio'
Execute(virtualenv vars should be used when ale_python_auto_virtualenv = 1):
let b:ale_python_auto_virtualenv = 1
call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py')
let b:venv_bin = ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir)
let b:sep = has('win32') ? ';' : ':'
let b:executable = ale#path#Simplify(b:venv_bin . '/pyright-langserver')
AssertLinter b:executable, ale#Env('PATH', b:venv_bin . b:sep . $PATH)
\ . ale#Escape(b:executable) . ' --stdio'
Execute(Setting executable to 'pipenv' should append 'run pyright'):
call ale#test#SetFilename('../test-files')