--- a/src/eric7/RemoteServerInterface/EricServerInterface.py Sat Feb 10 11:28:58 2024 +0100 +++ b/src/eric7/RemoteServerInterface/EricServerInterface.py Sun Feb 11 18:35:44 2024 +0100 @@ -7,6 +7,7 @@ Module implementing the interface to the eric remote server. """ +import collections import json import struct import uuid @@ -85,6 +86,7 @@ self.__connection = None self.__callbacks = {} # callback references indexed by UUID + self.__messageQueue = collections.deque() self.connectionStateChanged.connect(self.__connectionStateChanged) @@ -97,23 +99,31 @@ @type str @return reference to the service interface @rtype QObject + @exception ValueError raised to indicate an unsupported server interface + was requested """ lname = name.lower() try: return self.__serviceInterfaces[lname] except KeyError: - # instantiate the service interface - if lname == "filesystem": - from .EricServerFileSystemInterface import EricServerFileSystemInterface - self.__serviceInterfaces[lname] = EricServerFileSystemInterface(self) - elif lname == "debugger": - from .EricServerDebuggerInterface import EricServerDebuggerInterface - self.__serviceInterfaces[lname] = EricServerDebuggerInterface(self) - elif lname == "project": - # TODO: 'Project Interface' not implemented yet - pass + if lname not in ("debugger", "filesystem", "project"): + raise ValueError(f"no such service supported ({name})") else: - raise ValueError(f"no such service supported ({name})") + # instantiate the service interface + if lname == "filesystem": + from .EricServerFileSystemInterface import ( # noqa: I101 + EricServerFileSystemInterface + ) + self.__serviceInterfaces[lname] = ( + EricServerFileSystemInterface(self) + ) + elif lname == "debugger": + from .EricServerDebuggerInterface import EricServerDebuggerInterface + # noqa: I101 + self.__serviceInterfaces[lname] = EricServerDebuggerInterface(self) + elif lname == "project": + # TODO: 'Project Interface' not implemented yet + pass return self.__serviceInterfaces[lname] @@ -123,7 +133,7 @@ def connectToServer(self, host, port=None, timeout=None): """ - Public method to connect to the given host and port + Public method to connect to the given host and port. @param host host name or IP address of the eric remote server @type str @@ -132,6 +142,8 @@ @param timeout timeout im seconds for the connection attempt (defaults to None) @type int (optional) + @return flag indicating success + @rtype bool """ if not bool(port): # None or 0 # use default port @@ -140,7 +152,7 @@ if not bool(timeout): # None or 0 # use configured default timeout timeout = Preferences.getEricServer("ConnectionTimeout") - timeout = timeout * 1000 # convert to milliseconds + timeout *= 1000 # convert to milliseconds if self.__connection is not None: self.disconnectFromServer() @@ -225,9 +237,6 @@ def __receiveJson(self): """ Private slot handling received data from the eric remote server. - - @param idString id of the connection - @type str """ while self.__connection and self.__connection.bytesAvailable(): header = self.__connection.read(struct.calcsize(b"!II")) @@ -238,7 +247,9 @@ maxSize = length - len(data) if self.__connection.bytesAvailable() < maxSize: self.__connection.waitForReadyRead(50) - data += self.__connection.read(maxSize) + newData = self.__connection.read(maxSize) + if newData: + data += newData if zlib.adler32(data) & 0xFFFFFFFF != datahash: # corrupted data -> discard and continue @@ -246,7 +257,7 @@ jsonString = data.decode("utf-8", "backslashreplace") - # - print("Remote Server Interface: {0}".format(jsonString)) + # - print("Remote Server Interface Receive: {0}".format(jsonString)) # - this is for debugging only try: @@ -268,29 +279,34 @@ return reqUuid = serverDataDict["uuid"] - try: + if reqUuid: + # It is a response to a synchronous request -> handle the call back + # immediately. self.__callbacks[reqUuid]( serverDataDict["reply"], serverDataDict["params"] ) del self.__callbacks[reqUuid] + else: + self.__messageQueue.append(serverDataDict) + + while self.__messageQueue: + serverDataDict = self.__messageQueue.popleft() # get the first message + try: + self.__categorySignalMapping[serverDataDict["category"]].emit( + serverDataDict["reply"], serverDataDict["params"] + ) except KeyError: - # no callback for this UUID exists, send a signal - try: - self.__categorySignalMapping[serverDataDict["category"]].emit( + if serverDataDict["category"] == EricRequestCategory.Error: + # handle server errors in here + self.__handleServerError( serverDataDict["reply"], serverDataDict["params"] ) - except KeyError: - if serverDataDict["category"] == EricRequestCategory.Error: - # handle server errors in here - self.__handleServerError( - serverDataDict["reply"], serverDataDict["params"] - ) - else: - self.remoteReply.emit( - serverDataDict["category"], - serverDataDict["reply"], - serverDataDict["params"], - ) + else: + self.remoteReply.emit( + serverDataDict["category"], + serverDataDict["reply"], + serverDataDict["params"], + ) def sendJson(self, category, request, params, callback=None, flush=False): """ @@ -309,9 +325,11 @@ (defaults to False) @type bool (optional) """ - reqUuid = str(uuid.uuid4()) if callback: + reqUuid = str(uuid.uuid4()) self.__callbacks[reqUuid] = callback + else: + reqUuid = "" serviceDict = { "jsonrpc": "2.0", @@ -322,6 +340,9 @@ } jsonString = json.dumps(serviceDict) + "\n" + # - print("Remote Server Interface Send: {0}".format(jsonString)) + # - this is for debugging only + if self.__connection is not None: data = jsonString.encode("utf8", "backslashreplace") header = struct.pack(b"!II", len(data), zlib.adler32(data) & 0xFFFFFFFF) @@ -368,7 +389,10 @@ @type dict @exception ValueError raised in case of an unsupported reply """ - if reply == "Versions": + if reply != "Versions": + raise ValueError(f"unsupported reply received ({reply})") + + else: versionText = self.tr("""<h2>Server Version Numbers</h2><table>""") # Python version @@ -389,16 +413,13 @@ versionText, ) - else: - raise ValueError(f"unsupported reply received ({reply})") - ####################################################################### ## Reply handler methods ####################################################################### def __handleServerError(self, reply, params): """ - Public method handling server error replies. + Private method handling server error replies. @param reply name of the error reply @type str @@ -532,7 +553,7 @@ @return the menu generated @rtype QMenu """ - self.__serverProfilesMenu = QMenu(self.tr("Connect to"))##, self.__ui) + self.__serverProfilesMenu = QMenu(self.tr("Connect to")) self.__serverProfilesMenu.aboutToShow.connect(self.__showServerProfilesMenu) self.__serverProfilesMenu.triggered.connect(self.__serverProfileTriggered) @@ -554,7 +575,6 @@ ##"Recent": self.recentMenu, } - return menu def initToolbar(self, toolbarManager):