--- a/eric6/Debugger/DebuggerInterfacePython.py Sun Jan 26 16:00:52 2020 +0100 +++ b/eric6/Debugger/DebuggerInterfacePython.py Sun Jan 26 19:29:06 2020 +0100 @@ -60,6 +60,8 @@ self.qsock = None self.queue = [] + self.__connections = {} + self.__pendingConnections = [] # set default values for capabilities of clients self.clientCapabilities = ClientDefaultCapabilities @@ -501,27 +503,40 @@ @param sock reference to the socket object (QTcpSocket) @return flag indicating success (boolean) """ - # If we already have a connection, refuse this one. It will be closed - # automatically. - if self.qsock is not None: - return False + sock.disconnected.connect(self.debugServer.startClient) + sock.readyRead.connect(lambda: self.__parseClientLine(sock)) - sock.disconnected.connect(self.debugServer.startClient) - sock.readyRead.connect(self.__parseClientLine) - - self.qsock = sock + if self.qsock is None: + # first connection is the main one + self.qsock = sock + self.__pendingConnections.append(sock) # Get the remote clients capabilities self.remoteCapabilities() return True + def __assignDebuggerId(self, sock, debuggerId): + """ + Private method to set the debugger id for a recent debugger connection + attempt. + + @param sock reference to the socket object + @type QTcpSocket + @param debuggerId id of the connected debug client + @type str + """ + if sock in self.__pendingConnections: + self.__connections[debuggerId] = sock + self.__pendingConnections.remove(sock) + def flush(self): """ Public slot to flush the queue. """ - # Send commands that were waiting for the connection. - for cmd in self.queue: - self.__writeJsonCommandToSocket(cmd) + if self.qsock: + # Send commands that were waiting for the connection. + for cmd in self.queue: + self.__writeJsonCommandToSocket(cmd, self.qsock) self.queue = [] @@ -535,18 +550,24 @@ if self.qsock is None: return - # do not want any slots called during shutdown - self.qsock.disconnected.disconnect(self.debugServer.startClient) - self.qsock.readyRead.disconnect(self.__parseClientLine) + for sock in ( + list(self.__connections.values()) + self.__pendingConnections + ): + # do not want any slots called during shutdown + sock.disconnected.disconnect() + sock.readyRead.disconnect() - # close down socket, and shut down client as well. - self.__sendJsonCommand("RequestShutdown", {}) - self.qsock.flush() - self.qsock.close() + # close down socket, and shut down client as well. + self.__sendJsonCommand("RequestShutdown", {}, sock=sock) + sock.flush() + sock.close() # reinitialize self.qsock = None self.queue = [] + + self.__pendingConnections = [] + self.__connections = {} def isConnected(self): """ @@ -1045,12 +1066,15 @@ "target": "child", }) - def __parseClientLine(self): + def __parseClientLine(self, sock): """ Private method to handle data from the client. + + @param sock reference to the socket to read data from + @type QTcpSocket """ - while self.qsock and self.qsock.canReadLine(): - qs = self.qsock.readLine() + while sock and sock.canReadLine(): + qs = sock.readLine() if self.codec is not None: line = self.codec.toUnicode(qs) else: @@ -1059,10 +1083,10 @@ logging.debug("<Debug-Server> %s", line) ## print("Server: ", line) ##debug - self.__handleJsonCommand(line) + self.__handleJsonCommand(line, sock) continue - def __handleJsonCommand(self, jsonStr): + def __handleJsonCommand(self, jsonStr, sock): """ Private method to handle a command or response serialized as a JSON string. @@ -1070,6 +1094,8 @@ @param jsonStr string containing the command or response received from the debug backend @type str + @param sock reference to the socket the data was received from + @type QTcpSocket """ import json @@ -1093,7 +1119,10 @@ method = commandDict["method"] params = commandDict["params"] - if method == "ClientOutput": + if method == "DebuggerId": + self.__assignDebuggerId(sock, params["id"]) + + elif method == "ClientOutput": self.debugServer.signalClientOutput(params["text"]) elif method in ["ResponseLine", "ResponseStack"]: @@ -1269,7 +1298,7 @@ elif method == "RequestForkTo": self.__askForkTo() - def __sendJsonCommand(self, command, params): + def __sendJsonCommand(self, command, params, debuggerId="", sock=None): """ Private method to send a single command to the client. @@ -1277,6 +1306,11 @@ @type str @param params dictionary of named parameters for the command @type dict + @param debuggerId id of the debug client to send the command to + @type str + @param sock reference to the socket object to be used (only used if + debuggerId is not given) + @type QTcpSocket """ import json @@ -1286,22 +1320,29 @@ "params": params, } cmd = json.dumps(commandDict) + '\n' - if self.qsock is not None: - self.__writeJsonCommandToSocket(cmd) + + if debuggerId and debuggerId in self.__connections: + sock = self.__connections[debuggerId] + elif sock is None and self.qsock is not None: + sock = self.qsock + if sock is not None: + self.__writeJsonCommandToSocket(cmd, sock) else: self.queue.append(cmd) - def __writeJsonCommandToSocket(self, cmd): + def __writeJsonCommandToSocket(self, cmd, sock): """ Private method to write a JSON command to the socket. @param cmd JSON command to be sent @type str + @param sock reference to the socket to write to + @type QTcpSocket """ data = cmd.encode('utf8', 'backslashreplace') length = "{0:09d}".format(len(data)) - self.qsock.write(length.encode() + data) - self.qsock.flush() + sock.write(length.encode() + data) + sock.flush() def createDebuggerInterfacePython2(debugServer, passive):