--- a/DebugClients/Python/DebugClientBase.py Sat Sep 03 18:01:19 2016 +0200 +++ b/DebugClients/Python/DebugClientBase.py Sat Sep 03 18:02:37 2016 +0200 @@ -21,12 +21,12 @@ import inspect -import DebugProtocol import DebugClientCapabilities from DebugBase import setRecursionLimit, printerr # __IGNORE_WARNING__ from AsyncFile import AsyncFile, AsyncPendingWrite from DebugConfig import ConfigVarTypeStrings from FlexCompleter import Completer +from DebugUtilities import prepareJsonCommand DebugClientInstance = None @@ -44,7 +44,7 @@ @param echo flag indicating echoing of the input (boolean) @return result of the raw_input() call """ - if DebugClientInstance is None or DebugClientInstance.redirect == 0: + if DebugClientInstance is None or not DebugClientInstance.redirect: return DebugClientOrigRawInput(prompt) return DebugClientInstance.raw_input(prompt, echo) @@ -185,7 +185,8 @@ Constructor """ self.breakpoints = {} - self.redirect = 1 + self.redirect = True + self.__receiveBuffer = "" # The next couple of members are needed for the threaded version. # For this base class they contain static values for the non threaded @@ -214,16 +215,14 @@ self.globalsFilterObjects = [] self.localsFilterObjects = [] - self.pendingResponse = DebugProtocol.ResponseOK self._fncache = {} self.dircache = [] - self.inRawMode = 0 self.mainProcStr = None # used for the passive mode - self.passive = 0 # used to indicate the passive mode + self.passive = False # used to indicate the passive mode self.running = None self.test = None - self.tracePython = 0 - self.debugging = 0 + self.tracePython = False + self.debugging = False self.fork_auto = False self.fork_child = False @@ -333,8 +332,10 @@ d["broken"] = False threadList.append(d) - self.write('%s%s\n' % (DebugProtocol.ResponseThreadList, - unicode((currentId, threadList)))) + self.sendJsonCommand("ResponseThreadList", { + "currentID": currentId, + "threadList": threadList, + }) def raw_input(self, prompt, echo): """ @@ -344,9 +345,10 @@ @param echo Flag indicating echoing of the input (boolean) @return the entered string """ - self.write("%s%s\n" % (DebugProtocol.ResponseRaw, - unicode((prompt, echo)))) - self.inRawMode = 1 + self.sendJsonCommand("RequestRaw", { + "prompt": prompt, + "echo": echo, + }) self.eventLoop(True) return self.rawLine @@ -359,15 +361,7 @@ """ return eval(self.raw_input(prompt, 1)) - def __exceptionRaised(self): - """ - Private method called in the case of an exception. - - It ensures that the debug server is informed of the raised exception. - """ - self.pendingResponse = DebugProtocol.ResponseException - - def sessionClose(self, exit=1): + def sessionClose(self, exit=True): """ Public method to close the session with the debugger and optionally terminate. @@ -379,16 +373,14 @@ except Exception: pass - # clean up asyncio. - self.disconnect() - self.debugging = 0 + self.debugging = False # make sure we close down our end of the socket # might be overkill as normally stdin, stdout and stderr # SHOULD be closed on exit, but it does not hurt to do it here - self.readstream.close(1) - self.writestream.close(1) - self.errorstream.close(1) + self.readstream.close(True) + self.writestream.close(True) + self.errorstream.close(True) if exit: # Ok, go away. @@ -408,616 +400,631 @@ line = line[:-1] ## printerr(line) ##debug - - eoc = line.find('<') - - if eoc >= 0 and line[0] == '>': - # Get the command part and any argument. - cmd = line[:eoc + 1] - arg = line[eoc + 1:] - - if cmd == DebugProtocol.RequestVariables: - frmnr, scope, filter = eval(arg) - self.__dumpVariables(int(frmnr), int(scope), filter) - return - - if cmd == DebugProtocol.RequestVariable: - var, frmnr, scope, filter = eval(arg) - 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('%s%s\n' % (DebugProtocol.ResponseStack, - unicode(stack))) - return - - if cmd == DebugProtocol.RequestStep: - self.currentThread.step(1) - self.eventExit = 1 - return - - if cmd == DebugProtocol.RequestStepOver: - self.currentThread.step(0) - self.eventExit = 1 - return - - if cmd == DebugProtocol.RequestStepOut: - self.currentThread.stepOut() - self.eventExit = 1 - return - - if cmd == DebugProtocol.RequestStepQuit: - if self.passive: - self.progTerminated(42) - else: - self.set_quit() - self.eventExit = 1 - return - - if cmd == DebugProtocol.RequestContinue: - special = int(arg) - self.currentThread.go(special) - self.eventExit = 1 - return - - if cmd == DebugProtocol.RequestOK: - self.write(self.pendingResponse + '\n') - 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.RequestEnv: - env = eval(arg) - for key, value in env.items(): - if key.endswith("+"): - if key[:-1] in os.environ: - os.environ[key[:-1]] += value - else: - os.environ[key[:-1]] = value - else: - os.environ[key] = value - return - - if cmd == DebugProtocol.RequestLoad: - self._fncache = {} - self.dircache = [] - sys.argv = [] - wd, fn, args, tracePython = arg.split('|') - fn = fn.encode(sys.getfilesystemencoding()) - self.__setCoding(fn) - sys.argv.append(fn) - sys.argv.extend(eval(args)) - sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) - if wd == '': - os.chdir(sys.path[1]) - else: - os.chdir(wd) - tracePython = int(tracePython) - self.running = sys.argv[0] - self.mainFrame = None - self.inRawMode = 0 - self.debugging = 1 - - self.threads.clear() - self.attachThread(mainThread=1) - - # set the system exception handling function to ensure, that - # we report on all unhandled exceptions - sys.excepthook = self.__unhandled_exception - self.__interceptSignals() - - # clear all old breakpoints, they'll get set after we - # have started - self.mainThread.clear_all_breaks() - - self.mainThread.tracePython = tracePython - - # This will eventually enter a local event loop. - # Note the use of backquotes to cause a repr of self.running. - # The need for this is on Windows os where backslash is the - # path separator. They will get inadvertantly stripped away - # during the eval causing IOErrors, if self.running is passed - # as a normal str. - self.debugMod.__dict__['__file__'] = self.running - sys.modules['__main__'] = self.debugMod - self.callTraceEnabled = self.__newCallTraceEnabled - res = self.mainThread.run( - 'execfile(' + repr(self.running) + ')', - self.debugMod.__dict__) - self.progTerminated(res) - return - - if cmd == DebugProtocol.RequestRun: - sys.argv = [] - wd, fn, args = arg.split('|') - fn = fn.encode(sys.getfilesystemencoding()) - self.__setCoding(fn) - sys.argv.append(fn) - sys.argv.extend(eval(args)) - 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.mainFrame = None - self.botframe = None - self.inRawMode = 0 - - self.threads.clear() - self.attachThread(mainThread=1) - - # set the system exception handling function to ensure, that - # we report on all unhandled exceptions - sys.excepthook = self.__unhandled_exception - self.__interceptSignals() - - self.mainThread.tracePython = 0 - - self.debugMod.__dict__['__file__'] = sys.argv[0] - sys.modules['__main__'] = self.debugMod - res = 0 - try: - execfile(sys.argv[0], self.debugMod.__dict__) - except SystemExit as exc: - res = exc.code - atexit._run_exitfuncs() - self.writestream.flush() - self.progTerminated(res) - return - - if cmd == DebugProtocol.RequestCoverage: - from coverage import coverage - sys.argv = [] - wd, fn, args, erase = arg.split('@@') - fn = fn.encode(sys.getfilesystemencoding()) - self.__setCoding(fn) - sys.argv.append(fn) - sys.argv.extend(eval(args)) - sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) - if wd == '': - os.chdir(sys.path[1]) - else: - os.chdir(wd) - - # set the system exception handling function to ensure, that - # we report on all unhandled exceptions - sys.excepthook = self.__unhandled_exception - self.__interceptSignals() - - # generate a coverage object - self.cover = coverage( - auto_data=True, - data_file="%s.coverage" % os.path.splitext(sys.argv[0])[0]) - - if int(erase): - self.cover.erase() - sys.modules['__main__'] = self.debugMod - self.debugMod.__dict__['__file__'] = sys.argv[0] - self.running = sys.argv[0] - res = 0 - self.cover.start() - try: - execfile(sys.argv[0], self.debugMod.__dict__) - except SystemExit as exc: - res = exc.code - atexit._run_exitfuncs() - self.cover.stop() - self.cover.save() - self.writestream.flush() - self.progTerminated(res) - return - - if cmd == DebugProtocol.RequestProfile: - sys.setprofile(None) - import PyProfile - sys.argv = [] - wd, fn, args, erase = arg.split('|') - fn = fn.encode(sys.getfilesystemencoding()) - self.__setCoding(fn) - sys.argv.append(fn) - sys.argv.extend(eval(args)) - sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) - if wd == '': - os.chdir(sys.path[1]) - else: - os.chdir(wd) - - # set the system exception handling function to ensure, that - # we report on all unhandled exceptions - sys.excepthook = self.__unhandled_exception - self.__interceptSignals() - - # generate a profile object - self.prof = PyProfile.PyProfile(sys.argv[0]) - - if int(erase): - self.prof.erase() - self.debugMod.__dict__['__file__'] = sys.argv[0] - sys.modules['__main__'] = self.debugMod - self.running = sys.argv[0] - res = 0 - try: - self.prof.run('execfile(%r)' % sys.argv[0]) - except SystemExit as exc: - res = exc.code - atexit._run_exitfuncs() - self.prof.save() - 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('@@') - fn = fn.encode(sys.getfilesystemencoding()) - line = int(line) - set = int(set) - temporary = int(temporary) - - if set: - if cond == 'None' or cond == '': - cond = None + + self.handleJsonCommand(line) + + def handleJsonCommand(self, jsonStr): + """ + Public method to handle a command serialized as a JSON string. + + @param jsonStr string containing the command received from the IDE + @type str + """ + import json + + try: + commandDict = json.loads(jsonStr.strip()) + except json.JSONDecodeError as err: + printerr(str(err)) + return + + method = commandDict["method"] + params = commandDict["params"] + + if method == "RequestVariables": + self.__dumpVariables( + params["frameNumber"], params["scope"], params["filters"]) + + elif method == "RequestVariable": + self.__dumpVariable( + params["variable"], params["frameNumber"], + params["scope"], params["filters"]) + + elif method == "RequestThreadList": + self.__dumpThreadList() + + elif method == "RequestThreadSet": + if params["threadID"] in self.threads: + self.setCurrentThread(params["threadID"]) + self.sendJsonCommand("ResponseThreadSet", {}) + stack = self.currentThread.getStack() + self.sendJsonCommand("ResponseStack", { + "stack": stack, + }) + + elif method == "RequestCapabilities": + self.sendJsonCommand("ResponseCapabilities", { + "capabilities": self.__clientCapabilities(), + "clientType": "Python3" + }) + + elif method == "RequestBanner": + self.sendJsonCommand("ResponseBanner", { + "version": "Python {0}".format(sys.version), + "platform": socket.gethostname(), + "dbgclient": self.variant, + }) + + elif method == "RequestSetFilter": + self.__generateFilterObjects(params["scope"], params["filter"]) + + elif method == "RequestCallTrace": + if self.debugging: + self.callTraceEnabled = params["enable"] + else: + self.__newCallTraceEnabled = params["enable"] + # remember for later + + elif method == "RequestEnvironment": + for key, value in params["environment"].items(): + if key.endswith("+"): + if key[:-1] in os.environ: + os.environ[key[:-1]] += value else: - try: - compile(cond, '<string>', 'eval') - except SyntaxError: - self.write( - '%s%s,%d\n' % - (DebugProtocol.ResponseBPConditionError, - fn, line)) - return - self.mainThread.set_break(fn, line, temporary, cond) + os.environ[key[:-1]] = value else: - self.mainThread.clear_break(fn, line) - - return - - if cmd == DebugProtocol.RequestBreakEnable: - fn, line, enable = arg.split(',') - fn = fn.encode(sys.getfilesystemencoding()) - 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 + os.environ[key] = value + + elif method == "RequestLoad": + self._fncache = {} + self.dircache = [] + sys.argv = [] + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + self.__setCoding(params["filename"]) + sys.argv.append(params["filename"]) + sys.argv.extend(params["argv"]) + sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) + if params["workdir"] == '': + os.chdir(sys.path[1]) + else: + os.chdir(params["workdir"]) - if cmd == DebugProtocol.RequestBreakIgnore: - fn, line, count = arg.split(',') - fn = fn.encode(sys.getfilesystemencoding()) - line = int(line) - count = int(count) - - bp = self.mainThread.get_break(fn, line) - if bp is not None: - bp.ignore = count - - return + self.running = sys.argv[0] + self.mainFrame = None + self.debugging = True + + self.fork_auto = params["autofork"] + self.fork_child = params["forkChild"] + + self.threads.clear() + self.attachThread(mainThread=True) - 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('%s%s\n' % ( - DebugProtocol.ResponseWPConditionError, cond)) - return - self.mainThread.set_watch(cond, temporary) - else: - self.mainThread.clear_watch(cond) - - return + # set the system exception handling function to ensure, that + # we report on all unhandled exceptions + sys.excepthook = self.__unhandled_exception + self.__interceptSignals() + + # clear all old breakpoints, they'll get set after we have + # started + self.mainThread.clear_all_breaks() + + self.mainThread.tracePython = params["traceInterpreter"] - 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 + # This will eventually enter a local event loop. + self.debugMod.__dict__['__file__'] = self.running + sys.modules['__main__'] = self.debugMod + self.callTraceEnabled = self.__newCallTraceEnabled + res = self.mainThread.run( + 'execfile(' + repr(self.running) + ')', + self.debugMod.__dict__) + self.progTerminated(res) + + elif method == "RequestRun": + sys.argv = [] + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + self.__setCoding(params["filename"]) + sys.argv.append(params["filename"]) + sys.argv.extend(params["argv"]) + sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) + if params["workdir"] == '': + os.chdir(sys.path[1]) + else: + os.chdir(params["workdir"]) + + self.running = sys.argv[0] + self.mainFrame = None + self.botframe = None - 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 - - map(self.write, list) - - self.write(DebugProtocol.ResponseException + '\n') - - else: - self.write(unicode(value) + '\n') - self.write(DebugProtocol.ResponseOK + '\n') - - return + self.fork_auto = params["autofork"] + self.fork_child = params["forkChild"] + + self.threads.clear() + self.attachThread(mainThread=True) + + # set the system exception handling function to ensure, that + # we report on all unhandled exceptions + sys.excepthook = self.__unhandled_exception + self.__interceptSignals() - 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 in _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 - - map(self.write, list) + self.mainThread.tracePython = False + + self.debugMod.__dict__['__file__'] = sys.argv[0] + sys.modules['__main__'] = self.debugMod + res = 0 + try: + execfile(sys.argv[0], self.debugMod.__dict__) + except SystemExit as exc: + res = exc.code + atexit._run_exitfuncs() + self.writestream.flush() + self.progTerminated(res) - self.write(DebugProtocol.ResponseException + '\n') - - return + elif method == "RequestCoverage": + from coverage import coverage + sys.argv = [] + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + self.__setCoding(params["filename"]) + sys.argv.append(params["filename"]) + sys.argv.extend(params["argv"]) + sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) + if params["workdir"] == '': + os.chdir(sys.path[1]) + else: + os.chdir(params["workdir"]) - if cmd == DebugProtocol.RequestBanner: - self.write( - '%s%s\n' % ( - DebugProtocol.ResponseBanner, - unicode(("Python %s" % sys.version, - socket.gethostname(), - self.variant)))) - return + # set the system exception handling function to ensure, that + # we report on all unhandled exceptions + sys.excepthook = self.__unhandled_exception + self.__interceptSignals() + + # generate a coverage object + self.cover = coverage( + auto_data=True, + data_file="%s.coverage" % os.path.splitext(sys.argv[0])[0]) - if cmd == DebugProtocol.RequestCapabilities: - self.write('%s%d, "Python2"\n' % ( - DebugProtocol.ResponseCapabilities, - self.__clientCapabilities())) - return - - if cmd == DebugProtocol.RequestCompletion: - self.__completionList(arg) - return - - if cmd == DebugProtocol.RequestSetFilter: - scope, filterString = eval(arg) - self.__generateFilterObjects(int(scope), filterString) - return - - if cmd == DebugProtocol.RequestUTPrepare: - fn, tn, tfn, failed, cov, covname, erase = arg.split('|') - fn = fn.encode(sys.getfilesystemencoding()) - sys.path.insert(0, os.path.dirname(os.path.abspath(fn))) - os.chdir(sys.path[0]) - failed = eval(failed) + if params["erase"]: + self.cover.erase() + sys.modules['__main__'] = self.debugMod + self.debugMod.__dict__['__file__'] = sys.argv[0] + self.running = sys.argv[0] + res = 0 + self.cover.start() + try: + execfile(sys.argv[0], self.debugMod.__dict__) + except SystemExit as exc: + res = exc.code + atexit._run_exitfuncs() + self.cover.stop() + self.cover.save() + self.writestream.flush() + self.progTerminated(res) + + elif method == "RequestProfile": + sys.setprofile(None) + import PyProfile + sys.argv = [] + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + self.__setCoding(params["filename"]) + sys.argv.append(params["filename"]) + sys.argv.extend(params["argv"]) + sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) + if params["workdir"] == '': + os.chdir(sys.path[1]) + else: + os.chdir(params["workdir"]) - # set the system exception handling function to ensure, that - # we report on all unhandled exceptions - sys.excepthook = self.__unhandled_exception - self.__interceptSignals() - - try: - import unittest - utModule = __import__(tn) - try: - if failed: - self.test = unittest.defaultTestLoader\ - .loadTestsFromNames(failed, utModule) - else: - self.test = unittest.defaultTestLoader\ - .loadTestsFromName(tfn, utModule) - except AttributeError: - self.test = unittest.defaultTestLoader\ - .loadTestsFromModule(utModule) - except Exception: - exc_type, exc_value, exc_tb = sys.exc_info() - self.write( - '%s%s\n' % ( - DebugProtocol.ResponseUTPrepared, - unicode((0, str(exc_type), str(exc_value))))) - self.__exceptionRaised() + # set the system exception handling function to ensure, that + # we report on all unhandled exceptions + sys.excepthook = self.__unhandled_exception + self.__interceptSignals() + + # generate a profile object + self.prof = PyProfile.PyProfile(sys.argv[0]) + + if params["erase"]: + self.prof.erase() + self.debugMod.__dict__['__file__'] = sys.argv[0] + sys.modules['__main__'] = self.debugMod + self.running = sys.argv[0] + res = 0 + try: + self.prof.run('execfile(%r)' % sys.argv[0]) + except SystemExit as exc: + res = exc.code + atexit._run_exitfuncs() + self.prof.save() + self.writestream.flush() + self.progTerminated(res) + + elif method == "ExecuteStatement": + if self.buffer: + self.buffer = self.buffer + '\n' + params["statement"] + else: + self.buffer = params["statement"] + + try: + code = self.compile_command(self.buffer, self.readstream.name) + except (OverflowError, SyntaxError, ValueError): + # Report the exception + sys.last_type, sys.last_value, sys.last_traceback = \ + sys.exc_info() + self.sendJsonCommand("ClientOutput", { + "text": "".join(traceback.format_exception_only( + sys.last_type, sys.last_value)) + }) + self.buffer = '' + else: + if code is None: + self.sendJsonCommand("ResponseContinue", {}) return - - # generate a coverage object - if int(cov): - from coverage import coverage - self.cover = coverage( - auto_data=True, - data_file="%s.coverage" % os.path.splitext(covname)[0]) - if int(erase): - self.cover.erase() else: - self.cover = None - - self.write( - '%s%s\n' % ( - DebugProtocol.ResponseUTPrepared, - unicode((self.test.countTestCases(), "", "")))) - return - - if cmd == DebugProtocol.RequestUTRun: - from DCTestResult import DCTestResult - self.testResult = DCTestResult(self) - if self.cover: - self.cover.start() - self.test.run(self.testResult) - if self.cover: - self.cover.stop() - self.cover.save() - self.write('%s\n' % DebugProtocol.ResponseUTFinished) - return - - if cmd == DebugProtocol.RequestUTStop: - self.testResult.stop() - return - - if cmd == DebugProtocol.ResponseForkTo: - # this results from a separate event loop - self.fork_child = (arg == 'child') - self.eventExit = 1 - return - - if cmd == DebugProtocol.RequestForkMode: - self.fork_auto, self.fork_child = eval(arg) - return - - # If we are handling raw mode input then reset the mode and break out - # of the current event loop. - if self.inRawMode: - self.inRawMode = 0 - self.rawLine = line - self.eventExit = 1 - return + self.buffer = '' - if self.buffer: - self.buffer = self.buffer + '\n' + line - else: - self.buffer = line - - try: - code = self.compile_command(self.buffer, self.readstream.name) - except (OverflowError, SyntaxError, ValueError): - # Report the exception - sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() - map(self.write, traceback.format_exception_only( - sys.last_type, sys.last_value)) - self.buffer = '' - else: - if code is None: - self.pendingResponse = DebugProtocol.ResponseContinue - else: - self.buffer = '' - - try: - if self.running is None: - exec code in self.debugMod.__dict__ - else: - if self.currentThread is None: - # program has terminated - self.running = None - _globals = self.debugMod.__dict__ - _locals = _globals + try: + if self.running is None: + exec code in self.debugMod.__dict__ else: - cf = self.currentThread.getCurrentFrame() - # program has terminated - if cf is None: + if self.currentThread is None: + # program has terminated self.running = None _globals = self.debugMod.__dict__ _locals = _globals else: - frmnr = self.framenr - while cf is not None and frmnr > 0: - cf = cf.f_back - frmnr -= 1 - _globals = cf.f_globals - _locals = \ - self.currentThread.getFrameLocals( - self.framenr) - # reset sys.stdout to our redirector (unconditionally) - if "sys" in _globals: - __stdout = _globals["sys"].stdout - _globals["sys"].stdout = self.writestream - exec code in _globals, _locals - _globals["sys"].stdout = __stdout - elif "sys" in _locals: - __stdout = _locals["sys"].stdout - _locals["sys"].stdout = self.writestream - exec code in _globals, _locals - _locals["sys"].stdout = __stdout - else: - exec code in _globals, _locals - - self.currentThread.storeFrameLocals(self.framenr) - except SystemExit, exc: - self.progTerminated(exc.code) - except Exception: - # Report the exception and the traceback + cf = self.currentThread.getCurrentFrame() + # program has terminated + if cf is None: + self.running = None + _globals = self.debugMod.__dict__ + _locals = _globals + else: + frmnr = self.framenr + while cf is not None and frmnr > 0: + cf = cf.f_back + frmnr -= 1 + _globals = cf.f_globals + _locals = \ + self.currentThread.getFrameLocals( + self.framenr) + # reset sys.stdout to our redirector + # (unconditionally) + if "sys" in _globals: + __stdout = _globals["sys"].stdout + _globals["sys"].stdout = self.writestream + exec code in _globals, _locals + _globals["sys"].stdout = __stdout + elif "sys" in _locals: + __stdout = _locals["sys"].stdout + _locals["sys"].stdout = self.writestream + exec code in _globals, _locals + _locals["sys"].stdout = __stdout + else: + exec code in _globals, _locals + + self.currentThread.storeFrameLocals(self.framenr) + except SystemExit, exc: + self.progTerminated(exc.code) + except Exception: + # Report the exception and the traceback + tlist = [] + try: + exc_type, exc_value, exc_tb = sys.exc_info() + sys.last_type = exc_type + sys.last_value = exc_value + sys.last_traceback = exc_tb + tblist = traceback.extract_tb(exc_tb) + del tblist[:1] + tlist = traceback.format_list(tblist) + if tlist: + tlist.insert( + 0, "Traceback (innermost last):\n") + tlist.extend(traceback.format_exception_only( + exc_type, exc_value)) + finally: + tblist = exc_tb = None + + self.sendJsonCommand("ClientOutput", { + "text": "".join(tlist) + }) + + self.sendJsonCommand("ResponseOK", {}) + + elif method == "RequestStep": + self.currentThread.step(True) + self.eventExit = True + + elif method == "RequestStepOver": + self.currentThread.step(False) + self.eventExit = True + + elif method == "RequestStepOut": + self.currentThread.stepOut() + self.eventExit = True + + elif method == "RequestStepQuit": + if self.passive: + self.progTerminated(42) + else: + self.set_quit() + self.eventExit = True + + elif method == "RequestContinue": + self.currentThread.go(params["special"]) + self.eventExit = True + + elif method == "RawInput": + # If we are handling raw mode input then break out of the current + # event loop. + self.rawLine = params["input"] + self.eventExit = True + + elif method == "RequestBreakpoint": + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + 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"]) + + elif method == "RequestBreakpointEnable": + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + bp = self.mainThread.get_break(params["filename"], params["line"]) + if bp is not None: + if params["enable"]: + bp.enable() + else: + bp.disable() + + + elif method == "RequestBreakpointIgnore": + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + bp = self.mainThread.get_break(params["filename"], params["line"]) + if bp is not None: + bp.ignore = params["count"] + + elif method == "RequestWatch": + if params["setWatch"]: + if not params["condition"].endswith( + ('??created??', '??changed??')): 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 - - map(self.write, list) + 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"]) + + elif method == "RequestWatchEnable": + wp = self.mainThread.get_watch(params["condition"]) + if wp is not None: + if params["enable"]: + wp.enable() + else: + wp.disable() + + elif method == "RequestWatchIgnore": + wp = self.mainThread.get_watch(params["condition"]) + if wp is not None: + wp.ignore = params["count"] + + elif method == "RequestShutdown": + self.sessionClose() + + elif method == "RequestCompletion": + self.__completionList(params["text"]) + + elif method == "RequestUTPrepare": + params["filename"] = params["filename"].encode( + sys.getfilesystemencoding()) + sys.path.insert( + 0, os.path.dirname(os.path.abspath(params["filename"]))) + os.chdir(sys.path[0]) + + # set the system exception handling function to ensure, that + # we report on all unhandled exceptions + sys.excepthook = self.__unhandled_exception + self.__interceptSignals() + + try: + import unittest + utModule = __import__(params["testname"]) + try: + if params["failed"]: + self.test = unittest.defaultTestLoader\ + .loadTestsFromNames(params["failed"], utModule) + else: + self.test = unittest.defaultTestLoader\ + .loadTestsFromName(params["testfunctionname"], + utModule) + except AttributeError: + self.test = unittest.defaultTestLoader\ + .loadTestsFromModule(utModule) + except Exception: + exc_type, exc_value, exc_tb = sys.exc_info() + self.sendJsonCommand("ResponseUTPrepared", { + "count": 0, + "exception": exc_type.__name__, + "message": str(exc_value), + }) + return + + # generate a coverage object + if params["coverage"]: + from coverage import coverage + self.cover = coverage( + auto_data=True, + data_file="%s.coverage" % \ + os.path.splitext(params["coveragefile"])[0]) + if params["coverageerase"]: + self.cover.erase() + else: + self.cover = None + + self.sendJsonCommand("ResponseUTPrepared", { + "count": self.test.countTestCases(), + "exception": "", + "message": "", + }) + + elif method == "RequestUTRun": + from DCTestResult import DCTestResult + self.testResult = DCTestResult(self) + if self.cover: + self.cover.start() + self.test.run(self.testResult) + if self.cover: + self.cover.stop() + self.cover.save() + self.sendJsonCommand("ResponseUTFinished", {}) + + 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 sendJsonCommand(self, method, params): + """ + Public method to send a single command or response to the IDE. + + @param method command or response command name to be sent + @type str + @param params dictionary of named parameters for the command or + response + @type dict + """ + cmd = prepareJsonCommand(method, params) + + self.writestream.write_p(cmd) + self.writestream.flush() + + 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 + @type 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): """ @@ -1035,15 +1042,40 @@ except ImportError: return ( self.clientCapabilities & ~DebugClientCapabilities.HasProfiler) - - def write(self, s): + + def readReady(self, stream): """ - Public method to write data to the output stream. + Public method called when there is data ready to be read. - @param s data to be written (string) + @param stream file like object that has data to be written """ - self.writestream.write(s) - self.writestream.flush() + try: + got = stream.readline_p() + except Exception: + return + + if len(got) == 0: + self.sessionClose() + return + + self.__receiveBuffer = self.__receiveBuffer + got + + # Call handleLine for the line if it is complete. + eol = self.__receiveBuffer.find('\n') + while eol >= 0: + line = self.__receiveBuffer[:eol + 1] + self.__receiveBuffer = self.__receiveBuffer[eol + 1:] + self.handleLine(line) + eol = self.__receiveBuffer.find('\n') + + def writeReady(self, stream): + """ + Public method called when we are ready to write data. + + @param stream file like object that has data to be written + """ + stream.write_p("") + stream.flush() def __interact(self): """ @@ -1051,8 +1083,8 @@ """ global DebugClientInstance - self.setDescriptors(self.readstream, self.writestream) DebugClientInstance = self + self.__receiveBuffer = "" if not self.passive: # At this point simulate an event loop. @@ -1087,13 +1119,13 @@ continue if self.readstream in rrdy: - self.readReady(self.readstream.fileno()) + self.readReady(self.readstream) if self.writestream in wrdy: - self.writeReady(self.writestream.fileno()) + self.writeReady(self.writestream) if self.errorstream in wrdy: - self.writeReady(self.errorstream.fileno()) + self.writeReady(self.errorstream) self.eventExit = None self.pollingDisabled = False @@ -1128,13 +1160,13 @@ return if self.readstream in rrdy: - self.readReady(self.readstream.fileno()) + self.readReady(self.readstream) if self.writestream in wrdy: - self.writeReady(self.writestream.fileno()) + self.writeReady(self.writestream) if self.errorstream in wrdy: - self.writeReady(self.errorstream.fileno()) + self.writeReady(self.errorstream) def connectDebugger(self, port, remoteAddress=None, redirect=1): """ @@ -1177,7 +1209,7 @@ @param excval data about the exception @param exctb traceback for the exception """ - self.mainThread.user_exception(None, (exctype, excval, exctb), 1) + self.mainThread.user_exception(None, (exctype, excval, exctb), True) def __interceptSignals(self): """ @@ -1230,10 +1262,14 @@ else: fargs = "" - siglist = [message, [filename, linenr, ffunc, fargs]] - - self.write("%s%s" % (DebugProtocol.ResponseSignal, str(siglist))) - + self.sendJsonCommand("ResponseSignal", { + "message": message, + "filename": filename, + "linenumber": linenr, + "function": ffunc, + "arguments": fargs, + }) + def absPath(self, fn): """ Public method to convert a filename to an absolute name. @@ -1284,20 +1320,20 @@ zero otherwise. """ if self.mainThread.tracePython: # trace into Python library - return 0 + return False # Eliminate anything that is part of the Python installation. afn = self.absPath(fn) for d in self.skipdirs: if afn.startswith(d): - return 1 + return True # special treatment for paths containing site-packages or dist-packages for part in ["site-packages", "dist-packages"]: if part in afn: - return 1 + return True - return 0 + return False def getRunning(self): """ @@ -1307,25 +1343,28 @@ """ return self.running - def progTerminated(self, status): + def progTerminated(self, status, message=""): """ Public method to tell the debugger that the program has terminated. @param status return status @type int + @param message status message + @type str """ if status is None: status = 0 - else: - try: - int(status) - except ValueError: - status = 1 + elif not isinstance(status, int): + message = str(status) + status = 1 if self.running: self.set_quit() self.running = None - self.write('%s%d\n' % (DebugProtocol.ResponseExit, status)) + self.sendJsonCommand("ResponseExit", { + "status": status, + "message": message, + }) # reset coding self.__coding = self.defaultCoding @@ -1364,7 +1403,7 @@ else: dict = f.f_locals - varlist = [scope] + varlist = [] if scope != -1: keylist = dict.keys() @@ -1372,8 +1411,10 @@ vlist = self.__formatVariablesList(keylist, dict, scope, filter) varlist.extend(vlist) - self.write('%s%s\n' % ( - DebugProtocol.ResponseVariables, unicode(varlist))) + self.sendJsonCommand("ResponseVariables", { + "scope": scope, + "variables": varlist, + }) def __dumpVariable(self, var, frmnr, scope, filter): """ @@ -1408,7 +1449,7 @@ else: dict = f.f_locals - varlist = [scope, var] + varlist = [] if scope != -1: # search the correct dictionary @@ -1416,8 +1457,8 @@ rvar = var[:] dictkeys = None obj = None - isDict = 0 - formatSequences = 0 + isDict = False + formatSequences = False access = "" oaccess = "" odict = dict @@ -1437,7 +1478,7 @@ dictkeys = [var[i - 1]] else: dictkeys = [var[i][:-2]] - formatSequences = 1 + formatSequences = True if not access and not oaccess: if var[i][:-2] == '...': access = '["%s"]' % var[i - 1] @@ -1458,7 +1499,7 @@ else: access = '%s[%s]' % (access, var[i][:-2]) if var[i][-2:] == "{}": # __IGNORE_WARNING__ - isDict = 1 + isDict = True break else: if not access: @@ -1623,8 +1664,11 @@ except Exception: pass - self.write('%s%s\n' % ( - DebugProtocol.ResponseVariable, unicode(varlist))) + self.sendJsonCommand("ResponseVariable", { + "scope": scope, + "variable": var, + "variables": varlist, + }) def __formatQtVariable(self, value, vtype): """ @@ -1793,10 +1837,10 @@ for key in keylist: # filter based on the filter pattern - matched = 0 + matched = False for pat in patternFilterObjects: if pat.match(unicode(key)): - matched = 1 + matched = True break if matched: continue @@ -1907,8 +1951,10 @@ pass self.__getCompletionList(text, self.complete, completions) - self.write("%s%s||%s\n" % (DebugProtocol.ResponseCompletion, - unicode(list(completions)), text)) + self.sendJsonCommand("ResponseCompletion", { + "completions": list(completions), + "text": text, + }) def __getCompletionList(self, text, completer, completions): """ @@ -1932,7 +1978,8 @@ comp = None def startDebugger(self, filename=None, host=None, port=None, - enableTrace=1, exceptions=1, tracePython=0, redirect=1): + enableTrace=True, exceptions=True, tracePython=False, + redirect=True): """ Public method used to start the remote debugger. @@ -1964,19 +2011,17 @@ self.running = None if self.running: self.__setCoding(self.running) - self.passive = 1 - self.write("%s%s|%d\n" % ( - DebugProtocol.PassiveStartup, self.running, exceptions)) + self.passive = True + self.sendPassiveStartup(self.running, exceptions) self.__interact() # setup the debugger variables self._fncache = {} self.dircache = [] self.mainFrame = None - self.inRawMode = 0 - self.debugging = 1 + self.debugging = True - self.attachThread(mainThread=1) + self.attachThread(mainThread=True) self.mainThread.tracePython = tracePython # set the system exception handling function to ensure, that @@ -1989,8 +2034,8 @@ self.mainThread.set_trace() def startProgInDebugger(self, progargs, wd='', host=None, - port=None, exceptions=1, tracePython=0, - redirect=1): + port=None, exceptions=True, tracePython=False, + redirect=True): """ Public method used to start the remote debugger. @@ -2026,12 +2071,10 @@ self.running = sys.argv[0] self.__setCoding(self.running) self.mainFrame = None - self.inRawMode = 0 - self.debugging = 1 + self.debugging = True - self.passive = 1 - self.write("%s%s|%d\n" % ( - DebugProtocol.PassiveStartup, self.running, exceptions)) + self.passive = True + self.sendPassiveStartup(self.running, exceptions) self.__interact() self.attachThread(mainThread=1) @@ -2094,9 +2137,9 @@ host = None port = None wd = '' - tracePython = 0 - exceptions = 1 - redirect = 1 + tracePython = False + exceptions = True + redirect = True while args[0]: if args[0] == '-h': host = args[1] @@ -2111,13 +2154,13 @@ del args[0] del args[0] elif args[0] == '-t': - tracePython = 1 + tracePython = True del args[0] elif args[0] == '-e': - exceptions = 0 + exceptions = False del args[0] elif args[0] == '-n': - redirect = 0 + redirect = False del args[0] elif args[0] == '--no-encoding': self.noencoding = True @@ -2157,7 +2200,7 @@ try: redirect = int(sys.argv[2]) except (ValueError, IndexError): - redirect = 1 + redirect = True try: ipOrHost = sys.argv[3] if ':' in ipOrHost: @@ -2187,7 +2230,7 @@ @return process ID (integer) """ if not self.fork_auto: - self.write(DebugProtocol.RequestForkTo + '\n') + self.sendJsonCommand("RequestForkTo", {}) self.eventLoop(True) pid = DebugClientOrigFork() if pid == 0: