mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #9468 'checkhealth: detect broken pip"'
This commit is contained in:
		| @@ -258,71 +258,56 @@ function! s:check_python(version) abort | |||||||
|   call health#report_start('Python ' . a:version . ' provider (optional)') |   call health#report_start('Python ' . a:version . ' provider (optional)') | ||||||
|  |  | ||||||
|   let pyname = 'python'.(a:version == 2 ? '' : '3') |   let pyname = 'python'.(a:version == 2 ? '' : '3') | ||||||
|   let pyenv = resolve(exepath('pyenv')) |   let python_exe = '' | ||||||
|   let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : '' |  | ||||||
|   let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' |   let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' | ||||||
|   let host_prog_var = pyname.'_host_prog' |   let host_prog_var = pyname.'_host_prog' | ||||||
|   let loaded_var = 'g:loaded_'.pyname.'_provider' |   let loaded_var = 'g:loaded_'.pyname.'_provider' | ||||||
|   let python_bin = '' |  | ||||||
|   let python_multiple = [] |   let python_multiple = [] | ||||||
|  |  | ||||||
|   if exists(loaded_var) && !exists('*provider#'.pyname.'#Call') |   if exists(loaded_var) && !exists('*provider#'.pyname.'#Call') | ||||||
|     call health#report_info('Disabled ('.loaded_var.'='.eval(loaded_var).').  This might be due to some previous error.') |     call health#report_info('Disabled ('.loaded_var.'='.eval(loaded_var).').  This might be due to some previous error.') | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   if !empty(pyenv) |   let [pyenv, pyenv_root] = s:check_for_pyenv() | ||||||
|     if empty(pyenv_root) |  | ||||||
|       call health#report_info( |  | ||||||
|             \ 'pyenv was found, but $PYENV_ROOT is not set. `pyenv root` will be used.' |  | ||||||
|             \ .' If you run into problems, try setting $PYENV_ROOT explicitly.' |  | ||||||
|             \ ) |  | ||||||
|       let pyenv_root = s:trim(s:system([pyenv, 'root'])) |  | ||||||
|     endif |  | ||||||
|  |  | ||||||
|     if !isdirectory(pyenv_root) |  | ||||||
|       call health#report_error('Invalid pyenv root: '.pyenv_root) |  | ||||||
|     else |  | ||||||
|       call health#report_info(printf('pyenv: %s', pyenv)) |  | ||||||
|       call health#report_info(printf('pyenv root: %s', pyenv_root)) |  | ||||||
|     endif |  | ||||||
|   endif |  | ||||||
|  |  | ||||||
|   if exists('g:'.host_prog_var) |   if exists('g:'.host_prog_var) | ||||||
|     call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var))) |     call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var))) | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   let [pyname, pythonx_errs] = provider#pythonx#Detect(a:version) |   let [pyname, pythonx_errors] = provider#pythonx#Detect(a:version) | ||||||
|  |  | ||||||
|   if empty(pyname) |   if empty(pyname) | ||||||
|     call health#report_warn('No Python interpreter was found with the pynvim ' |     call health#report_warn('No Python executable found that can `import neovim`. ' | ||||||
|             \ . 'module.  Using the first available for diagnostics.') |             \ . 'Using the first available executable for diagnostics.') | ||||||
|   elseif exists('g:'.host_prog_var) |   elseif exists('g:'.host_prog_var) | ||||||
|     let python_bin = pyname |     let python_exe = pyname | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   if !empty(pythonx_errs) |   " No Python executable could `import neovim`. | ||||||
|     call health#report_error('Python provider error', pythonx_errs) |   if !empty(pythonx_errors) | ||||||
|  |     call health#report_error('Python provider error:', pythonx_errors) | ||||||
|  |  | ||||||
|   elseif !empty(pyname) && empty(python_bin) |   elseif !empty(pyname) && empty(python_exe) | ||||||
|     if !exists('g:'.host_prog_var) |     if !exists('g:'.host_prog_var) | ||||||
|       call health#report_info(printf('`g:%s` is not set.  Searching for ' |       call health#report_info(printf('`g:%s` is not set.  Searching for ' | ||||||
|             \ . '%s in the environment.', host_prog_var, pyname)) |             \ . '%s in the environment.', host_prog_var, pyname)) | ||||||
|     endif |     endif | ||||||
|  |  | ||||||
|     if !empty(pyenv) |     if !empty(pyenv) | ||||||
|       let python_bin = s:trim(s:system([pyenv, 'which', pyname], '', 1)) |       let python_exe = s:trim(s:system([pyenv, 'which', pyname], '', 1)) | ||||||
|  |  | ||||||
|       if empty(python_bin) |       if empty(python_exe) | ||||||
|         call health#report_warn(printf('pyenv could not find %s.', pyname)) |         call health#report_warn(printf('pyenv could not find %s.', pyname)) | ||||||
|       endif |       endif | ||||||
|     endif |     endif | ||||||
|  |  | ||||||
|     if empty(python_bin) |     if empty(python_exe) | ||||||
|       let python_bin = exepath(pyname) |       let python_exe = exepath(pyname) | ||||||
|  |  | ||||||
|       if exists('$PATH') |       if exists('$PATH') | ||||||
|         for path in split($PATH, has('win32') ? ';' : ':') |         for path in split($PATH, has('win32') ? ';' : ':') | ||||||
|           let path_bin = s:normalize_path(path.'/'.pyname) |           let path_bin = s:normalize_path(path.'/'.pyname) | ||||||
|           if path_bin != s:normalize_path(python_bin) |           if path_bin != s:normalize_path(python_exe) | ||||||
|                 \ && index(python_multiple, path_bin) == -1 |                 \ && index(python_multiple, path_bin) == -1 | ||||||
|                 \ && executable(path_bin) |                 \ && executable(path_bin) | ||||||
|             call add(python_multiple, path_bin) |             call add(python_multiple, path_bin) | ||||||
| @@ -336,8 +321,8 @@ function! s:check_python(version) abort | |||||||
|                 \ . 'Set `g:%s` to avoid surprises.', pyname, host_prog_var)) |                 \ . 'Set `g:%s` to avoid surprises.', pyname, host_prog_var)) | ||||||
|         endif |         endif | ||||||
|  |  | ||||||
|         if python_bin =~# '\<shims\>' |         if python_exe =~# '\<shims\>' | ||||||
|           call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_bin), [ |           call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_exe), [ | ||||||
|                       \ '`pyenv` is not in $PATH, your pyenv installation is broken. ' |                       \ '`pyenv` is not in $PATH, your pyenv installation is broken. ' | ||||||
|                       \ .'Set `g:'.host_prog_var.'` to avoid surprises.', |                       \ .'Set `g:'.host_prog_var.'` to avoid surprises.', | ||||||
|                       \ ]) |                       \ ]) | ||||||
| @@ -346,9 +331,9 @@ function! s:check_python(version) abort | |||||||
|     endif |     endif | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   if !empty(python_bin) && !exists('g:'.host_prog_var) |   if !empty(python_exe) && !exists('g:'.host_prog_var) | ||||||
|     if empty(venv) && !empty(pyenv) |     if empty(venv) && !empty(pyenv) | ||||||
|           \ && !empty(pyenv_root) && resolve(python_bin) !~# '^'.pyenv_root.'/' |           \ && !empty(pyenv_root) && resolve(python_exe) !~# '^'.pyenv_root.'/' | ||||||
|       call health#report_warn('pyenv is not set up optimally.', [ |       call health#report_warn('pyenv is not set up optimally.', [ | ||||||
|             \ printf('Create a virtualenv specifically ' |             \ printf('Create a virtualenv specifically ' | ||||||
|             \ . 'for Neovim using pyenv, and set `g:%s`.  This will avoid ' |             \ . 'for Neovim using pyenv, and set `g:%s`.  This will avoid ' | ||||||
| @@ -362,7 +347,7 @@ function! s:check_python(version) abort | |||||||
|         let venv_root = fnamemodify(venv, ':h') |         let venv_root = fnamemodify(venv, ':h') | ||||||
|       endif |       endif | ||||||
|  |  | ||||||
|       if resolve(python_bin) !~# '^'.venv_root.'/' |       if resolve(python_exe) !~# '^'.venv_root.'/' | ||||||
|         call health#report_warn('Your virtualenv is not set up optimally.', [ |         call health#report_warn('Your virtualenv is not set up optimally.', [ | ||||||
|               \ printf('Create a virtualenv specifically ' |               \ printf('Create a virtualenv specifically ' | ||||||
|               \ . 'for Neovim and use `g:%s`.  This will avoid ' |               \ . 'for Neovim and use `g:%s`.  This will avoid ' | ||||||
| @@ -373,16 +358,16 @@ function! s:check_python(version) abort | |||||||
|     endif |     endif | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   if empty(python_bin) && !empty(pyname) |   if empty(python_exe) && !empty(pyname) | ||||||
|     " An error message should have already printed. |     " An error message should have already printed. | ||||||
|     call health#report_error(printf('`%s` was not found.', pyname)) |     call health#report_error(printf('`%s` was not found.', pyname)) | ||||||
|   elseif !empty(python_bin) && !s:check_bin(python_bin) |   elseif !empty(python_exe) && !s:check_bin(python_exe) | ||||||
|     let python_bin = '' |     let python_exe = '' | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   " Check if $VIRTUAL_ENV is valid. |   " Check if $VIRTUAL_ENV is valid. | ||||||
|   if exists('$VIRTUAL_ENV') && !empty(python_bin) |   if exists('$VIRTUAL_ENV') && !empty(python_exe) | ||||||
|     if $VIRTUAL_ENV ==# matchstr(python_bin, '^\V'.$VIRTUAL_ENV) |     if $VIRTUAL_ENV ==# matchstr(python_exe, '^\V'.$VIRTUAL_ENV) | ||||||
|       call health#report_info('$VIRTUAL_ENV matches executable') |       call health#report_info('$VIRTUAL_ENV matches executable') | ||||||
|     else |     else | ||||||
|       call health#report_warn( |       call health#report_warn( | ||||||
| @@ -393,7 +378,7 @@ function! s:check_python(version) abort | |||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   " Diagnostic output |   " Diagnostic output | ||||||
|   call health#report_info('Executable: ' . (empty(python_bin) ? 'Not found' : python_bin)) |   call health#report_info('Executable: ' . (empty(python_exe) ? 'Not found' : python_exe)) | ||||||
|   if len(python_multiple) |   if len(python_multiple) | ||||||
|     for path_bin in python_multiple |     for path_bin in python_multiple | ||||||
|       call health#report_info('Other python executable: ' . path_bin) |       call health#report_info('Other python executable: ' . path_bin) | ||||||
| @@ -402,29 +387,37 @@ function! s:check_python(version) abort | |||||||
|  |  | ||||||
|   let pip = 'pip' . (a:version == 2 ? '' : '3') |   let pip = 'pip' . (a:version == 2 ? '' : '3') | ||||||
|  |  | ||||||
|   if !empty(python_bin) |   if empty(python_exe) | ||||||
|     let [pyversion, current, latest, status] = s:version_info(python_bin) |     " 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', a:version) | ||||||
|  |     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" | ||||||
|  |             \ . pip ." uninstall pynvim neovim\n" | ||||||
|  |             \ . pip ." install pynvim\n" | ||||||
|  |             \ . pip ." install neovim  # only if needed by third-party software") | ||||||
|  |     endif | ||||||
|  |   else | ||||||
|  |     let [pyversion, current, latest, status] = s:version_info(python_exe) | ||||||
|  |  | ||||||
|     if a:version != str2nr(pyversion) |     if a:version != str2nr(pyversion) | ||||||
|       call health#report_warn('Unexpected Python version.' . |       call health#report_warn('Unexpected Python version.' . | ||||||
|                   \ ' This could lead to confusing error messages.') |                   \ ' This could lead to confusing error messages.') | ||||||
|     endif |     endif | ||||||
|  |  | ||||||
|     if a:version == 3 && str2float(pyversion) < 3.3 |     if a:version == 3 && str2float(pyversion) < 3.3 | ||||||
|       call health#report_warn('Python 3.3+ is recommended.') |       call health#report_warn('Python 3.3+ is recommended.') | ||||||
|     endif |     endif | ||||||
|  |  | ||||||
|     call health#report_info('Python version: ' . pyversion) |     call health#report_info('Python version: ' . pyversion) | ||||||
|  |  | ||||||
|     if s:is_bad_response(status) |     if s:is_bad_response(status) | ||||||
|       call health#report_info(printf('pynvim version: %s (%s)', current, status)) |       call health#report_info(printf('pynvim version: %s (%s)', current, status)) | ||||||
|       let [module_found, _msg] = provider#pythonx#CheckForModule(python_bin, |  | ||||||
|             \ 'pynvim', a:version) |  | ||||||
|       if status !=? '^outdated' && module_found |  | ||||||
|         " neovim module was not found, but pynvim was |  | ||||||
|         call health#report_error('Importing "neovim" failed.', |  | ||||||
|               \ "Reinstall \"pynvim\" and optionally \"neovim\" packages.\n" . |  | ||||||
|               \    pip ." uninstall pynvim neovim\n" . |  | ||||||
|               \    pip ." install pynvim\n" . |  | ||||||
|               \    pip ." install neovim # only if needed by third-party software") |  | ||||||
|       endif |  | ||||||
|     else |     else | ||||||
|       call health#report_info(printf('pynvim version: %s', current)) |       call health#report_info(printf('pynvim version: %s', current)) | ||||||
|     endif |     endif | ||||||
| @@ -444,7 +437,37 @@ function! s:check_python(version) abort | |||||||
|       call health#report_ok(printf('Latest pynvim is installed.')) |       call health#report_ok(printf('Latest pynvim is installed.')) | ||||||
|     endif |     endif | ||||||
|   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, effectivly 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 | endfunction | ||||||
|  |  | ||||||
| function! s:check_ruby() abort | function! s:check_ruby() abort | ||||||
|   | |||||||
| @@ -21,37 +21,45 @@ function! provider#pythonx#Require(host) abort | |||||||
|   return provider#Poll(args, a:host.orig_name, '$NVIM_PYTHON_LOG_FILE') |   return provider#Poll(args, a:host.orig_name, '$NVIM_PYTHON_LOG_FILE') | ||||||
| endfunction | endfunction | ||||||
|  |  | ||||||
| function! provider#pythonx#Detect(major_ver) abort | function! s:get_python_executable_from_host_var(major_version) abort | ||||||
|   if a:major_ver == 2 |   return expand(get(g:, 'python'.(a:major_version == 3 ? '3' : '').'_host_prog', '')) | ||||||
|     if exists('g:python_host_prog') | endfunction | ||||||
|       return [expand(g:python_host_prog), ''] |  | ||||||
|     else | function! s:get_python_candidates(major_version) abort | ||||||
|       let progs = ['python2', 'python2.7', 'python2.6', 'python'] |   return { | ||||||
|     endif |         \ 2: ['python2', 'python2.7', 'python2.6', 'python'], | ||||||
|   else |         \ 3: ['python3', 'python3.7', 'python3.6', 'python3.5', 'python3.4', 'python3.3', | ||||||
|     if exists('g:python3_host_prog') |         \     'python'] | ||||||
|       return [expand(g:python3_host_prog), ''] |         \ }[a:major_version] | ||||||
|     else | endfunction | ||||||
|       let progs = ['python3', 'python3.7', 'python3.6', 'python3.5', |  | ||||||
|             \ 'python3.4', 'python3.3', 'python'] | " Returns [path_to_python_executable, error_message] | ||||||
|     endif | function! provider#pythonx#Detect(major_version) abort | ||||||
|  |   return provider#pythonx#DetectByModule('neovim', a:major_version) | ||||||
|  | endfunction | ||||||
|  |  | ||||||
|  | " Returns [path_to_python_executable, error_message] | ||||||
|  | function! provider#pythonx#DetectByModule(module, major_version) abort | ||||||
|  |   let python_exe = s:get_python_executable_from_host_var(a:major_version) | ||||||
|  |  | ||||||
|  |   if !empty(python_exe) | ||||||
|  |     return [python_exe, ''] | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|  |   let candidates = s:get_python_candidates(a:major_version) | ||||||
|   let errors = [] |   let errors = [] | ||||||
|  |  | ||||||
|   for prog in progs |   for exe in candidates | ||||||
|     let [result, err] = provider#pythonx#CheckForModule(prog, 'neovim', a:major_ver) |     let [result, error] = provider#pythonx#CheckForModule(exe, a:module, a:major_version) | ||||||
|     if result |     if result | ||||||
|       return [prog, err] |       return [exe, error] | ||||||
|     endif |     endif | ||||||
|     " Accumulate errors in case we don't find |     " Accumulate errors in case we don't find any suitable Python executable. | ||||||
|     " any suitable Python interpreter. |     call add(errors, error) | ||||||
|     call add(errors, err) |  | ||||||
|   endfor |   endfor | ||||||
|  |  | ||||||
|   " No suitable Python interpreter found. |   " No suitable Python executable found. | ||||||
|   return ['', 'provider/pythonx: Could not load Python ' . a:major_ver |   return ['', 'provider/pythonx: Could not load Python '.a:major_version.":\n".join(errors, "\n")] | ||||||
|         \ . ":\n" .  join(errors, "\n")] |  | ||||||
| endfunction | endfunction | ||||||
|  |  | ||||||
| " Returns array: [prog_exitcode, prog_version] | " Returns array: [prog_exitcode, prog_version] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes