58 self.__variant = pythonVariant |
58 self.__variant = pythonVariant |
59 self.__startedVenv = "" |
59 self.__startedVenv = "" |
60 |
60 |
61 self.qsock = None |
61 self.qsock = None |
62 self.queue = [] |
62 self.queue = [] |
|
63 self.__connections = {} |
|
64 self.__pendingConnections = [] |
63 |
65 |
64 # set default values for capabilities of clients |
66 # set default values for capabilities of clients |
65 self.clientCapabilities = ClientDefaultCapabilities |
67 self.clientCapabilities = ClientDefaultCapabilities |
66 |
68 |
67 # set translation function |
69 # set translation function |
499 Public slot to handle a new connection. |
501 Public slot to handle a new connection. |
500 |
502 |
501 @param sock reference to the socket object (QTcpSocket) |
503 @param sock reference to the socket object (QTcpSocket) |
502 @return flag indicating success (boolean) |
504 @return flag indicating success (boolean) |
503 """ |
505 """ |
504 # If we already have a connection, refuse this one. It will be closed |
|
505 # automatically. |
|
506 if self.qsock is not None: |
|
507 return False |
|
508 |
|
509 sock.disconnected.connect(self.debugServer.startClient) |
506 sock.disconnected.connect(self.debugServer.startClient) |
510 sock.readyRead.connect(self.__parseClientLine) |
507 sock.readyRead.connect(lambda: self.__parseClientLine(sock)) |
511 |
508 |
512 self.qsock = sock |
509 if self.qsock is None: |
|
510 # first connection is the main one |
|
511 self.qsock = sock |
|
512 self.__pendingConnections.append(sock) |
513 |
513 |
514 # Get the remote clients capabilities |
514 # Get the remote clients capabilities |
515 self.remoteCapabilities() |
515 self.remoteCapabilities() |
516 return True |
516 return True |
517 |
517 |
|
518 def __assignDebuggerId(self, sock, debuggerId): |
|
519 """ |
|
520 Private method to set the debugger id for a recent debugger connection |
|
521 attempt. |
|
522 |
|
523 @param sock reference to the socket object |
|
524 @type QTcpSocket |
|
525 @param debuggerId id of the connected debug client |
|
526 @type str |
|
527 """ |
|
528 if sock in self.__pendingConnections: |
|
529 self.__connections[debuggerId] = sock |
|
530 self.__pendingConnections.remove(sock) |
|
531 |
518 def flush(self): |
532 def flush(self): |
519 """ |
533 """ |
520 Public slot to flush the queue. |
534 Public slot to flush the queue. |
521 """ |
535 """ |
522 # Send commands that were waiting for the connection. |
536 if self.qsock: |
523 for cmd in self.queue: |
537 # Send commands that were waiting for the connection. |
524 self.__writeJsonCommandToSocket(cmd) |
538 for cmd in self.queue: |
|
539 self.__writeJsonCommandToSocket(cmd, self.qsock) |
525 |
540 |
526 self.queue = [] |
541 self.queue = [] |
527 |
542 |
528 def shutdown(self): |
543 def shutdown(self): |
529 """ |
544 """ |
533 the debug client. (Needed on Win OS) |
548 the debug client. (Needed on Win OS) |
534 """ |
549 """ |
535 if self.qsock is None: |
550 if self.qsock is None: |
536 return |
551 return |
537 |
552 |
538 # do not want any slots called during shutdown |
553 for sock in ( |
539 self.qsock.disconnected.disconnect(self.debugServer.startClient) |
554 list(self.__connections.values()) + self.__pendingConnections |
540 self.qsock.readyRead.disconnect(self.__parseClientLine) |
555 ): |
541 |
556 # do not want any slots called during shutdown |
542 # close down socket, and shut down client as well. |
557 sock.disconnected.disconnect() |
543 self.__sendJsonCommand("RequestShutdown", {}) |
558 sock.readyRead.disconnect() |
544 self.qsock.flush() |
559 |
545 self.qsock.close() |
560 # close down socket, and shut down client as well. |
|
561 self.__sendJsonCommand("RequestShutdown", {}, sock=sock) |
|
562 sock.flush() |
|
563 sock.close() |
546 |
564 |
547 # reinitialize |
565 # reinitialize |
548 self.qsock = None |
566 self.qsock = None |
549 self.queue = [] |
567 self.queue = [] |
|
568 |
|
569 self.__pendingConnections = [] |
|
570 self.__connections = {} |
550 |
571 |
551 def isConnected(self): |
572 def isConnected(self): |
552 """ |
573 """ |
553 Public method to test, if a debug client has connected. |
574 Public method to test, if a debug client has connected. |
554 |
575 |
1043 else: |
1064 else: |
1044 self.__sendJsonCommand("ResponseForkTo", { |
1065 self.__sendJsonCommand("ResponseForkTo", { |
1045 "target": "child", |
1066 "target": "child", |
1046 }) |
1067 }) |
1047 |
1068 |
1048 def __parseClientLine(self): |
1069 def __parseClientLine(self, sock): |
1049 """ |
1070 """ |
1050 Private method to handle data from the client. |
1071 Private method to handle data from the client. |
1051 """ |
1072 |
1052 while self.qsock and self.qsock.canReadLine(): |
1073 @param sock reference to the socket to read data from |
1053 qs = self.qsock.readLine() |
1074 @type QTcpSocket |
|
1075 """ |
|
1076 while sock and sock.canReadLine(): |
|
1077 qs = sock.readLine() |
1054 if self.codec is not None: |
1078 if self.codec is not None: |
1055 line = self.codec.toUnicode(qs) |
1079 line = self.codec.toUnicode(qs) |
1056 else: |
1080 else: |
1057 line = bytes(qs).decode() |
1081 line = bytes(qs).decode() |
1058 |
1082 |
1059 logging.debug("<Debug-Server> %s", line) |
1083 logging.debug("<Debug-Server> %s", line) |
1060 ## print("Server: ", line) ##debug |
1084 ## print("Server: ", line) ##debug |
1061 |
1085 |
1062 self.__handleJsonCommand(line) |
1086 self.__handleJsonCommand(line, sock) |
1063 continue |
1087 continue |
1064 |
1088 |
1065 def __handleJsonCommand(self, jsonStr): |
1089 def __handleJsonCommand(self, jsonStr, sock): |
1066 """ |
1090 """ |
1067 Private method to handle a command or response serialized as a |
1091 Private method to handle a command or response serialized as a |
1068 JSON string. |
1092 JSON string. |
1069 |
1093 |
1070 @param jsonStr string containing the command or response received |
1094 @param jsonStr string containing the command or response received |
1071 from the debug backend |
1095 from the debug backend |
1072 @type str |
1096 @type str |
|
1097 @param sock reference to the socket the data was received from |
|
1098 @type QTcpSocket |
1073 """ |
1099 """ |
1074 import json |
1100 import json |
1075 |
1101 |
1076 try: |
1102 try: |
1077 commandDict = json.loads(jsonStr.strip()) |
1103 commandDict = json.loads(jsonStr.strip()) |
1091 return |
1117 return |
1092 |
1118 |
1093 method = commandDict["method"] |
1119 method = commandDict["method"] |
1094 params = commandDict["params"] |
1120 params = commandDict["params"] |
1095 |
1121 |
1096 if method == "ClientOutput": |
1122 if method == "DebuggerId": |
|
1123 self.__assignDebuggerId(sock, params["id"]) |
|
1124 |
|
1125 elif method == "ClientOutput": |
1097 self.debugServer.signalClientOutput(params["text"]) |
1126 self.debugServer.signalClientOutput(params["text"]) |
1098 |
1127 |
1099 elif method in ["ResponseLine", "ResponseStack"]: |
1128 elif method in ["ResponseLine", "ResponseStack"]: |
1100 # Check if obsolet thread was clicked |
1129 # Check if obsolet thread was clicked |
1101 if params["stack"] == []: |
1130 if params["stack"] == []: |
1267 params["testname"], params["id"]) |
1296 params["testname"], params["id"]) |
1268 |
1297 |
1269 elif method == "RequestForkTo": |
1298 elif method == "RequestForkTo": |
1270 self.__askForkTo() |
1299 self.__askForkTo() |
1271 |
1300 |
1272 def __sendJsonCommand(self, command, params): |
1301 def __sendJsonCommand(self, command, params, debuggerId="", sock=None): |
1273 """ |
1302 """ |
1274 Private method to send a single command to the client. |
1303 Private method to send a single command to the client. |
1275 |
1304 |
1276 @param command command name to be sent |
1305 @param command command name to be sent |
1277 @type str |
1306 @type str |
1278 @param params dictionary of named parameters for the command |
1307 @param params dictionary of named parameters for the command |
1279 @type dict |
1308 @type dict |
|
1309 @param debuggerId id of the debug client to send the command to |
|
1310 @type str |
|
1311 @param sock reference to the socket object to be used (only used if |
|
1312 debuggerId is not given) |
|
1313 @type QTcpSocket |
1280 """ |
1314 """ |
1281 import json |
1315 import json |
1282 |
1316 |
1283 commandDict = { |
1317 commandDict = { |
1284 "jsonrpc": "2.0", |
1318 "jsonrpc": "2.0", |
1285 "method": command, |
1319 "method": command, |
1286 "params": params, |
1320 "params": params, |
1287 } |
1321 } |
1288 cmd = json.dumps(commandDict) + '\n' |
1322 cmd = json.dumps(commandDict) + '\n' |
1289 if self.qsock is not None: |
1323 |
1290 self.__writeJsonCommandToSocket(cmd) |
1324 if debuggerId and debuggerId in self.__connections: |
|
1325 sock = self.__connections[debuggerId] |
|
1326 elif sock is None and self.qsock is not None: |
|
1327 sock = self.qsock |
|
1328 if sock is not None: |
|
1329 self.__writeJsonCommandToSocket(cmd, sock) |
1291 else: |
1330 else: |
1292 self.queue.append(cmd) |
1331 self.queue.append(cmd) |
1293 |
1332 |
1294 def __writeJsonCommandToSocket(self, cmd): |
1333 def __writeJsonCommandToSocket(self, cmd, sock): |
1295 """ |
1334 """ |
1296 Private method to write a JSON command to the socket. |
1335 Private method to write a JSON command to the socket. |
1297 |
1336 |
1298 @param cmd JSON command to be sent |
1337 @param cmd JSON command to be sent |
1299 @type str |
1338 @type str |
|
1339 @param sock reference to the socket to write to |
|
1340 @type QTcpSocket |
1300 """ |
1341 """ |
1301 data = cmd.encode('utf8', 'backslashreplace') |
1342 data = cmd.encode('utf8', 'backslashreplace') |
1302 length = "{0:09d}".format(len(data)) |
1343 length = "{0:09d}".format(len(data)) |
1303 self.qsock.write(length.encode() + data) |
1344 sock.write(length.encode() + data) |
1304 self.qsock.flush() |
1345 sock.flush() |
1305 |
1346 |
1306 |
1347 |
1307 def createDebuggerInterfacePython2(debugServer, passive): |
1348 def createDebuggerInterfacePython2(debugServer, passive): |
1308 """ |
1349 """ |
1309 Module function to create a debugger interface instance. |
1350 Module function to create a debugger interface instance. |