RefactoringRope/JsonServer.py

branch
server_client_variant
changeset 163
6a9e7b37a18b
child 166
6fc202183b3b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RefactoringRope/JsonServer.py	Tue Sep 12 18:55:25 2017 +0200
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the JSON based server base class.
+"""
+
+from __future__ import unicode_literals
+
+import json
+
+from PyQt5.QtCore import pyqtSlot, QProcess
+from PyQt5.QtNetwork import QTcpServer, QHostAddress
+
+from E5Gui import E5MessageBox
+
+import Preferences
+import Utilities
+
+
+class JsonServer(QTcpServer):
+    """
+    Class implementing the JSON based server base class.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent parent object
+        @type QObject
+        """
+        super(JsonServer, self).__init__(parent)
+        
+        self.__clientProcess = None
+        self.__connection = None
+        
+        # setup the network interface
+        networkInterface = Preferences.getDebugger("NetworkInterface")
+        if networkInterface == "all" or '.' in networkInterface:
+            # IPv4
+            self.__hostAddress = '127.0.0.1'
+        else:
+            # IPv6
+            self.__hostAddress = '::1'
+        self.listen(QHostAddress(self.__hostAddress))
+
+        self.newConnection.connect(self.__handleNewConnection)
+        
+        port = self.serverPort()
+        ## Note: Need the port if started external in debugger:
+        print('Refactoring server listening on: {0:d}'.format(port))
+        # __IGNORE_WARNING__
+    
+    @pyqtSlot()
+    def __handleNewConnection(self):
+        """
+        Private slot for new incomming connections from the refactoring client.
+        """
+        if self.__connection is not None:
+            self.__connection.close()
+            self.__connection = None
+        
+        connection = self.nextPendingConnection()
+        if not connection.isValid():
+            return
+        
+        self.__connection = connection
+        connection.readyRead.connect(self.__receiveJson)
+        connection.disconnected.connect(self.__handleDisconnect)
+        
+        self.sendJson("ping", {})
+    
+    @pyqtSlot()
+    def __handleDisconnect(self):
+        """
+        Private slot handling a disconnect of the refactoring client.
+        """
+        if self.__connection is not None:
+            self.__connection.close()
+        
+        self.__connection = None
+    
+    @pyqtSlot()
+    def __receiveJson(self):
+        """
+        Private slot handling received data from the client.
+        """
+        while self.__connection and self.__connection.canReadLine():
+            data = self.__connection.readLine()
+            jsonLine = bytes(data).decode("utf-8", 'backslashreplace')
+            
+            print("JSON Server: ", jsonLine)          ##debug
+            
+            try:
+                clientDict = json.loads(jsonLine.strip())
+            except (TypeError, ValueError) as err:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("JSON Protocol Error"),
+                    self.tr("""<p>The response received from the client"""
+                            """ could not be decoded. Please report"""
+                            """ this issue with the received data to the"""
+                            """ eric bugs email address.</p>"""
+                            """<p>Error: {0}</p>"""
+                            """<p>Data:<br/>{0}</p>""").format(
+                        str(err), Utilities.html_encode(jsonLine.strip())),
+                    E5MessageBox.StandardButtons(
+                        E5MessageBox.Ok))
+                return
+            
+            method = clientDict["method"]
+            params = clientDict["params"]
+            
+            # TODO: remove these once done
+            print("Method:", method)
+            print("Params:", params)
+            
+            self.handleCall(method, params)
+    
+    def handleCall(self, method, params):
+        """
+        Public method to handle a method call from the client.
+        
+        Note: This is an empty implementation that must be overridden in
+        derived classes.
+        
+        @param method requested method name
+        @type str
+        @param params dictionary with method specific parameters
+        @type dict
+        """
+        pass
+    
+    def sendJson(self, command, params):
+        """
+        Public method to send a single refactoring command to the client.
+        
+        @param command command name to be sent
+        @type str
+        @param params dictionary of named parameters for the command
+        @type dict
+        """
+        commandDict = {
+            "jsonrpc": "2.0",
+            "method": command,
+            "params": params,
+        }
+        cmd = json.dumps(commandDict) + '\n'
+        
+        if self.__connection is not None:
+            self.__connection.write(cmd.encode('utf8', 'backslashreplace'))
+    
+    def startClient(self, interpreter, clientScript, clientArgs):
+        """
+        Public method to start the client process.
+        
+        @param interpreter interpreter to be used for the client
+        @type str
+        @param clientScript path to the client script
+        @type str
+        @param clientArgs list of arguments for the client
+        @return flag indicating a successful client start
+        @rtype bool
+        """
+        if interpreter == "" or not Utilities.isinpath(interpreter):
+            return False
+        
+        proc = QProcess()
+        proc.setProcessChannelMode(QProcess.ForwardedChannels)
+        args = [clientScript, self.__hostAddress, str(self.serverPort())]
+        args.extend(clientArgs)
+        proc.start(interpreter, args)
+        if not proc.waitForStarted(10000):
+            proc = None
+        
+        self.__clientProcess = proc
+        
+        return proc is not None
+    
+    def stopClient(self):
+        """
+        Public method to stop the client process.
+        """
+        self.__clientProcess.close()
+        self.__clientProcess = None
+
+#
+# eflag: noqa = M801

eric ide

mercurial