10 import contextlib |
10 import contextlib |
11 import json |
11 import json |
12 import logging |
12 import logging |
13 import os |
13 import os |
14 import shlex |
14 import shlex |
|
15 import struct |
|
16 import zlib |
15 |
17 |
16 from PyQt6.QtCore import QObject, QProcess, QProcessEnvironment, QTimer |
18 from PyQt6.QtCore import QObject, QProcess, QProcessEnvironment, QTimer |
17 |
19 |
18 from eric7 import Preferences, Utilities |
20 from eric7 import Preferences, Utilities |
19 from eric7.EricWidgets import EricMessageBox |
21 from eric7.EricWidgets import EricMessageBox |
51 self.debugServer = debugServer |
53 self.debugServer = debugServer |
52 self.passive = passive |
54 self.passive = passive |
53 self.process = None |
55 self.process = None |
54 self.__startedVenv = "" |
56 self.__startedVenv = "" |
55 |
57 |
56 self.queue = [] |
58 self.__commandQueue = [] |
57 self.__mainDebugger = None |
59 self.__mainDebugger = None |
58 self.__connections = {} |
60 self.__connections = {} |
59 self.__pendingConnections = [] |
61 self.__pendingConnections = [] |
60 self.__inShutdown = False |
62 self.__inShutdown = False |
61 |
63 |
206 str(configOverride["redirect"]) |
208 str(configOverride["redirect"]) |
207 if configOverride and configOverride["enable"] |
209 if configOverride and configOverride["enable"] |
208 else str(Preferences.getDebugger("Python3Redirect")) |
210 else str(Preferences.getDebugger("Python3Redirect")) |
209 ) |
211 ) |
210 noencoding = ( |
212 noencoding = ( |
211 Preferences.getDebugger("Python3NoEncoding") and "--no-encoding" or "" |
213 "--no-encoding" if Preferences.getDebugger("Python3NoEncoding") else "" |
212 ) |
214 ) |
213 multiprocessEnabled = ( |
215 multiprocessEnabled = ( |
214 "--multiprocess" if Preferences.getDebugger("MultiProcessEnabled") else "" |
216 "--multiprocess" if Preferences.getDebugger("MultiProcessEnabled") else "" |
215 ) |
217 ) |
216 callTraceOptimization = ( |
218 callTraceOptimization = ( |
704 Private slot to flush the queue. |
706 Private slot to flush the queue. |
705 """ |
707 """ |
706 if self.__mainDebugger: |
708 if self.__mainDebugger: |
707 # Send commands that were waiting for the connection. |
709 # Send commands that were waiting for the connection. |
708 conn = self.__connections[self.__mainDebugger] |
710 conn = self.__connections[self.__mainDebugger] |
709 for cmd in self.queue: |
711 for jsonStr in self.__commandQueue: |
710 self.__writeJsonCommandToSocket(cmd, conn) |
712 self.__writeJsonCommandToSocket(jsonStr, conn) |
711 |
713 |
712 self.queue = [] |
714 self.__commandQueue.clear() |
713 |
715 |
714 def shutdown(self): |
716 def shutdown(self): |
715 """ |
717 """ |
716 Public method to cleanly shut down. |
718 Public method to cleanly shut down. |
717 |
719 |
730 while self.__pendingConnections: |
732 while self.__pendingConnections: |
731 sock = self.__pendingConnections.pop() |
733 sock = self.__pendingConnections.pop() |
732 self.__shutdownSocket(sock) |
734 self.__shutdownSocket(sock) |
733 |
735 |
734 # reinitialize |
736 # reinitialize |
735 self.queue = [] |
737 self.__commandQueue.clear() |
736 |
738 |
737 self.__mainDebugger = None |
739 self.__mainDebugger = None |
738 |
740 |
739 def __shutdownSocket(self, sock): |
741 def __shutdownSocket(self, sock): |
740 """ |
742 """ |
1399 Private method to handle data from the client. |
1401 Private method to handle data from the client. |
1400 |
1402 |
1401 @param sock reference to the socket to read data from |
1403 @param sock reference to the socket to read data from |
1402 @type QTcpSocket |
1404 @type QTcpSocket |
1403 """ |
1405 """ |
1404 while sock and sock.canReadLine(): |
1406 while sock and sock.bytesAvailable(): |
1405 qs = sock.readLine() |
1407 header = sock.read(struct.calcsize(b"!II")) |
1406 line = bytes(qs).decode(encoding=Preferences.getSystem("StringEncoding")) |
1408 length, datahash = struct.unpack(b"!II", header) |
1407 |
1409 |
1408 logging.debug("<Debug-Server> %s", line) |
1410 data = bytearray() |
1409 ##print("Server: ", line) ## debug # __IGNORE_WARNING_M891__ |
1411 while len(data) < length: |
1410 |
1412 maxSize = length - len(data) |
1411 self.__handleJsonCommand(line, sock) |
1413 if sock.bytesAvailable() < maxSize: |
|
1414 sock.waitForReadyRead(50) |
|
1415 data += sock.read(maxSize) |
|
1416 |
|
1417 if zlib.adler32(data) & 0xFFFFFFFF != datahash: |
|
1418 # corrupted data -> discard and continue |
|
1419 continue |
|
1420 |
|
1421 jsonStr = data.decode("utf-8", "backslashreplace") |
|
1422 |
|
1423 logging.debug("<Debug-Server> %s", jsonStr) |
|
1424 ##print("Server: ", jsonStr) ## debug # __IGNORE_WARNING_M891__ |
|
1425 |
|
1426 self.__handleJsonCommand(jsonStr, sock) |
1412 |
1427 |
1413 def __handleJsonCommand(self, jsonStr, sock): |
1428 def __handleJsonCommand(self, jsonStr, sock): |
1414 """ |
1429 """ |
1415 Private method to handle a command or response serialized as a |
1430 Private method to handle a command or response serialized as a |
1416 JSON string. |
1431 JSON string. |
1644 commandDict = { |
1659 commandDict = { |
1645 "jsonrpc": "2.0", |
1660 "jsonrpc": "2.0", |
1646 "method": command, |
1661 "method": command, |
1647 "params": params, |
1662 "params": params, |
1648 } |
1663 } |
1649 cmd = json.dumps(commandDict) + "\n" |
1664 jsonStr = json.dumps(commandDict) |
1650 |
1665 |
1651 if debuggerId and debuggerId in self.__connections: |
1666 if debuggerId and debuggerId in self.__connections: |
1652 sock = self.__connections[debuggerId] |
1667 sock = self.__connections[debuggerId] |
1653 elif sock is None and self.__mainDebugger is not None: |
1668 elif sock is None and self.__mainDebugger is not None: |
1654 sock = self.__connections[self.__mainDebugger] |
1669 sock = self.__connections[self.__mainDebugger] |
1655 if sock is not None: |
1670 if sock is not None: |
1656 self.__writeJsonCommandToSocket(cmd, sock) |
1671 self.__writeJsonCommandToSocket(jsonStr, sock) |
1657 else: |
1672 else: |
1658 self.queue.append(cmd) |
1673 self.__commandQueue.append(jsonStr) |
1659 |
1674 |
1660 def __writeJsonCommandToSocket(self, cmd, sock): |
1675 def __writeJsonCommandToSocket(self, jsonCommand, sock): |
1661 """ |
1676 """ |
1662 Private method to write a JSON command to the socket. |
1677 Private method to write a JSON command to the socket. |
1663 |
1678 |
1664 @param cmd JSON command to be sent |
1679 @param jsonCommand JSON encoded command to be sent |
1665 @type str |
1680 @type str |
1666 @param sock reference to the socket to write to |
1681 @param sock reference to the socket to write to |
1667 @type QTcpSocket |
1682 @type QTcpSocket |
1668 """ |
1683 """ |
1669 data = cmd.encode("utf8", "backslashreplace") |
1684 data = jsonCommand.encode("utf8", "backslashreplace") |
1670 length = "{0:09d}".format(len(data)) |
1685 header = struct.pack(b"!II", len(data), zlib.adler32(data) & 0xFFFFFFFF) |
1671 sock.write(length.encode() + data) |
1686 sock.write(header) |
|
1687 sock.write(data) |
1672 sock.flush() |
1688 sock.flush() |
1673 |
1689 |
1674 |
1690 |
1675 def createDebuggerInterfacePython3(debugServer, passive): |
1691 def createDebuggerInterfacePython3(debugServer, passive): |
1676 """ |
1692 """ |