aboutsummaryrefslogtreecommitdiff
path: root/runtime/autoload
diff options
context:
space:
mode:
authordundargoc <33953936+dundargoc@users.noreply.github.com>2023-04-09 22:28:00 +0200
committerGitHub <noreply@github.com>2023-04-09 22:28:00 +0200
commit71225228fc2700a18c56c57f9dc14494cef83149 (patch)
tree3fcb92b8015b5e23c0c88413388c6f310c56c5f7 /runtime/autoload
parent41b7586cbb947b3215489fd7445b9bfb6e7008d2 (diff)
downloadrneovim-71225228fc2700a18c56c57f9dc14494cef83149.tar.gz
rneovim-71225228fc2700a18c56c57f9dc14494cef83149.tar.bz2
rneovim-71225228fc2700a18c56c57f9dc14494cef83149.zip
refactor: rewrite python provider healthcheck in Lua
This is required to remove the vimscript checkhealth functions.
Diffstat (limited to 'runtime/autoload')
-rw-r--r--runtime/autoload/health/provider2.vim350
1 files changed, 0 insertions, 350 deletions
diff --git a/runtime/autoload/health/provider2.vim b/runtime/autoload/health/provider2.vim
index 196a3ce9de..0ebf203788 100644
--- a/runtime/autoload/health/provider2.vim
+++ b/runtime/autoload/health/provider2.vim
@@ -1,24 +1,10 @@
let s:shell_error = 0
-function! s:is_bad_response(s) abort
- return a:s =~? '\v(^unable)|(^error)|(^outdated)'
-endfunction
-
-function! s:trim(s) abort
- return substitute(a:s, '^\_s*\|\_s*$', '', 'g')
-endfunction
-
" Convert '\' to '/'. Collapse '//' and '/./'.
function! s:normalize_path(s) abort
return substitute(substitute(a:s, '\', '/', 'g'), '/\./\|/\+', '/', 'g')
endfunction
-" Returns TRUE if `cmd` exits with success, else FALSE.
-function! s:cmd_ok(cmd) abort
- call system(a:cmd)
- return v:shell_error == 0
-endfunction
-
" Handler for s:system() function.
function! s:system_handler(jobid, data, event) dict abort
if a:event ==# 'stderr'
@@ -88,143 +74,6 @@ function! s:system(cmd, ...) abort
return opts.output
endfunction
-function! s:systemlist(cmd, ...) abort
- let stdout = split(s:system(a:cmd, a:0 ? a:1 : ''), "\n")
- if a:0 > 1 && !empty(a:2)
- return filter(stdout, '!empty(v:val)')
- endif
- return stdout
-endfunction
-
-" Fetch the contents of a URL.
-function! s:download(url) abort
- let has_curl = executable('curl')
- if has_curl && system(['curl', '-V']) =~# 'Protocols:.*https'
- let rv = s:system(['curl', '-sL', a:url], '', 1, 1)
- return s:shell_error ? 'curl error with '.a:url.': '.s:shell_error : rv
- elseif executable('python')
- let script = "
- \try:\n
- \ from urllib.request import urlopen\n
- \except ImportError:\n
- \ from urllib2 import urlopen\n
- \\n
- \response = urlopen('".a:url."')\n
- \print(response.read().decode('utf8'))\n
- \"
- let rv = s:system(['python', '-c', script])
- return empty(rv) && s:shell_error
- \ ? 'python urllib.request error: '.s:shell_error
- \ : rv
- endif
- return 'missing `curl` '
- \ .(has_curl ? '(with HTTPS support) ' : '')
- \ .'and `python`, cannot make web request'
-endfunction
-
-" Get the latest Nvim Python client (pynvim) version from PyPI.
-function! s:latest_pypi_version() abort
- let pypi_version = 'unable to get pypi response'
- let pypi_response = s:download('https://pypi.python.org/pypi/pynvim/json')
- if !empty(pypi_response)
- try
- let pypi_data = json_decode(pypi_response)
- catch /E474/
- return 'error: '.pypi_response
- endtry
- let pypi_version = get(get(pypi_data, 'info', {}), 'version', 'unable to parse')
- endif
- return pypi_version
-endfunction
-
-" Get version information using the specified interpreter. The interpreter is
-" used directly in case breaking changes were introduced since the last time
-" Nvim's Python client was updated.
-"
-" Returns: [
-" {python executable version},
-" {current nvim version},
-" {current pypi nvim status},
-" {installed version status}
-" ]
-function! s:version_info(python) abort
- let pypi_version = s:latest_pypi_version()
- let python_version = s:trim(s:system([
- \ a:python,
- \ '-c',
- \ 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))',
- \ ]))
-
- if empty(python_version)
- let python_version = 'unable to parse '.a:python.' response'
- endif
-
- let nvim_path = s:trim(s:system([
- \ a:python, '-c',
- \ 'import sys; ' .
- \ 'sys.path = [p for p in sys.path if p != ""]; ' .
- \ 'import neovim; print(neovim.__file__)']))
- if s:shell_error || empty(nvim_path)
- return [python_version, 'unable to load neovim Python module', pypi_version,
- \ nvim_path]
- endif
-
- " Assuming that multiple versions of a package are installed, sort them
- " numerically in descending order.
- function! s:compare(metapath1, metapath2) abort
- let a = matchstr(fnamemodify(a:metapath1, ':p:h:t'), '[0-9.]\+')
- let b = matchstr(fnamemodify(a:metapath2, ':p:h:t'), '[0-9.]\+')
- return a == b ? 0 : a > b ? 1 : -1
- endfunction
-
- " Try to get neovim.VERSION (added in 0.1.11dev).
- let nvim_version = s:system([a:python, '-c',
- \ 'from neovim import VERSION as v; '.
- \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'],
- \ '', 1, 1)
- if empty(nvim_version)
- let nvim_version = 'unable to find pynvim module version'
- let base = fnamemodify(nvim_path, ':h')
- let metas = glob(base.'-*/METADATA', 1, 1)
- \ + glob(base.'-*/PKG-INFO', 1, 1)
- \ + glob(base.'.egg-info/PKG-INFO', 1, 1)
- let metas = sort(metas, 's:compare')
-
- if !empty(metas)
- for meta_line in readfile(metas[0])
- if meta_line =~# '^Version:'
- let nvim_version = matchstr(meta_line, '^Version: \zs\S\+')
- break
- endif
- endfor
- endif
- endif
-
- let nvim_path_base = fnamemodify(nvim_path, ':~:h')
- let version_status = 'unknown; '.nvim_path_base
- if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version)
- if v:lua.vim.version.lt(nvim_version, pypi_version)
- let version_status = 'outdated; from '.nvim_path_base
- else
- let version_status = 'up to date'
- endif
- endif
-
- return [python_version, nvim_version, pypi_version, version_status]
-endfunction
-
-" Check the Python interpreter's usability.
-function! s:check_bin(bin) abort
- if !filereadable(a:bin) && (!has('win32') || !filereadable(a:bin.'.exe'))
- call health#report_error(printf('"%s" was not found.', a:bin))
- return 0
- elseif executable(a:bin) != 1
- call health#report_error(printf('"%s" is not executable.', a:bin))
- return 0
- endif
- return 1
-endfunction
-
" Check "loaded" var for given a:provider.
" Returns 1 if the caller should return (skip checks).
function! s:disabled_via_loaded_var(provider) abort
@@ -241,204 +90,6 @@ function! s:disabled_via_loaded_var(provider) abort
return 0
endfunction
-function! s:check_python() abort
- call health#report_start('Python 3 provider (optional)')
-
- let pyname = 'python3'
- let python_exe = ''
- let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : ''
- let host_prog_var = pyname.'_host_prog'
- let python_multiple = []
-
- if s:disabled_via_loaded_var(pyname)
- return
- endif
-
- let [pyenv, pyenv_root] = s:check_for_pyenv()
-
- if exists('g:'.host_prog_var)
- call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var)))
- endif
-
- let [pyname, pythonx_warnings] = provider#pythonx#Detect(3)
-
- if empty(pyname)
- call health#report_warn('No Python executable found that can `import neovim`. '
- \ . 'Using the first available executable for diagnostics.')
- elseif exists('g:'.host_prog_var)
- let python_exe = pyname
- endif
-
- " No Python executable could `import neovim`, or host_prog_var was used.
- if !empty(pythonx_warnings)
- call health#report_warn(pythonx_warnings, ['See :help provider-python for more information.',
- \ 'You may disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim'])
-
- elseif !empty(pyname) && empty(python_exe)
- if !exists('g:'.host_prog_var)
- call health#report_info(printf('`g:%s` is not set. Searching for '
- \ . '%s in the environment.', host_prog_var, pyname))
- endif
-
- if !empty(pyenv)
- let python_exe = s:trim(s:system([pyenv, 'which', pyname], '', 1))
-
- if empty(python_exe)
- call health#report_warn(printf('pyenv could not find %s.', pyname))
- endif
- endif
-
- if empty(python_exe)
- let python_exe = exepath(pyname)
-
- if exists('$PATH')
- for path in split($PATH, has('win32') ? ';' : ':')
- let path_bin = s:normalize_path(path.'/'.pyname)
- if path_bin != s:normalize_path(python_exe)
- \ && index(python_multiple, path_bin) == -1
- \ && executable(path_bin)
- call add(python_multiple, path_bin)
- endif
- endfor
-
- if len(python_multiple)
- " This is worth noting since the user may install something
- " that changes $PATH, like homebrew.
- call health#report_info(printf('Multiple %s executables found. '
- \ . 'Set `g:%s` to avoid surprises.', pyname, host_prog_var))
- endif
-
- if python_exe =~# '\<shims\>'
- call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_exe), [
- \ '`pyenv` is not in $PATH, your pyenv installation is broken. '
- \ .'Set `g:'.host_prog_var.'` to avoid surprises.',
- \ ])
- endif
- endif
- endif
- endif
-
- if !empty(python_exe) && !exists('g:'.host_prog_var)
- if empty(venv) && !empty(pyenv)
- \ && !empty(pyenv_root) && resolve(python_exe) !~# '^'.pyenv_root.'/'
- call health#report_warn('pyenv is not set up optimally.', [
- \ printf('Create a virtualenv specifically '
- \ . 'for Nvim using pyenv, and set `g:%s`. This will avoid '
- \ . 'the need to install the pynvim module in each '
- \ . 'version/virtualenv.', host_prog_var)
- \ ])
- elseif !empty(venv)
- if !empty(pyenv_root)
- let venv_root = pyenv_root
- else
- let venv_root = fnamemodify(venv, ':h')
- endif
-
- if resolve(python_exe) !~# '^'.venv_root.'/'
- call health#report_warn('Your virtualenv is not set up optimally.', [
- \ printf('Create a virtualenv specifically '
- \ . 'for Nvim and use `g:%s`. This will avoid '
- \ . 'the need to install the pynvim module in each '
- \ . 'virtualenv.', host_prog_var)
- \ ])
- endif
- endif
- endif
-
- if empty(python_exe) && !empty(pyname)
- " An error message should have already printed.
- call health#report_error(printf('`%s` was not found.', pyname))
- elseif !empty(python_exe) && !s:check_bin(python_exe)
- let python_exe = ''
- endif
-
- " Diagnostic output
- call health#report_info('Executable: ' . (empty(python_exe) ? 'Not found' : python_exe))
- if len(python_multiple)
- for path_bin in python_multiple
- call health#report_info('Other python executable: ' . path_bin)
- endfor
- endif
-
- if empty(python_exe)
- " No Python executable can import 'neovim'. Check if any Python executable
- " can import 'pynvim'. If so, that Python failed to import 'neovim' as
- " well, which is most probably due to a failed pip upgrade:
- " https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
- let [pynvim_exe, errors] = provider#pythonx#DetectByModule('pynvim', 3)
- if !empty(pynvim_exe)
- call health#report_error(
- \ 'Detected pip upgrade failure: Python executable can import "pynvim" but '
- \ . 'not "neovim": '. pynvim_exe,
- \ "Use that Python version to reinstall \"pynvim\" and optionally \"neovim\".\n"
- \ . pynvim_exe ." -m pip uninstall pynvim neovim\n"
- \ . pynvim_exe ." -m pip install pynvim\n"
- \ . pynvim_exe ." -m pip install neovim # only if needed by third-party software")
- endif
- else
- let [majorpyversion, current, latest, status] = s:version_info(python_exe)
-
- if 3 != str2nr(majorpyversion)
- call health#report_warn('Unexpected Python version.' .
- \ ' This could lead to confusing error messages.')
- endif
-
- call health#report_info('Python version: ' . majorpyversion)
-
- if s:is_bad_response(status)
- call health#report_info(printf('pynvim version: %s (%s)', current, status))
- else
- call health#report_info(printf('pynvim version: %s', current))
- endif
-
- if s:is_bad_response(current)
- call health#report_error(
- \ "pynvim is not installed.\nError: ".current,
- \ ['Run in shell: '. python_exe .' -m pip install pynvim'])
- endif
-
- if s:is_bad_response(latest)
- call health#report_warn('Could not contact PyPI to get latest version.')
- call health#report_error('HTTP request failed: '.latest)
- elseif s:is_bad_response(status)
- call health#report_warn(printf('Latest pynvim is NOT installed: %s', latest))
- elseif !s:is_bad_response(current)
- call health#report_ok(printf('Latest pynvim is installed.'))
- endif
- endif
-endfunction
-
-" Check if pyenv is available and a valid pyenv root can be found, then return
-" their respective paths. If either of those is invalid, return two empty
-" strings, effectively ignoring pyenv.
-function! s:check_for_pyenv() abort
- let pyenv_path = resolve(exepath('pyenv'))
-
- if empty(pyenv_path)
- return ['', '']
- endif
-
- call health#report_info('pyenv: Path: '. pyenv_path)
-
- let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : ''
-
- if empty(pyenv_root)
- let pyenv_root = s:trim(s:system([pyenv_path, 'root']))
- call health#report_info('pyenv: $PYENV_ROOT is not set. Infer from `pyenv root`.')
- endif
-
- if !isdirectory(pyenv_root)
- call health#report_warn(
- \ printf('pyenv: Root does not exist: %s. '
- \ . 'Ignoring pyenv for all following checks.', pyenv_root))
- return ['', '']
- endif
-
- call health#report_info('pyenv: Root: '.pyenv_root)
-
- return [pyenv_path, pyenv_root]
-endfunction
-
" Resolves Python executable path by invoking and checking `sys.executable`.
function! s:python_exepath(invocation) abort
return s:normalize_path(system(fnameescape(a:invocation)
@@ -721,7 +372,6 @@ function! s:check_perl() abort
endfunction
function! health#provider2#check() abort
- call s:check_python()
call s:check_virtualenv()
call s:check_ruby()
call s:check_node()