--- 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