eric6/DebugClients/Python/MultiProcessDebugExtension.py

branch
multi_processing
changeset 7871
eb65864ca038
parent 7802
eefe954f01e8
child 7872
433dacbfa456
diff -r ab8f95bc7d2d -r eb65864ca038 eric6/DebugClients/Python/MultiProcessDebugExtension.py
--- a/eric6/DebugClients/Python/MultiProcessDebugExtension.py	Mon Dec 07 19:53:15 2020 +0100
+++ b/eric6/DebugClients/Python/MultiProcessDebugExtension.py	Thu Dec 10 20:16:21 2020 +0100
@@ -9,7 +9,10 @@
 """
 
 
-from DebugUtilities import patchArguments, isPythonProgram
+from DebugUtilities import (
+    patchArguments, patchArgumentStringWindows, isPythonProgram,
+    isWindowsPlatform
+)
 
 _debugClient = None
 
@@ -47,7 +50,56 @@
         """
         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]
+        return getattr(os, originalName)(path, *args)
+    return newExecl
+
+
+def createExecv(originalName):
+    """
+    Function to patch the 'execv' process creation functions.
+    
+    <ul>
+        <li>os.execv(path, args)</li>
+        <li>os.execvp(file, args)</li>
+    </ul>
+    """
+    def newExecv(path, args):
+        """
+        Function replacing the 'execv' functions of the os module.
+        """
+        import os
+        if (
+            _debugClient.debugging and
+            _debugClient.multiprocessSupport
+        ):
+            args = patchArguments(_debugClient, args)
+            if isPythonProgram(args[0]):
+                path = args[0]
+        return getattr(os, originalName)(path, args)
+    return newExecv
+
+
+def createExecve(originalName):
+    """
+    Function to patch the 'execve' process creation functions.
+    
+    <ul>
+        <li>os.execve(path, args, env)</li>
+        <li>os.execvpe(file, args, env)</li>
+    </ul>
+    """
+    def newExecve(path, args, env):
+        """
+        Function replacing the 'execve' functions of the os module.
+        """
         import os
         if (
             _debugClient.debugging and
@@ -56,9 +108,123 @@
             args = patchArguments(_debugClient, args)
             if isPythonProgram(args[0]):
                 path = args[0]
-            print(args)
-        return getattr(os, originalName)(path, *args)
-    return newExecl
+        return getattr(os, originalName)(path, args, env)
+    return newExecve
+
+
+# TODO: add createSpawn...
+
+
+def createForkExec(originalName):
+    """
+    Function to patch the 'fork_exec' process creation functions.
+    
+    <ul>
+        <li>_posixsubprocess.fork_exec(args, executable_list, close_fds,
+            ... (13 more))</li>
+    </ul>
+    """
+    def newForkExec(args, *other_args):
+        """
+        Function replacing the 'fork_exec' functions of the _posixsubprocess
+        module.
+        """
+        import _posixsubprocess
+        if (
+            _debugClient.debugging and
+            _debugClient.multiprocessSupport
+        ):
+            args = patchArguments(_debugClient, args)
+        return getattr(_posixsubprocess, originalName)(args, *other_args)
+    return newForkExec
+
+
+def createFork(original_name):
+    """
+    Function to patch the 'fork' process creation functions.
+    
+    <ul>
+        <li>os.fork()</li>
+    </ul>
+    """
+    def new_fork():
+        """
+        Function replacing the 'fork' function of the os module.
+        """
+        import os
+        import sys
+        
+        # A simple fork will result in a new python process
+        isNewPythonProcess = True
+        frame = sys._getframe()
+        
+        multiprocess = (
+            _debugClient.debugging and _debugClient.multiprocessSupport
+        )
+        
+        isSubprocessFork = False
+        while frame is not None:
+            if (
+                frame.f_code.co_name == '_execute_child' and
+                'subprocess' in frame.f_code.co_filename
+            ):
+                isSubprocessFork = True
+                # If we're actually in subprocess.Popen creating a child, it
+                # may result in something which is not a Python process, (so,
+                # we don't want to connect with it in the forked version).
+                executable = frame.f_locals.get('executable')
+                if executable is not None:
+                    isNewPythonProcess = False
+                    if isPythonProgram(executable):
+                        isNewPythonProcess = True
+                break
+            
+            frame = frame.f_back
+        frame = None    # Just make sure we don't hold on to it.
+        
+        childProcess = getattr(os, original_name)()     # fork
+        if not childProcess:
+            if isNewPythonProcess:
+                sys.settrace(None)
+                sys.setprofile(None)
+                _debugClient.sessionClose(False)
+                (wd, host, port, exceptions, tracePython, redirect,
+                 noencoding, fork_auto, fork_child) = _debugClient.startOptions
+                _debugClient.startDebugger(
+                    filename=sys.argv[0],
+                    host=host,
+                    port=port,
+                    enableTrace=multiprocess and not isSubprocessFork,
+                    exceptions=exceptions,
+                    tracePython=tracePython,
+                    redirect=redirect,
+                    passive=False,
+                    multiprocessSupport=multiprocess)
+        return childProcess
+
+    return new_fork
+
+
+def createCreateProcess(originalName):
+    """
+    Function to patch the 'CreateProcess' process creation function of
+    Windows.
+    """
+    def newCreateProcess(appName, cmdline, *args):
+        """
+        Function replacing the 'CreateProcess' function of the _subprocess
+        or _winapi module.
+        """
+        try:
+            import _subprocess
+        except ImportError:
+            import _winapi as _subprocess
+        return getattr(_subprocess, originalName)(
+            appName, patchArgumentStringWindows(_debugClient, cmdline), *args)
+    return newCreateProcess
+
+
+# TODO: add 'createFork'
 
 
 def patchNewProcessFunctions(multiprocessEnabled, debugClient):
@@ -84,7 +250,25 @@
     patchModule(os, "execle", createExecl)
     patchModule(os, "execlp", createExecl)
     patchModule(os, "execlpe", createExecl)
+    patchModule(os, "execv", createExecv)
+    patchModule(os, "execve", createExecve)
+    patchModule(os, "execvp", createExecv)
+    patchModule(os, "execvpe", createExecve)
     
     # TODO: implement patching of the various functions of the os module
     
+    if isWindowsPlatform():
+        try:
+            import _subprocess
+        except ImportError:
+            import _winapi as _subprocess
+        patchModule(_subprocess, 'CreateProcess', createCreateProcess)
+    else:
+        patchModule(os, "fork", createFork)
+        try:
+            import _posixsubprocess
+            patchModule(_posixsubprocess, "fork_exec", createForkExec)
+        except ImportError:
+            pass
+    
     _debugClient = debugClient

eric ide

mercurial