26 from eric5config import getConfig |
26 from eric5config import getConfig |
27 |
27 |
28 |
28 |
29 ClientDefaultCapabilities = DebugClientCapabilities.HasAll |
29 ClientDefaultCapabilities = DebugClientCapabilities.HasAll |
30 |
30 |
|
31 |
31 def getRegistryData(): |
32 def getRegistryData(): |
32 """ |
33 """ |
33 Module function to get characterising data for the debugger interface. |
34 Module function to get characterising data for the debugger interface. |
34 |
35 |
35 @return list of the following data. Client type (string), client |
36 @return list of the following data. Client type (string), client |
36 capabilities (integer), client type association (list of strings) |
37 capabilities (integer), client type association (list of strings) |
37 """ |
38 """ |
38 exts = [] |
39 exts = [] |
39 for ext in Preferences.getDebugger("PythonExtensions").split(): |
40 for ext in Preferences.getDebugger("PythonExtensions").split(): |
40 if ext.startswith("."): |
41 if ext.startswith("."): |
45 if exts and Preferences.getDebugger("PythonInterpreter"): |
46 if exts and Preferences.getDebugger("PythonInterpreter"): |
46 return ["Python2", ClientDefaultCapabilities, exts] |
47 return ["Python2", ClientDefaultCapabilities, exts] |
47 else: |
48 else: |
48 return ["", 0, []] |
49 return ["", 0, []] |
49 |
50 |
|
51 |
50 class DebuggerInterfacePython(QObject): |
52 class DebuggerInterfacePython(QObject): |
51 """ |
53 """ |
52 Class implementing the Python debugger interface for the debug server. |
54 Class implementing the Python debugger interface for the debug server. |
53 """ |
55 """ |
54 def __init__(self, debugServer, passive): |
56 def __init__(self, debugServer, passive): |
85 Preferences.getDebugger("PathTranslationLocal") |
87 Preferences.getDebugger("PathTranslationLocal") |
86 self.translate = self.__remoteTranslation |
88 self.translate = self.__remoteTranslation |
87 else: |
89 else: |
88 self.translate = self.__identityTranslation |
90 self.translate = self.__identityTranslation |
89 |
91 |
90 def __identityTranslation(self, fn, remote2local = True): |
92 def __identityTranslation(self, fn, remote2local=True): |
91 """ |
93 """ |
92 Private method to perform the identity path translation. |
94 Private method to perform the identity path translation. |
93 |
95 |
94 @param fn filename to be translated (string) |
96 @param fn filename to be translated (string) |
95 @param remote2local flag indicating the direction of translation |
97 @param remote2local flag indicating the direction of translation |
96 (False = local to remote, True = remote to local [default]) |
98 (False = local to remote, True = remote to local [default]) |
97 @return translated filename (string) |
99 @return translated filename (string) |
98 """ |
100 """ |
99 return fn |
101 return fn |
100 |
102 |
101 def __remoteTranslation(self, fn, remote2local = True): |
103 def __remoteTranslation(self, fn, remote2local=True): |
102 """ |
104 """ |
103 Private method to perform the path translation. |
105 Private method to perform the path translation. |
104 |
106 |
105 @param fn filename to be translated (string) |
107 @param fn filename to be translated (string) |
106 @param remote2local flag indicating the direction of translation |
108 @param remote2local flag indicating the direction of translation |
110 if remote2local: |
112 if remote2local: |
111 return fn.replace(self.translateRemote, self.translateLocal) |
113 return fn.replace(self.translateRemote, self.translateLocal) |
112 else: |
114 else: |
113 return fn.replace(self.translateLocal, self.translateRemote) |
115 return fn.replace(self.translateLocal, self.translateRemote) |
114 |
116 |
115 def __startProcess(self, program, arguments, environment = None): |
117 def __startProcess(self, program, arguments, environment=None): |
116 """ |
118 """ |
117 Private method to start the debugger client process. |
119 Private method to start the debugger client process. |
118 |
120 |
119 @param program name of the executable to start (string) |
121 @param program name of the executable to start (string) |
120 @param arguments arguments to be passed to the program (list of string) |
122 @param arguments arguments to be passed to the program (list of string) |
139 def startRemote(self, port, runInConsole): |
141 def startRemote(self, port, runInConsole): |
140 """ |
142 """ |
141 Public method to start a remote Python interpreter. |
143 Public method to start a remote Python interpreter. |
142 |
144 |
143 @param port portnumber the debug server is listening on (integer) |
145 @param port portnumber the debug server is listening on (integer) |
144 @param runInConsole flag indicating to start the debugger in a |
146 @param runInConsole flag indicating to start the debugger in a |
145 console window (boolean) |
147 console window (boolean) |
146 @return client process object (QProcess) and a flag to indicate |
148 @return client process object (QProcess) and a flag to indicate |
147 a network connection (boolean) |
149 a network connection (boolean) |
148 """ |
150 """ |
149 interpreter = Preferences.getDebugger("PythonInterpreter") |
151 interpreter = Preferences.getDebugger("PythonInterpreter") |
154 """<p>No Python2 interpreter configured.</p>""")) |
156 """<p>No Python2 interpreter configured.</p>""")) |
155 return None, False |
157 return None, False |
156 |
158 |
157 debugClientType = Preferences.getDebugger("DebugClientType") |
159 debugClientType = Preferences.getDebugger("DebugClientType") |
158 if debugClientType == "standard": |
160 if debugClientType == "standard": |
159 debugClient = os.path.join(getConfig('ericDir'), |
161 debugClient = os.path.join(getConfig('ericDir'), |
160 "DebugClients", "Python", "DebugClient.py") |
162 "DebugClients", "Python", "DebugClient.py") |
161 elif debugClientType == "threaded": |
163 elif debugClientType == "threaded": |
162 debugClient = os.path.join(getConfig('ericDir'), |
164 debugClient = os.path.join(getConfig('ericDir'), |
163 "DebugClients", "Python", "DebugClientThreads.py") |
165 "DebugClients", "Python", "DebugClientThreads.py") |
164 else: |
166 else: |
233 self.trUtf8("Start Debugger"), |
235 self.trUtf8("Start Debugger"), |
234 self.trUtf8( |
236 self.trUtf8( |
235 """<p>The debugger backend could not be started.</p>""")) |
237 """<p>The debugger backend could not be started.</p>""")) |
236 return process, self.__isNetworked |
238 return process, self.__isNetworked |
237 |
239 |
238 process = self.__startProcess(interpreter, |
240 process = self.__startProcess(interpreter, |
239 [debugClient, noencoding, str(port), redirect, ipaddr], |
241 [debugClient, noencoding, str(port), redirect, ipaddr], |
240 clientEnv) |
242 clientEnv) |
241 if process is None: |
243 if process is None: |
242 E5MessageBox.critical(None, |
244 E5MessageBox.critical(None, |
243 self.trUtf8("Start Debugger"), |
245 self.trUtf8("Start Debugger"), |
244 self.trUtf8("""<p>The debugger backend could not be started.</p>""")) |
246 self.trUtf8("""<p>The debugger backend could not be started.</p>""")) |
247 def startRemoteForProject(self, port, runInConsole): |
249 def startRemoteForProject(self, port, runInConsole): |
248 """ |
250 """ |
249 Public method to start a remote Python interpreter for a project. |
251 Public method to start a remote Python interpreter for a project. |
250 |
252 |
251 @param port portnumber the debug server is listening on (integer) |
253 @param port portnumber the debug server is listening on (integer) |
252 @param runInConsole flag indicating to start the debugger in a |
254 @param runInConsole flag indicating to start the debugger in a |
253 console window (boolean) |
255 console window (boolean) |
254 @return client process object (QProcess) and a flag to indicate |
256 @return client process object (QProcess) and a flag to indicate |
255 a network connection (boolean) |
257 a network connection (boolean) |
256 """ |
258 """ |
257 project = e5App().getObject("Project") |
259 project = e5App().getObject("Project") |
326 self.trUtf8("Start Debugger"), |
328 self.trUtf8("Start Debugger"), |
327 self.trUtf8( |
329 self.trUtf8( |
328 """<p>The debugger backend could not be started.</p>""")) |
330 """<p>The debugger backend could not be started.</p>""")) |
329 return process, self.__isNetworked |
331 return process, self.__isNetworked |
330 |
332 |
331 process = self.__startProcess(interpreter, |
333 process = self.__startProcess(interpreter, |
332 [debugClient, noencoding, str(port), redirect, ipaddr], |
334 [debugClient, noencoding, str(port), redirect, ipaddr], |
333 clientEnv) |
335 clientEnv) |
334 if process is None: |
336 if process is None: |
335 E5MessageBox.critical(None, |
337 E5MessageBox.critical(None, |
336 self.trUtf8("Start Debugger"), |
338 self.trUtf8("Start Debugger"), |
337 self.trUtf8("""<p>The debugger backend could not be started.</p>""")) |
339 self.trUtf8("""<p>The debugger backend could not be started.</p>""")) |
414 |
416 |
415 @param env environment settings (dictionary) |
417 @param env environment settings (dictionary) |
416 """ |
418 """ |
417 self.__sendCommand('{0}{1}\n'.format(RequestEnv, str(env))) |
419 self.__sendCommand('{0}{1}\n'.format(RequestEnv, str(env))) |
418 |
420 |
419 def remoteLoad(self, fn, argv, wd, traceInterpreter = False, autoContinue = True, |
421 def remoteLoad(self, fn, argv, wd, traceInterpreter=False, autoContinue=True, |
420 autoFork = False, forkChild = False): |
422 autoFork=False, forkChild=False): |
421 """ |
423 """ |
422 Public method to load a new program to debug. |
424 Public method to load a new program to debug. |
423 |
425 |
424 @param fn the filename to debug (string) |
426 @param fn the filename to debug (string) |
425 @param argv the commandline arguments to pass to the program (string) |
427 @param argv the commandline arguments to pass to the program (string) |
438 self.__sendCommand('{0}{1}\n'.format(RequestForkMode, repr((autoFork, forkChild)))) |
440 self.__sendCommand('{0}{1}\n'.format(RequestForkMode, repr((autoFork, forkChild)))) |
439 self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format( |
441 self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format( |
440 RequestLoad, wd, fn, str(Utilities.parseOptionString(argv)), |
442 RequestLoad, wd, fn, str(Utilities.parseOptionString(argv)), |
441 traceInterpreter)) |
443 traceInterpreter)) |
442 |
444 |
443 def remoteRun(self, fn, argv, wd, autoFork = False, forkChild = False): |
445 def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False): |
444 """ |
446 """ |
445 Public method to load a new program to run. |
447 Public method to load a new program to run. |
446 |
448 |
447 @param fn the filename to run (string) |
449 @param fn the filename to run (string) |
448 @param argv the commandline arguments to pass to the program (string) |
450 @param argv the commandline arguments to pass to the program (string) |
454 fn = self.translate(os.path.abspath(fn), False) |
456 fn = self.translate(os.path.abspath(fn), False) |
455 self.__sendCommand('{0}{1}\n'.format(RequestForkMode, repr((autoFork, forkChild)))) |
457 self.__sendCommand('{0}{1}\n'.format(RequestForkMode, repr((autoFork, forkChild)))) |
456 self.__sendCommand('{0}{1}|{2}|{3}\n'.format( |
458 self.__sendCommand('{0}{1}|{2}|{3}\n'.format( |
457 RequestRun, wd, fn, str(Utilities.parseOptionString(argv)))) |
459 RequestRun, wd, fn, str(Utilities.parseOptionString(argv)))) |
458 |
460 |
459 def remoteCoverage(self, fn, argv, wd, erase = False): |
461 def remoteCoverage(self, fn, argv, wd, erase=False): |
460 """ |
462 """ |
461 Public method to load a new program to collect coverage data. |
463 Public method to load a new program to collect coverage data. |
462 |
464 |
463 @param fn the filename to run (string) |
465 @param fn the filename to run (string) |
464 @param argv the commandline arguments to pass to the program (string) |
466 @param argv the commandline arguments to pass to the program (string) |
465 @param wd the working directory for the program (string) |
467 @param wd the working directory for the program (string) |
466 @keyparam erase flag indicating that coverage info should be |
468 @keyparam erase flag indicating that coverage info should be |
467 cleared first (boolean) |
469 cleared first (boolean) |
468 """ |
470 """ |
469 wd = self.translate(wd, False) |
471 wd = self.translate(wd, False) |
470 fn = self.translate(os.path.abspath(fn), False) |
472 fn = self.translate(os.path.abspath(fn), False) |
471 self.__sendCommand('{0}{1}@@{2}@@{3}@@{4:d}\n'.format( |
473 self.__sendCommand('{0}{1}@@{2}@@{3}@@{4:d}\n'.format( |
472 RequestCoverage, wd, fn, str(Utilities.parseOptionString(argv)), |
474 RequestCoverage, wd, fn, str(Utilities.parseOptionString(argv)), |
473 erase)) |
475 erase)) |
474 |
476 |
475 def remoteProfile(self, fn, argv, wd, erase = False): |
477 def remoteProfile(self, fn, argv, wd, erase=False): |
476 """ |
478 """ |
477 Public method to load a new program to collect profiling data. |
479 Public method to load a new program to collect profiling data. |
478 |
480 |
479 @param fn the filename to run (string) |
481 @param fn the filename to run (string) |
480 @param argv the commandline arguments to pass to the program (string) |
482 @param argv the commandline arguments to pass to the program (string) |
486 self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format( |
488 self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format( |
487 RequestProfile, wd, fn, str(Utilities.parseOptionString(argv)), erase)) |
489 RequestProfile, wd, fn, str(Utilities.parseOptionString(argv)), erase)) |
488 |
490 |
489 def remoteStatement(self, stmt): |
491 def remoteStatement(self, stmt): |
490 """ |
492 """ |
491 Public method to execute a Python statement. |
493 Public method to execute a Python statement. |
492 |
494 |
493 @param stmt the Python statement to execute (string). It |
495 @param stmt the Python statement to execute (string). It |
494 should not have a trailing newline. |
496 should not have a trailing newline. |
495 """ |
497 """ |
496 self.__sendCommand('{0}\n'.format(stmt)) |
498 self.__sendCommand('{0}\n'.format(stmt)) |
518 """ |
520 """ |
519 Public method to stop the debugged program. |
521 Public method to stop the debugged program. |
520 """ |
522 """ |
521 self.__sendCommand(RequestStepQuit + '\n') |
523 self.__sendCommand(RequestStepQuit + '\n') |
522 |
524 |
523 def remoteContinue(self, special = False): |
525 def remoteContinue(self, special=False): |
524 """ |
526 """ |
525 Public method to continue the debugged program. |
527 Public method to continue the debugged program. |
526 |
528 |
527 @param special flag indicating a special continue operation (boolean) |
529 @param special flag indicating a special continue operation (boolean) |
528 """ |
530 """ |
529 self.__sendCommand('{0}{1:d}\n'.format(RequestContinue, special)) |
531 self.__sendCommand('{0}{1:d}\n'.format(RequestContinue, special)) |
530 |
532 |
531 def remoteBreakpoint(self, fn, line, set, cond = None, temp = False): |
533 def remoteBreakpoint(self, fn, line, set, cond=None, temp=False): |
532 """ |
534 """ |
533 Public method to set or clear a breakpoint. |
535 Public method to set or clear a breakpoint. |
534 |
536 |
535 @param fn filename the breakpoint belongs to (string) |
537 @param fn filename the breakpoint belongs to (string) |
536 @param line linenumber of the breakpoint (int) |
538 @param line linenumber of the breakpoint (int) |
564 """ |
566 """ |
565 fn = self.translate(fn, False) |
567 fn = self.translate(fn, False) |
566 self.__sendCommand('{0}{1},{2:d},{3:d}\n'.format( |
568 self.__sendCommand('{0}{1},{2:d},{3:d}\n'.format( |
567 RequestBreakIgnore, fn, line, count)) |
569 RequestBreakIgnore, fn, line, count)) |
568 |
570 |
569 def remoteWatchpoint(self, cond, set, temp = False): |
571 def remoteWatchpoint(self, cond, set, temp=False): |
570 """ |
572 """ |
571 Public method to set or clear a watch expression. |
573 Public method to set or clear a watch expression. |
572 |
574 |
573 @param cond expression of the watch expression (string) |
575 @param cond expression of the watch expression (string) |
574 @param set flag indicating setting or resetting a watch expression (boolean) |
576 @param set flag indicating setting or resetting a watch expression (boolean) |
595 @param count number of occurrences to ignore (int) |
597 @param count number of occurrences to ignore (int) |
596 """ |
598 """ |
597 # cond is combination of cond and special (s. watch expression viewer) |
599 # cond is combination of cond and special (s. watch expression viewer) |
598 self.__sendCommand('{0}{1},{2:d}\n'.format(RequestWatchIgnore, cond, count)) |
600 self.__sendCommand('{0}{1},{2:d}\n'.format(RequestWatchIgnore, cond, count)) |
599 |
601 |
600 def remoteRawInput(self,s): |
602 def remoteRawInput(self, s): |
601 """ |
603 """ |
602 Public method to send the raw input to the debugged program. |
604 Public method to send the raw input to the debugged program. |
603 |
605 |
604 @param s the raw input (string) |
606 @param s the raw input (string) |
605 """ |
607 """ |
617 |
619 |
618 @param tid id of the thread (integer) |
620 @param tid id of the thread (integer) |
619 """ |
621 """ |
620 self.__sendCommand('{0}{1:d}\n'.format(RequestThreadSet, tid)) |
622 self.__sendCommand('{0}{1:d}\n'.format(RequestThreadSet, tid)) |
621 |
623 |
622 def remoteClientVariables(self, scope, filter, framenr = 0): |
624 def remoteClientVariables(self, scope, filter, framenr=0): |
623 """ |
625 """ |
624 Public method to request the variables of the debugged program. |
626 Public method to request the variables of the debugged program. |
625 |
627 |
626 @param scope the scope of the variables (0 = local, 1 = global) |
628 @param scope the scope of the variables (0 = local, 1 = global) |
627 @param filter list of variable types to filter out (list of int) |
629 @param filter list of variable types to filter out (list of int) |
628 @param framenr framenumber of the variables to retrieve (int) |
630 @param framenr framenumber of the variables to retrieve (int) |
629 """ |
631 """ |
630 self.__sendCommand('{0}{1:d}, {2:d}, {3}\n'.format( |
632 self.__sendCommand('{0}{1:d}, {2:d}, {3}\n'.format( |
631 RequestVariables, framenr, scope, str(filter))) |
633 RequestVariables, framenr, scope, str(filter))) |
632 |
634 |
633 def remoteClientVariable(self, scope, filter, var, framenr = 0): |
635 def remoteClientVariable(self, scope, filter, var, framenr=0): |
634 """ |
636 """ |
635 Public method to request the variables of the debugged program. |
637 Public method to request the variables of the debugged program. |
636 |
638 |
637 @param scope the scope of the variables (0 = local, 1 = global) |
639 @param scope the scope of the variables (0 = local, 1 = global) |
638 @param filter list of variable types to filter out (list of int) |
640 @param filter list of variable types to filter out (list of int) |
773 cf = stack[0] |
775 cf = stack[0] |
774 if self.__autoContinue: |
776 if self.__autoContinue: |
775 self.__autoContinue = False |
777 self.__autoContinue = False |
776 QTimer.singleShot(0, self.remoteContinue) |
778 QTimer.singleShot(0, self.remoteContinue) |
777 else: |
779 else: |
778 self.debugServer.signalClientLine(cf[0], int(cf[1]), |
780 self.debugServer.signalClientLine(cf[0], int(cf[1]), |
779 resp == ResponseStack) |
781 resp == ResponseStack) |
780 self.debugServer.signalClientStack(stack) |
782 self.debugServer.signalClientStack(stack) |
781 continue |
783 continue |
782 |
784 |
783 if resp == ResponseThreadList: |
785 if resp == ResponseThreadList: |