#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 function! ale_linters#python#jedils#GetCommand(buffer) abort
let l:executable = ale_linters#python#jedils#GetExecutable(a:buffer) let l:executable = ale_linters#python#jedils#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv$' let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run jedi-language-server' \ ? ' 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 endfunction
call ale#linter#Define('python', { call ale#linter#Define('python', {

View File

@@ -37,12 +37,16 @@ endfunction
function! ale_linters#python#pylsp#GetCommand(buffer) abort function! ale_linters#python#pylsp#GetCommand(buffer) abort
let l:executable = ale_linters#python#pylsp#GetExecutable(a:buffer) let l:executable = ale_linters#python#pylsp#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv\|poetry$' let l:exec_args = l:executable =~? 'pipenv\|poetry$'
\ ? ' run pylsp' \ ? ' 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 endfunction
call ale#linter#Define('python', { call ale#linter#Define('python', {

View File

@@ -64,12 +64,16 @@ endfunction
function! ale_linters#python#pyright#GetCommand(buffer) abort function! ale_linters#python#pyright#GetCommand(buffer) abort
let l:executable = ale_linters#python#pyright#GetExecutable(a:buffer) let l:executable = ale_linters#python#pyright#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv\|poetry$' let l:exec_args = l:executable =~? 'pipenv\|poetry$'
\ ? ' run pyright' \ ? ' 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 endfunction
call ale#linter#Define('python', { 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. " Description: Functions for integrating with Python linters.
call ale#Set('python_auto_pipenv', '0') call ale#Set('python_auto_pipenv', '0')
@@ -96,6 +96,24 @@ function! ale#python#FindVirtualenv(buffer) abort
return $VIRTUAL_ENV return $VIRTUAL_ENV
endfunction 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. " 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 " First search on a virtualenv for Python, if nothing is found, try the global
" command. Returns an empty string if cannot find the executable " 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. 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* ALE Python Project Root Behavior *ale-python-root*

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. " Enable automatic detection of poetry for Python linters.
let g:ale_python_auto_poetry = get(g:, 'ale_python_auto_poetry', 0) 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. " This variable can be overridden to set the GO111MODULE environment variable.
let g:ale_go_go111module = get(g:, 'ale_go_go111module', '') 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: Before:
call ale#assert#SetUpLinterTest('python', 'pylsp') call ale#assert#SetUpLinterTest('python', 'pylsp')
Save b:ale_python_auto_virtualenv
let b:bin_dir = has('win32') ? 'Scripts' : 'bin' let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
After: After:
unlet! b:bin_dir unlet! b:bin_dir
unlet! b:venv_bin
unlet! b:sep
unlet! b:executable unlet! b:executable
call ale#assert#TearDownLinterTest() call ale#assert#TearDownLinterTest()
@@ -40,6 +43,17 @@ Execute(The pylsp executable should be run from the virtualenv path):
AssertEqual ale#Escape(b:executable), AssertEqual ale#Escape(b:executable),
\ ale_linters#python#pylsp#GetCommand(bufnr('')) \ 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): Execute(You should be able to override the pylsp virtualenv lookup):
call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py') call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py')

View File

@@ -1,10 +1,13 @@
Before: Before:
call ale#assert#SetUpLinterTest('python', 'pyright') call ale#assert#SetUpLinterTest('python', 'pyright')
Save b:ale_python_auto_virtualenv
let b:bin_dir = has('win32') ? 'Scripts' : 'bin' let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
After: After:
unlet! b:bin_dir unlet! b:bin_dir
unlet! b:venv_bin
unlet! b:sep
unlet! b:executable unlet! b:executable
call ale#assert#TearDownLinterTest() call ale#assert#TearDownLinterTest()
@@ -132,6 +135,17 @@ Execute(The pyright callbacks should detect virtualenv directories):
AssertLinter b:executable, ale#Escape(b:executable) . ' --stdio' 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'): Execute(Setting executable to 'pipenv' should append 'run pyright'):
call ale#test#SetFilename('../test-files') call ale#test#SetFilename('../test-files')