diff -r eb65864ca038 -r 433dacbfa456 eric6/DebugClients/Python/MultiProcessDebugExtension.py --- 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: