eric6/DebugClients/Python/MultiProcessDebugExtension.py

branch
multi_processing
changeset 7872
433dacbfa456
parent 7871
eb65864ca038
child 7873
cb2badbdf26c
--- a/eric6/DebugClients/Python/MultiProcessDebugExtension.py	Thu Dec 10 20:16:21 2020 +0100
+++ b/eric6/DebugClients/Python/MultiProcessDebugExtension.py	Sat Dec 12 15:30:05 2020 +0100
@@ -17,6 +17,16 @@
 _debugClient = None
 
 
+def _shallPatch():
+    """
+    Function to determine, if the multiprocessing patches should be done.
+    
+    @return flag indicating patching should be performed
+    @rtype bool
+    """
+    return _debugClient.debugging and _debugClient.multiprocessSupport
+
+
 def patchModule(module, functionName, createFunction):
     """
     Function to replace a function of a module with a modified one.
@@ -45,16 +55,18 @@
         <li>os.execlp(file, arg0, arg1, ...)</li>
         <li>os.execlpe(file, arg0, arg1, ..., env)</li>
     </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
     """
     def newExecl(path, *args):
         """
         Function replacing the 'execl' functions of the os module.
         """
         import os
-        if (
-            _debugClient.debugging and
-            _debugClient.multiprocessSupport
-        ):
+        if _shallPatch():
             args = patchArguments(_debugClient, args)
             if isPythonProgram(args[0]):
                 path = args[0]
@@ -70,16 +82,18 @@
         <li>os.execv(path, args)</li>
         <li>os.execvp(file, args)</li>
     </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
     """
     def newExecv(path, args):
         """
         Function replacing the 'execv' functions of the os module.
         """
         import os
-        if (
-            _debugClient.debugging and
-            _debugClient.multiprocessSupport
-        ):
+        if _shallPatch():
             args = patchArguments(_debugClient, args)
             if isPythonProgram(args[0]):
                 path = args[0]
@@ -95,16 +109,18 @@
         <li>os.execve(path, args, env)</li>
         <li>os.execvpe(file, args, env)</li>
     </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
     """
     def newExecve(path, args, env):
         """
         Function replacing the 'execve' functions of the os module.
         """
         import os
-        if (
-            _debugClient.debugging and
-            _debugClient.multiprocessSupport
-        ):
+        if _shallPatch():
             args = patchArguments(_debugClient, args)
             if isPythonProgram(args[0]):
                 path = args[0]
@@ -112,7 +128,102 @@
     return newExecve
 
 
-# TODO: add createSpawn...
+def createSpawnl(originalName):
+    """
+    Function to patch the 'spawnl' process creation functions.
+    
+    <ul>
+        <li>os.spawnl(mode, path, arg0, arg1, ...)</li>
+        <li>os.spawnlp(mode, file, arg0, arg1, ...)</li>
+    </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
+    """
+    def newSpawnl(mode, path, *args):
+        """
+        Function replacing the 'spawnl' functions of the os module.
+        """
+        import os
+        args = patchArguments(_debugClient, args)
+        return getattr(os, originalName)(mode, path, *args)
+    return newSpawnl
+
+
+def createSpawnv(originalName):
+    """
+    Function to patch the 'spawnv' process creation functions.
+    
+    <ul>
+        <li>os.spawnv(mode, path, args)</li>
+        <li>os.spawnvp(mode, file, args)</li>
+    </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
+    """
+    def newSpawnv(mode, path, args):
+        """
+        Function replacing the 'spawnv' functions of the os module.
+        """
+        import os
+        args = patchArguments(_debugClient, args)
+        return getattr(os, originalName)(mode, path, args)
+    return newSpawnv
+
+
+def createSpawnve(originalName):
+    """
+    Function to patch the 'spawnve' process creation functions.
+    
+    <ul>
+        <li>os.spawnve(mode, path, args, env)</li>
+        <li>os.spawnvpe(mode, file, args, env)</li>
+    </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
+    """
+    def newSpawnve(mode, path, args, env):
+        """
+        Function replacing the 'spawnve' functions of the os module.
+        """
+        import os
+        args = patchArguments(_debugClient, args)
+        return getattr(os, originalName)(mode, path, args, env)
+    return newSpawnve
+
+
+def createPosixSpawn(originalName):
+    """
+    Function to patch the 'posix_spawn' process creation functions.
+    
+    <ul>
+        <li>os.posix_spawn(path, argv, env, *, file_actions=None, ...
+            (6 more))</li>
+        <li>os.posix_spawnp(path, argv, env, *, file_actions=None, ...
+            (6 more))</li>
+    </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
+    """
+    def newPosixSpawn(path, argv, env, **kwargs):
+        """
+        Function replacing the 'posix_spawn' functions of the os module.
+        """
+        import os
+        argv = patchArguments(_debugClient, argv)
+        return getattr(os, originalName)(path, argv, env, **kwargs)
+    return newPosixSpawn
 
 
 def createForkExec(originalName):
@@ -123,6 +234,11 @@
         <li>_posixsubprocess.fork_exec(args, executable_list, close_fds,
             ... (13 more))</li>
     </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
     """
     def newForkExec(args, *other_args):
         """
@@ -130,22 +246,24 @@
         module.
         """
         import _posixsubprocess
-        if (
-            _debugClient.debugging and
-            _debugClient.multiprocessSupport
-        ):
+        if _shallPatch():
             args = patchArguments(_debugClient, args)
         return getattr(_posixsubprocess, originalName)(args, *other_args)
     return newForkExec
 
 
-def createFork(original_name):
+def createFork(originalName):
     """
     Function to patch the 'fork' process creation functions.
     
     <ul>
         <li>os.fork()</li>
     </ul>
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
     """
     def new_fork():
         """
@@ -158,13 +276,18 @@
         isNewPythonProcess = True
         frame = sys._getframe()
         
-        multiprocess = (
-            _debugClient.debugging and _debugClient.multiprocessSupport
-        )
+        multiprocess = _shallPatch()
         
         isSubprocessFork = False
+        isMultiprocessingPopen = False
         while frame is not None:
-            if (
+            if frame.f_code.co_name == "_Popen":
+                # fork() was called from multiprocessing; ignore this here
+                # because it is handled in 'MultiprocessingExtension.py'.
+                isMultiprocessingPopen = True
+                break
+            
+            elif (
                 frame.f_code.co_name == '_execute_child' and
                 'subprocess' in frame.f_code.co_filename
             ):
@@ -182,14 +305,11 @@
             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:
+        childProcess = getattr(os, originalName)()     # fork
+        if not childProcess and not isMultiprocessingPopen:
             if isNewPythonProcess:
-                sys.settrace(None)
-                sys.setprofile(None)
-                _debugClient.sessionClose(False)
                 (wd, host, port, exceptions, tracePython, redirect,
-                 noencoding, fork_auto, fork_child) = _debugClient.startOptions
+                 noencoding) = _debugClient.startOptions
                 _debugClient.startDebugger(
                     filename=sys.argv[0],
                     host=host,
@@ -201,7 +321,7 @@
                     passive=False,
                     multiprocessSupport=multiprocess)
         return childProcess
-
+    
     return new_fork
 
 
@@ -209,6 +329,11 @@
     """
     Function to patch the 'CreateProcess' process creation function of
     Windows.
+    
+    @param originalName original name of the function to be patched
+    @type str
+    @return function replacing the original one
+    @type function
     """
     def newCreateProcess(appName, cmdline, *args):
         """
@@ -224,9 +349,6 @@
     return newCreateProcess
 
 
-# TODO: add 'createFork'
-
-
 def patchNewProcessFunctions(multiprocessEnabled, debugClient):
     """
     Function to patch the process creation functions to support multiprocess
@@ -244,6 +366,7 @@
         return
     
     import os
+    import sys
     
     # patch 'os.exec...()' functions
     patchModule(os, "execl", createExecl)
@@ -255,7 +378,20 @@
     patchModule(os, "execvp", createExecv)
     patchModule(os, "execvpe", createExecve)
     
-    # TODO: implement patching of the various functions of the os module
+    # patch 'os.spawn...()' functions
+    patchModule(os, "spawnl", createSpawnl)
+    patchModule(os, "spawnle", createSpawnl)
+    patchModule(os, "spawnlp", createSpawnl)
+    patchModule(os, "spawnlpe", createSpawnl)
+    patchModule(os, "spawnv", createSpawnv)
+    patchModule(os, "spawnve", createSpawnve)
+    patchModule(os, "spawnvp", createSpawnv)
+    patchModule(os, "spawnvpe", createSpawnve)
+    
+    # patch 'os.posix_spawn...()' functions
+    if sys.version_info >= (3, 8) and not isWindowsPlatform():
+        patchModule(os, "posix_spawn", createPosixSpawn)
+        patchModule(os, "posix_spawnp", createPosixSpawn)
     
     if isWindowsPlatform():
         try:

eric ide

mercurial