From 9581ebde0bebccae1b955eeee76aa05f6159abf3 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Tue, 23 Dec 2014 01:58:20 +0100 Subject: [PATCH] Fix debugger output transmission after program termination for remote debug and save_signatures mode (PY-13861). --- python/helpers/pydev/pydevd.py | 47 +++++++++++++++++++++++------ python/helpers/pydev/pydevd_comm.py | 2 ++ python/helpers/pydev/pydevd_io.py | 2 ++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 5aadbe1a1983..fd98116826fa 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -245,14 +245,22 @@ def killAllPydevThreads(): #======================================================================================================================= -# PyDBCheckAliveThread +# CheckOutputThread +# Non-daemonic thread guaranties that all data is written even if program is finished #======================================================================================================================= -class PyDBCheckAliveThread(PyDBDaemonThread): +class CheckOutputThread(PyDBDaemonThread): def __init__(self, pyDb): PyDBDaemonThread.__init__(self) self.pyDb = pyDb self.setName('pydevd.CheckAliveThread') + pyDb.output_checker = self + + def start(self): + # it should be non daemon + thread = threading.Thread(target=self.run) + thread.daemon = False + thread.start() def OnRun(self): if self.dontTraceMe: @@ -270,7 +278,8 @@ class PyDBCheckAliveThread(PyDBDaemonThread): pydevd_tracing.SetTrace(None) # no debugging on this thread while not self.killReceived: - if not self.pyDb.haveAliveThreads(): + if not self.pyDb.haveAliveThreads() and self.pyDb.writer.empty() \ + and not has_data_to_redirect(): try: pydev_log.debug("No alive threads, finishing debug session") self.pyDb.FinishDebuggingSession() @@ -279,10 +288,14 @@ class PyDBCheckAliveThread(PyDBDaemonThread): traceback.print_exc() self.killReceived = True - return + + self.pyDb.checkOutputRedirect() time.sleep(0.3) + + + def doKillPydevThread(self): pass @@ -311,6 +324,7 @@ class PyDB: pydevd_tracing.ReplaceSysSetTraceFunc() self.reader = None self.writer = None + self.output_checker = None self.quitting = None self.cmdFactory = NetCommandFactory() self._cmd_queue = {} # the hash of Queues. Key is thread id, value is thread @@ -1453,10 +1467,8 @@ class PyDB: if self._finishDebuggingSession and not self._terminationEventSent: #that was not working very well because jython gave some socket errors try: - threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads) - for t in threads: - if hasattr(t, 'doKillPydevThread'): - t.doKillPydevThread() + if self.output_checker is None: + killAllPydevThreads() except: traceback.print_exc() self._terminationEventSent = True @@ -1577,7 +1589,9 @@ class PyDB: PyDBCommandThread(self).start() - PyDBCheckAliveThread(self).start() + if self.signature_factory is not None: + # we need all data to be sent to IDE even after program finishes + CheckOutputThread(self).start() def patch_threads(self): @@ -1736,13 +1750,26 @@ def usage(doExit=0): def initStdoutRedirect(): if not getattr(sys, 'stdoutBuf', None): sys.stdoutBuf = pydevd_io.IOBuf() + sys.stdout_original = sys.stdout sys.stdout = pydevd_io.IORedirector(sys.stdout, sys.stdoutBuf) #@UndefinedVariable def initStderrRedirect(): if not getattr(sys, 'stderrBuf', None): sys.stderrBuf = pydevd_io.IOBuf() + sys.stderr_original = sys.stderr sys.stderr = pydevd_io.IORedirector(sys.stderr, sys.stderrBuf) #@UndefinedVariable + +def has_data_to_redirect(): + if getattr(sys, 'stdoutBuf', None): + if not sys.stdoutBuf.empty(): + return True + if getattr(sys, 'stderrBuf', None): + if not sys.stderrBuf.empty(): + return True + + return False + #======================================================================================================================= # settrace #======================================================================================================================= @@ -1881,7 +1908,7 @@ def _locked_settrace( debugger.setSuspend(t, CMD_THREAD_SUSPEND) PyDBCommandThread(debugger).start() - PyDBCheckAliveThread(debugger).start() + CheckOutputThread(debugger).start() else: # ok, we're already in debug mode, with all set, so, let's just set the break diff --git a/python/helpers/pydev/pydevd_comm.py b/python/helpers/pydev/pydevd_comm.py index e00ae732eff3..36bd7c752ac2 100644 --- a/python/helpers/pydev/pydevd_comm.py +++ b/python/helpers/pydev/pydevd_comm.py @@ -454,6 +454,8 @@ class WriterThread(PyDBDaemonThread): if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 0: traceback.print_exc() + def empty(self): + return self.cmdQueue.empty() diff --git a/python/helpers/pydev/pydevd_io.py b/python/helpers/pydev/pydevd_io.py index 2e74154df667..d05eb86eea34 100644 --- a/python/helpers/pydev/pydevd_io.py +++ b/python/helpers/pydev/pydevd_io.py @@ -57,6 +57,8 @@ class IOBuf: def flush(self): pass + def empty(self): + return len(self.buflist) == 0 class _RedirectionsHolder: _stack_stdout = []