Started with the modernization of the debugger interface. jsonrpc

Wed, 31 Aug 2016 18:31:57 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 31 Aug 2016 18:31:57 +0200
branch
jsonrpc
changeset 5119
80bd41498eef
parent 5118
4a79a6153485
child 5120
c5189d404cc7

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):
     """

eric ide

mercurial