mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 14:04:26 +08:00
Try to recover from errors that are happening in subprocesses
This commit is contained in:
@@ -42,3 +42,4 @@ from jedi.api import Script, Interpreter, set_debug_function, \
|
||||
preload_module, names
|
||||
from jedi import settings
|
||||
from jedi.api.environment import find_virtualenvs, find_python_environments
|
||||
from jedi.api.exceptions import InternalError
|
||||
|
||||
2
jedi/api/exceptions.py
Normal file
2
jedi/api/exceptions.py
Normal file
@@ -0,0 +1,2 @@
|
||||
class InternalError(Exception):
|
||||
pass
|
||||
@@ -19,6 +19,7 @@ from jedi.cache import memoize_method
|
||||
from jedi.evaluate.compiled.subprocess import functions
|
||||
from jedi.evaluate.compiled.access import DirectObjectAccess, AccessPath, \
|
||||
SignatureParam
|
||||
from jedi.api.exceptions import InternalError
|
||||
|
||||
_PICKLE_PROTOCOL = 2
|
||||
|
||||
@@ -141,25 +142,6 @@ class _Subprocess(object):
|
||||
# stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
def _send(self, evaluator_id, function, args=(), kwargs={}):
|
||||
if not is_py3:
|
||||
# Python 2 compatibility
|
||||
kwargs = {force_unicode(key): value for key, value in kwargs.items()}
|
||||
|
||||
data = evaluator_id, function, args, kwargs
|
||||
pickle.dump(data, self._process.stdin, protocol=_PICKLE_PROTOCOL)
|
||||
self._process.stdin.flush()
|
||||
is_exception, result = _pickle_load(self._process.stdout)
|
||||
if is_exception:
|
||||
raise result
|
||||
return result
|
||||
|
||||
def terminate(self):
|
||||
self._process.terminate()
|
||||
|
||||
def kill(self):
|
||||
self._process.kill()
|
||||
|
||||
|
||||
class _CompiledSubprocess(_Subprocess):
|
||||
def __init__(self, executable):
|
||||
@@ -170,6 +152,7 @@ class _CompiledSubprocess(_Subprocess):
|
||||
os.path.dirname(os.path.dirname(parso_path))
|
||||
)
|
||||
)
|
||||
self._executable = executable
|
||||
self._evaluator_deletion_queue = queue.deque()
|
||||
|
||||
def run(self, evaluator, function, args=(), kwargs={}):
|
||||
@@ -188,6 +171,38 @@ class _CompiledSubprocess(_Subprocess):
|
||||
def get_sys_path(self):
|
||||
return self._send(None, functions.get_sys_path, (), {})
|
||||
|
||||
def kill(self):
|
||||
try:
|
||||
subprocess = _subprocesses[self._executable]
|
||||
except KeyError:
|
||||
# Fine it was already removed from the cache.
|
||||
pass
|
||||
else:
|
||||
# In the `!=` case there is already a new subprocess in place
|
||||
# and we don't need to do anything here anymore.
|
||||
if subprocess == self:
|
||||
del _subprocesses[self._executable]
|
||||
|
||||
self._process.kill()
|
||||
|
||||
def _send(self, evaluator_id, function, args=(), kwargs={}):
|
||||
if not is_py3:
|
||||
# Python 2 compatibility
|
||||
kwargs = {force_unicode(key): value for key, value in kwargs.items()}
|
||||
|
||||
data = evaluator_id, function, args, kwargs
|
||||
pickle.dump(data, self._process.stdin, protocol=_PICKLE_PROTOCOL)
|
||||
self._process.stdin.flush()
|
||||
try:
|
||||
is_exception, result = _pickle_load(self._process.stdout)
|
||||
except EOFError:
|
||||
self.kill()
|
||||
raise InternalError("The subprocess crashed.")
|
||||
|
||||
if is_exception:
|
||||
raise result
|
||||
return result
|
||||
|
||||
def delete_evaluator(self, evaluator_id):
|
||||
"""
|
||||
Currently we are not deleting evalutors instantly. They only get
|
||||
|
||||
@@ -66,6 +66,13 @@ def get_builtin_module_names(evaluator):
|
||||
return list(map(force_unicode, sys.builtin_module_names))
|
||||
|
||||
|
||||
def _test_raise_error(evaluator, exception_type):
|
||||
"""
|
||||
Raise an error to simulate certain problems for unit tests.
|
||||
"""
|
||||
raise exception_type
|
||||
|
||||
|
||||
def _get_init_path(directory_path):
|
||||
"""
|
||||
The __init__ file can be searched in a directory. If found return it, else
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
|
||||
import jedi
|
||||
from jedi._compatibility import py_version
|
||||
from jedi.api.environment import Environment, get_default_environment, \
|
||||
InvalidPythonEnvironment, find_python_environments
|
||||
@@ -45,3 +46,12 @@ def test_load_module(evaluator):
|
||||
assert access_handle.get_api_type() == 'module'
|
||||
with pytest.raises(AttributeError):
|
||||
access_handle.py__mro__()
|
||||
|
||||
|
||||
def test_error_in_environment(evaluator, Script):
|
||||
# Provoke an error to show how Jedi can recover from it.
|
||||
with pytest.raises(jedi.InternalError):
|
||||
evaluator.compiled_subprocess._test_raise_error(KeyboardInterrupt)
|
||||
# Jedi should still work.
|
||||
def_, = Script('str').goto_definitions()
|
||||
assert def_.name == 'str'
|
||||
|
||||
Reference in New Issue
Block a user