Started implementing the patching of the os module. multi_processing

Sun, 16 Feb 2020 19:36:46 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 16 Feb 2020 19:36:46 +0100
branch
multi_processing
changeset 7422
9a008ab4811b
parent 7421
4a9900aef04e
child 7424
9bb7d8b0f966

Started implementing the patching of the os module.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/DebugClientBase.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/DebugUtilities.py file | annotate | diff | comparison | revisions
eric6/DebugClients/Python/MultiProcessDebugExtension.py file | annotate | diff | comparison | revisions
eric6/Debugger/DebuggerInterfacePython.py file | annotate | diff | comparison | revisions
eric6/Debugger/StartDialog.py file | annotate | diff | comparison | revisions
eric6/Preferences/ConfigurationPages/DebuggerGeneralPage.py file | annotate | diff | comparison | revisions
eric6/Preferences/ConfigurationPages/DebuggerGeneralPage.ui file | annotate | diff | comparison | revisions
eric6/Preferences/__init__.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6.e4p	Sun Feb 16 19:36:46 2020 +0100
@@ -50,6 +50,7 @@
     <Source>eric6/DebugClients/Python/DebugVariables.py</Source>
     <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/PyProfile.py</Source>
     <Source>eric6/DebugClients/Python/QProcessExtension.py</Source>
     <Source>eric6/DebugClients/Python/ThreadExtension.py</Source>
--- a/eric6/DebugClients/Python/DebugClientBase.py	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6/DebugClients/Python/DebugClientBase.py	Sun Feb 16 19:36:46 2020 +0100
@@ -33,6 +33,7 @@
 from FlexCompleter import Completer
 from DebugUtilities import prepareJsonCommand
 from BreakpointWatch import Breakpoint, Watch
+from MultiProcessDebugExtension import patchNewProcessFunctions
 
 if sys.version_info[0] == 2:
     from inspect import getargvalues, formatargvalues
@@ -2285,6 +2286,7 @@
                 )
                 if not self.noencoding:
                     self.__coding = self.defaultCoding
+                patchNewProcessFunctions(multiprocess, self)
                 res = self.startProgInDebugger(
                     args, wd, host, port, exceptions=exceptions,
                     tracePython=tracePython, redirect=redirect,
@@ -2296,6 +2298,10 @@
                 self.noencoding = True
                 del sys.argv[1]
             
+            if sys.argv[1] == '--multiprocess':
+                self.multiprocessSupport = True
+                del sys.argv[1]
+            
             if sys.argv[1] == '':
                 del sys.argv[1]
             
@@ -2338,6 +2344,7 @@
                 if not self.noencoding:
                     self.__coding = self.defaultCoding
                 self.connectDebugger(port, remoteAddress, redirect)
+                patchNewProcessFunctions(self.multiprocessSupport, self)
                 self.__interact()
             else:
                 print("No network port given. Aborting...")
--- a/eric6/DebugClients/Python/DebugUtilities.py	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6/DebugClients/Python/DebugUtilities.py	Sun Feb 16 19:36:46 2020 +0100
@@ -242,7 +242,7 @@
     @return modified argument list
     @rtype list of str
     """
-    args = arguments[:]    # create a copy of the arguments list
+    args = list(arguments[:])    # create a copy of the arguments list
     
     # support for shebang line
     program = os.path.basename(args[0]).lower()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/DebugClients/Python/MultiProcessDebugExtension.py	Sun Feb 16 19:36:46 2020 +0100
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a function to patch the process creation functions to
+support multiprocess debugging.
+"""
+
+import sys
+
+from DebugUtilities import isWindowsPlatform, patchArguments, isPythonProgram
+
+_debugClient = None
+
+
+def patchModule(module, functionName, createFunction):
+    """
+    Function to replace a function of a module with a modified one.
+    
+    @param module reference to the module
+    @type types.ModuleType
+    @param functionName name of the function to be replaced
+    @type str
+    @param createFunction function creating the replacement
+    @type types.FunctionType
+    """
+    if hasattr(module, functionName):
+        originalName = 'original_' + functionName
+        if not hasattr(module, originalName):
+            setattr(module, originalName, getattr(module, functionName))
+            setattr(module, functionName, createFunction(originalName))
+
+
+def createExecl(originalName):
+    """
+    Function to patch the 'execl' process creation functions.
+    
+    <ul>
+        <li>os.execl(path, arg0, arg1, ...)</li>
+        <li>os.execle(path, arg0, arg1, ..., env)</li>
+        <li>os.execlp(file, arg0, arg1, ...)</li>
+        <li>os.execlpe(file, arg0, arg1, ..., env)</li>
+    </ul>
+    """
+    def newExecl(path, *args):
+        """
+        Function replacing the 'execl' functions of the os module.
+        """
+        print(args)
+        import os
+        if (
+            _debugClient.debugging and
+            _debugClient.multiprocessSupport
+        ):
+            args = patchArguments(_debugClient, args)
+            if isPythonProgram(args[0]):
+                path = args[0]
+            print(args)
+        return getattr(os, originalName)(path, *args)
+    return newExecl
+
+def patchNewProcessFunctions(multiprocessEnabled, debugClient):
+    """
+    Function to patch the process creation functions to support multiprocess
+    debugging.
+    
+    @param multiprocessEnabled flag indicating multiprocess support
+    @type bool
+    @param debugClient reference to the debug client object
+    @type DebugClient
+    """
+    global _debugClient
+    
+    if not multiprocessEnabled:
+        # return without patching
+        return
+    
+    import os
+    
+    # patch 'os.exec...()' functions
+    patchModule(os, "execl", createExecl)
+    patchModule(os, "execle", createExecl)
+    patchModule(os, "execlp", createExecl)
+    patchModule(os, "execlpe", createExecl)
+    
+    # TODO: implement patching of the various functions of the os module
+    
+    _debugClient = debugClient
--- a/eric6/Debugger/DebuggerInterfacePython.py	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6/Debugger/DebuggerInterfacePython.py	Sun Feb 16 19:36:46 2020 +0100
@@ -226,6 +226,10 @@
             redirect = str(Preferences.getDebugger("Python3Redirect"))
             noencoding = (Preferences.getDebugger("Python3NoEncoding") and
                           '--no-encoding' or '')
+        multiprocessEnabled = (
+            '--multiprocess' if Preferences.getDebugger("MultiProcessEnabled")
+            else ''
+        )
         
         if Preferences.getDebugger("RemoteDbgEnabled"):
             ipaddr = self.debugServer.getHostAddress(False)
@@ -235,8 +239,12 @@
                 rhost = "localhost"
             if rexec:
                 args = Utilities.parseOptionString(rexec) + [
-                    rhost, interpreter, debugClient, noencoding, str(port),
-                    redirect, ipaddr]
+                    rhost, interpreter, debugClient]
+                if noencoding:
+                    args.append(noencoding)
+                if multiprocessEnabled:
+                    args.append(multiprocessEnabled)
+                args.extend([str(port), redirect, ipaddr])
                 if Utilities.isWindowsPlatform():
                     if not os.path.splitext(args[0])[1]:
                         for ext in [".exe", ".com", ".cmd", ".bat"]:
@@ -301,8 +309,12 @@
             ccmd = Preferences.getDebugger("ConsoleDbgCommand")
             if ccmd:
                 args = Utilities.parseOptionString(ccmd) + [
-                    interpreter, os.path.abspath(debugClient), noencoding,
-                    str(port), '0', ipaddr]
+                    interpreter, os.path.abspath(debugClient)]
+                if noencoding:
+                    args.append(noencoding)
+                if multiprocessEnabled:
+                    args.append(multiprocessEnabled)
+                args.extend([str(port), '0', ipaddr])
                 args[0] = Utilities.getExecutablePath(args[0])
                 process = self.__startProcess(args[0], args[1:], clientEnv,
                                               workingDir=workingDir)
@@ -315,11 +327,14 @@
                             """ started.</p>"""))
                 return process, self.__isNetworked, interpreter
         
-        process = self.__startProcess(
-            interpreter,
-            [debugClient, noencoding, str(port), redirect, ipaddr],
-            clientEnv,
-            workingDir=workingDir)
+        args = [debugClient]
+        if noencoding:
+            args.append(noencoding)
+        if multiprocessEnabled:
+            args.append(multiprocessEnabled)
+        args.extend([str(port), redirect, ipaddr])
+        process = self.__startProcess(interpreter, args, clientEnv,
+                                      workingDir=workingDir)
         if process is None:
             self.__startedVenv = ""
             E5MessageBox.critical(
@@ -370,7 +385,12 @@
         
         redirect = str(project.getDebugProperty("REDIRECT"))
         noencoding = (
-            project.getDebugProperty("NOENCODING") and '--no-encoding' or '')
+            '--no-encoding' if project.getDebugProperty("NOENCODING") else ''
+        )
+        multiprocessEnabled = (
+            '--multiprocess' if Preferences.getDebugger("MultiProcessEnabled")
+            else ''
+        )
         
         venvManager = e5App().getObject("VirtualEnvManager")
         interpreter = venvManager.getVirtualenvInterpreter(venvName)
@@ -396,8 +416,12 @@
                 rhost = "localhost"
             if rexec:
                 args = Utilities.parseOptionString(rexec) + [
-                    rhost, interpreter, debugClient, noencoding, str(port),
-                    redirect, ipaddr]
+                    rhost, interpreter, debugClient]
+                if noencoding:
+                    args.append(noencoding)
+                if multiprocessEnabled:
+                    args.append(multiprocessEnabled)
+                args.extend([str(port), redirect, ipaddr])
                 if Utilities.isWindowsPlatform():
                     if not os.path.splitext(args[0])[1]:
                         for ext in [".exe", ".com", ".cmd", ".bat"]:
@@ -464,8 +488,12 @@
                     Preferences.getDebugger("ConsoleDbgCommand"))
             if ccmd:
                 args = Utilities.parseOptionString(ccmd) + [
-                    interpreter, os.path.abspath(debugClient), noencoding,
-                    str(port), '0', ipaddr]
+                    interpreter, os.path.abspath(debugClient)]
+                if noencoding:
+                    args.append(noencoding)
+                if multiprocessEnabled:
+                    args.append(multiprocessEnabled)
+                args.extend([str(port), '0', ipaddr])
                 args[0] = Utilities.getExecutablePath(args[0])
                 process = self.__startProcess(args[0], args[1:], clientEnv,
                                               workingDir=workingDir)
@@ -478,11 +506,14 @@
                             """ started.</p>"""))
                 return process, self.__isNetworked, interpreter
         
-        process = self.__startProcess(
-            interpreter,
-            [debugClient, noencoding, str(port), redirect, ipaddr],
-            clientEnv,
-            workingDir=workingDir)
+        args = [debugClient]
+        if noencoding:
+            args.append(noencoding)
+        if multiprocessEnabled:
+            args.append(multiprocessEnabled)
+        args.extend([str(port), redirect, ipaddr])
+        process = self.__startProcess(interpreter, args, clientEnv,
+                                      workingDir=workingDir)
         if process is None:
             self.__startedVenv = ""
             E5MessageBox.critical(
--- a/eric6/Debugger/StartDialog.py	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6/Debugger/StartDialog.py	Sun Feb 16 19:36:46 2020 +0100
@@ -134,12 +134,16 @@
         self.ui.venvComboBox.setCurrentIndex(venvIndex)
         
         if dialogType == 0:        # start debug dialog
+            enableMultiprocessGlobal = Preferences.getDebugger(
+                "MultiProcessEnabled")
             self.ui.tracePythonCheckBox.setChecked(tracePython)
             self.ui.tracePythonCheckBox.show()
             self.ui.autoContinueCheckBox.setChecked(autoContinue)
             self.ui.forkModeCheckBox.setChecked(autoFork)
             self.ui.forkChildCheckBox.setChecked(forkChild)
-            self.ui.multiprocessGroup.setChecked(enableMultiprocess)
+            self.ui.multiprocessGroup.setEnabled(enableMultiprocessGlobal)
+            self.ui.multiprocessGroup.setChecked(
+                enableMultiprocess & enableMultiprocessGlobal)
             self.ui.multiprocessNoDebugCombo.clear()
             if multiprocessNoDebugHistory:
                 self.ui.multiprocessNoDebugCombo.addItems(
--- a/eric6/Preferences/ConfigurationPages/DebuggerGeneralPage.py	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6/Preferences/ConfigurationPages/DebuggerGeneralPage.py	Sun Feb 16 19:36:46 2020 +0100
@@ -137,6 +137,8 @@
             Preferences.getDebugger("PathTranslationRemote"))
         self.dbgTranslationLocalEdit.setText(
             Preferences.getDebugger("PathTranslationLocal"))
+        self.multiprocessCheckBox.setChecked(
+            Preferences.getDebugger("MultiProcessEnabled"))
         self.debugThreeStateBreakPoint.setChecked(
             Preferences.getDebugger("ThreeStateBreakPoints"))
         self.recentFilesSpinBox.setValue(
@@ -230,6 +232,9 @@
             "PathTranslationLocal",
             self.dbgTranslationLocalEdit.text())
         Preferences.setDebugger(
+            "MultiProcessEnabled",
+            self.multiprocessCheckBox.isChecked())
+        Preferences.setDebugger(
             "ThreeStateBreakPoints",
             self.debugThreeStateBreakPoint.isChecked())
         Preferences.setDebugger(
--- a/eric6/Preferences/ConfigurationPages/DebuggerGeneralPage.ui	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6/Preferences/ConfigurationPages/DebuggerGeneralPage.ui	Sun Feb 16 19:36:46 2020 +0100
@@ -479,6 +479,25 @@
     </widget>
    </item>
    <item>
+    <widget class="QGroupBox" name="groupBox_6">
+     <property name="title">
+      <string>Multi Process Debugging</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_8">
+      <item>
+       <widget class="QCheckBox" name="multiprocessCheckBox">
+        <property name="toolTip">
+         <string>Select to enable multiprocess debugging support globally</string>
+        </property>
+        <property name="text">
+         <string>Enable Multi Process Debugging Support</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
     <widget class="QGroupBox" name="groupBox_8">
      <property name="title">
       <string>Breakpoints</string>
@@ -804,6 +823,7 @@
   <tabstop>debugAutoSaveScriptsCheckBox</tabstop>
   <tabstop>automaticResetCheckBox</tabstop>
   <tabstop>dontShowClientExitCheckBox</tabstop>
+  <tabstop>multiprocessCheckBox</tabstop>
   <tabstop>debugThreeStateBreakPoint</tabstop>
   <tabstop>recentFilesSpinBox</tabstop>
   <tabstop>exceptionBreakCheckBox</tabstop>
--- a/eric6/Preferences/__init__.py	Sun Feb 16 16:14:25 2020 +0100
+++ b/eric6/Preferences/__init__.py	Sun Feb 16 19:36:46 2020 +0100
@@ -111,7 +111,9 @@
         # space separated list of Python2 extensions
         "PythonExtensions": ".py2 .pyw2 .ptl",
         # space separated list of Python3 extensions
-        "Python3Extensions": ".py .pyw .py3 .pyw3"
+        "Python3Extensions": ".py .pyw .py3 .pyw3",
+        # Global Multiprocess Debugging Support
+        "MultiProcessEnabled": True,
     }
     
     # defaults for the UI settings
@@ -1723,6 +1725,7 @@
                "Autosave", "ThreeStateBreakPoints",
                "SuppressClientExit", "BreakAlways",
                "AutoViewSourceCode", "ShowExceptionInShell",
+               "MultiProcessEnabled",
                ]:
         return toBool(prefClass.settings.value(
             "Debugger/" + key, prefClass.debuggerDefaults[key]))

eric ide

mercurial