RefactoringRope/JsonServer.py

Sat, 16 Sep 2017 16:40:50 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 16 Sep 2017 16:40:50 +0200
branch
server_client_variant
changeset 166
6fc202183b3b
parent 163
6a9e7b37a18b
child 168
53d76b4fc1ac
permissions
-rw-r--r--

Implemented the distributed 'query' methods.

# -*- 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, QThread
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("GetConfig", {})
    
    @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.sendJson("Exit", {})
        
        QThread.msleep(200)
        
        self.__clientProcess.close()
        self.__clientProcess = None

#
# eflag: noqa = M801

eric ide

mercurial