eric6/DebugClients/Python/ThreadExtension.py

branch
multi_processing
changeset 7404
663f1c3d6f53
parent 7390
052ce4cf06c6
child 7412
0a995393d2ba
diff -r 7446a7eacfc3 -r 663f1c3d6f53 eric6/DebugClients/Python/ThreadExtension.py
--- a/eric6/DebugClients/Python/ThreadExtension.py	Sat Feb 08 17:01:47 2020 +0100
+++ b/eric6/DebugClients/Python/ThreadExtension.py	Sat Feb 08 17:02:40 2020 +0100
@@ -9,7 +9,6 @@
 
 import os
 import sys
-import importlib
 
 if sys.version_info[0] == 2:
     import thread as _thread
@@ -35,11 +34,7 @@
         Constructor
         """
         self.threadNumber = 1
-        self.enableImportHooks = True
         self._original_start_new_thread = None
-        self.threadingAttached = False
-        self.qtThreadAttached = False
-        self.greenlet = False
         
         self.clientLock = threading.RLock()
         
@@ -53,17 +48,6 @@
         
         # special objects representing the main scripts thread and frame
         self.mainThread = self
-        
-        if sys.version_info[0] == 2:
-            self.threadModName = 'thread'
-        else:
-            self.threadModName = '_thread'
-        
-        # reset already imported thread module to apply hooks at next import
-        del sys.modules[self.threadModName]
-        del sys.modules['threading']
-        
-        sys.meta_path.insert(0, self)
 
     def attachThread(self, target=None, args=None, kwargs=None,
                      mainThread=False):
@@ -249,192 +233,180 @@
         self.threads = {id_: thrd for id_, thrd in self.threads.items()
                         if id_ in frames}
     
-    def find_module(self, fullname, path=None):
+    #######################################################################
+    ## Methods below deal with patching various modules to support
+    ## debugging of threads.
+    #######################################################################
+    
+    def patchPyThread(self, module):
         """
-        Public method returning the module loader.
-        
-        @param fullname name of the module to be loaded
-        @type str
-        @param path path to resolve the module name
-        @type str
-        @return module loader object
-        @rtype object
-        """
-        if fullname in sys.modules or not self.debugging:
-            return None
+        Public method to patch Python _thread (Python3) and thread (Python2)
+        modules.
         
-        if fullname in [self.threadModName, 'PyQt4.QtCore', 'PyQt5.QtCore',
-                        'PySide.QtCore', 'PySide2.QtCore', 'greenlet',
-                        'threading'] and self.enableImportHooks:
-            # Disable hook to be able to import original module
-            self.enableImportHooks = False
-            return self
-        
-        return None
+        @param module reference to the imported module to be patched
+        @type module
+        """
+        # make thread hooks available to system
+        self._original_start_new_thread = module.start_new_thread
+        module.start_new_thread = self.attachThread
     
-    def load_module(self, fullname):
+    def patchGreenlet(self, module):
         """
-        Public method to load a module.
+        Public method to patch the 'greenlet' module.
         
-        @param fullname name of the module to be loaded
-        @type str
-        @return reference to the loaded module
-        @rtype module
+        @param module reference to the imported module to be patched
+        @type module
+        @return flag indicating that the module was processed
+        @rtype bool
+        """
+        # Check for greenlet.settrace
+        if hasattr(module, 'settrace'):
+            DebugBase.pollTimerEnabled = False
+            return True
+        return False
+    
+    def patchPyThreading(self, module):
+        """
+        Public method to patch the Python threading module.
+        
+        @param module reference to the imported module to be patched
+        @type module
         """
-        module = importlib.import_module(fullname)
-        sys.modules[fullname] = module
-        if (fullname == self.threadModName and
-                self._original_start_new_thread is None):
-            # make thread hooks available to system
-            self._original_start_new_thread = module.start_new_thread
-            module.start_new_thread = self.attachThread
-
-        elif (fullname == 'greenlet' and self.greenlet is False):
-            # Check for greenlet.settrace
-            if hasattr(module, 'settrace'):
-                self.greenlet = True
-                DebugBase.pollTimerEnabled = False
+        # _debugClient as a class attribute can't be accessed in following
+        # class. Therefore we need a global variable.
+        _debugClient = self
         
-        # Add hook for threading.run()
-        elif (fullname == "threading" and self.threadingAttached is False):
-            self.threadingAttached = True
+        def _bootstrap(self, run):
+            """
+            Bootstrap for threading, which reports exceptions correctly.
             
-            # _debugClient as a class attribute can't be accessed in following
-            # class. Therefore we need a global variable.
-            _debugClient = self
-            
-            def _bootstrap(self, run):
-                """
-                Bootstrap for threading, which reports exceptions correctly.
-                
-                @param run the run method of threading.Thread
-                @type method pointer
+            @param run the run method of threading.Thread
+            @type method pointer
+            """
+            newThread = DebugBase(_debugClient)
+            _debugClient.threads[self.ident] = newThread
+            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)
+            finally:
+                sys.settrace(None)
+        
+        class ThreadWrapper(module.Thread):
+            """
+            Wrapper class for threading.Thread.
+            """
+            def __init__(self, *args, **kwargs):
                 """
-                newThread = DebugBase(_debugClient)
-                _debugClient.threads[self.ident] = newThread
-                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)
-                finally:
-                    sys.settrace(None)
-            
-            class ThreadWrapper(module.Thread):
+                Constructor
                 """
-                Wrapper class for threading.Thread.
-                """
-                def __init__(self, *args, **kwargs):
-                    """
-                    Constructor
-                    """
-                    # 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
+                # 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
+        
+        # Special handling of threading.(_)Timer
+        if sys.version_info[0] == 2:
+            timer = module._Timer
+        else:
+            timer = module.Timer
             
-            # Special handling of threading.(_)Timer
-            if sys.version_info[0] == 2:
-                timer = module._Timer
-            else:
-                timer = module.Timer
-                
-            class TimerWrapper(timer, ThreadWrapper):
+        class TimerWrapper(timer, ThreadWrapper):
+            """
+            Wrapper class for threading.(_)Timer.
+            """
+            def __init__(self, interval, function, *args, **kwargs):
                 """
-                Wrapper class for threading.(_)Timer.
+                Constructor
                 """
-                def __init__(self, interval, function, *args, **kwargs):
-                    """
-                    Constructor
-                    """
-                    super(TimerWrapper, self).__init__(
-                        interval, function, *args, **kwargs)
-            
-            if sys.version_info[0] == 2:
-                module._Timer = TimerWrapper
-            else:
-                module.Timer = TimerWrapper
+                super(TimerWrapper, self).__init__(
+                    interval, function, *args, **kwargs)
+        
+        if sys.version_info[0] == 2:
+            module._Timer = TimerWrapper
+        else:
+            module.Timer = TimerWrapper
+    
+        # Special handling of threading._DummyThread
+        class DummyThreadWrapper(module._DummyThread, ThreadWrapper):
+            """
+            Wrapper class for threading._DummyThread.
+            """
+            def __init__(self, *args, **kwargs):
+                """
+                Constructor
+                """
+                super(DummyThreadWrapper, self).__init__(*args, **kwargs)
+        
+        module._DummyThread = DummyThreadWrapper
+    
+    def patchQThread(self, module):
+        """
+        Public method to patch the QtCore module's QThread.
         
-            # Special handling of threading._DummyThread
-            class DummyThreadWrapper(module._DummyThread, ThreadWrapper):
-                """
-                Wrapper class for threading._DummyThread.
-                """
-                def __init__(self, *args, **kwargs):
-                    """
-                    Constructor
-                    """
-                    super(DummyThreadWrapper, self).__init__(*args, **kwargs)
+        @param module reference to the imported module to be patched
+        @type module
+        """
+        # _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.
             
-            module._DummyThread = DummyThreadWrapper
+            @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
         
-        # Add hook for *.QThread
-        elif (fullname in ['PyQt4.QtCore', 'PyQt5.QtCore',
-                           'PySide.QtCore', 'PySide2.QtCore'] and
-                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):
+            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)
+            finally:
+                sys.settrace(None)
+    
+        class QThreadWrapper(module.QThread):
+            """
+            Wrapper class for *.QThread.
+            """
+            def __init__(self, *args, **kwargs):
                 """
-                Bootstrap for QThread, which reports exceptions correctly.
+                Constructor
+                """
+                # Overwrite the provided run method with our own, to
+                # intercept the thread creation by Qt
+                self.run = lambda s=self, run=self.run: (
+                    _bootstrapQThread(s, run))
                 
-                @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)
-                finally:
-                    sys.settrace(None)
+                super(QThreadWrapper, self).__init__(*args, **kwargs)
         
-            class QThreadWrapper(module.QThread):
-                """
-                Wrapper class for *.QThread.
-                """
-                def __init__(self, *args, **kwargs):
-                    """
-                    Constructor
-                    """
-                    # Overwrite the provided run method with our own, to
-                    # intercept the thread creation by Qt
-                    self.run = lambda s=self, run=self.run: (
-                        _bootstrapQThread(s, run))
-                    
-                    super(QThreadWrapper, self).__init__(*args, **kwargs)
-            
-            module.QThread = QThreadWrapper
-        
-        self.enableImportHooks = True
-        return module
+        module.QThread = QThreadWrapper
 
 #
 # eflag: noqa = M702

eric ide

mercurial