--- /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