Wed, 31 Aug 2016 18:31:57 +0200
Started with the modernization of the debugger interface.
DebugClients/Python3/DebugClientBase.py | file | annotate | diff | comparison | revisions | |
Debugger/DebuggerInterfacePython3.py | file | annotate | diff | comparison | revisions |
--- a/DebugClients/Python3/DebugClientBase.py Wed Aug 31 18:30:40 2016 +0200 +++ b/DebugClients/Python3/DebugClientBase.py Wed Aug 31 18:31:57 2016 +0200 @@ -399,6 +399,9 @@ line = line[:-1] ## printerr(line) ##debug + + if "jsonrpc" in line: + return self.__handleJsonCommand(line) eoc = line.find('<') @@ -477,66 +480,61 @@ # remember for later return - if cmd == DebugProtocol.RequestEnv: - env = eval(arg.replace("u'", "'")) - 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('|') - 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 = False - self.debugging = True - - 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() - - # 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 - code = self.__compileFileSource(self.running) - if code: - self.callTraceEnabled = self.__newCallTraceEnabled - res = self.mainThread.run(code, self.debugMod.__dict__) - self.progTerminated(res) - return - +## if cmd == DebugProtocol.RequestEnv: +## env = eval(arg.replace("u'", "'")) +## 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('|') +## 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 = False +## self.debugging = True +## +## 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() +## +## # 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. +## self.debugMod.__dict__['__file__'] = self.running +## sys.modules['__main__'] = self.debugMod +## code = self.__compileFileSource(self.running) +## if code: +## self.callTraceEnabled = self.__newCallTraceEnabled +## res = self.mainThread.run(code, self.debugMod.__dict__) +## self.progTerminated(res) +## return +## if cmd == DebugProtocol.RequestRun: sys.argv = [] wd, fn, args = arg.split('|') @@ -836,18 +834,18 @@ return - if cmd == DebugProtocol.RequestBanner: - self.write('{0}{1}\n'.format(DebugProtocol.ResponseBanner, - str(("Python {0}".format(sys.version), - socket.gethostname(), self.variant)))) - return - - if cmd == DebugProtocol.RequestCapabilities: - self.write('{0}{1:d}, "Python3"\n'.format( - DebugProtocol.ResponseCapabilities, - self.__clientCapabilities())) - return - +## if cmd == DebugProtocol.RequestBanner: +## self.write('{0}{1}\n'.format(DebugProtocol.ResponseBanner, +## str(("Python {0}".format(sys.version), +## socket.gethostname(), self.variant)))) +## return +## +## if cmd == DebugProtocol.RequestCapabilities: +## self.write('{0}{1:d}, "Python3"\n'.format( +## DebugProtocol.ResponseCapabilities, +## self.__clientCapabilities())) +## return +## if cmd == DebugProtocol.RequestCompletion: self.__completionList(arg.replace("u'", "'")) return @@ -1021,7 +1019,113 @@ for l in list: self.write(l) + + def __handleJsonCommand(self, jsonStr): + """ + Private method to handle a command serialized as a JSON string. + """ + 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 == "RequestCapabilities": + self.__sendJsonCommand("ResponseCapabilities", { + "capabilities": self.__clientCapabilities(), + "clientType": "Python3" + }) + return + + if method == "RequestBanner": + self.__sendJsonCommand("ResponseBanner", { + "version": "Python {0}".format(sys.version), + "platform": socket.gethostname(), + "dbgclient": self.variant, + }) + return + + if method == "RequestEnvironment": + for key, value in params["environment"].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 method == "RequestLoad": + printerr(method) + self._fncache = {} + self.dircache = [] + sys.argv = [] + 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.inRawMode = False + self.debugging = True + + 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() + + # clear all old breakpoints, they'll get set after we have + # started + self.mainThread.clear_all_breaks() + + self.mainThread.tracePython = params["traceInterpreter"] + + # 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 code: + self.callTraceEnabled = self.__newCallTraceEnabled + res = self.mainThread.run(code, self.debugMod.__dict__) + self.progTerminated(res) + return + + def __sendJsonCommand(self, command, params): + """ + Private method to send a single command to the client. + + @param command command name to be sent + @type str + @param params dictionary of named parameters for the command + @type dict + """ + import json + + commandDict = { + "jsonrpc": "2.0", + "method": command, + "params": params, + } + cmd = json.dumps(commandDict) + '\n' + self.write(cmd) + def __clientCapabilities(self): """ Private method to determine the clients capabilities.
--- a/Debugger/DebuggerInterfacePython3.py Wed Aug 31 18:30:40 2016 +0200 +++ b/Debugger/DebuggerInterfacePython3.py Wed Aug 31 18:31:57 2016 +0200 @@ -427,8 +427,9 @@ @param env environment settings (dictionary) """ - self.__sendCommand('{0}{1}\n'.format( - DebugProtocol.RequestEnv, str(env))) +## self.__sendCommand('{0}{1}\n'.format( +## DebugProtocol.RequestEnv, str(env))) + self.__sendJsonCommand("RequestEnvironment", {"environment": env}) def remoteLoad(self, fn, argv, wd, traceInterpreter=False, autoContinue=True, autoFork=False, forkChild=False): @@ -451,12 +452,20 @@ wd = self.translate(wd, False) fn = self.translate(os.path.abspath(fn), False) - self.__sendCommand('{0}{1}\n'.format( - DebugProtocol.RequestForkMode, repr((autoFork, forkChild)))) - self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format( - DebugProtocol.RequestLoad, wd, fn, - str(Utilities.parseOptionString(argv)), - traceInterpreter)) +## self.__sendCommand('{0}{1}\n'.format( +## DebugProtocol.RequestForkMode, repr((autoFork, forkChild)))) +## self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format( +## DebugProtocol.RequestLoad, wd, fn, +## str(Utilities.parseOptionString(argv)), +## traceInterpreter)) + self.__sendJsonCommand("RequestLoad", { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + "traceInterpreter": traceInterpreter, + "autofork": autoFork, + "forkChild": forkChild, + }) def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False): """ @@ -727,13 +736,15 @@ """ Public slot to get the banner info of the remote client. """ - self.__sendCommand(DebugProtocol.RequestBanner + '\n') +## self.__sendCommand(DebugProtocol.RequestBanner + '\n') + self.__sendJsonCommand("RequestBanner", {}) def remoteCapabilities(self): """ Public slot to get the debug clients capabilities. """ - self.__sendCommand(DebugProtocol.RequestCapabilities + '\n') +## self.__sendCommand(DebugProtocol.RequestCapabilities + '\n') + self.__sendJsonCommand("RequestCapabilities", {}) def remoteCompletion(self, text): """ @@ -814,6 +825,10 @@ ## print("Server: ", line) ##debug + if line.startswith("{") and "jsonrpc" in line: + self.__handleJsonCommand(line) + continue + eoc = line.find('<') + 1 # Deal with case where user has written directly to stdout @@ -978,18 +993,18 @@ self.debugServer.signalClientRawInput(prompt, echo) continue - if resp == DebugProtocol.ResponseBanner: - version, platform, dbgclient = eval(line[eoc:-1]) - self.debugServer.signalClientBanner( - version, platform, dbgclient) - continue - - if resp == DebugProtocol.ResponseCapabilities: - cap, clType = eval(line[eoc:-1]) - self.clientCapabilities = cap - self.debugServer.signalClientCapabilities(cap, clType) - continue - +## if resp == DebugProtocol.ResponseBanner: +## version, platform, dbgclient = eval(line[eoc:-1]) +## self.debugServer.signalClientBanner( +## version, platform, dbgclient) +## continue +## +## if resp == DebugProtocol.ResponseCapabilities: +## cap, clType = eval(line[eoc:-1]) +## self.clientCapabilities = cap +## self.debugServer.signalClientCapabilities(cap, clType) +## continue +## if resp == DebugProtocol.ResponseCompletion: clstring, text = line[eoc:-1].split('||') cl = eval(clstring) @@ -1055,7 +1070,35 @@ continue self.debugServer.signalClientOutput(line) - + + def __handleJsonCommand(self, jsonStr): + """ + Private method to handle a command serialized as a JSON string. + """ + import json + + try: + commandDict = json.loads(jsonStr.strip()) + except json.JSONDecodeError as err: + print(str(err)) + return + + method = commandDict["method"] + params = commandDict["params"] + + if method == "ResponseCapabilities": + self.clientCapabilities = params["capabilities"] + self.debugServer.signalClientCapabilities( + params["capabilities"], params["clientType"]) + return + + if method == "ResponseBanner": + self.debugServer.signalClientBanner( + params["version"], + params["platform"], + params["dbgclient"]) + return + def __sendCommand(self, cmd): """ Private method to send a single line command to the client. @@ -1067,6 +1110,28 @@ else: self.queue.append(cmd) + def __sendJsonCommand(self, command, params): + """ + Private method to send a single command to the client. + + @param command command name to be sent + @type str + @param params dictionary of named parameters for the command + @type dict + """ + import json + + commandDict = { + "jsonrpc": "2.0", + "method": command, + "params": params, + } + cmd = json.dumps(commandDict) + '\n' + if self.qsock is not None: + self.qsock.write(cmd.encode('utf8', 'backslashreplace')) + else: + self.queue.append(cmd) + def createDebuggerInterfacePython3(debugServer, passive): """