diff --git a/jedi/evaluate/compiled/subprocess/__init__.py b/jedi/evaluate/compiled/subprocess/__init__.py index 88804c78..20367702 100644 --- a/jedi/evaluate/compiled/subprocess/__init__.py +++ b/jedi/evaluate/compiled/subprocess/__init__.py @@ -10,6 +10,8 @@ goals: import os import sys import subprocess +import socket +import errno import weakref import pickle from functools import partial @@ -192,7 +194,16 @@ class _CompiledSubprocess(object): data = evaluator_id, function, args, kwargs pickle.dump(data, self._process.stdin, protocol=_PICKLE_PROTOCOL) - self._process.stdin.flush() + try: + self._process.stdin.flush() + except socket.error as e: + # Once Python2 will be removed we can just use `BrokenPipeError`. + if e.errno != errno.EPIPE: + # Not a broken pipe + raise + self.kill() + raise InternalError("The subprocess was killed. Maybe out of memory?") + try: is_exception, result = _pickle_load(self._process.stdout) except EOFError: diff --git a/test/test_api/test_environment.py b/test/test_api/test_environment.py index a18e17e9..13bf26ad 100644 --- a/test/test_api/test_environment.py +++ b/test/test_api/test_environment.py @@ -58,3 +58,16 @@ def test_error_in_environment(evaluator, Script): # Jedi should still work. def_, = Script('str').goto_definitions() assert def_.name == 'str' + + +def test_killed_subprocess(evaluator, Script): + # Just kill the subprocess. + evaluator.compiled_subprocess._compiled_subprocess._process.kill() + # Since the process was terminated (and nobody knows about it) the first + # Jedi call fails. + with pytest.raises(jedi.InternalError): + Script('str').goto_definitions() + + def_, = Script('str').goto_definitions() + # Jedi should now work again. + assert def_.name == 'str'