src/eric7/EricNetwork/EricJsonServer.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
--- a/src/eric7/EricNetwork/EricJsonServer.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/EricNetwork/EricJsonServer.py	Wed Jul 13 14:55:47 2022 +0200
@@ -11,8 +11,13 @@
 import json
 
 from PyQt6.QtCore import (
-    pyqtSlot, QProcess, QProcessEnvironment, QCoreApplication, QEventLoop,
-    QTimer, QThread
+    pyqtSlot,
+    QProcess,
+    QProcessEnvironment,
+    QCoreApplication,
+    QEventLoop,
+    QTimer,
+    QThread,
 )
 from PyQt6.QtNetwork import QTcpServer, QHostAddress
 
@@ -26,10 +31,11 @@
     """
     Class implementing a JSON based server base class.
     """
+
     def __init__(self, name="", multiplex=False, parent=None):
         """
         Constructor
-        
+
         @param name name of the server (used for output only)
         @type str
         @param multiplex flag indicating a multiplexing server
@@ -38,7 +44,7 @@
         @type QObject
         """
         super().__init__(parent)
-        
+
         self.__name = name
         self.__multiplex = multiplex
         if self.__multiplex:
@@ -47,23 +53,26 @@
         else:
             self.__clientProcess = None
             self.__connection = None
-        
+
         # setup the network interface
         networkInterface = Preferences.getDebugger("NetworkInterface")
-        if networkInterface == "all" or '.' in networkInterface:
+        if networkInterface == "all" or "." in networkInterface:
             # IPv4
-            self.__hostAddress = '127.0.0.1'
+            self.__hostAddress = "127.0.0.1"
         else:
             # IPv6
-            self.__hostAddress = '::1'
+            self.__hostAddress = "::1"
         self.listen(QHostAddress(self.__hostAddress))
 
         self.newConnection.connect(self.handleNewConnection)
-        
+
         ## Note: Need the port if client is started external in debugger.
-        print('JSON server ({1}) listening on: {0:d}'   # __IGNORE_WARNING__
-              .format(self.serverPort(), self.__name))
-    
+        print(  # __IGNORE_WARNING_M801__
+            "JSON server ({1}) listening on: {0:d}".format(
+                self.serverPort(), self.__name
+            )
+        )
+
     @pyqtSlot()
     def handleNewConnection(self):
         """
@@ -72,12 +81,13 @@
         connection = self.nextPendingConnection()
         if not connection.isValid():
             return
-        
+
         if self.__multiplex:
             if not connection.waitForReadyRead(3000):
                 return
-            idString = bytes(connection.readLine()).decode(
-                "utf-8", 'backslashreplace').strip()
+            idString = (
+                bytes(connection.readLine()).decode("utf-8", "backslashreplace").strip()
+            )
             if idString in self.__connections:
                 self.__connections[idString].close()
             self.__connections[idString] = connection
@@ -85,19 +95,17 @@
             idString = ""
             if self.__connection is not None:
                 self.__connection.close()
-            
+
             self.__connection = connection
-        
-        connection.readyRead.connect(
-            lambda: self.__receiveJson(idString))
-        connection.disconnected.connect(
-            lambda: self.__handleDisconnect(idString))
-    
+
+        connection.readyRead.connect(lambda: self.__receiveJson(idString))
+        connection.disconnected.connect(lambda: self.__handleDisconnect(idString))
+
     @pyqtSlot()
     def __handleDisconnect(self, idString):
         """
         Private slot handling a disconnect of the client.
-        
+
         @param idString id of the connection been disconnected
         @type str
         """
@@ -108,15 +116,15 @@
         else:
             if self.__connection is not None:
                 self.__connection.close()
-            
+
             self.__connection = None
-    
+
     def connectionNames(self):
         """
         Public method to get the list of active connection names.
-        
+
         If this is not a multiplexing server, an empty list is returned.
-        
+
         @return list of active connection names
         @rtype list of str
         """
@@ -124,12 +132,12 @@
             return list(self.__connections.keys())
         else:
             return []
-    
+
     @pyqtSlot()
     def __receiveJson(self, idString):
         """
         Private slot handling received data from the client.
-        
+
         @param idString id of the connection
         @type str
         """
@@ -140,36 +148,38 @@
                 connection = None
         else:
             connection = self.__connection
-        
+
         while connection and connection.canReadLine():
             data = connection.readLine()
-            jsonLine = bytes(data).decode("utf-8", 'backslashreplace')
-            
-            #- print("JSON Server ({0}): {1}".format(self.__name, jsonLine))
-            #- this is for debugging only
-            
+            jsonLine = bytes(data).decode("utf-8", "backslashreplace")
+
+            # - print("JSON Server ({0}): {1}".format(self.__name, jsonLine))
+            # - this is for debugging only
+
             try:
                 clientDict = json.loads(jsonLine.strip())
             except (TypeError, ValueError) as err:
                 EricMessageBox.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/>{1}</p>""").format(
-                        str(err), Utilities.html_encode(jsonLine.strip())),
-                    EricMessageBox.Ok)
+                    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/>{1}</p>"""
+                    ).format(str(err), Utilities.html_encode(jsonLine.strip())),
+                    EricMessageBox.Ok,
+                )
                 return
-            
+
             self.handleCall(clientDict["method"], clientDict["params"])
-    
+
     def sendJson(self, command, params, flush=False, idString=""):
         """
         Public method to send a single command to a client.
-        
+
         @param command command name to be sent
         @type str
         @param params dictionary of named parameters for the command
@@ -184,8 +194,8 @@
             "method": command,
             "params": params,
         }
-        cmd = json.dumps(commandDict) + '\n'
-        
+        cmd = json.dumps(commandDict) + "\n"
+
         if idString:
             try:
                 connection = self.__connections[idString]
@@ -193,19 +203,20 @@
                 connection = None
         else:
             connection = self.__connection
-        
+
         if connection is not None:
-            data = cmd.encode('utf8', 'backslashreplace')
+            data = cmd.encode("utf8", "backslashreplace")
             length = "{0:09d}".format(len(data))
             connection.write(length.encode() + data)
             if flush:
                 connection.flush()
-    
-    def startClient(self, interpreter, clientScript, clientArgs, idString="",
-                    environment=None):
+
+    def startClient(
+        self, interpreter, clientScript, clientArgs, idString="", environment=None
+    ):
         """
         Public method to start a client process.
-        
+
         @param interpreter interpreter to be used for the client
         @type str
         @param clientScript path to the client script
@@ -221,12 +232,11 @@
         """
         if interpreter == "" or not Utilities.isinpath(interpreter):
             return False
-        
+
         exitCode = None
-        
+
         proc = QProcess()
-        proc.setProcessChannelMode(
-            QProcess.ProcessChannelMode.ForwardedChannels)
+        proc.setProcessChannelMode(QProcess.ProcessChannelMode.ForwardedChannels)
         if environment is not None:
             env = QProcessEnvironment()
             for key, value in list(environment.items()):
@@ -239,58 +249,57 @@
         proc.start(interpreter, args)
         if not proc.waitForStarted(10000):
             proc = None
-        
+
         if idString:
             self.__clientProcesses[idString] = proc
             if proc:
                 timer = QTimer()
                 timer.setSingleShot(True)
-                timer.start(30000)           # 30s timeout
-                while (
-                    idString not in self.connectionNames() and
-                    timer.isActive()
-                ):
+                timer.start(30000)  # 30s timeout
+                while idString not in self.connectionNames() and timer.isActive():
                     # Give the event loop the chance to process the new
                     # connection of the client (= slow start).
                     QCoreApplication.processEvents(
-                        QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
+                        QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents
+                    )
                     QThread.msleep(100)
-                    
+
                     # check if client exited prematurely
                     if proc.state() == QProcess.ProcessState.NotRunning:
                         exitCode = proc.exitCode()
                         proc = None
                         self.__clientProcesses[idString] = None
                         break
-                    
+
                     QThread.msleep(500)
         else:
             if proc:
                 timer = QTimer()
                 timer.setSingleShot(True)
-                timer.start(1000)           # 1s timeout
+                timer.start(1000)  # 1s timeout
                 while timer.isActive():
                     # check if client exited prematurely
                     QCoreApplication.processEvents(
-                        QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
+                        QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents
+                    )
                     QThread.msleep(100)
                     if proc.state() == QProcess.ProcessState.NotRunning:
                         exitCode = proc.exitCode()
                         proc = None
                         break
             self.__clientProcess = proc
-        
+
         return proc is not None, exitCode
-    
+
     def stopClient(self, idString=""):
         """
         Public method to stop a client process.
-        
+
         @param idString id of the client to be stopped
         @type str
         """
         self.sendJson("Exit", {}, flush=True, idString=idString)
-        
+
         if idString:
             try:
                 connection = self.__connections[idString]
@@ -300,17 +309,17 @@
             connection = self.__connection
         if connection is not None:
             connection.waitForDisconnected()
-        
+
         if idString:
             with contextlib.suppress(KeyError):
-                if self .__clientProcesses[idString] is not None:
-                    self .__clientProcesses[idString].close()
+                if self.__clientProcesses[idString] is not None:
+                    self.__clientProcesses[idString].close()
                 del self.__clientProcesses[idString]
         else:
             if self.__clientProcess is not None:
                 self.__clientProcess.close()
                 self.__clientProcess = None
-    
+
     def stopAllClients(self):
         """
         Public method to stop all clients.
@@ -318,18 +327,18 @@
         clientNames = self.connectionNames()[:]
         for clientName in clientNames:
             self.stopClient(clientName)
-    
+
     #######################################################################
     ## The following methods should be overridden by derived classes
     #######################################################################
-    
+
     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

eric ide

mercurial