diff -r d07dd3cf0dc3 -r 1ba8ee313b57 DebugClients/Python3/DebugClientBase.py --- a/DebugClients/Python3/DebugClientBase.py Thu Sep 01 18:28:27 2016 +0200 +++ b/DebugClients/Python3/DebugClientBase.py Thu Sep 01 19:00:46 2016 +0200 @@ -191,7 +191,7 @@ self.pendingResponse = DebugProtocol.ResponseOK self._fncache = {} self.dircache = [] - self.inRawMode = False +## self.inRawMode = False self.mainProcStr = None # used for the passive mode self.passive = False # used to indicate the passive mode self.running = None @@ -302,8 +302,12 @@ d["broken"] = False threadList.append(d) - self.write("{0}{1!r}\n".format(DebugProtocol.ResponseThreadList, - (currentId, threadList))) +## self.write("{0}{1!r}\n".format(DebugProtocol.ResponseThreadList, +## (currentId, threadList))) + self.__sendJsonCommand("ResponseThreadList", { + "currentID": currentId, + "threadList": threadList, + }) def input(self, prompt, echo=True): """ @@ -319,7 +323,7 @@ "prompt": prompt, "echo": echo, }) - self.inRawMode = True +## self.inRawMode = True self.eventLoop(True) return self.rawLine @@ -329,7 +333,8 @@ It ensures that the debug server is informed of the raised exception. """ - self.pendingResponse = DebugProtocol.ResponseException +## self.pendingResponse = DebugProtocol.ResponseException + self.__sendJsonCommand("ResponseException", {}) def sessionClose(self, exit=True): """ @@ -376,15 +381,14 @@ try: message = str(excval) filename = excval.filename - linenr = excval.lineno - charnr = excval.offset + lineno = excval.lineno + charno = excval.offset except (AttributeError, ValueError): - exclist = [] - else: - exclist = [message, [filename, linenr, charnr]] - - self.write("{0}{1}\n".format( - DebugProtocol.ResponseSyntax, str(exclist))) + message = "" + filename = "" + lineno = 0 + charno = 0 + self.sendSyntaxError(message, filename, lineno, charno) return None return code @@ -405,7 +409,7 @@ ## printerr(line) ##debug if "jsonrpc" in line: - return self.__handleJsonCommand(line) + return self.handleJsonCommand(line) eoc = line.find('<') @@ -414,30 +418,30 @@ cmd = line[:eoc + 1] arg = line[eoc + 1:] - if cmd == DebugProtocol.RequestVariables: - frmnr, scope, filter = eval(arg.replace("u'", "'")) - self.__dumpVariables(int(frmnr), int(scope), filter) - return - - if cmd == DebugProtocol.RequestVariable: - var, frmnr, scope, filter = eval(arg.replace("u'", "'")) - self.__dumpVariable(var, int(frmnr), int(scope), filter) - return - - if cmd == DebugProtocol.RequestThreadList: - self.__dumpThreadList() - return - - if cmd == DebugProtocol.RequestThreadSet: - tid = eval(arg) - if tid in self.threads: - self.setCurrentThread(tid) - self.write(DebugProtocol.ResponseThreadSet + '\n') - stack = self.currentThread.getStack() - self.write('{0}{1!r}\n'.format( - DebugProtocol.ResponseStack, stack)) - return - +## if cmd == DebugProtocol.RequestVariables: +## frmnr, scope, filter = eval(arg.replace("u'", "'")) +## self.__dumpVariables(int(frmnr), int(scope), filter) +## return +## +## if cmd == DebugProtocol.RequestVariable: +## var, frmnr, scope, filter = eval(arg.replace("u'", "'")) +## self.__dumpVariable(var, int(frmnr), int(scope), filter) +## return +## +## if cmd == DebugProtocol.RequestThreadList: +## self.__dumpThreadList() +## return +## +## if cmd == DebugProtocol.RequestThreadSet: +## tid = eval(arg) +## if tid in self.threads: +## self.setCurrentThread(tid) +## self.write(DebugProtocol.ResponseThreadSet + '\n') +## stack = self.currentThread.getStack() +## self.write('{0}{1!r}\n'.format( +## DebugProtocol.ResponseStack, stack)) +## return +## ## if cmd == DebugProtocol.RequestStep: ## self.currentThread.step(True) ## self.eventExit = True @@ -472,18 +476,18 @@ ## self.pendingResponse = DebugProtocol.ResponseOK ## return ## - if cmd == DebugProtocol.RequestCallTrace: - if arg.strip().lower() == "on": - callTraceEnabled = True - else: - callTraceEnabled = False - if self.debugging: - self.callTraceEnabled = callTraceEnabled - else: - self.__newCallTraceEnabled = callTraceEnabled - # remember for later - return - +## if cmd == DebugProtocol.RequestCallTrace: +## if arg.strip().lower() == "on": +## callTraceEnabled = True +## else: +## callTraceEnabled = False +## if self.debugging: +## self.callTraceEnabled = callTraceEnabled +## else: +## self.__newCallTraceEnabled = callTraceEnabled +## # remember for later +## return +## ## if cmd == DebugProtocol.RequestEnv: ## env = eval(arg.replace("u'", "'")) ## for key, value in env.items(): @@ -676,168 +680,168 @@ ## self.writestream.flush() ## self.progTerminated(res) ## return - - if cmd == DebugProtocol.RequestShutdown: - self.sessionClose() - return - - if cmd == DebugProtocol.RequestBreak: - fn, line, temporary, set, cond = arg.split('@@') - line = int(line) - set = int(set) - temporary = int(temporary) - - if set: - if cond == 'None' or cond == '': - cond = None - else: - try: - compile(cond, '<string>', 'eval') - except SyntaxError: - self.write('{0}{1},{2:d}\n'.format( - DebugProtocol.ResponseBPConditionError, - fn, line)) - return - self.mainThread.set_break(fn, line, temporary, cond) - else: - self.mainThread.clear_break(fn, line) - - return - - if cmd == DebugProtocol.RequestBreakEnable: - fn, line, enable = arg.split(',') - line = int(line) - enable = int(enable) - - bp = self.mainThread.get_break(fn, line) - if bp is not None: - if enable: - bp.enable() - else: - bp.disable() - - return - - if cmd == DebugProtocol.RequestBreakIgnore: - fn, line, count = arg.split(',') - line = int(line) - count = int(count) - - bp = self.mainThread.get_break(fn, line) - if bp is not None: - bp.ignore = count - - return - - if cmd == DebugProtocol.RequestWatch: - cond, temporary, set = arg.split('@@') - set = int(set) - temporary = int(temporary) - - if set: - if not cond.endswith('??created??') and \ - not cond.endswith('??changed??'): - try: - compile(cond, '<string>', 'eval') - except SyntaxError: - self.write('{0}{1}\n'.format( - DebugProtocol.ResponseWPConditionError, cond)) - return - self.mainThread.set_watch(cond, temporary) - else: - self.mainThread.clear_watch(cond) - - return - - if cmd == DebugProtocol.RequestWatchEnable: - cond, enable = arg.split(',') - enable = int(enable) - - bp = self.mainThread.get_watch(cond) - if bp is not None: - if enable: - bp.enable() - else: - bp.disable() - - return - - if cmd == DebugProtocol.RequestWatchIgnore: - cond, count = arg.split(',') - count = int(count) - - bp = self.mainThread.get_watch(cond) - if bp is not None: - bp.ignore = count - - return - - if cmd == DebugProtocol.RequestEval: - try: - value = eval( - arg, - self.currentThread.getCurrentFrame().f_globals, - self.currentThread.getFrameLocals(self.framenr)) - self.currentThread.storeFrameLocals(self.framenr) - except Exception: - # Report the exception and the traceback - try: - type, value, tb = sys.exc_info() - sys.last_type = type - sys.last_value = value - sys.last_traceback = tb - tblist = traceback.extract_tb(tb) - del tblist[:1] - list = traceback.format_list(tblist) - if list: - list.insert(0, "Traceback (innermost last):\n") - list[len(list):] = \ - traceback.format_exception_only(type, value) - finally: - tblist = tb = None - - for l in list: - self.write(l) - - self.write(DebugProtocol.ResponseException + '\n') - - else: - self.write(str(value) + '\n') - self.write(DebugProtocol.ResponseOK + '\n') - - return - - if cmd == DebugProtocol.RequestExec: - _globals = self.currentThread.getCurrentFrame().f_globals - _locals = self.currentThread.getFrameLocals(self.framenr) - try: - code = compile(arg + '\n', '<stdin>', 'single') - exec(code, _globals, _locals) - self.currentThread.storeFrameLocals(self.framenr) - except Exception: - # Report the exception and the traceback - try: - type, value, tb = sys.exc_info() - sys.last_type = type - sys.last_value = value - sys.last_traceback = tb - tblist = traceback.extract_tb(tb) - del tblist[:1] - list = traceback.format_list(tblist) - if list: - list.insert(0, "Traceback (innermost last):\n") - list[len(list):] = \ - traceback.format_exception_only(type, value) - finally: - tblist = tb = None - - for l in list: - self.write(l) - - self.write(DebugProtocol.ResponseException + '\n') - - return - +## +## if cmd == DebugProtocol.RequestShutdown: +## self.sessionClose() +## return +## +## if cmd == DebugProtocol.RequestBreak: +## fn, line, temporary, set, cond = arg.split('@@') +## line = int(line) +## set = int(set) +## temporary = int(temporary) +## +## if set: +## if cond == 'None' or cond == '': +## cond = None +## else: +## try: +## compile(cond, '<string>', 'eval') +## except SyntaxError: +## self.write('{0}{1},{2:d}\n'.format( +## DebugProtocol.ResponseBPConditionError, +## fn, line)) +## return +## self.mainThread.set_break(fn, line, temporary, cond) +## else: +## self.mainThread.clear_break(fn, line) +## +## return +## +## if cmd == DebugProtocol.RequestBreakEnable: +## fn, line, enable = arg.split(',') +## line = int(line) +## enable = int(enable) +## +## bp = self.mainThread.get_break(fn, line) +## if bp is not None: +## if enable: +## bp.enable() +## else: +## bp.disable() +## +## return +## +## if cmd == DebugProtocol.RequestBreakIgnore: +## fn, line, count = arg.split(',') +## line = int(line) +## count = int(count) +## +## bp = self.mainThread.get_break(fn, line) +## if bp is not None: +## bp.ignore = count +## +## return +## +## if cmd == DebugProtocol.RequestWatch: +## cond, temporary, set = arg.split('@@') +## set = int(set) +## temporary = int(temporary) +## +## if set: +## if not cond.endswith('??created??') and \ +## not cond.endswith('??changed??'): +## try: +## compile(cond, '<string>', 'eval') +## except SyntaxError: +## self.write('{0}{1}\n'.format( +## DebugProtocol.ResponseWPConditionError, cond)) +## return +## self.mainThread.set_watch(cond, temporary) +## else: +## self.mainThread.clear_watch(cond) +## +## return +## +## if cmd == DebugProtocol.RequestWatchEnable: +## cond, enable = arg.split(',') +## enable = int(enable) +## +## bp = self.mainThread.get_watch(cond) +## if bp is not None: +## if enable: +## bp.enable() +## else: +## bp.disable() +## +## return +## +## if cmd == DebugProtocol.RequestWatchIgnore: +## cond, count = arg.split(',') +## count = int(count) +## +## bp = self.mainThread.get_watch(cond) +## if bp is not None: +## bp.ignore = count +## +## return +## +## if cmd == DebugProtocol.RequestEval: +## try: +## value = eval( +## arg, +## self.currentThread.getCurrentFrame().f_globals, +## self.currentThread.getFrameLocals(self.framenr)) +## self.currentThread.storeFrameLocals(self.framenr) +## except Exception: +## # Report the exception and the traceback +## try: +## type, value, tb = sys.exc_info() +## sys.last_type = type +## sys.last_value = value +## sys.last_traceback = tb +## tblist = traceback.extract_tb(tb) +## del tblist[:1] +## list = traceback.format_list(tblist) +## if list: +## list.insert(0, "Traceback (innermost last):\n") +## list[len(list):] = \ +## traceback.format_exception_only(type, value) +## finally: +## tblist = tb = None +## +## for l in list: +## self.write(l) +## +## self.write(DebugProtocol.ResponseException + '\n') +## +## else: +## self.write(str(value) + '\n') +## self.write(DebugProtocol.ResponseOK + '\n') +## +## return +## +## if cmd == DebugProtocol.RequestExec: +## _globals = self.currentThread.getCurrentFrame().f_globals +## _locals = self.currentThread.getFrameLocals(self.framenr) +## try: +## code = compile(arg + '\n', '<stdin>', 'single') +## exec(code, _globals, _locals) +## self.currentThread.storeFrameLocals(self.framenr) +## except Exception: +## # Report the exception and the traceback +## try: +## type, value, tb = sys.exc_info() +## sys.last_type = type +## sys.last_value = value +## sys.last_traceback = tb +## tblist = traceback.extract_tb(tb) +## del tblist[:1] +## list = traceback.format_list(tblist) +## if list: +## list.insert(0, "Traceback (innermost last):\n") +## list[len(list):] = \ +## traceback.format_exception_only(type, value) +## finally: +## tblist = tb = None +## +## for l in list: +## self.write(l) +## +## self.write(DebugProtocol.ResponseException + '\n') +## +## return +## ## if cmd == DebugProtocol.RequestBanner: ## self.write('{0}{1}\n'.format(DebugProtocol.ResponseBanner, ## str(("Python {0}".format(sys.version), @@ -850,15 +854,15 @@ ## self.__clientCapabilities())) ## return ## - if cmd == DebugProtocol.RequestCompletion: - self.__completionList(arg.replace("u'", "'")) - return - - if cmd == DebugProtocol.RequestSetFilter: - scope, filterString = eval(arg.replace("u'", "'")) - self.__generateFilterObjects(int(scope), filterString) - return - +## if cmd == DebugProtocol.RequestCompletion: +## self.__completionList(arg.replace("u'", "'")) +## return +## +## if cmd == DebugProtocol.RequestSetFilter: +## scope, filterString = eval(arg.replace("u'", "'")) +## self.__generateFilterObjects(int(scope), filterString) +## return +## if cmd == DebugProtocol.RequestUTPrepare: fn, tn, tfn, failed, cov, covname, erase = arg.split('|') sys.path.insert(0, os.path.dirname(os.path.abspath(fn))) @@ -1024,9 +1028,9 @@ ## for l in list: ## self.write(l) - def __handleJsonCommand(self, jsonStr): + def handleJsonCommand(self, jsonStr): """ - Private method to handle a command serialized as a JSON string. + Public method to handle a command serialized as a JSON string. """ import json @@ -1039,6 +1043,31 @@ method = commandDict["method"] params = commandDict["params"] + if method == "RequestVariables": + self.__dumpVariables( + params["frameNumber"], params["scope"], params["filters"]) + return + + if method == "RequestVariable": + self.__dumpVariable( + params["variable"], params["frameNumber"], + params["scope"], params["filters"]) + return + + if method == "RequestThreadList": + self.__dumpThreadList() + return + + if method == "RequestThreadSet": + if params["threadID"] in self.threads: + self.setCurrentThread(params["threadID"]) + self.__sendJsonCommand("ResponseThreadSet", {}) + stack = self.currentThread.getStack() + self.__sendJsonCommand("ResponseStack", { + "stack": stack, + }) + return + if method == "RequestCapabilities": self.__sendJsonCommand("ResponseCapabilities", { "capabilities": self.__clientCapabilities(), @@ -1054,6 +1083,18 @@ }) return + if method == "RequestSetFilter": + self.__generateFilterObjects(params["scope"], params["filter"]) + return + + if method == "RequestCallTrace": + if self.debugging: + self.callTraceEnabled = params["enable"] + else: + self.__newCallTraceEnabled = params["enable"] + # remember for later + return + if method == "RequestEnvironment": for key, value in params["environment"].items(): if key.endswith("+"): @@ -1080,7 +1121,7 @@ self.running = sys.argv[0] self.mainFrame = None - self.inRawMode = False +## self.inRawMode = False self.debugging = True self.fork_auto = params["autofork"] @@ -1124,7 +1165,7 @@ self.running = sys.argv[0] self.mainFrame = None self.botframe = None - self.inRawMode = False +## self.inRawMode = False self.fork_auto = params["autofork"] self.fork_child = params["forkChild"] @@ -1367,14 +1408,87 @@ return if method == "RawInput": - # If we are handling raw mode input then reset the mode and break out - # of the current event loop. -## if self.inRawMode: -## self.inRawMode = False + # If we are handling raw mode input then break out of the current + # event loop. self.rawLine = params["input"] self.eventExit = True return + + if method == "RequestBreakpoint": + if params["setBreakpoint"]: + if params["condition"] in ['None', '']: + params["condition"] = None + elif params["condition"] is not None: + try: + compile(params["condition"], '<string>', 'eval') + except SyntaxError: + self.__sendJsonCommand("ResponseBPConditionError", { + "filename": params["filename"], + "line": params["line"], + }) + return + self.mainThread.set_break( + params["filename"], params["line"], params["temporary"], + params["condition"]) + else: + self.mainThread.clear_break(params["filename"], params["line"]) + return + + if method == "RequestBreakpointEnable": + bp = self.mainThread.get_break(params["filename"], params["line"]) + if bp is not None: + if params["enable"]: + bp.enable() + else: + bp.disable() + return + + if method == "RequestBreakpointIgnore": + bp = self.mainThread.get_break(params["filename"], params["line"]) + if bp is not None: + bp.ignore = params["count"] + return + + if method == "RequestWatch": + if params["setWatch"]: + if not params["condition"].endswith( + ('??created??', '??changed??')): + try: + compile(params["condition"], '<string>', 'eval') + except SyntaxError: + self.__sendJsonCommand("ResponseWatchConditionError", { + "condition": params["condition"], + }) + return + self.mainThread.set_watch( + params["condition"], params["temporary"]) + else: + self.mainThread.clear_watch(params["condition"]) + return + + if method == "RequestWatchEnable": + wp = self.mainThread.get_watch(params["condition"]) + if wp is not None: + if params["enable"]: + wp.enable() + else: + wp.disable() + return + + if method == "RequestWatchIgnore": + wp = self.mainThread.get_watch(params["condition"]) + if wp is not None: + wp.ignore = params["count"] + return + + if method == "RequestShutdown": + self.sessionClose() + return + + if method == "RequestCompletion": + self.__completionList(params["text"]) + return def __sendJsonCommand(self, command, params): """ @@ -1395,6 +1509,110 @@ cmd = json.dumps(commandDict) + '\n' self.write(cmd) + def sendClearTemporaryBreakpoint(self, filename, lineno): + """ + Public method to signal the deletion of a temporary breakpoint. + + @param filename name of the file the bp belongs to + @type str + @param lineno linenumber of the bp + @type int + """ + self.__sendJsonCommand("ResponseClearBreakpoint", { + "filename": filename, + "line": lineno + }) + + def sendClearTemporaryWatch(self, condition): + """ + Public method to signal the deletion of a temporary watch expression. + + @param condition condition of the watch expression to be cleared + @type str + """ + self.__sendJsonCommand("ResponseClearWatch", { + "condition": condition, + }) + + def sendResponseLine(self, stack): + """ + Public method to send the current call stack. + + @param stack call stack + @type list + """ + self.__sendJsonCommand("ResponseLine", { + "stack": stack, + }) + + def sendCallTrace(self, event, fromStr, toStr): + """ + Public method to send a call trace entry. + + @param event trace event (call or return) + @type str + @param fromstr pre-formatted origin info + @type str + @param toStr pre-formatted target info + @type str + """ + self.__sendJsonCommand("CallTrace", { + "event": event[0], + "from": fromStr, + "to": toStr, + }) + + def sendException(self, exceptionType, exceptionMessage, stack): + """ + Public method to send information for an exception. + + @param exceptionType type of exception raised + @type str + @param exceptionMessage message of the exception + @type str + @param stack stack trace information + @param list + """ + self.__sendJsonCommand("ResponseException", { + "type": exceptionType, + "message": exceptionMessage, + "stack": stack, + }) + + def sendSyntaxError(self, message, filename, lineno, charno): + """ + Public method to send information for a syntax error. + + @param message syntax error message + @type str + @param filename name of the faulty file + @type str + @param lineno line number info + @type int + @param charno character number info + @tyoe int + """ + self.__sendJsonCommand("ResponseSyntax", { + "message": message, + "filename": filename, + "linenumber": lineno, + "characternumber": charno, + }) + + def sendPassiveStartup(self, filename, exceptions): + """ + Public method to send the passive start information. + + @param filename name of the script + @type str + @param exceptions flag to enable exception reporting of the IDE + @type bool + """ + self.__sendJsonCommand("PassiveStartup", { + "filename": filename, + "exceptions": exceptions, + }) + def __clientCapabilities(self): """ Private method to determine the clients capabilities. @@ -1606,9 +1824,16 @@ else: fargs = "" - siglist = [message, [filename, linenr, ffunc, fargs]] - - self.write("{0}{1}".format(DebugProtocol.ResponseSignal, str(siglist))) +## siglist = [message, [filename, linenr, ffunc, fargs]] +## +## self.write("{0}{1}".format(DebugProtocol.ResponseSignal, str(siglist))) + self.__sendJsonCommand("ResponseSignal", { + "message": message, + "filename": filename, + "linenumber": linenr, + "function": ffunc, + "arguments": fargs, + }) def absPath(self, fn): """ @@ -1701,7 +1926,10 @@ if self.running: self.set_quit() self.running = None - self.write('{0}{1:d}\n'.format(DebugProtocol.ResponseExit, status)) +## self.write('{0}{1:d}\n'.format(DebugProtocol.ResponseExit, status)) + self.__sendJsonCommand("ResponseExit", { + "status": status, + }) # reset coding self.__coding = self.defaultCoding @@ -1740,7 +1968,7 @@ else: dict = f.f_locals - varlist = [scope] + varlist = [] if scope != -1: keylist = dict.keys() @@ -1748,8 +1976,12 @@ vlist = self.__formatVariablesList(keylist, dict, scope, filter) varlist.extend(vlist) - self.write('{0}{1}\n'.format( - DebugProtocol.ResponseVariables, str(varlist))) +## self.write('{0}{1}\n'.format( +## DebugProtocol.ResponseVariables, str(varlist))) + self.__sendJsonCommand("ResponseVariables", { + "scope": scope, + "variables": varlist, + }) def __dumpVariable(self, var, frmnr, scope, filter): """ @@ -1784,7 +2016,7 @@ else: dict = f.f_locals - varlist = [scope, var] + varlist = [] if scope != -1: # search the correct dictionary @@ -1807,7 +2039,7 @@ udict = dict ndict = {} # this has to be in line with VariablesViewer.indicators - if var[i][-2:] in ["[]", "()", "{}"]: # __IGNORE_WARNING__ + if var[i][-2:] in ["[]", "()", "{}"]: # __IGNORE_WARNING__ if i + 1 == len(var): if var[i][:-2] == '...': dictkeys = [var[i - 1]] @@ -2033,8 +2265,13 @@ except Exception: pass - self.write('{0}{1}\n'.format( - DebugProtocol.ResponseVariable, str(varlist))) +## self.write('{0}{1}\n'.format( +## DebugProtocol.ResponseVariable, str(varlist))) + self.__sendJsonCommand("ResponseVariable", { + "scope": scope, + "variable": var, + "variables": varlist, + }) def __formatQtVariable(self, value, vtype): """ @@ -2330,8 +2567,12 @@ pass self.__getCompletionList(text, self.complete, completions) - self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion, - str(list(completions)), text)) +## self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion, +## str(list(completions)), text)) + self.__sendJsonCommand("ResponseCompletion", { + "completions": list(completions), + "text": text, + }) def __getCompletionList(self, text, completer, completions): """ @@ -2389,15 +2630,14 @@ if self.running: self.__setCoding(self.running) self.passive = True - self.write("{0}{1}|{2:d}\n".format(DebugProtocol.PassiveStartup, - self.running, exceptions)) + self.sendPassiveStartup(self.running, exceptions) self.__interact() # setup the debugger variables self._fncache = {} self.dircache = [] self.mainFrame = None - self.inRawMode = False +## self.inRawMode = False self.debugging = True self.attachThread(mainThread=True) @@ -2450,12 +2690,13 @@ self.running = sys.argv[0] self.__setCoding(self.running) self.mainFrame = None - self.inRawMode = False +## self.inRawMode = False self.debugging = True self.passive = True - self.write("{0}{1}|{2:d}\n".format( - DebugProtocol.PassiveStartup, self.running, exceptions)) + self.sendPassiveStartup(self.running, exceptions) +## self.write("{0}{1}|{2:d}\n".format( +## DebugProtocol.PassiveStartup, self.running, exceptions)) self.__interact() self.attachThread(mainThread=True)