diff -r 16f9a0bccda1 -r 313a91a38aed DebugClients/Python/ThreadExtension.py --- a/DebugClients/Python/ThreadExtension.py Thu Feb 23 21:40:26 2017 +0100 +++ b/DebugClients/Python/ThreadExtension.py Thu Feb 23 22:13:28 2017 +0100 @@ -20,6 +20,8 @@ from DebugBase import DebugBase +_qtThreadNumber = 1 + class ThreadExtension(object): """ @@ -36,7 +38,7 @@ self.enableImportHooks = True self._original_start_new_thread = None self.threadingAttached = False - self._qtThread = None + self.qtThreadAttached = False self.clientLock = threading.RLock() @@ -285,6 +287,12 @@ _debugClient = self def _bootstrap(self, run): + """ + Bootstrap for threading, which reports exceptions correctly. + + @param run the run method of threading.Thread + @type method pointer + """ newThread = _debugClient.threads[self.ident] newThread.name = self.name # see DebugBase.bootstrap @@ -296,6 +304,7 @@ newThread.user_exception(excinfo, True) class ThreadWrapper(module.Thread): + """ Wrapper class for threading.Thread. """ def __init__(self, *args, **kwargs): # Overwrite the provided run method with our own, to @@ -306,50 +315,61 @@ module.Thread = ThreadWrapper + # Add hook for *.QThread elif (fullname in ['PyQt4.QtCore', 'PyQt5.QtCore', 'PySide.QtCore', 'PySide2.QtCore'] and - self._qtThread is None): - self._qtThread = module.QThread + self.qtThreadAttached is False): + self.qtThreadAttached = True # _debugClient as a class attribute can't be accessed in following # class. Therefore we need a global variable. _debugClient = self + + def _bootstrapQThread(self, run): + """ + Bootstrap for QThread, which reports exceptions correctly. + + @param run the run method of *.QThread + @type method pointer + """ + global _qtThreadNumber + + newThread = DebugBase(_debugClient) + ident = _thread.get_ident() + name = 'QtThread-{0}'.format(_qtThreadNumber) + + _qtThreadNumber += 1 + newThread.id = ident + newThread.name = name + + _debugClient.threads[ident] = newThread + + # see DebugBase.bootstrap + sys.settrace(newThread.trace_dispatch) + try: + run() + except SystemExit: + # *.QThreads doesn't like SystemExit + pass + except Exception: + excinfo = sys.exc_info() + newThread.user_exception(excinfo, True) + class QThreadWrapper(module.QThread): - __qtThreadNumber = 1 + """ Wrapper class for *.QThread. """ def __init__(self, *args, **kwargs): # Overwrite the provided run method with our own, to # intercept the thread creation by Qt - self._ApplicationRun = self.run - self.run = self.__bootstrapQThread + self.run = lambda s=self, run=self.run: ( + _bootstrapQThread(s, run)) super(QThreadWrapper, self).__init__(*args, **kwargs) - - def __bootstrapQThread(self): - newThread = DebugBase(_debugClient) - ident = _thread.get_ident() - name = 'QtThread-{0}'.format(self.__qtThreadNumber) - self.__qtThreadNumber += 1 - - newThread.id = ident - newThread.name = name - - _debugClient.threads[ident] = newThread - - frame = sys._getframe() - newThread.botframe = frame - frame.f_trace = newThread.trace_dispatch - # see DebugBase.bootstrap - sys.settrace( - lambda frame, event, arg: newThread.trace_dispatch) - - return self._ApplicationRun() module.QThread = QThreadWrapper self.enableImportHooks = True return module - # # eflag: noqa = M702