diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index ee1acbea..55c66a14 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -369,6 +369,7 @@ def print_to_stderr(*args): eval("print(*args, file=sys.stderr)") else: print >> sys.stderr, args + sys.stderr.flush() def utf8_repr(func): diff --git a/jedi/evaluate/compiled/subprocess/__init__.py b/jedi/evaluate/compiled/subprocess/__init__.py index ddf38bf7..fcefca66 100644 --- a/jedi/evaluate/compiled/subprocess/__init__.py +++ b/jedi/evaluate/compiled/subprocess/__init__.py @@ -15,9 +15,15 @@ import errno import weakref import traceback from functools import partial +from threading import Thread +try: + from Queue import Queue, Empty +except ImportError: + from queue import Queue, Empty # python 3.x from jedi._compatibility import queue, is_py3, force_unicode, \ pickle_dump, pickle_load, GeneralizedPopen +from jedi import debug from jedi.cache import memoize_method from jedi.evaluate.compiled.subprocess import functions from jedi.evaluate.compiled.access import DirectObjectAccess, AccessPath, \ @@ -28,6 +34,12 @@ from jedi.api.exceptions import InternalError _MAIN_PATH = os.path.join(os.path.dirname(__file__), '__main__.py') +def _enqueue_output(out, queue): + for line in iter(out.readline, b''): + queue.put(line) + out.close() + + def _get_function(name): return getattr(functions, name) @@ -142,7 +154,7 @@ class CompiledSubprocess(object): os.path.dirname(os.path.dirname(parso_path)), '.'.join(str(x) for x in sys.version_info[:3]), ) - return GeneralizedPopen( + process = GeneralizedPopen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, @@ -151,6 +163,14 @@ class CompiledSubprocess(object): # (this is already the case on Python 3). bufsize=-1 ) + self._stderr_queue = Queue() + self._stderr_thread = t = Thread( + target=_enqueue_output, + args=(process.stderr, self._stderr_queue) + ) + t.daemon = True + t.start() + return process def run(self, evaluator, function, args=(), kwargs={}): # Delete old evaluators. @@ -219,6 +239,15 @@ class CompiledSubprocess(object): stderr, )) + while True: + # Try to do some error reporting from the subprocess and print its + # stderr contents. + try: + line = self._stderr_queue.get_nowait() + debug.warning('stderr output: %s' % line.rstrip('\n')) + except Empty: + break + if is_exception: # Replace the attribute error message with a the traceback. It's # way more informative. @@ -282,11 +311,9 @@ class Listener(object): def listen(self): stdout = sys.stdout - # Mute stdout/stderr. Nobody should actually be able to write to those, - # because stdout is used for IPC and stderr will just be annoying if it - # leaks (on module imports). + # Mute stdout. Nobody should actually be able to write to it, + # because stdout is used for IPC. sys.stdout = open(os.devnull, 'w') - sys.stderr = open(os.devnull, 'w') stdin = sys.stdin if sys.version_info[0] > 2: stdout = stdout.buffer