Merged with debugger changes done by Tobias.

Wed, 22 Feb 2017 19:00:39 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 22 Feb 2017 19:00:39 +0100
changeset 5545
4aec52243595
parent 5542
520713d0a381 (current diff)
parent 5544
5a90f78a73c9 (diff)
child 5546
971f5ab7429c

Merged with debugger changes done by Tobias.

--- a/DebugClients/Python/DebugBase.py	Tue Feb 21 19:29:29 2017 +0100
+++ b/DebugClients/Python/DebugBase.py	Wed Feb 22 19:00:39 2017 +0100
@@ -92,7 +92,6 @@
         self.currentFrame = None
         
         # frames, where we want to stop or release debugger
-        self.botframe = None
         self.stopframe = None
         self.returnframe = None
         self.stop_everywhere = False
@@ -312,19 +311,13 @@
             return self.trace_dispatch
         
         if event == 'call':
-            if self.botframe is None and frame.f_lineno >= 1:
-                self.botframe = frame
-                frame.f_trace = self.trace_dispatch
-                
-                self.user_line(frame)
-                return self.trace_dispatch
-            
-            if not (self.stop_here(frame) or
+            if (self.stop_here(frame) or
                     self.__checkBreakInFrame(frame) or
                     Watch.watches != []):
+                return self.trace_dispatch
+            else:
                 # No need to trace this function
-                return
-            return self.trace_dispatch
+                return 
         
         if event == 'return':
             return
@@ -384,7 +377,6 @@
             # stop at erics debugger frame or a threading bootstrap
             if (frame.f_back.f_code == stopOnHandleLine):
                 frame.f_trace = self.trace_dispatch
-                self.botframe = frame
                 break
             
             frame = frame.f_back
@@ -408,17 +400,9 @@
         @type dict
         """
         try:
-            frame = sys._getframe()
-            self.botframe = frame
-            # First time the dispach function is called, a "base debug"
-            # function has to be returned, which is called at every user code
-            # function call. Because of setting botframe manually, the
-            # specific branch returning the trace_dispatch itself is skipped.
-            # Because the next step is always in threading.py and we assume
-            # that there is no breakpoint in it, it's save to return
-            # trace_dispatch unconditionally.
-            sys.settrace(lambda frame, event, arg: self.trace_dispatch)
-            frame.f_trace = self.trace_dispatch
+            # Because in the initial run method the "base debug" function is
+            # set up, it's also valid for the threads afterwards.
+            sys.settrace(self.trace_dispatch)
             
             target(*args, **kwargs)
         except Exception:
@@ -450,6 +434,10 @@
             cmd = compile(cmd, "<string>", "exec")
         
         if debug:
+            # First time the trace_dispatch function is called, a "base debug"
+            # function has to be returned, which is called at every user code
+            # function call. This is ensured by setting stop_everywhere.
+            self.stop_everywhere = True
             sys.settrace(self.trace_dispatch)
         
         try:
--- a/DebugClients/Python/ThreadExtension.py	Tue Feb 21 19:29:29 2017 +0100
+++ b/DebugClients/Python/ThreadExtension.py	Wed Feb 22 19:00:39 2017 +0100
@@ -35,6 +35,7 @@
         self.threadNumber = 1
         self.enableImportHooks = True
         self._original_start_new_thread = None
+        self.threadingAttached = False
         self._qtThread = None
         
         self.clientLock = threading.RLock()
@@ -250,7 +251,8 @@
             return None
         
         if fullname in [self.threadModName, 'PyQt4.QtCore', 'PyQt5.QtCore',
-                        'PySide.QtCore'] and self.enableImportHooks:
+                        'PySide.QtCore', 'PySide2.QtCore',
+                        'threading'] and self.enableImportHooks:
             # Disable hook to be able to import original module
             self.enableImportHooks = False
             return self
@@ -273,8 +275,40 @@
             # make thread hooks available to system
             self._original_start_new_thread = module.start_new_thread
             module.start_new_thread = self.attachThread
-        elif fullname in ['PyQt4.QtCore', 'PyQt5.QtCore',
-                          'PySide.QtCore'] and self._qtThread is None:
+
+        # Add hook for threading.run()
+        elif (fullname == "threading" and self.threadingAttached is False):
+            self.threadingAttached = True
+            
+            # _debugClient as a class attribute can't be accessed in following
+            # class. Therefore we need a global variable.
+            _debugClient = self
+            
+            def _bootstrap(self, run):
+                newThread = _debugClient.threads[self.ident]
+                newThread.name = self.name
+                # see DebugBase.bootstrap
+                sys.settrace(newThread.trace_dispatch)
+                try:
+                    run()
+                except Exception:
+                    excinfo = sys.exc_info()
+                    newThread.user_exception(excinfo, True)
+            
+            class ThreadWrapper(module.Thread):
+                
+                def __init__(self, *args, **kwargs):
+                    # Overwrite the provided run method with our own, to
+                    # intercept the thread creation by threading.Thread
+                    self.run = lambda s=self, run=self.run: _bootstrap(s, run)
+                    
+                    super(ThreadWrapper, self).__init__(*args, **kwargs)
+            
+            module.Thread = ThreadWrapper
+        
+        elif (fullname in ['PyQt4.QtCore', 'PyQt5.QtCore',
+                           'PySide.QtCore', 'PySide2.QtCore'] and
+                self._qtThread is None):
             self._qtThread = module.QThread
             # _debugClient as a class attribute can't be accessed in following
             # class. Therefore we need a global variable.

eric ide

mercurial