13 import os |
13 import os |
14 import shlex |
14 import shlex |
15 import struct |
15 import struct |
16 import zlib |
16 import zlib |
17 |
17 |
18 from PyQt6.QtCore import QObject, QProcess, QProcessEnvironment, QTimer |
18 from PyQt6.QtCore import QObject, QProcess, QProcessEnvironment, QTimer, pyqtSlot |
19 |
19 |
20 from eric7 import Preferences, Utilities |
20 from eric7 import Preferences, Utilities |
21 from eric7.EricWidgets import EricMessageBox |
21 from eric7.EricWidgets import EricMessageBox |
22 from eric7.EricWidgets.EricApplication import ericApp |
22 from eric7.EricWidgets.EricApplication import ericApp |
23 from eric7.Globals import getConfig |
23 from eric7.Globals import getConfig |
55 self.__ericServerDebuggerInterface = ericApp().getObject( |
55 self.__ericServerDebuggerInterface = ericApp().getObject( |
56 "EricServer" |
56 "EricServer" |
57 ).getServiceInterface("Debugger") |
57 ).getServiceInterface("Debugger") |
58 self.__ericServerDebuggerInterface.debugClientResponse.connect( |
58 self.__ericServerDebuggerInterface.debugClientResponse.connect( |
59 lambda jsonStr: self.handleJsonCommand(jsonStr, None) |
59 lambda jsonStr: self.handleJsonCommand(jsonStr, None) |
|
60 ) |
|
61 self.__ericServerDebuggerInterface.debugClientDisconnected.connect( |
|
62 self.__handleServerDebugClientDisconnected |
|
63 ) |
|
64 self.__ericServerDebuggerInterface.lastClientExited.connect( |
|
65 self.__handleServerLastClientExited |
60 ) |
66 ) |
61 except KeyError: |
67 except KeyError: |
62 self.__ericServerDebuggerInterface = None |
68 self.__ericServerDebuggerInterface = None |
63 |
69 |
64 self.debugServer = debugServer |
70 self.debugServer = debugServer |
202 @type str (optional) |
208 @type str (optional) |
203 @param configOverride dictionary containing the global config override data |
209 @param configOverride dictionary containing the global config override data |
204 (defaults to None) |
210 (defaults to None) |
205 @type dict (optional) |
211 @type dict (optional) |
206 @param startRemote flag indicating to start the client via an eric-ide server |
212 @param startRemote flag indicating to start the client via an eric-ide server |
207 (defaults to False) |
213 (defaults to None) |
208 @type bool (optional) |
214 @type bool (optional) |
209 @return client process object, a flag to indicate a network connection |
215 @return client process object, a flag to indicate a network connection |
210 and the name of the interpreter in case of a local execution |
216 and the name of the interpreter in case of a local execution |
211 @rtype tuple of (QProcess, bool, str) |
217 @rtype tuple of (QProcess, bool, str) |
212 """ |
218 """ |
213 global origPathEnv |
219 global origPathEnv |
214 |
220 |
215 if startRemote or venvName == self.debugServer.getEricServerEnvironmentString(): |
221 if ( |
|
222 ( |
|
223 startRemote is True |
|
224 or ( |
|
225 startRemote is None and ( |
|
226 venvName == self.debugServer.getEricServerEnvironmentString() |
|
227 or self.__ericServerDebugging |
|
228 ) |
|
229 ) |
|
230 ) |
|
231 and ericApp().getObject("EricServer").isServerConnected() |
|
232 ): |
216 # TODO change this once server environment definitions are supported |
233 # TODO change this once server environment definitions are supported |
217 startRemote = True |
234 startRemote = True |
218 venvName = self.debugServer.getEricServerEnvironmentString() |
235 venvName = self.debugServer.getEricServerEnvironmentString() |
219 interpreter = "" |
236 interpreter = "" |
220 else: |
237 else: |
240 ) |
257 ) |
241 return None, False, "" |
258 return None, False, "" |
242 |
259 |
243 self.__inShutdown = False |
260 self.__inShutdown = False |
244 |
261 |
|
262 self.__ericServerDebuggerInterface.stopClient() |
245 self.__ericServerDebugging = False |
263 self.__ericServerDebugging = False |
246 self.__ericServerDebuggerInterface.stopClient() |
|
247 self.__mainDebugger = None |
264 self.__mainDebugger = None |
248 |
265 |
249 redirect = ( |
266 redirect = ( |
250 str(configOverride["redirect"]) |
267 str(configOverride["redirect"]) |
251 if configOverride and configOverride["enable"] |
268 if configOverride and configOverride["enable"] |
332 ) |
349 ) |
333 return None, False, "" |
350 return None, False, "" |
334 |
351 |
335 elif startRemote and self.__ericServerDebuggerInterface is not None: |
352 elif startRemote and self.__ericServerDebuggerInterface is not None: |
336 # debugging via an eric-ide server |
353 # debugging via an eric-ide server |
337 ##self.__ericServerDebuggerInterface.stopClient() |
|
338 ##self.__mainDebugger = None |
|
339 ## |
|
340 self.translate = self.__ericServerTranslation |
354 self.translate = self.__ericServerTranslation |
341 self.__ericServerDebugging = True |
355 self.__ericServerDebugging = True |
342 |
356 |
343 args = [] |
357 args = [] |
344 if noencoding: |
358 if noencoding: |
486 @type bool (optional) |
500 @type bool (optional) |
487 @return client process object, a flag to indicate a network connection |
501 @return client process object, a flag to indicate a network connection |
488 and the name of the interpreter in case of a local execution |
502 and the name of the interpreter in case of a local execution |
489 @rtype tuple of (QProcess, bool, str) |
503 @rtype tuple of (QProcess, bool, str) |
490 """ |
504 """ |
|
505 # TODO: 'startRemoteForProject()' - implement the 'startRemote' logic like |
|
506 # for 'startRemote'. |
491 global origPathEnv |
507 global origPathEnv |
492 |
508 |
493 project = ericApp().getObject("Project") |
509 project = ericApp().getObject("Project") |
494 if not project.isDebugPropertiesLoaded(): |
510 if not project.isDebugPropertiesLoaded(): |
495 return None, self.__isNetworked, "" |
511 return None, self.__isNetworked, "" |
526 ) |
542 ) |
527 return None, self.__isNetworked, "" |
543 return None, self.__isNetworked, "" |
528 |
544 |
529 self.__inShutdown = False |
545 self.__inShutdown = False |
530 |
546 |
|
547 self.__ericServerDebuggerInterface.stopClient() |
531 self.__ericServerDebugging = False |
548 self.__ericServerDebugging = False |
532 self.__ericServerDebuggerInterface.stopClient() |
|
533 self.__mainDebugger = None |
549 self.__mainDebugger = None |
534 |
550 |
535 if project.getDebugProperty("REMOTEDEBUGGER"): |
551 if project.getDebugProperty("REMOTEDEBUGGER"): |
536 # remote debugging code |
552 # remote debugging code |
537 ipaddr = self.debugServer.getHostAddress(False) |
553 ipaddr = self.debugServer.getHostAddress(False) |
741 @type QTcpSocket |
757 @type QTcpSocket |
742 """ |
758 """ |
743 for debuggerId in list(self.__connections): |
759 for debuggerId in list(self.__connections): |
744 if self.__connections[debuggerId] is sock: |
760 if self.__connections[debuggerId] is sock: |
745 del self.__connections[debuggerId] |
761 del self.__connections[debuggerId] |
746 if debuggerId == self.__mainDebugger: |
762 self.__handleServerDebugClientDisconnected(debuggerId) |
747 self.__mainDebugger = None |
|
748 if debuggerId in self.__autoContinued: |
|
749 self.__autoContinued.remove(debuggerId) |
|
750 if not self.__inShutdown: |
|
751 with contextlib.suppress(RuntimeError): |
|
752 # can be ignored during a shutdown |
|
753 self.debugServer.signalClientDisconnected(debuggerId) |
|
754 break |
763 break |
755 else: |
764 else: |
756 if sock in self.__pendingConnections: |
765 if sock in self.__pendingConnections: |
757 self.__pendingConnections.remove(sock) |
766 self.__pendingConnections.remove(sock) |
758 |
767 |
759 if not self.__connections: |
768 if not self.__connections: |
760 # no active connections anymore |
769 # no active connections anymore |
|
770 self.__handleServerLastClientExited() |
|
771 |
|
772 @pyqtSlot(str) |
|
773 def __handleServerDebugClientDisconnected(self, debuggerId): |
|
774 """ |
|
775 Private slot handling the disconnect of a debug client. |
|
776 |
|
777 @param debuggerId ID of the disconnected debugger |
|
778 @type str |
|
779 """ |
|
780 if debuggerId == self.__mainDebugger: |
|
781 self.__mainDebugger = None |
|
782 if debuggerId in self.__autoContinued: |
|
783 self.__autoContinued.remove(debuggerId) |
|
784 if not self.__inShutdown: |
761 with contextlib.suppress(RuntimeError): |
785 with contextlib.suppress(RuntimeError): |
762 # debug server object might have been deleted already |
786 # can be ignored during a shutdown |
763 # ignore this |
787 self.debugServer.signalClientDisconnected(debuggerId) |
764 self.debugServer.signalLastClientExited() |
788 |
765 self.__autoContinued.clear() |
789 @pyqtSlot() |
766 if not self.__inShutdown: |
790 def __handleServerLastClientExited(self): |
767 self.debugServer.startClient() |
791 """ |
|
792 Private slot to handle the exit of the last debug client connected. |
|
793 """ |
|
794 with contextlib.suppress(RuntimeError): |
|
795 # debug server object might have been deleted already |
|
796 # ignore this |
|
797 self.debugServer.signalLastClientExited() |
|
798 self.__autoContinued.clear() |
|
799 if not self.__inShutdown: |
|
800 self.debugServer.startClient() |
768 |
801 |
769 def getDebuggerIds(self): |
802 def getDebuggerIds(self): |
770 """ |
803 """ |
771 Public method to return the IDs of the connected debugger backends. |
804 Public method to return the IDs of the connected debugger backends. |
772 |
805 |
888 @type bool |
921 @type bool |
889 @param reportAllExceptions flag indicating to report all exceptions |
922 @param reportAllExceptions flag indicating to report all exceptions |
890 instead of unhandled exceptions only |
923 instead of unhandled exceptions only |
891 @type bool |
924 @type bool |
892 """ |
925 """ |
|
926 if FileSystemUtilities.isPlainFileName(fn): |
|
927 fn = os.path.abspath(fn) |
|
928 |
893 self.__autoContinue = autoContinue |
929 self.__autoContinue = autoContinue |
894 self.__scriptName = os.path.abspath(fn) |
930 self.__scriptName = fn |
895 self.__isStepCommand = False |
931 self.__isStepCommand = False |
896 |
932 |
897 wd = self.translate(wd, False) |
933 wd = self.translate(wd, False) |
898 fn = self.translate(os.path.abspath(fn), False) |
934 fn = self.translate(fn, False) |
899 self.__sendJsonCommand( |
935 self.__sendJsonCommand( |
900 "RequestLoad", |
936 "RequestLoad", |
901 { |
937 { |
902 "workdir": wd, |
938 "workdir": wd, |
903 "filename": fn, |
939 "filename": fn, |
1120 @param cond condition of the breakpoint |
1156 @param cond condition of the breakpoint |
1121 @type str |
1157 @type str |
1122 @param temp flag indicating a temporary breakpoint |
1158 @param temp flag indicating a temporary breakpoint |
1123 @type bool |
1159 @type bool |
1124 """ |
1160 """ |
1125 debuggerList = [debuggerId] if debuggerId else list(self.__connections) |
1161 if debuggerId: |
|
1162 debuggerList = [debuggerId] |
|
1163 elif self.__ericServerDebugging: |
|
1164 debuggerList = ["<<all>>"] |
|
1165 else: |
|
1166 debuggerList = list(self.__connections) |
1126 for debuggerId in debuggerList: |
1167 for debuggerId in debuggerList: |
1127 self.__sendJsonCommand( |
1168 self.__sendJsonCommand( |
1128 "RequestBreakpoint", |
1169 "RequestBreakpoint", |
1129 { |
1170 { |
1130 "filename": self.translate(fn, False), |
1171 "filename": self.translate(fn, False), |
1147 @param line line number of the breakpoint |
1188 @param line line number of the breakpoint |
1148 @type int |
1189 @type int |
1149 @param enable flag indicating enabling or disabling a breakpoint |
1190 @param enable flag indicating enabling or disabling a breakpoint |
1150 @type bool |
1191 @type bool |
1151 """ |
1192 """ |
1152 debuggerList = [debuggerId] if debuggerId else list(self.__connections) |
1193 if debuggerId: |
|
1194 debuggerList = [debuggerId] |
|
1195 elif self.__ericServerDebugging: |
|
1196 debuggerList = ["<<all>>"] |
|
1197 else: |
|
1198 debuggerList = list(self.__connections) |
1153 for debuggerId in debuggerList: |
1199 for debuggerId in debuggerList: |
1154 self.__sendJsonCommand( |
1200 self.__sendJsonCommand( |
1155 "RequestBreakpointEnable", |
1201 "RequestBreakpointEnable", |
1156 { |
1202 { |
1157 "filename": self.translate(fn, False), |
1203 "filename": self.translate(fn, False), |
1172 @param line line number of the breakpoint |
1218 @param line line number of the breakpoint |
1173 @type int |
1219 @type int |
1174 @param count number of occurrences to ignore |
1220 @param count number of occurrences to ignore |
1175 @type int |
1221 @type int |
1176 """ |
1222 """ |
1177 debuggerList = [debuggerId] if debuggerId else list(self.__connections) |
1223 if debuggerId: |
|
1224 debuggerList = [debuggerId] |
|
1225 elif self.__ericServerDebugging: |
|
1226 debuggerList = ["<<all>>"] |
|
1227 else: |
|
1228 debuggerList = list(self.__connections) |
1178 for debuggerId in debuggerList: |
1229 for debuggerId in debuggerList: |
1179 self.__sendJsonCommand( |
1230 self.__sendJsonCommand( |
1180 "RequestBreakpointIgnore", |
1231 "RequestBreakpointIgnore", |
1181 { |
1232 { |
1182 "filename": self.translate(fn, False), |
1233 "filename": self.translate(fn, False), |
1197 @param setWatch flag indicating setting or resetting a watch expression |
1248 @param setWatch flag indicating setting or resetting a watch expression |
1198 @type bool |
1249 @type bool |
1199 @param temp flag indicating a temporary watch expression |
1250 @param temp flag indicating a temporary watch expression |
1200 @type bool |
1251 @type bool |
1201 """ |
1252 """ |
1202 debuggerList = [debuggerId] if debuggerId else list(self.__connections) |
1253 if debuggerId: |
|
1254 debuggerList = [debuggerId] |
|
1255 elif self.__ericServerDebugging: |
|
1256 debuggerList = ["<<all>>"] |
|
1257 else: |
|
1258 debuggerList = list(self.__connections) |
1203 for debuggerId in debuggerList: |
1259 for debuggerId in debuggerList: |
1204 # cond is combination of cond and special (s. watch expression |
1260 # cond is combination of cond and special (s. watch expression |
1205 # viewer) |
1261 # viewer) |
1206 self.__sendJsonCommand( |
1262 self.__sendJsonCommand( |
1207 "RequestWatch", |
1263 "RequestWatch", |
1222 @param cond expression of the watch expression |
1278 @param cond expression of the watch expression |
1223 @type str |
1279 @type str |
1224 @param enable flag indicating enabling or disabling a watch expression |
1280 @param enable flag indicating enabling or disabling a watch expression |
1225 @type bool |
1281 @type bool |
1226 """ |
1282 """ |
1227 debuggerList = [debuggerId] if debuggerId else list(self.__connections) |
1283 if debuggerId: |
|
1284 debuggerList = [debuggerId] |
|
1285 elif self.__ericServerDebugging: |
|
1286 debuggerList = ["<<all>>"] |
|
1287 else: |
|
1288 debuggerList = list(self.__connections) |
1228 for debuggerId in debuggerList: |
1289 for debuggerId in debuggerList: |
1229 # cond is combination of cond and special (s. watch expression |
1290 # cond is combination of cond and special (s. watch expression |
1230 # viewer) |
1291 # viewer) |
1231 self.__sendJsonCommand( |
1292 self.__sendJsonCommand( |
1232 "RequestWatchEnable", |
1293 "RequestWatchEnable", |
1247 @param cond expression of the watch expression |
1308 @param cond expression of the watch expression |
1248 @type str |
1309 @type str |
1249 @param count number of occurrences to ignore |
1310 @param count number of occurrences to ignore |
1250 @type int |
1311 @type int |
1251 """ |
1312 """ |
1252 debuggerList = [debuggerId] if debuggerId else list(self.__connections) |
1313 if debuggerId: |
|
1314 debuggerList = [debuggerId] |
|
1315 elif self.__ericServerDebugging: |
|
1316 debuggerList = ["<<all>>"] |
|
1317 else: |
|
1318 debuggerList = list(self.__connections) |
1253 for debuggerId in debuggerList: |
1319 for debuggerId in debuggerList: |
1254 # cond is combination of cond and special (s. watch expression |
1320 # cond is combination of cond and special (s. watch expression |
1255 # viewer) |
1321 # viewer) |
1256 self.__sendJsonCommand( |
1322 self.__sendJsonCommand( |
1257 "RequestWatchIgnore", |
1323 "RequestWatchIgnore", |
1700 ) |
1766 ) |
1701 |
1767 |
1702 elif method == "ResponseExit": |
1768 elif method == "ResponseExit": |
1703 self.__scriptName = "" |
1769 self.__scriptName = "" |
1704 self.debugServer.signalClientExit( |
1770 self.debugServer.signalClientExit( |
1705 params["program"], |
1771 self.translate(params["program"], True), |
1706 params["status"], |
1772 params["status"], |
1707 params["message"], |
1773 params["message"], |
1708 params["debuggerId"], |
1774 params["debuggerId"], |
1709 ) |
1775 ) |
1710 if params["debuggerId"] == self.__mainDebugger: |
1776 if params["debuggerId"] == self.__mainDebugger: |