eric7/E5Network/E5JsonServer.py

branch
eric7
changeset 8354
12ebd3934fef
parent 8353
799196d0b05d
child 8355
8a7677a63c8d
diff -r 799196d0b05d -r 12ebd3934fef eric7/E5Network/E5JsonServer.py
--- a/eric7/E5Network/E5JsonServer.py	Sat May 22 12:54:57 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,335 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2017 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Module implementing the JSON based server base class.
-"""
-
-import contextlib
-import json
-
-from PyQt6.QtCore import (
-    pyqtSlot, QProcess, QProcessEnvironment, QCoreApplication, QEventLoop,
-    QTimer
-)
-from PyQt6.QtNetwork import QTcpServer, QHostAddress
-
-from E5Gui import E5MessageBox
-
-import Preferences
-import Utilities
-
-
-class E5JsonServer(QTcpServer):
-    """
-    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
-        @type bool
-        @param parent parent object
-        @type QObject
-        """
-        super().__init__(parent)
-        
-        self.__name = name
-        self.__multiplex = multiplex
-        if self.__multiplex:
-            self.__clientProcesses = {}
-            self.__connections = {}
-        else:
-            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 client is started external in debugger.
-        print('JSON server ({1}) listening on: {0:d}'   # __IGNORE_WARNING__
-              .format(port, self.__name))
-    
-    @pyqtSlot()
-    def handleNewConnection(self):
-        """
-        Public slot for new incoming connections from a client.
-        """
-        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()
-            if idString in self.__connections:
-                self.__connections[idString].close()
-            self.__connections[idString] = connection
-        else:
-            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))
-    
-    @pyqtSlot()
-    def __handleDisconnect(self, idString):
-        """
-        Private slot handling a disconnect of the client.
-        
-        @param idString id of the connection been disconnected
-        @type str
-        """
-        if idString:
-            if idString in self.__connections:
-                self.__connections[idString].close()
-                del self.__connections[idString]
-        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
-        """
-        if self.__multiplex:
-            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 been disconnected
-        @type str
-        """
-        if idString:
-            try:
-                connection = self.__connections[idString]
-            except KeyError:
-                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
-            
-            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/>{1}</p>""").format(
-                        str(err), Utilities.html_encode(jsonLine.strip())),
-                    E5MessageBox.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
-        @type dict
-        @param flush flag indicating to flush the data to the socket
-        @type bool
-        @param idString id of the connection to send data to
-        @type str
-        """
-        commandDict = {
-            "jsonrpc": "2.0",
-            "method": command,
-            "params": params,
-        }
-        cmd = json.dumps(commandDict) + '\n'
-        
-        if idString:
-            try:
-                connection = self.__connections[idString]
-            except KeyError:
-                connection = None
-        else:
-            connection = self.__connection
-        
-        if connection is not None:
-            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):
-        """
-        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
-        @type str
-        @param clientArgs list of arguments for the client
-        @param idString id of the client to be started
-        @type str
-        @param environment dictionary of environment settings to pass
-        @type dict
-        @return flag indicating a successful client start and the exit code
-            in case of an issue
-        @rtype bool, int
-        """
-        if interpreter == "" or not Utilities.isinpath(interpreter):
-            return False
-        
-        exitCode = None
-        
-        proc = QProcess()
-        proc.setProcessChannelMode(
-            QProcess.ProcessChannelMode.ForwardedChannels)
-        if environment is not None:
-            env = QProcessEnvironment()
-            for key, value in list(environment.items()):
-                env.insert(key, value)
-            proc.setProcessEnvironment(env)
-        args = [clientScript, self.__hostAddress, str(self.serverPort())]
-        if idString:
-            args.append(idString)
-        args.extend(clientArgs)
-        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()
-                ):
-                    # Give the event loop the chance to process the new
-                    # connection of the client (= slow start).
-                    QCoreApplication.processEvents(
-                        QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
-                    
-                    # check if client exited prematurely
-                    if proc.state() == QProcess.ProcessState.NotRunning:
-                        exitCode = proc.exitCode()
-                        proc = None
-                        self.__clientProcesses[idString] = None
-                        break
-        else:
-            if proc:
-                timer = QTimer()
-                timer.setSingleShot(True)
-                timer.start(1000)           # 1s timeout
-                while timer.isActive():
-                    # check if client exited prematurely
-                    QCoreApplication.processEvents(
-                        QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
-                    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]
-            except KeyError:
-                connection = None
-        else:
-            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()
-                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.
-        """
-        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
-        @type dict
-        """
-        pass

eric ide

mercurial