diff -r 866adc8c315b -r 0acf98cd089a eric6/DebugClients/Python/DebugClientBase.py --- a/eric6/DebugClients/Python/DebugClientBase.py Sun Jan 17 13:53:08 2021 +0100 +++ b/eric6/DebugClients/Python/DebugClientBase.py Mon Feb 01 10:38:16 2021 +0100 @@ -20,17 +20,19 @@ import signal import time import types -import importlib +import importlib.util +import fnmatch import DebugClientCapabilities import DebugVariables from DebugBase import setRecursionLimit, printerr # __IGNORE_WARNING__ from AsyncFile import AsyncFile, AsyncPendingWrite -from DebugConfig import ConfigQtNames, ConfigVarTypeStrings +from DebugConfig import ConfigQtNames, SpecialAttributes from FlexCompleter import Completer from DebugUtilities import prepareJsonCommand from BreakpointWatch import Breakpoint, Watch +from MultiProcessDebugExtension import patchNewProcessFunctions from DebugUtilities import getargvalues, formatargvalues @@ -67,25 +69,6 @@ ############################################################################### -def DebugClientFork(): - """ - Replacement for the standard os.fork(). - - @return result of the fork() call - """ - if DebugClientInstance is None: - return DebugClientOrigFork() - - return DebugClientInstance.fork() - -# use our own fork(). -if 'fork' in dir(os): - DebugClientOrigFork = os.fork - os.fork = DebugClientFork - -############################################################################### - - def DebugClientClose(fd): """ Replacement for the standard os.close(fd). @@ -179,20 +162,17 @@ self.running = None self.test = None self.debugging = False - - self.fork_auto = False - self.fork_child = False + self.multiprocessSupport = False + self.noDebugList = [] self.readstream = None self.writestream = None self.errorstream = None self.pollingDisabled = False - self.callTraceEnabled = None + self.__debuggerId = "" - self.disassembly = None - - self.variant = 'You should not see this' + self.callTraceEnabled = None self.compile_command = codeop.CommandCompiler() @@ -200,6 +180,8 @@ self.defaultCoding = 'utf-8' self.__coding = self.defaultCoding self.noencoding = False + + self.startOptions = None def getCoding(self): """ @@ -263,6 +245,8 @@ pass self.debugging = False + self.multiprocessSupport = False + self.noDebugList = [] # make sure we close down our end of the socket # might be overkill as normally stdin, stdout and stderr @@ -279,13 +263,29 @@ """ Private method to compile source code read from a file. - @param filename name of the source file (string) - @param mode kind of code to be generated (string, exec or eval) + @param filename name of the source file + @type str + @param mode kind of code to be generated (exec or eval) + @type str @return compiled code object (None in case of errors) """ with codecs.open(filename, encoding=self.__coding) as fp: statement = fp.read() + return self.__compileCommand(statement, filename=filename, mode=mode) + + def __compileCommand(self, statement, filename="<string>", mode="exec"): + """ + Private method to compile source code. + + @param statement source code string to be compiled + @type str + @param filename name of the source file + @type str + @param mode kind of code to be generated (exec or eval) + @type str + @return compiled code object (None in case of errors) + """ try: code = compile(statement + '\n', filename, mode) except SyntaxError: @@ -304,7 +304,7 @@ lineno = 0 charno = 0 - self.sendSyntaxError(message, filename, lineno, charno) + self.sendSyntaxError(message, filename, lineno, charno, self.name) return None return code @@ -316,7 +316,7 @@ @param jsonStr string containing the command received from the IDE @type str """ -## printerr(jsonStr) ##debug +## printerr(jsonStr) ## debug # __IGNORE_WARNING_M891__ try: commandDict = json.loads(jsonStr.strip()) @@ -337,16 +337,29 @@ params["variable"], params["frameNumber"], params["scope"], params["filters"]) + elif method == "RequestStack": + stack = self.mainThread.getStack() + self.sendResponseLine(stack, self.mainThread.name) + elif method == "RequestThreadList": self.dumpThreadList() elif method == "RequestThreadSet": - if params["threadID"] in self.threads: - self.setCurrentThread(params["threadID"]) + if params["threadID"] == -1: + # -1 is indication for the main thread + threadId = -1 + for thread in self.threads.values(): + if thread.name == "MainThread": + threadId = thread.id + else: + threadId = params["threadID"] + if threadId in self.threads: + self.setCurrentThread(threadId) self.sendJsonCommand("ResponseThreadSet", {}) stack = self.currentThread.getStack() self.sendJsonCommand("ResponseStack", { "stack": stack, + "threadName": self.currentThread.name, }) elif method == "RequestDisassembly": @@ -370,7 +383,6 @@ self.sendJsonCommand("ResponseBanner", { "version": "Python {0}".format(sys.version), "platform": socket.gethostname(), - "dbgclient": self.variant, }) elif method == "RequestSetFilter": @@ -414,9 +426,7 @@ self.running = sys.argv[0] self.debugging = True - - self.fork_auto = params["autofork"] - self.fork_child = params["forkChild"] + self.multiprocessSupport = params["multiprocess"] self.threads.clear() self.attachThread(mainThread=True) @@ -439,7 +449,8 @@ code = self.__compileFileSource(self.running) if code: sys.setprofile(self.callTraceEnabled) - self.mainThread.run(code, self.debugMod.__dict__, debug=True) + self.mainThread.run(code, self.debugMod.__dict__, debug=True, + closeSession=False) elif method == "RequestRun": self.disassembly = None @@ -456,9 +467,6 @@ self.running = sys.argv[0] self.botframe = None - self.fork_auto = params["autofork"] - self.fork_child = params["forkChild"] - self.threads.clear() self.attachThread(mainThread=True) @@ -474,7 +482,8 @@ res = 0 code = self.__compileFileSource(self.running) if code: - self.mainThread.run(code, self.debugMod.__dict__, debug=False) + self.mainThread.run(code, self.debugMod.__dict__, debug=False, + closeSession=False) elif method == "RequestCoverage": from coverage import Coverage @@ -508,7 +517,8 @@ if code: self.running = sys.argv[0] self.cover.start() - self.mainThread.run(code, self.debugMod.__dict__, debug=False) + self.mainThread.run(code, self.debugMod.__dict__, debug=False, + closeSession=False) self.cover.stop() self.cover.save() @@ -558,7 +568,7 @@ self.__unhandled_exception(*excinfo) self.prof.save() - self.progTerminated(res) + self.progTerminated(res, closeSession=False) elif method == "ExecuteStatement": if self.buffer: @@ -609,8 +619,8 @@ _locals = ( self.currentThread.getFrameLocals( self.framenr)) - ## reset sys.stdout to our redirector - ## (unconditionally) + #- reset sys.stdout to our redirector + #- (unconditionally) if "sys" in _globals: __stdout = _globals["sys"].stdout _globals["sys"].stdout = self.writestream @@ -679,6 +689,11 @@ self.currentThreadExec.go(params["special"]) self.eventExit = True + elif method == "RequestContinueUntil": + newLine = params["newLine"] + self.currentThreadExec.set_until(lineno=newLine) + self.eventExit = True + elif method == "RawInput": # If we are handling raw mode input then break out of the current # event loop. @@ -758,6 +773,9 @@ elif method == "RequestShutdown": self.sessionClose() + elif method == "RequestSetNoDebugList": + self.noDebugList = params["noDebug"][:] + elif method == "RequestCompletion": self.__completionList(params["text"]) @@ -809,14 +827,10 @@ elif method == "RequestUTPrepare": if params["syspath"]: sys.path = params["syspath"] + sys.path - sys.path.insert( - 0, os.path.dirname(os.path.abspath(params["filename"]))) top_level_dir = None if params["workdir"]: os.chdir(params["workdir"]) top_level_dir = params["workdir"] - else: - os.chdir(sys.path[0]) # set the system exception handling function to ensure, that # we report on all unhandled exceptions @@ -830,6 +844,8 @@ discoveryStart = params["discoverystart"] if not discoveryStart: discoveryStart = params["workdir"] + sys.path.insert( + 0, os.path.abspath(discoveryStart)) if params["testcases"]: self.test = testLoader.loadTestsFromNames( params["testcases"]) @@ -837,6 +853,10 @@ self.test = testLoader.discover( discoveryStart, top_level_dir=top_level_dir) else: + sys.path.insert( + 0, + os.path.dirname(os.path.abspath(params["filename"])) + ) if params["filename"]: spec = importlib.util.spec_from_file_location( params["testname"], params["filename"]) @@ -897,9 +917,13 @@ self.threads.clear() self.attachThread(mainThread=True) sys.setprofile(None) + self.running = sys.argv[0] self.mainThread.run( "result = self.test.run(self.testResult)\n", - localsDict=locals_) + self.debugMod.__dict__, + localsDict=locals_, + debug=True, + closeSession=False) result = locals_["result"] else: result = self.test.run(self.testResult) @@ -912,11 +936,6 @@ elif method == "RequestUTStop": self.testResult.stop() - - elif method == "ResponseForkTo": - # this results from a separate event loop - self.fork_child = (params["target"] == 'child') - self.eventExit = True def __assembleTestCasesList(self, suite, start): """ @@ -969,6 +988,10 @@ response @type dict """ + # send debugger ID with all responses + if "debuggerId" not in params: + params["debuggerId"] = self.__debuggerId + cmd = prepareJsonCommand(method, params) self.writestream.write_p(cmd) @@ -999,15 +1022,18 @@ "condition": condition, }) - def sendResponseLine(self, stack): + def sendResponseLine(self, stack, threadName): """ Public method to send the current call stack. @param stack call stack @type list + @param threadName name of the thread sending the event + @type str """ self.sendJsonCommand("ResponseLine", { "stack": stack, + "threadName": threadName, }) def sendCallTrace(self, event, fromInfo, toInfo): @@ -1029,7 +1055,8 @@ "to": toInfo, }) - def sendException(self, exceptionType, exceptionMessage, stack): + def sendException(self, exceptionType, exceptionMessage, stack, + threadName): """ Public method to send information for an exception. @@ -1039,14 +1066,17 @@ @type str @param stack stack trace information @type list + @param threadName name of the thread sending the event + @type str """ self.sendJsonCommand("ResponseException", { "type": exceptionType, "message": exceptionMessage, "stack": stack, + "threadName": threadName, }) - def sendSyntaxError(self, message, filename, lineno, charno): + def sendSyntaxError(self, message, filename, lineno, charno, threadName): """ Public method to send information for a syntax error. @@ -1058,12 +1088,15 @@ @type int @param charno character number info @type int + @param threadName name of the thread sending the event + @type str """ self.sendJsonCommand("ResponseSyntax", { "message": message, "filename": filename, "linenumber": lineno, "characternumber": charno, + "threadName": threadName, }) def sendPassiveStartup(self, filename, exceptions): @@ -1080,6 +1113,17 @@ "exceptions": exceptions, }) + def sendDebuggerId(self, debuggerId): + """ + Public method to send the debug client id. + + @param debuggerId id of this debug client instance (made up of + hostname and process ID) + @type str + """ + # debugger ID is added automatically by sendJsonCommand + self.sendJsonCommand("DebuggerId", {}) + def __clientCapabilities(self): """ Private method to determine the clients capabilities. @@ -1101,7 +1145,7 @@ """ Public method called when there is data ready to be read. - @param stream file like object that has data to be written + @param stream file like object that has data to be read @return flag indicating an error condition @rtype bool """ @@ -1177,6 +1221,9 @@ else: # give up for too many errors break + except ValueError: + # the client socket might already be closed, i.e. its fd is -1 + break # reset the select error counter selectErrors = 0 @@ -1224,7 +1271,8 @@ if self.errorstream in wrdy: self.writeReady(self.errorstream) - def connectDebugger(self, port, remoteAddress=None, redirect=True): + def connectDebugger(self, port, remoteAddress=None, redirect=True, + name=""): """ Public method to establish a session with the debugger. @@ -1232,19 +1280,28 @@ stdout and stderr and saves these file objects in case the application being debugged redirects them itself. - @param port the port number to connect to (int) + @param port the port number to connect to + @type int @param remoteAddress the network address of the debug server host - (string) + @type str @param redirect flag indicating redirection of stdin, stdout and - stderr (boolean) + stderr + @type bool + @param name name to be attached to the debugger ID + @type str """ if remoteAddress is None: remoteAddress = "127.0.0.1" elif "@@i" in remoteAddress: remoteAddress = remoteAddress.split("@@i")[0] sock = socket.create_connection((remoteAddress, port)) - - self.readstream = AsyncFile(sock, sys.stdin.mode, sys.stdin.name) + + stdinName = sys.stdin.name + # Special case if in a multiprocessing.Process + if isinstance(stdinName, int): + stdinName = '<stdin>' + + self.readstream = AsyncFile(sock, sys.stdin.mode, stdinName) self.writestream = AsyncFile(sock, sys.stdout.mode, sys.stdout.name) self.errorstream = AsyncFile(sock, sys.stderr.mode, sys.stderr.name) @@ -1256,6 +1313,14 @@ # attach to the main thread here self.attachThread(mainThread=True) + + if not name: + name = "main" + self.__debuggerId = "{0}/{1}/{2}".format( + socket.gethostname(), os.getpid(), name + ) + + self.sendDebuggerId(self.__debuggerId) def __unhandled_exception(self, exctype, excval, exctb): """ @@ -1375,7 +1440,7 @@ """ return self.running - def progTerminated(self, status, message=""): + def progTerminated(self, status, message="", closeSession=True): """ Public method to tell the debugger that the program has terminated. @@ -1383,23 +1448,32 @@ @type int @param message status message @type str + @param closeSession flag indicating to close the debugger session + @type bool """ if status is None: status = 0 elif not isinstance(status, int): message = str(status) status = 1 - + if message is None: + message = "" + if self.running: self.set_quit() + program = self.running self.running = None self.sendJsonCommand("ResponseExit", { "status": status, "message": message, + "program": program, }) # reset coding self.__coding = self.defaultCoding + + if closeSession: + self.sessionClose(False) def __dumpVariables(self, frmnr, scope, filterList): """ @@ -1409,8 +1483,8 @@ @type int @param scope 1 to report global variables, 0 for local variables @type int - @param filterList the indices of variable types to be filtered - @type list of int + @param filterList list of variable types to be filtered + @type list of str """ if self.currentThread is None: return @@ -1458,7 +1532,7 @@ @type int @param scope 1 to report global variables, 0 for local variables @type int - @param filterList the indices of variable types to be filtered + @param filterList list of variable types to be filtered @type list of int """ if self.currentThread is None: @@ -1714,10 +1788,10 @@ Variables are only added to the list, if their name do not match any of the filter expressions. @type int - @param filterList the indices of variable types to be filtered. + @param filterList list of variable types to be filtered. Variables are only added to the list, if their type is not contained in the filter list. - @type list of int + @type list of str @return A tuple consisting of a list of formatted variables. Each variable entry is a tuple of three elements, the variable name, its type and value. @@ -1748,15 +1822,25 @@ continue # filter hidden attributes (filter #0) - if 0 in filterList and str(key)[:2] == '__': + if '__' in filterList and str(key)[:2] == '__': continue # special handling for '__builtins__' (it's way too big) if key == '__builtins__': rvalue = '<module builtins (built-in)>' valtype = 'module' - if ConfigVarTypeStrings.index(valtype) in filterList: + if valtype in filterList: continue + elif ( + key in SpecialAttributes and + "special_attributes" in filterList + ): + continue + elif ( + key == "__hash__" and + "builtin_function_or_method" in filterList + ): + continue else: isQt = False # valtypestr, e.g. class 'PyQt5.QtCore.QPoint' @@ -1767,37 +1851,37 @@ # Strip 'instance' to be equal with Python 3 if valtype == "instancemethod": valtype = "method" - elif valtype == "type" or valtype == "classobj": + elif valtype in ("type", "classobj"): valtype = "class" + elif valtype == "method-wrapper": + valtype = "builtin_function_or_method" # valtypename, e.g. QPoint valtypename = type(value).__name__ - try: - if ConfigVarTypeStrings.index(valtype) in filterList: - continue - except ValueError: - if valtype in ("sip.enumtype", "sip.wrappertype"): - if ConfigVarTypeStrings.index('class') in filterList: - continue - elif (valtype == "sip.methoddescriptor" or - valtype == "method_descriptor"): - if ConfigVarTypeStrings.index('method') in filterList: - continue - elif valtype in ("numpy.ndarray", "array.array"): - if ConfigVarTypeStrings.index('list') in filterList: - continue - elif valtypename == "MultiValueDict": - if ConfigVarTypeStrings.index('dict') in filterList: - continue - elif ConfigVarTypeStrings.index('instance') in filterList: - continue - - isQt = valtype.startswith(ConfigQtNames) - if (not valtypestr.startswith('type ') and - valtypename not in ("ndarray", "MultiValueDict", - "array", "defaultdict") and - not isQt): - valtype = valtypestr + if valtype in filterList: + continue + elif ( + valtype in ("sip.enumtype", "sip.wrappertype") and + 'class' in filterList + ): + continue + elif ( + valtype in ( + "sip.methoddescriptor", "method_descriptor") and + 'method' in filterList + ): + continue + elif ( + valtype in ("numpy.ndarray", "array.array") and + 'list' in filterList + ): + continue + elif valtypename == "MultiValueDict" and 'dict' in filterList: + continue + elif 'instance' in filterList: + continue + + isQt = valtype.startswith(ConfigQtNames) try: if valtype in self.arrayTypes: @@ -1909,32 +1993,45 @@ comp = completer(text, state) except Exception: comp = None - + def startDebugger(self, filename=None, host=None, port=None, enableTrace=True, exceptions=True, tracePython=False, - redirect=True): + redirect=True, passive=True, multiprocessSupport=False): """ Public method used to start the remote debugger. - @param filename the program to be debugged (string) - @param host hostname of the debug server (string) - @param port portnumber of the debug server (int) - @param enableTrace flag to enable the tracing function (boolean) + @param filename the program to be debugged + @type str + @param host hostname of the debug server + @type str + @param port portnumber of the debug server + @type int + @param enableTrace flag to enable the tracing function + @type bool @param exceptions flag to enable exception reporting of the IDE - (boolean) + @type bool @param tracePython flag to enable tracing into the Python library - (boolean) + @type bool @param redirect flag indicating redirection of stdin, stdout and - stderr (boolean) + stderr + @type bool + @param passive flag indicating a passive debugging session + @type bool + @param multiprocessSupport flag indicating to enable multiprocess + debugging support + @type bool """ - global debugClient if host is None: host = os.getenv('ERICHOST', 'localhost') if port is None: port = os.getenv('ERICPORT', 42424) remoteAddress = self.__resolveHost(host) - self.connectDebugger(port, remoteAddress, redirect) + if filename is not None: + name = os.path.basename(filename) + else: + name = "" + self.connectDebugger(port, remoteAddress, redirect, name=name) if filename is not None: self.running = os.path.abspath(filename) else: @@ -1944,8 +2041,7 @@ self.running = None if self.running: self.__setCoding(self.running) - self.passive = True - self.sendPassiveStartup(self.running, exceptions) + self.passive = passive self.__interact() # setup the debugger variables @@ -1967,7 +2063,9 @@ def startProgInDebugger(self, progargs, wd='', host=None, port=None, exceptions=True, tracePython=False, - redirect=True): + redirect=True, passive=True, + multiprocessSupport=False, codeStr="", + scriptModule=""): """ Public method used to start the remote debugger. @@ -1982,6 +2080,17 @@ (boolean) @param redirect flag indicating redirection of stdin, stdout and stderr (boolean) + @param passive flag indicating a passive debugging session + @type bool + @param multiprocessSupport flag indicating to enable multiprocess + debugging support + @type bool + @param codeStr string containing Python code to execute + @type str + @param scriptModule name of a module to be executed as a script + @type str + @return exit code of the debugged program + @rtype int """ if host is None: host = os.getenv('ERICHOST', 'localhost') @@ -1989,24 +2098,36 @@ port = os.getenv('ERICPORT', 42424) remoteAddress = self.__resolveHost(host) - self.connectDebugger(port, remoteAddress, redirect) + if progargs: + if not progargs[0].startswith("-"): + name = os.path.basename(progargs[0]) + else: + name = "debug_client_code" + else: + name = "debug_client_code" + self.connectDebugger(port, remoteAddress, redirect, name=name) self._fncache = {} self.dircache = [] - sys.argv = progargs[:] - sys.argv[0] = os.path.abspath(sys.argv[0]) - sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) - if wd == '': - os.chdir(sys.path[1]) + if codeStr: + self.running = "<string>" + sys.argv = ["<string>"] + progargs[:] else: - os.chdir(wd) - self.running = sys.argv[0] - self.__setCoding(self.running) + sys.argv = progargs[:] + sys.argv[0] = os.path.abspath(sys.argv[0]) + sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) + if wd == '': + os.chdir(sys.path[1]) + else: + os.chdir(wd) + self.running = sys.argv[0] + self.__setCoding(self.running) self.debugging = True + self.multiprocessSupport = multiprocessSupport - self.passive = True - self.sendPassiveStartup(self.running, exceptions) - self.__interact() + self.passive = passive + if passive: + self.sendPassiveStartup(self.running, exceptions) self.attachThread(mainThread=True) self.mainThread.tracePythonLibs(tracePython) @@ -2019,12 +2140,28 @@ # This will eventually enter a local event loop. self.debugMod.__dict__['__file__'] = self.running sys.modules['__main__'] = self.debugMod - code = self.__compileFileSource(self.running) + if codeStr: + code = self.__compileCommand(codeStr) + elif scriptModule: + import runpy + modName, modSpec, code = runpy._get_module_details(scriptModule) + self.running = code.co_filename + self.debugMod.__dict__.clear() + self.debugMod.__dict__.update({ + "__name__": "__main__", + "__file__": self.running, + "__package__": modSpec.parent, + "__loader__": modSpec.loader, + "__spec__": modSpec, + "__builtins__": __builtins__, + }) + else: + code = self.__compileFileSource(self.running) if code: res = self.mainThread.run(code, self.debugMod.__dict__, debug=True) - self.progTerminated(res) else: - self.progTerminated(42) # should not happen + res = 42 # should not happen + return res def run_call(self, scriptname, func, *args): """ @@ -2037,7 +2174,7 @@ """ self.startDebugger(scriptname, enableTrace=False) res = self.mainThread.runcall(func, *args) - self.progTerminated(res) + self.progTerminated(res, closeSession=False) return res def __resolveHost(self, host): @@ -2079,6 +2216,10 @@ tracePython = False exceptions = True redirect = True + passive = True + multiprocess = False + codeStr = "" + scriptModule = "" while args[0]: if args[0] == '-h': host = args[1] @@ -2104,13 +2245,19 @@ elif args[0] == '--no-encoding': self.noencoding = True del args[0] - elif args[0] == '--fork-child': - self.fork_auto = True - self.fork_child = True + elif args[0] == '--no-passive': + passive = False + del args[0] + elif args[0] == '--multiprocess': + multiprocess = True del args[0] - elif args[0] == '--fork-parent': - self.fork_auto = True - self.fork_child = False + elif args[0] in ('-c', '--code'): + codeStr = args[1] + del args[0] + del args[0] + elif args[0] in ('-m', '--module'): + scriptModule = args[1] + del args[0] del args[0] elif args[0] == '--': del args[0] @@ -2120,18 +2267,34 @@ if not args: print("No program given. Aborting!") # __IGNORE_WARNING_M801__ + elif "-m" in args: + print("Running module as a script is not supported. Aborting!") + # __IGNORE_WARNING_M801__ else: + # Store options in case a new Python process is created + self.startOptions = ( + wd, host, port, exceptions, tracePython, redirect, + self.noencoding, + ) if not self.noencoding: self.__coding = self.defaultCoding - self.startProgInDebugger(args, wd, host, port, - exceptions=exceptions, - tracePython=tracePython, - redirect=redirect) + patchNewProcessFunctions(multiprocess, self) + res = self.startProgInDebugger( + args, wd, host, port, exceptions=exceptions, + tracePython=tracePython, redirect=redirect, + passive=passive, multiprocessSupport=multiprocess, + codeStr=codeStr, scriptModule=scriptModule, + ) + sys.exit(res) else: if sys.argv[1] == '--no-encoding': self.noencoding = True del sys.argv[1] + if sys.argv[1] == '--multiprocess': + self.multiprocessSupport = True + del sys.argv[1] + if sys.argv[1] == '': del sys.argv[1] @@ -2165,39 +2328,20 @@ sys.path.insert(0, '') if port >= 0: + # Store options in case a new Python process is created + self.startOptions = ( + '', remoteAddress, port, True, False, redirect, + self.noencoding, + ) if not self.noencoding: self.__coding = self.defaultCoding + patchNewProcessFunctions(self.multiprocessSupport, self) self.connectDebugger(port, remoteAddress, redirect) self.__interact() else: print("No network port given. Aborting...") # __IGNORE_WARNING_M801__ - - def fork(self): - """ - Public method implementing a fork routine deciding which branch - to follow. - - @return process ID (integer) - """ - if not self.fork_auto: - self.sendJsonCommand("RequestForkTo", {}) - self.eventLoop(True) - pid = DebugClientOrigFork() - if pid == 0: - # child - if not self.fork_child: - sys.settrace(None) - sys.setprofile(None) - self.sessionClose(False) - else: - # parent - if self.fork_child: - sys.settrace(None) - sys.setprofile(None) - self.sessionClose(False) - return pid - + def close(self, fd): """ Public method implementing a close method as a replacement for @@ -2212,7 +2356,7 @@ return DebugClientOrigClose(fd) - + def __getSysPath(self, firstEntry): """ Private slot to calculate a path list including the PYTHONPATH @@ -2229,3 +2373,18 @@ sysPath.insert(0, firstEntry) sysPath.insert(0, '') return sysPath + + def skipMultiProcessDebugging(self, scriptName): + """ + Public method to check, if the given script is eligible for debugging. + + @param scriptName name of the script to check + @type str + @return flag indicating eligibility + @rtype bool + """ + for pattern in self.noDebugList: + if fnmatch.fnmatch(scriptName, pattern): + return True + + return False