init: improve handling of import errors (#840)

* jedi_vim.jedi_import_error: add location

This is useful for debugging.  It contains e.g. the path to parso, if
importing failed from there.

Example:

> Error: jedi-vim failed to initialize Python: jedi#setup_python_imports: could not import jedi: cannot import name 'PythonTokenTypes' (in /…/jedi-vim/pythonx/jedi/jedi/api/completion.py:1). (in function jedi#init_python[3]..<SNR>44_init_python[27]..jedi#setup_python_imports, line 37)

* init: handle jedi_vim.jedi_import_error in Vim plugin

Using `set shortmess+=F` would suppress the `:echom` used in
`jedi_vim.no_jedi_warning` [1].

This patch makes `jedi#setup_python_imports` handle the error instead.

1: https://github.com/neovim/neovim/issues/8675

* Revisit error handling with loading jedi_vim

* jedi#debug_info: display parso submodule separately

* Fix jedi#reinit_python

* fixup! Revisit error handling with loading jedi_vim

* display_debug_info: handle exceptions with environment.get_sys_path

* fixup! Revisit error handling with loading jedi_vim

[ci skip]
This commit is contained in:
Daniel Hahler
2018-07-04 22:12:07 +02:00
committed by Dave Halter
parent 0361d6c633
commit d3d9a91ae6
4 changed files with 109 additions and 72 deletions

View File

@@ -91,7 +91,7 @@ endfunction
function! jedi#reinit_python() abort function! jedi#reinit_python() abort
unlet! s:_init_python let s:_init_python = -1
call jedi#init_python() call jedi#init_python()
endfunction endfunction
@@ -99,13 +99,29 @@ endfunction
let s:_init_python = -1 let s:_init_python = -1
function! jedi#init_python() abort function! jedi#init_python() abort
if s:_init_python == -1 if s:_init_python == -1
let s:_init_python = 0
try try
let s:_init_python = s:init_python() let s:_init_python = s:init_python()
catch let s:_init_python = 1
let s:_init_python = 0 catch /^jedi/
" Only catch errors from jedi-vim itself here, so that for
" unexpected Python exceptions the traceback will be shown
" (e.g. with NameError in jedi#setup_python_imports's code).
if !exists('g:jedi#squelch_py_warning') if !exists('g:jedi#squelch_py_warning')
echoerr 'Error: jedi-vim failed to initialize Python: ' let error_lines = split(v:exception, '\n')
\ .v:exception.' (in '.v:throwpoint.')' let msg = 'Error: jedi-vim failed to initialize Python: '
\ .error_lines[0].' (in '.v:throwpoint.')'
if len(error_lines) > 1
echohl ErrorMsg
echom 'jedi-vim error: '.error_lines[0]
for line in error_lines[1:]
echom line
endfor
echohl None
let msg .= '. See :messages for more information.'
endif
redraw " Redraw to only have the main message by default.
echoerr msg
endif endif
endtry endtry
endif endif
@@ -127,42 +143,27 @@ function! jedi#setup_python_imports(py_version) abort
execute 'command! -nargs=1 PythonJedi '.cmd_exec.' <args>' execute 'command! -nargs=1 PythonJedi '.cmd_exec.' <args>'
let s:init_outcome = 0 let g:_jedi_init_error = 0
let init_lines = [ let init_lines = [
\ 'import vim', \ 'import vim',
\ 'try:', \ 'try:',
\ ' import jedi_vim', \ ' import jedi_vim',
\ ' if hasattr(jedi_vim, "jedi_import_error"):',
\ ' from jedi_vim_debug import format_exc_info',
\ ' vim.vars["_jedi_init_error"] = format_exc_info(jedi_vim.jedi_import_error)',
\ 'except Exception as exc:', \ 'except Exception as exc:',
\ ' if isinstance(exc, SyntaxError):', \ ' from jedi_vim_debug import format_exc_info',
\ ' exc_msg = repr(exc)', \ ' vim.vars["_jedi_init_error"] = format_exc_info()',
\ ' else:', \ ]
\ ' exc_msg = "%s: %s" % (exc.__class__.__name__, exc)', exe 'PythonJedi exec('''.escape(join(init_lines, '\n'), "'").''')'
\ ' vim.command(''let s:init_outcome = "could not import jedi_vim: {0}"''.format(exc_msg))', if g:_jedi_init_error isnot 0
\ 'else:', throw printf('jedi#setup_python_imports: %s', g:_jedi_init_error)
\ ' vim.command(''let s:init_outcome = 1'')']
try
exe 'PythonJedi exec('''.escape(join(init_lines, '\n'), "'").''')'
catch
throw printf('jedi#setup_python_imports: failed to run Python for initialization: %s.', v:exception)
endtry
if s:init_outcome is 0
throw 'jedi#setup_python_imports: failed to run Python for initialization.'
elseif s:init_outcome isnot 1
throw printf('jedi#setup_python_imports: %s.', s:init_outcome)
endif endif
return 1 return 1
endfunction endfunction
function! jedi#debug_info() abort function! jedi#debug_info() abort
if s:python_version ==# 'null'
try
call s:init_python()
catch
echohl WarningMsg | echom v:exception | echohl None
return
endtry
endif
if &verbose if &verbose
if &filetype !=# 'python' if &filetype !=# 'python'
echohl WarningMsg | echo 'You should run this in a buffer with filetype "python".' | echohl None echohl WarningMsg | echo 'You should run this in a buffer with filetype "python".' | echohl None
@@ -187,7 +188,9 @@ function! jedi#debug_info() abort
echo ' - jedi-vim git version: ' echo ' - jedi-vim git version: '
echon substitute(system('git -C '.s:script_path.' describe --tags --always --dirty'), '\v\n$', '', '') echon substitute(system('git -C '.s:script_path.' describe --tags --always --dirty'), '\v\n$', '', '')
echo ' - jedi git submodule status: ' echo ' - jedi git submodule status: '
echon substitute(system('git -C '.s:script_path.' submodule status'), '\v\n$', '', '') echon substitute(system('git -C '.s:script_path.' submodule status pythonx/jedi'), '\v\n$', '', '')
echo ' - parso git submodule status: '
echon substitute(system('git -C '.s:script_path.' submodule status pythonx/parso'), '\v\n$', '', '')
echo "\n" echo "\n"
echo '##### Settings' echo '##### Settings'
echo '```' echo '```'

View File

@@ -48,7 +48,21 @@ endif
" Pyimport command " Pyimport command
command! -nargs=1 -complete=custom,jedi#py_import_completions Pyimport :call jedi#py_import(<q-args>) command! -nargs=1 -complete=custom,jedi#py_import_completions Pyimport :call jedi#py_import(<q-args>)
command! -nargs=0 -bar JediDebugInfo call jedi#debug_info() function! s:jedi_debug_info()
" Ensure the autoload file has been loaded (and ignore any errors, which
" will be displayed with the debug info).
let unset = {}
let saved_squelch_py_warning = get(g:, 'jedi#squelch_py_warning', unset)
let g:jedi#squelch_py_warning = 1
call jedi#init_python()
if saved_squelch_py_warning is unset
unlet g:jedi#squelch_py_warning
else
let g:jedi#squelch_py_warning = saved_squelch_py_warning
endif
call jedi#debug_info()
endfunction
command! -nargs=0 -bar JediDebugInfo call s:jedi_debug_info()
command! -nargs=0 -bang JediClearCache call jedi#clear_cache(<bang>0) command! -nargs=0 -bang JediClearCache call jedi#clear_cache(<bang>0)
" vim: set et ts=4: " vim: set et ts=4:

View File

@@ -110,10 +110,9 @@ sys.path.insert(0, parso_path)
try: try:
import jedi import jedi
except ImportError as e: except ImportError:
no_jedi_warning(str(e))
jedi = None jedi = None
jedi_import_error = str(e) jedi_import_error = sys.exc_info()
else: else:
try: try:
version = jedi.__version__ version = jedi.__version__

View File

@@ -1,57 +1,78 @@
"""Used in jedi-vim's jedi#debug_info()""" """Used in jedi-vim's jedi#debug_info()"""
import sys
import vim import vim
from jedi_vim import PythonToVimStr
def echo(msg): def echo(msg):
vim.command('echo {0}'.format(msg)) vim.command('echo %r' % PythonToVimStr(msg))
def echo_error(msg):
vim.command('echohl ErrorMsg')
echo(msg)
vim.command('echohl None')
def format_exc_info(exc_info=None, tb_indent=2):
import traceback
if exc_info is None:
exc_info = sys.exc_info()
exc_msg = traceback.format_exception_only(exc_info[0], exc_info[1])
lines = ''.join(exc_msg).rstrip('\n').split('\n')
lines.append('Traceback (most recent call last):')
tb = traceback.format_tb(exc_info[2])
lines.extend(''.join(tb).rstrip('\n').split('\n'))
indent = ' ' * tb_indent
return '{0}'.format(('\n' + indent).join(lines))
def display_debug_info(): def display_debug_info():
echo("printf(' - global sys.version: `%s`', {0!r})".format( echo(' - global sys.version: `{0}`'.format(
', '.join([x.strip() ', '.join([x.strip()
for x in __import__('sys').version.split('\n')]))) for x in sys.version.split('\n')])))
echo("printf(' - global site module: `%s`', {0!r})".format( echo(' - global site module: `{0}`'.format(__import__('site').__file__))
__import__('site').__file__))
try: try:
import jedi_vim import jedi_vim
except Exception as e: except Exception:
echo("printf('ERROR: jedi_vim is not available: %s: %s', " echo_error('ERROR: could not import jedi_vim: {0}'.format(
"{0!r}, {1!r})".format(e.__class__.__name__, str(e))) format_exc_info()))
return return
try: if jedi_vim.jedi is None:
if jedi_vim.jedi is None: if hasattr(jedi_vim, 'jedi_import_error'):
echo("'ERROR: could not import the \"jedi\" Python module.'") error_msg = format_exc_info(jedi_vim.jedi_import_error)
echo("printf(' The error was: %s', {0!r})".format(
getattr(jedi_vim, "jedi_import_error", "UNKNOWN")))
else: else:
echo("printf('Jedi path: `%s`', {0!r})".format( error_msg = 'unknown error'
jedi_vim.jedi.__file__)) echo_error('ERROR: could not import the "jedi" Python module: {0}'.format(
echo("printf(' - version: %s', {0!r})".format( error_msg))
jedi_vim.jedi.__version__)) else:
echo('Jedi path: `{0}`'.format(jedi_vim.jedi.__file__))
echo(' - version: {0}'.format(jedi_vim.jedi.__version__))
try:
environment = jedi_vim.get_environment(use_cache=False)
except AttributeError:
script_evaluator = jedi_vim.jedi.Script('')._evaluator
try: try:
environment = jedi_vim.get_environment(use_cache=False) sys_path = script_evaluator.project.sys_path
except AttributeError: except AttributeError:
script_evaluator = jedi_vim.jedi.Script('')._evaluator sys_path = script_evaluator.sys_path
try: else:
sys_path = script_evaluator.project.sys_path echo(' - environment: `{0}`'.format(environment))
except AttributeError: try:
sys_path = script_evaluator.sys_path
else:
echo("printf(' - environment: %s', {0!r})".format(
str(environment)))
sys_path = environment.get_sys_path() sys_path = environment.get_sys_path()
except Exception:
echo_error('ERROR: failed to get sys path from environment: {0}'.format(
format_exc_info()))
return
echo("' - sys_path:'") echo(' - sys_path:')
for p in sys_path: for p in sys_path:
echo("printf(' - `%s`', {0!r})".format(p)) echo(' - `{0}`'.format(p))
except Exception as e:
vim.command('echohl ErrorMsg')
echo("printf('There was an error accessing jedi_vim.jedi: %s', {0!r})".format(
str(e)))
import traceback
for l in traceback.format_exc().splitlines():
echo("printf('%s', {0!r})".format(l))
vim.command('echohl None')