47 |
47 |
48 self.__isNetworked = True |
48 self.__isNetworked = True |
49 self.__autoContinue = False |
49 self.__autoContinue = False |
50 self.__autoContinued = [] |
50 self.__autoContinued = [] |
51 self.__isStepCommand = False |
51 self.__isStepCommand = False |
|
52 |
|
53 self.__ericServerDebugging = False # are we debugging via the eric-ide server? |
|
54 try: |
|
55 self.__ericServerDebuggerInterface = ericApp().getObject( |
|
56 "EricServer" |
|
57 ).getServiceInterface("Debugger") |
|
58 self.__ericServerDebuggerInterface.debugClientResponse.connect( |
|
59 lambda jsonStr: self.handleJsonCommand(jsonStr, None) |
|
60 ) |
|
61 except KeyError: |
|
62 self.__ericServerDebuggerInterface = None |
52 |
63 |
53 self.debugServer = debugServer |
64 self.debugServer = debugServer |
54 self.passive = passive |
65 self.passive = passive |
55 self.process = None |
66 self.process = None |
56 self.__startedVenv = "" |
67 self.__startedVenv = "" |
100 Private method to perform the path translation. |
111 Private method to perform the path translation. |
101 |
112 |
102 @param fn filename to be translated |
113 @param fn filename to be translated |
103 @type str |
114 @type str |
104 @param remote2local flag indicating the direction of translation |
115 @param remote2local flag indicating the direction of translation |
105 (False = local to remote, True = remote to local [default]) |
116 (False = local to remote, True = remote to local) (defaults to True) |
106 @type bool |
117 @type bool (optional) |
107 @return translated filename |
118 @return translated filename |
108 @rtype str |
119 @rtype str |
109 """ |
120 """ |
110 if remote2local: |
121 if remote2local: |
111 path = fn.replace(self.translateRemote, self.translateLocal) |
122 path = fn.replace(self.translateRemote, self.translateLocal) |
116 if not self.translateRemoteWindows: |
127 if not self.translateRemoteWindows: |
117 path = path.replace("\\", "/") |
128 path = path.replace("\\", "/") |
118 |
129 |
119 return path |
130 return path |
120 |
131 |
121 def __startProcess(self, program, arguments, environment=None, workingDir=None): |
132 def __ericServerTranslation(self, fn, remote2local=True): |
|
133 """ |
|
134 Private method to perform the eric-ide server path translation. |
|
135 |
|
136 @param fn filename to be translated |
|
137 @type str |
|
138 @param remote2local flag indicating the direction of translation |
|
139 (False = local to remote, True = remote to local) (defaults to True) |
|
140 @type bool (optional) |
|
141 @return translated filename |
|
142 @rtype str |
|
143 """ |
|
144 if remote2local: |
|
145 return FileSystemUtilities.remoteFileName(fn) |
|
146 else: |
|
147 return FileSystemUtilities.plainFileName(fn) |
|
148 |
|
149 def __startProcess(self, program, arguments, environment=None, workingDir=""): |
122 """ |
150 """ |
123 Private method to start the debugger client process. |
151 Private method to start the debugger client process. |
124 |
152 |
125 @param program name of the executable to start |
153 @param program name of the executable to start |
126 @type str |
154 @type str |
167 @type bool |
196 @type bool |
168 @param venvName name of the virtual environment to be used |
197 @param venvName name of the virtual environment to be used |
169 @type str |
198 @type str |
170 @param originalPathString original PATH environment variable |
199 @param originalPathString original PATH environment variable |
171 @type str |
200 @type str |
172 @param workingDir directory to start the debugger client in |
201 @param workingDir directory to start the debugger client in (defaults to "") |
173 @type str |
202 @type str (optional) |
174 @param configOverride dictionary containing the global config override |
203 @param configOverride dictionary containing the global config override data |
175 data |
204 (defaults to None) |
176 @type dict |
205 @type dict (optional) |
|
206 @param startRemote flag indicating to start the client via an eric-ide server |
|
207 (defaults to False) |
|
208 @type bool (optional) |
177 @return client process object, a flag to indicate a network connection |
209 @return client process object, a flag to indicate a network connection |
178 and the name of the interpreter in case of a local execution |
210 and the name of the interpreter in case of a local execution |
179 @rtype tuple of (QProcess, bool, str) |
211 @rtype tuple of (QProcess, bool, str) |
180 """ |
212 """ |
181 global origPathEnv |
213 global origPathEnv |
182 |
214 |
183 if not venvName: |
215 if startRemote or venvName == self.debugServer.getEricServerEnvironmentString(): |
184 venvName = Preferences.getDebugger("Python3VirtualEnv") |
216 # TODO change this once server environment definitions are supported |
185 if venvName == self.debugServer.getProjectEnvironmentString(): |
217 startRemote = True |
186 project = ericApp().getObject("Project") |
218 venvName = self.debugServer.getEricServerEnvironmentString() |
187 venvName = project.getProjectVenv() |
219 interpreter = "" |
188 execPath = project.getProjectExecPath() |
|
189 interpreter = project.getProjectInterpreter() |
|
190 else: |
220 else: |
191 venvManager = ericApp().getObject("VirtualEnvManager") |
221 if not venvName: |
192 interpreter = venvManager.getVirtualenvInterpreter(venvName) |
222 venvName = Preferences.getDebugger("Python3VirtualEnv") |
193 execPath = venvManager.getVirtualenvExecPath(venvName) |
223 if venvName == self.debugServer.getProjectEnvironmentString(): |
194 if interpreter == "": |
224 project = ericApp().getObject("Project") |
195 # use the interpreter used to run eric for identical variants |
225 venvName = project.getProjectVenv() |
196 interpreter = PythonUtilities.getPythonExecutable() |
226 execPath = project.getProjectExecPath() |
197 if interpreter == "": |
227 interpreter = project.getProjectInterpreter() |
198 EricMessageBox.critical( |
228 else: |
199 None, |
229 venvManager = ericApp().getObject("VirtualEnvManager") |
200 self.tr("Start Debugger"), |
230 interpreter = venvManager.getVirtualenvInterpreter(venvName) |
201 self.tr("""<p>No suitable Python3 environment configured.</p>"""), |
231 execPath = venvManager.getVirtualenvExecPath(venvName) |
202 ) |
232 if interpreter == "": |
203 return None, False, "" |
233 # use the interpreter used to run eric for identical variants |
|
234 interpreter = PythonUtilities.getPythonExecutable() |
|
235 if interpreter == "": |
|
236 EricMessageBox.critical( |
|
237 None, |
|
238 self.tr("Start Debugger"), |
|
239 self.tr("""<p>No suitable Python3 environment configured.</p>"""), |
|
240 ) |
|
241 return None, False, "" |
204 |
242 |
205 self.__inShutdown = False |
243 self.__inShutdown = False |
|
244 |
|
245 self.__ericServerDebugging = False |
|
246 self.__ericServerDebuggerInterface.stopClient() |
|
247 self.__mainDebugger = None |
206 |
248 |
207 redirect = ( |
249 redirect = ( |
208 str(configOverride["redirect"]) |
250 str(configOverride["redirect"]) |
209 if configOverride and configOverride["enable"] |
251 if configOverride and configOverride["enable"] |
210 else str(Preferences.getDebugger("Python3Redirect")) |
252 else str(Preferences.getDebugger("Python3Redirect")) |
288 " login was given.</p>" |
330 " login was given.</p>" |
289 ), |
331 ), |
290 ) |
332 ) |
291 return None, False, "" |
333 return None, False, "" |
292 |
334 |
|
335 elif startRemote and self.__ericServerDebuggerInterface is not None: |
|
336 # debugging via an eric-ide server |
|
337 ##self.__ericServerDebuggerInterface.stopClient() |
|
338 ##self.__mainDebugger = None |
|
339 ## |
|
340 self.translate = self.__ericServerTranslation |
|
341 self.__ericServerDebugging = True |
|
342 |
|
343 args = [] |
|
344 if noencoding: |
|
345 args.append(noencoding) |
|
346 if multiprocessEnabled: |
|
347 args.append(multiprocessEnabled) |
|
348 if callTraceOptimization: |
|
349 args.append(callTraceOptimization) |
|
350 self.__ericServerDebuggerInterface.startClient( |
|
351 interpreter, originalPathString, args, workingDir=workingDir, |
|
352 ) |
|
353 self.__startedVenv = venvName |
|
354 |
|
355 return None, self.__isNetworked, "" |
|
356 |
293 else: |
357 else: |
294 # local debugging code below |
358 # local debugging code below |
295 debugClient = self.__determineDebugClient() |
359 debugClient = self.__determineDebugClient() |
296 |
360 |
297 # set translation function |
361 # set translation function |
414 @param workingDir directory to start the debugger client in |
479 @param workingDir directory to start the debugger client in |
415 @type str |
480 @type str |
416 @param configOverride dictionary containing the global config override |
481 @param configOverride dictionary containing the global config override |
417 data |
482 data |
418 @type dict |
483 @type dict |
|
484 @param startRemote flag indicating to start the client via an eric-ide server |
|
485 (defaults to False) |
|
486 @type bool (optional) |
419 @return client process object, a flag to indicate a network connection |
487 @return client process object, a flag to indicate a network connection |
420 and the name of the interpreter in case of a local execution |
488 and the name of the interpreter in case of a local execution |
421 @rtype tuple of (QProcess, bool, str) |
489 @rtype tuple of (QProcess, bool, str) |
422 """ |
490 """ |
423 global origPathEnv |
491 global origPathEnv |
457 self.tr("""<p>No suitable Python3 environment configured.</p>"""), |
525 self.tr("""<p>No suitable Python3 environment configured.</p>"""), |
458 ) |
526 ) |
459 return None, self.__isNetworked, "" |
527 return None, self.__isNetworked, "" |
460 |
528 |
461 self.__inShutdown = False |
529 self.__inShutdown = False |
|
530 |
|
531 self.__ericServerDebugging = False |
|
532 self.__ericServerDebuggerInterface.stopClient() |
|
533 self.__mainDebugger = None |
462 |
534 |
463 if project.getDebugProperty("REMOTEDEBUGGER"): |
535 if project.getDebugProperty("REMOTEDEBUGGER"): |
464 # remote debugging code |
536 # remote debugging code |
465 ipaddr = self.debugServer.getHostAddress(False) |
537 ipaddr = self.debugServer.getHostAddress(False) |
466 rexec = project.getDebugProperty("REMOTECOMMAND") |
538 rexec = project.getDebugProperty("REMOTECOMMAND") |
515 return process, self.__isNetworked, "" |
587 return process, self.__isNetworked, "" |
516 else: |
588 else: |
517 # remote shell command is missing |
589 # remote shell command is missing |
518 return None, self.__isNetworked, "" |
590 return None, self.__isNetworked, "" |
519 |
591 |
|
592 # TODO: add server debugging for projects |
|
593 |
520 else: |
594 else: |
521 # local debugging code below |
595 # local debugging code below |
522 debugClient = project.getDebugProperty("DEBUGCLIENT") |
596 debugClient = project.getDebugProperty("DEBUGCLIENT") |
523 if not bool(debugClient) or not os.path.exists(debugClient): |
597 if not bool(debugClient) or not os.path.exists(debugClient): |
524 debugClient = self.__determineDebugClient() |
598 debugClient = self.__determineDebugClient() |
632 @param sock reference to the socket object |
706 @param sock reference to the socket object |
633 @type QTcpSocket |
707 @type QTcpSocket |
634 @param debuggerId id of the connected debug client |
708 @param debuggerId id of the connected debug client |
635 @type str |
709 @type str |
636 """ |
710 """ |
637 if sock in self.__pendingConnections: |
711 if sock and sock in self.__pendingConnections: |
638 self.__connections[debuggerId] = sock |
712 self.__connections[debuggerId] = sock |
639 self.__pendingConnections.remove(sock) |
713 self.__pendingConnections.remove(sock) |
640 |
714 |
641 if self.__mainDebugger is None: |
715 if self.__mainDebugger is None: |
642 self.__mainDebugger = debuggerId |
716 self.__mainDebugger = debuggerId |
643 # Get the remote clients capabilities |
717 # Get the remote clients capabilities |
644 self.remoteCapabilities(debuggerId) |
718 self.remoteCapabilities(debuggerId) |
645 |
719 |
646 self.debugServer.signalClientDebuggerId(debuggerId) |
720 self.debugServer.signalClientDebuggerId(debuggerId) |
647 |
721 |
648 if debuggerId == self.__mainDebugger: |
722 if debuggerId == self.__mainDebugger: |
649 self.__flush() |
723 self.__flush() |
650 self.debugServer.mainClientConnected() |
724 self.debugServer.mainClientConnected() |
651 |
725 |
652 self.debugServer.initializeClient(debuggerId) |
726 self.debugServer.initializeClient(debuggerId) |
653 |
727 |
654 # perform auto-continue except for main |
728 # perform auto-continue except for main |
655 if ( |
729 if ( |
656 debuggerId != self.__mainDebugger |
730 debuggerId != self.__mainDebugger |
657 and self.__autoContinue |
731 and self.__autoContinue |
658 and not self.__isStepCommand |
732 and not self.__isStepCommand |
659 ): |
733 ): |
660 QTimer.singleShot(0, lambda: self.remoteContinue(debuggerId)) |
734 QTimer.singleShot(0, lambda: self.remoteContinue(debuggerId)) |
661 |
735 |
662 def __socketDisconnected(self, sock): |
736 def __socketDisconnected(self, sock): |
663 """ |
737 """ |
664 Private slot handling a socket disconnecting. |
738 Private slot handling a socket disconnecting. |
665 |
739 |
705 """ |
779 """ |
706 Private slot to flush the queue. |
780 Private slot to flush the queue. |
707 """ |
781 """ |
708 if self.__mainDebugger: |
782 if self.__mainDebugger: |
709 # Send commands that were waiting for the connection. |
783 # Send commands that were waiting for the connection. |
710 conn = self.__connections[self.__mainDebugger] |
784 if self.__ericServerDebugging: |
711 for jsonStr in self.__commandQueue: |
785 for jsonStr in self.__commandQueue: |
712 self.__writeJsonCommandToSocket(jsonStr, conn) |
786 self.__ericServerDebuggerInterface.sendClientCommand( |
|
787 self.__mainDebugger, jsonStr |
|
788 ) |
|
789 else: |
|
790 conn = self.__connections[self.__mainDebugger] |
|
791 for jsonStr in self.__commandQueue: |
|
792 self.__writeJsonCommandToSocket(jsonStr, conn) |
713 |
793 |
714 self.__commandQueue.clear() |
794 self.__commandQueue.clear() |
715 |
795 |
716 def shutdown(self): |
796 def shutdown(self): |
717 """ |
797 """ |
1421 jsonStr = data.decode("utf-8", "backslashreplace") |
1501 jsonStr = data.decode("utf-8", "backslashreplace") |
1422 |
1502 |
1423 logging.debug("<Debug-Server> %s", jsonStr) |
1503 logging.debug("<Debug-Server> %s", jsonStr) |
1424 ##print("Server: ", jsonStr) ## debug # __IGNORE_WARNING_M891__ |
1504 ##print("Server: ", jsonStr) ## debug # __IGNORE_WARNING_M891__ |
1425 |
1505 |
1426 self.__handleJsonCommand(jsonStr, sock) |
1506 self.handleJsonCommand(jsonStr, sock) |
1427 |
1507 |
1428 def __handleJsonCommand(self, jsonStr, sock): |
1508 def handleJsonCommand(self, jsonStr, sock): |
1429 """ |
1509 """ |
1430 Private method to handle a command or response serialized as a |
1510 Public method to handle a command or response serialized as a |
1431 JSON string. |
1511 JSON string. |
1432 |
1512 |
1433 @param jsonStr string containing the command or response received |
1513 @param jsonStr string containing the command or response received |
1434 from the debug backend |
1514 from the debug backend |
1435 @type str |
1515 @type str |
1661 "method": command, |
1741 "method": command, |
1662 "params": params, |
1742 "params": params, |
1663 } |
1743 } |
1664 jsonStr = json.dumps(commandDict) |
1744 jsonStr = json.dumps(commandDict) |
1665 |
1745 |
1666 if debuggerId and debuggerId in self.__connections: |
1746 if self.__ericServerDebugging: |
1667 sock = self.__connections[debuggerId] |
1747 # Debugging via the eric-ide server -> pass the command on to it |
1668 elif sock is None and self.__mainDebugger is not None: |
1748 if self.__mainDebugger is None: |
1669 sock = self.__connections[self.__mainDebugger] |
1749 # debugger has not connected yet -> queue the command |
1670 if sock is not None: |
1750 self.__commandQueue.append(jsonStr) |
1671 self.__writeJsonCommandToSocket(jsonStr, sock) |
1751 else: |
|
1752 self.__ericServerDebuggerInterface.sendClientCommand( |
|
1753 debuggerId, jsonStr |
|
1754 ) |
1672 else: |
1755 else: |
1673 self.__commandQueue.append(jsonStr) |
1756 # Local debugging -> send the command to the client |
|
1757 if debuggerId and debuggerId in self.__connections: |
|
1758 sock = self.__connections[debuggerId] |
|
1759 elif sock is None and self.__mainDebugger is not None: |
|
1760 sock = self.__connections[self.__mainDebugger] |
|
1761 if sock is not None: |
|
1762 self.__writeJsonCommandToSocket(jsonStr, sock) |
|
1763 else: |
|
1764 self.__commandQueue.append(jsonStr) |
1674 |
1765 |
1675 def __writeJsonCommandToSocket(self, jsonCommand, sock): |
1766 def __writeJsonCommandToSocket(self, jsonCommand, sock): |
1676 """ |
1767 """ |
1677 Private method to write a JSON command to the socket. |
1768 Private method to write a JSON command to the socket. |
1678 |
1769 |