diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/DebugClients/Python/MultiProcessDebugExtension.py --- a/src/eric7/DebugClients/Python/MultiProcessDebugExtension.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/DebugClients/Python/MultiProcessDebugExtension.py Wed Jul 13 14:55:47 2022 +0200 @@ -11,8 +11,10 @@ import contextlib from DebugUtilities import ( - patchArguments, patchArgumentStringWindows, isPythonProgram, - isWindowsPlatform + patchArguments, + patchArgumentStringWindows, + isPythonProgram, + isWindowsPlatform, ) _debugClient = None @@ -21,7 +23,7 @@ def _shallPatch(): """ Function to determine, if the multiprocessing patches should be done. - + @return flag indicating patching should be performed @rtype bool """ @@ -31,7 +33,7 @@ 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 @@ -40,7 +42,7 @@ @type types.FunctionType """ if hasattr(module, functionName): - originalName = 'original_' + functionName + originalName = "original_" + functionName if not hasattr(module, originalName): setattr(module, originalName, getattr(module, functionName)) setattr(module, functionName, createFunction(originalName)) @@ -49,236 +51,261 @@ 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> - + @param originalName original name of the function to be patched @type str @return function replacing the original one @rtype function """ + def newExecl(path, *args): """ Function replacing the 'execl' functions of the os module. """ import os + if _shallPatch(): 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> - + @param originalName original name of the function to be patched @type str @return function replacing the original one @rtype function """ + def newExecv(path, args): """ Function replacing the 'execv' functions of the os module. """ import os + if _shallPatch(): 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> - + @param originalName original name of the function to be patched @type str @return function replacing the original one @rtype function """ + def newExecve(path, args, env): """ Function replacing the 'execve' functions of the os module. """ import os + if _shallPatch(): args = patchArguments(_debugClient, args) if isPythonProgram(args[0]): path = args[0] return getattr(os, originalName)(path, args, env) + return newExecve 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 @rtype 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 @rtype 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 @rtype 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 @rtype 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): """ Function to patch the 'fork_exec' process creation functions. - + <ul> <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 @rtype function """ + def newForkExec(args, *other_args): """ Function replacing the 'fork_exec' functions of the _posixsubprocess module. """ import _posixsubprocess + if _shallPatch(): args = patchArguments(_debugClient, args) return getattr(_posixsubprocess, originalName)(args, *other_args) + return newForkExec 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 @rtype function """ + def newFork(): """ 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 = _shallPatch() - + isSubprocessFork = False isMultiprocessingPopen = False while frame is not None: @@ -287,33 +314,36 @@ # 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 + 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') + 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, originalName)() # fork - if ( - not childProcess and - not isMultiprocessingPopen and - isNewPythonProcess - ): - (wd, host, port, exceptions, tracePython, redirect, - noencoding) = _debugClient.startOptions + frame = None # Just make sure we don't hold on to it. + + childProcess = getattr(os, originalName)() # fork + if not childProcess and not isMultiprocessingPopen and isNewPythonProcess: + ( + wd, + host, + port, + exceptions, + tracePython, + redirect, + noencoding, + ) = _debugClient.startOptions _debugClient.startDebugger( filename=sys.argv[0], host=host, @@ -323,9 +353,10 @@ tracePython=tracePython, redirect=redirect, passive=False, - multiprocessSupport=multiprocess) + multiprocessSupport=multiprocess, + ) return childProcess - + return newFork @@ -333,12 +364,13 @@ """ 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 @rtype function """ + def newCreateProcess(appName, cmdline, *args): """ Function replacing the 'CreateProcess' function of the _subprocess @@ -349,7 +381,9 @@ except ImportError: import _winapi as _subprocess return getattr(_subprocess, originalName)( - appName, patchArgumentStringWindows(_debugClient, cmdline), *args) + appName, patchArgumentStringWindows(_debugClient, cmdline), *args + ) + return newCreateProcess @@ -357,31 +391,31 @@ """ 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 import sys - + # patch 'os.exec...()' functions -#- patchModule(os, "execl", createExecl) -#- 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) - + # - patchModule(os, "execl", createExecl) + # - 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) + # patch 'os.spawn...()' functions patchModule(os, "spawnl", createSpawnl) patchModule(os, "spawnle", createSpawnl) @@ -391,22 +425,23 @@ 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: import _subprocess except ImportError: import _winapi as _subprocess - patchModule(_subprocess, 'CreateProcess', createCreateProcess) + patchModule(_subprocess, "CreateProcess", createCreateProcess) else: patchModule(os, "fork", createFork) with contextlib.suppress(ImportError): import _posixsubprocess + patchModule(_posixsubprocess, "fork_exec", createForkExec) - + _debugClient = debugClient