Implemented multi process debugging support for the 'multiprocessing' module. multi_processing

Sat, 02 May 2020 14:35:03 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 02 May 2020 14:35:03 +0200
branch
multi_processing
changeset 7563
b0d6b63f2843
parent 7428
27c55a3d0b89
child 7564
787684e6f2f3

Implemented multi process debugging support for the 'multiprocessing' module.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/DebugBase.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/DebugClientBase.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/ModuleLoader.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/MultiprocessingExtension.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/SubprocessExtension.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Sat Feb 22 17:03:43 2020 +0100
+++ b/eric6.e4p	Sat May 02 14:35:03 2020 +0200
@@ -51,6 +51,7 @@
     <Source>eric6/DebugClients/Python/FlexCompleter.py</Source>
     <Source>eric6/DebugClients/Python/ModuleLoader.py</Source>
     <Source>eric6/DebugClients/Python/MultiProcessDebugExtension.py</Source>
+    <Source>eric6/DebugClients/Python/MultiprocessingExtension.py</Source>
     <Source>eric6/DebugClients/Python/PyProfile.py</Source>
     <Source>eric6/DebugClients/Python/QProcessExtension.py</Source>
     <Source>eric6/DebugClients/Python/SubprocessExtension.py</Source>
--- a/eric6/DebugClients/Python/DebugBase.py	Sat Feb 22 17:03:43 2020 +0100
+++ b/eric6/DebugClients/Python/DebugBase.py	Sat May 02 14:35:03 2020 +0200
@@ -634,6 +634,8 @@
             # No need to handle special case if a lot of lines between
             # (e.g. closure), because the additional lines won't cause a bp
             for co_lno in co_lnotab:
+                if co_lno >= 0x80:
+                    lineNo -= 0x100
                 lineNo += co_lno
                 lineNumbers.append(lineNo)
             
--- a/eric6/DebugClients/Python/DebugClientBase.py	Sat Feb 22 17:03:43 2020 +0100
+++ b/eric6/DebugClients/Python/DebugClientBase.py	Sat May 02 14:35:03 2020 +0200
@@ -2078,8 +2078,8 @@
         if self.running:
             self.__setCoding(self.running)
         self.passive = passive
-        if passive:
-            self.sendPassiveStartup(self.running, exceptions)
+#        if passive:
+#            self.sendPassiveStartup(self.running, exceptions)
         self.__interact()
         
         # setup the debugger variables
@@ -2357,16 +2357,16 @@
         
         @return process ID (integer)
         """
-        if not self.fork_auto:
-            self.sendJsonCommand("RequestForkTo", {})
-            self.eventLoop(True)
+##        if not self.fork_auto:
+##            self.sendJsonCommand("RequestForkTo", {})
+##            self.eventLoop(True)
         pid = DebugClientOrigFork()
         if pid == 0:
             # child
-            if not self.fork_child:
-                sys.settrace(None)
-                sys.setprofile(None)
-                self.sessionClose(False)
+##            if not self.fork_child:
+            sys.settrace(None)
+            sys.setprofile(None)
+            self.sessionClose(False)
 ##                (wd, host, port, exceptions, tracePython, redirect,
 ##                 noencoding, fork_auto, fork_child) = self.startOptions
 ##                self.startDebugger(sys.argv[0], host, port,
--- a/eric6/DebugClients/Python/ModuleLoader.py	Sat Feb 22 17:03:43 2020 +0100
+++ b/eric6/DebugClients/Python/ModuleLoader.py	Sat May 02 14:35:03 2020 +0200
@@ -12,6 +12,7 @@
 
 from QProcessExtension import patchQProcess
 from SubprocessExtension import patchSubprocess
+from MultiprocessingExtension import patchMultiprocessing
 
 
 class ModuleLoader(object):
@@ -38,6 +39,7 @@
             'thread', '_thread', 'threading',
             'greenlet',
             'subprocess',
+            'multiprocessing',
             'PyQt4.QtCore', 'PyQt5.QtCore',
             'PySide.QtCore', 'PySide2.QtCore',
         )
@@ -87,7 +89,15 @@
         ):
             module.eric6_patched = True
             patchSubprocess(module, self.__dbgClient)
-            
+        
+        ## Add hook for multiprocessing.Process
+        elif (
+            fullname == 'multiprocessing' and
+            not hasattr(module, 'eric6_patched')
+        ):
+            module.eric6_patched = True
+            patchMultiprocessing(module, self.__dbgClient)
+        
         ## Add hook for *.QThread and *.QProcess
         elif (
             fullname in ('PyQt4.QtCore', 'PyQt5.QtCore',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/DebugClients/Python/MultiprocessingExtension.py	Sat May 02 14:35:03 2020 +0200
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a function to patch multiprocessing.Process to support
+debugging of the process.
+"""
+
+import sys
+import traceback
+
+_debugClient = None
+_originalProcess = None
+_originalBootstrap = None
+
+
+
+def patchMultiprocessing(module, debugClient):
+    """
+    Function to patch the multiprocessing module.
+    
+    @param module reference to the imported module to be patched
+    @type module
+    @param debugClient reference to the debug client object
+    @type DebugClient
+    """     # __IGNORE_WARNING_D234__
+    global _debugClient, _originalProcess, _originalBootstrap
+    
+    _debugClient = debugClient
+    
+    if sys.version_info >= (3, 4):
+        _originalProcess = module.process.BaseProcess
+    else:
+        _originalProcess = module.Process
+    _originalBootstrap = _originalProcess._bootstrap
+    
+    class ProcessWrapper(_originalProcess):
+        """
+        Wrapper class for multiprocessing.Process.
+        """
+        def _bootstrap(self, *args, **kwargs):
+            """
+            Wrapper around _bootstrap to start debugger.
+            
+            @param args function arguments
+            @type list
+            @param kwargs keyword only arguments
+            @type dict
+            @return exit code of the process
+            @rtype int
+            """
+            if (
+                _debugClient.debugging and
+                _debugClient.multiprocessSupport
+            ):
+                try:
+                    (wd, host, port, exceptions, tracePython, redirect,
+                     noencoding) = _debugClient.startOptions[:7]
+                    _debugClient.startDebugger(
+                        sys.argv[0], host=host, port=port,
+                        exceptions=exceptions, tracePython=tracePython,
+                        redirect=redirect, passive=False,
+                        multiprocessSupport=True
+                    )
+                except Exception:
+                    print("Exception during multiprocessing bootstrap init:")
+                    traceback.print_exc(file=sys.stdout)
+                    sys.stdout.flush()
+                    raise
+            
+            return _originalBootstrap(self, *args, **kwargs)
+    
+    if sys.version_info >= (3, 4):
+        _originalProcess._bootstrap = ProcessWrapper._bootstrap
+    else:
+        module.Process = ProcessWrapper
+    
+##    if sys.version_info >= (3, 4):
+##        _originalProcess = module.Process
+####        _originalProcess = module.process.BaseProcess
+##    else:
+##        _originalProcess = module.Process
+##    class ProcessWrapper(_originalProcess):
+##        def __init__(self, *args, **kwargs):
+##            super(ProcessWrapper, self).__init__(*args, **kwargs)
+##            self._run = self.run
+##            self.run = self._bootstrap_eric6
+##            # Class attributes are not transfered to new process. Therefore make a
+##            # copy as instance attribute
+##            self._options = _debugClient.startOptions
+##        
+##        def _bootstrap_eric6(self):
+##            """
+##            Bootstrap for threading, which reports exceptions correctly.
+##            
+##            @param run the run method of threading.Thread
+##            @type method pointer
+##            """
+##            from DebugClient import DebugClient
+##            self.debugClient = DebugClient()
+##            
+##            (_wd, host, port, exceptions, tracePython, redirect,
+##             noencoding) = self._options[:7]
+##            
+##            args = sys.argv
+##            self.debugClient.name = self.name
+##            
+##            self.debugClient.startDebugger(args[0], host, port,
+##                                     exceptions=exceptions,
+##                                     tracePython=tracePython,
+##                                     redirect=redirect)
+##
+##            try:
+##                self._run()
+##            except Exception:
+##                excinfo = sys.exc_info()
+##                self.debugClient.user_exception(excinfo, True)
+##            finally:
+##                sys.settrace(None)
+##    
+##    if sys.version_info >= (3, 4):
+##        module.Process = ProcessWrapper
+####        module.process.BaseProcess = ProcessWrapper
+##    else:
+##        module.Process = ProcessWrapper
--- a/eric6/DebugClients/Python/SubprocessExtension.py	Sat Feb 22 17:03:43 2020 +0100
+++ b/eric6/DebugClients/Python/SubprocessExtension.py	Sat May 02 14:35:03 2020 +0200
@@ -41,7 +41,7 @@
             @type list of str or str
             @param args constructor arguments of Popen
             @type list
-            @param kwargs constructor keword only arguments of Popen
+            @param kwargs constructor keyword only arguments of Popen
             @type dict
             """
             if (

eric ide

mercurial