eric7/Debugger/DebugUI.py

branch
eric7
changeset 8312
800c432b34c8
parent 8265
0090cfa83159
child 8314
e3642a6a1e71
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the debugger UI.
8 """
9
10 import os
11 import copy
12 import contextlib
13
14 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt
15 from PyQt5.QtGui import QKeySequence
16 from PyQt5.QtWidgets import QMenu, QToolBar, QApplication, QDialog
17
18 from E5Gui.E5Action import E5Action, createActionGroup
19 from E5Gui import E5MessageBox
20
21 from UI.Info import Program
22 from UI.NotificationWidget import NotificationTypes
23
24 from .DebugClientCapabilities import (
25 HasDebugger, HasInterpreter, HasProfiler, HasCoverage
26 )
27
28 import Preferences
29 import Utilities
30 import UI.PixmapCache
31 import UI.Config
32
33 from eric6config import getConfig
34
35
36 class DebugUI(QObject):
37 """
38 Class implementing the debugger part of the UI.
39
40 @signal clientStack(stack, debuggerId) emitted at breaking after a reported
41 exception
42 @signal compileForms() emitted if changed project forms should be compiled
43 @signal compileResources() emitted if changed project resources should be
44 compiled
45 @signal executeMake() emitted if a project specific make run should be
46 performed
47 @signal debuggingStarted(filename) emitted when a debugging session was
48 started
49 @signal resetUI(full) emitted to reset the UI partially or fully
50 @signal exceptionInterrupt() emitted after the execution was interrupted
51 by an exception and acknowledged by the user
52 @signal appendStdout(msg) emitted when the client program has terminated
53 and the display of the termination dialog is suppressed
54 """
55 clientStack = pyqtSignal(list, str)
56 resetUI = pyqtSignal(bool)
57 exceptionInterrupt = pyqtSignal()
58 compileForms = pyqtSignal()
59 compileResources = pyqtSignal()
60 executeMake = pyqtSignal()
61 debuggingStarted = pyqtSignal(str)
62 appendStdout = pyqtSignal(str)
63
64 def __init__(self, ui, vm, debugServer, debugViewer, project):
65 """
66 Constructor
67
68 @param ui reference to the main UI
69 @param vm reference to the viewmanager
70 @param debugServer reference to the debug server
71 @param debugViewer reference to the debug viewer widget
72 @param project reference to the project object
73 """
74 super().__init__(ui)
75
76 self.ui = ui
77 self.viewmanager = vm
78 self.debugServer = debugServer
79 self.debugViewer = debugViewer
80 self.project = project
81
82 # Clear some variables
83 self.projectOpen = False
84 self.editorOpen = False
85
86 # read the saved debug info values
87 self.lastUsedVenvName = Preferences.Prefs.settings.value(
88 'DebugInfo/VirtualEnvironment', '')
89 self.argvHistory = Preferences.toList(
90 Preferences.Prefs.settings.value('DebugInfo/ArgumentsHistory'))
91 self.wdHistory = Preferences.toList(
92 Preferences.Prefs.settings.value(
93 'DebugInfo/WorkingDirectoryHistory'))
94 self.envHistory = Preferences.toList(
95 Preferences.Prefs.settings.value('DebugInfo/EnvironmentHistory'))
96 self.excList = Preferences.toList(
97 Preferences.Prefs.settings.value('DebugInfo/Exceptions'))
98 self.excIgnoreList = Preferences.toList(
99 Preferences.Prefs.settings.value('DebugInfo/IgnoredExceptions'))
100 self.exceptions = Preferences.toBool(
101 Preferences.Prefs.settings.value(
102 'DebugInfo/ReportExceptions', True))
103 self.autoClearShell = Preferences.toBool(
104 Preferences.Prefs.settings.value('DebugInfo/AutoClearShell', True))
105 self.tracePython = Preferences.toBool(
106 Preferences.Prefs.settings.value('DebugInfo/TracePython', False))
107 self.autoContinue = Preferences.toBool(
108 Preferences.Prefs.settings.value('DebugInfo/AutoContinue', True))
109 self.enableMultiprocess = Preferences.toBool(
110 Preferences.Prefs.settings.value(
111 'DebugInfo/EnableMultiprocess', False))
112 self.multiprocessNoDebugHistory = Preferences.toList(
113 Preferences.Prefs.settings.value(
114 'DebugInfo/MultiprocessNoDebugHistory'))
115 self.overrideGlobalConfig = {
116 "enable": Preferences.toBool(Preferences.Prefs.settings.value(
117 'DebugInfo/OverrideGlobal', False)),
118 "redirect": Preferences.toBool(Preferences.Prefs.settings.value(
119 'DebugInfo/RedirectStdinStdout', True)),
120 }
121
122 self.lastDebuggedFile = None
123 self.lastStartAction = 0 # 0=None, 1=Script, 2=Project
124 self.clientType = ""
125 self.lastAction = -1
126 self.debugActions = [
127 self.__continue, self.__step, self.__stepOver, self.__stepOut,
128 self.__stepQuit, self.__runToCursor, self.__runUntil,
129 self.__moveInstructionPointer
130 ]
131 self.__localsVarFilterList, self.__globalsVarFilterList = (
132 Preferences.getVarFilters())
133 self.debugViewer.setVariablesFilter(
134 self.__globalsVarFilterList, self.__localsVarFilterList)
135
136 self.__clientDebuggerIds = set()
137
138 # Connect the signals emitted by the debug-server
139 debugServer.clientGone.connect(self.__clientGone)
140 debugServer.clientLine.connect(self.__clientLine)
141 debugServer.clientDisconnected.connect(self.__clientDisconnected)
142 debugServer.clientExit.connect(self.__clientExit)
143 debugServer.lastClientExited.connect(self.__lastClientExited)
144 debugServer.clientSyntaxError.connect(self.__clientSyntaxError)
145 debugServer.clientException.connect(self.__clientException)
146 debugServer.clientSignal.connect(self.__clientSignal)
147 debugServer.clientVariables.connect(self.__clientVariables)
148 debugServer.clientVariable.connect(self.__clientVariable)
149 debugServer.clientBreakConditionError.connect(
150 self.__clientBreakConditionError)
151 debugServer.clientWatchConditionError.connect(
152 self.__clientWatchConditionError)
153 debugServer.passiveDebugStarted.connect(self.__passiveDebugStarted)
154 debugServer.clientThreadSet.connect(self.__clientThreadSet)
155 debugServer.clientDebuggerId.connect(self.__clientDebuggerId)
156
157 # Connect the signals emitted by the viewmanager
158 vm.editorOpened.connect(self.__editorOpened)
159 vm.lastEditorClosed.connect(self.__lastEditorClosed)
160 vm.checkActions.connect(self.__checkActions)
161 vm.cursorChanged.connect(self.__cursorChanged)
162 vm.breakpointToggled.connect(self.__cursorChanged)
163
164 # Connect the signals emitted by the project
165 project.projectOpened.connect(self.__projectOpened)
166 project.newProject.connect(self.__projectOpened)
167 project.projectClosed.connect(self.__projectClosed)
168
169 # Set a flag for the passive debug mode
170 self.passive = Preferences.getDebugger("PassiveDbgEnabled")
171
172 def showNotification(self, notification,
173 kind=NotificationTypes.INFORMATION, timeout=None):
174 """
175 Public method to show some notification message.
176
177 @param notification message to be shown
178 @type str
179 @param kind kind of notification to be shown
180 @type NotificationTypes
181 @param timeout timeout for the notification (None = use configured
182 default, 0 = indefinitely)
183 @type int
184 """
185 self.ui.showNotification(
186 UI.PixmapCache.getPixmap("debug48"),
187 self.tr("Notification"), notification, kind=kind, timeout=timeout)
188
189 def variablesFilter(self, scope):
190 """
191 Public method to get the variables filter for a scope.
192
193 @param scope flag indicating global (True) or local (False) scope
194 @return filters list
195 @rtype list of str
196 """
197 if scope:
198 return self.__globalsVarFilterList[:]
199 else:
200 return self.__localsVarFilterList[:]
201
202 def initActions(self):
203 """
204 Public method defining the user interface actions.
205 """
206 self.actions = []
207
208 self.runAct = E5Action(
209 self.tr('Run Script'),
210 UI.PixmapCache.getIcon("runScript"),
211 self.tr('&Run Script...'),
212 Qt.Key.Key_F2, 0, self, 'dbg_run_script')
213 self.runAct.setStatusTip(self.tr('Run the current Script'))
214 self.runAct.setWhatsThis(self.tr(
215 """<b>Run Script</b>"""
216 """<p>Set the command line arguments and run the script outside"""
217 """ the debugger. If the file has unsaved changes it may be"""
218 """ saved first.</p>"""
219 ))
220 self.runAct.triggered.connect(self.__runScript)
221 self.actions.append(self.runAct)
222
223 self.runProjectAct = E5Action(
224 self.tr('Run Project'),
225 UI.PixmapCache.getIcon("runProject"),
226 self.tr('Run &Project...'),
227 Qt.Modifier.SHIFT + Qt.Key.Key_F2,
228 0, self, 'dbg_run_project')
229 self.runProjectAct.setStatusTip(self.tr('Run the current Project'))
230 self.runProjectAct.setWhatsThis(self.tr(
231 """<b>Run Project</b>"""
232 """<p>Set the command line arguments and run the current project"""
233 """ outside the debugger."""
234 """ If files of the current project have unsaved changes they"""
235 """ may be saved first.</p>"""
236 ))
237 self.runProjectAct.triggered.connect(self.__runProject)
238 self.actions.append(self.runProjectAct)
239
240 self.coverageAct = E5Action(
241 self.tr('Coverage run of Script'),
242 UI.PixmapCache.getIcon("coverageScript"),
243 self.tr('Coverage run of Script...'), 0, 0, self,
244 'dbg_coverage_script')
245 self.coverageAct.setStatusTip(
246 self.tr('Perform a coverage run of the current Script'))
247 self.coverageAct.setWhatsThis(self.tr(
248 """<b>Coverage run of Script</b>"""
249 """<p>Set the command line arguments and run the script under"""
250 """ the control of a coverage analysis tool. If the file has"""
251 """ unsaved changes it may be saved first.</p>"""
252 ))
253 self.coverageAct.triggered.connect(self.__coverageScript)
254 self.actions.append(self.coverageAct)
255
256 self.coverageProjectAct = E5Action(
257 self.tr('Coverage run of Project'),
258 UI.PixmapCache.getIcon("coverageProject"),
259 self.tr('Coverage run of Project...'), 0, 0, self,
260 'dbg_coverage_project')
261 self.coverageProjectAct.setStatusTip(
262 self.tr('Perform a coverage run of the current Project'))
263 self.coverageProjectAct.setWhatsThis(self.tr(
264 """<b>Coverage run of Project</b>"""
265 """<p>Set the command line arguments and run the current project"""
266 """ under the control of a coverage analysis tool."""
267 """ If files of the current project have unsaved changes"""
268 """ they may be saved first.</p>"""
269 ))
270 self.coverageProjectAct.triggered.connect(self.__coverageProject)
271 self.actions.append(self.coverageProjectAct)
272
273 self.profileAct = E5Action(
274 self.tr('Profile Script'),
275 UI.PixmapCache.getIcon("profileScript"),
276 self.tr('Profile Script...'), 0, 0, self, 'dbg_profile_script')
277 self.profileAct.setStatusTip(self.tr('Profile the current Script'))
278 self.profileAct.setWhatsThis(self.tr(
279 """<b>Profile Script</b>"""
280 """<p>Set the command line arguments and profile the script."""
281 """ If the file has unsaved changes it may be saved first.</p>"""
282 ))
283 self.profileAct.triggered.connect(self.__profileScript)
284 self.actions.append(self.profileAct)
285
286 self.profileProjectAct = E5Action(
287 self.tr('Profile Project'),
288 UI.PixmapCache.getIcon("profileProject"),
289 self.tr('Profile Project...'), 0, 0, self,
290 'dbg_profile_project')
291 self.profileProjectAct.setStatusTip(
292 self.tr('Profile the current Project'))
293 self.profileProjectAct.setWhatsThis(self.tr(
294 """<b>Profile Project</b>"""
295 """<p>Set the command line arguments and profile the current"""
296 """ project. If files of the current project have unsaved"""
297 """ changes they may be saved first.</p>"""
298 ))
299 self.profileProjectAct.triggered.connect(self.__profileProject)
300 self.actions.append(self.profileProjectAct)
301
302 self.debugAct = E5Action(
303 self.tr('Debug Script'),
304 UI.PixmapCache.getIcon("debugScript"),
305 self.tr('&Debug Script...'), Qt.Key.Key_F5, 0, self,
306 'dbg_debug_script')
307 self.debugAct.setStatusTip(self.tr('Debug the current Script'))
308 self.debugAct.setWhatsThis(self.tr(
309 """<b>Debug Script</b>"""
310 """<p>Set the command line arguments and set the current line"""
311 """ to be the first executable Python statement of the current"""
312 """ editor window. If the file has unsaved changes it may be"""
313 """ saved first.</p>"""
314 ))
315 self.debugAct.triggered.connect(self.__debugScript)
316 self.actions.append(self.debugAct)
317
318 self.debugProjectAct = E5Action(
319 self.tr('Debug Project'),
320 UI.PixmapCache.getIcon("debugProject"),
321 self.tr('Debug &Project...'),
322 Qt.Modifier.SHIFT + Qt.Key.Key_F5,
323 0, self, 'dbg_debug_project')
324 self.debugProjectAct.setStatusTip(self.tr(
325 'Debug the current Project'))
326 self.debugProjectAct.setWhatsThis(self.tr(
327 """<b>Debug Project</b>"""
328 """<p>Set the command line arguments and set the current line"""
329 """ to be the first executable Python statement of the main"""
330 """ script of the current project. If files of the current"""
331 """ project have unsaved changes they may be saved first.</p>"""
332 ))
333 self.debugProjectAct.triggered.connect(self.__debugProject)
334 self.actions.append(self.debugProjectAct)
335
336 self.restartAct = E5Action(
337 self.tr('Restart'),
338 UI.PixmapCache.getIcon("debugRestart"),
339 self.tr('Restart'), Qt.Key.Key_F4, 0, self, 'dbg_restart_script')
340 self.restartAct.setStatusTip(self.tr(
341 'Restart the last debugged script'))
342 self.restartAct.setWhatsThis(self.tr(
343 """<b>Restart</b>"""
344 """<p>Set the command line arguments and set the current line"""
345 """ to be the first executable Python statement of the script"""
346 """ that was debugged last. If there are unsaved changes, they"""
347 """ may be saved first.</p>"""
348 ))
349 self.restartAct.triggered.connect(self.__doRestart)
350 self.actions.append(self.restartAct)
351
352 self.stopAct = E5Action(
353 self.tr('Stop'),
354 UI.PixmapCache.getIcon("stopScript"),
355 self.tr('Stop'), Qt.Modifier.SHIFT + Qt.Key.Key_F10, 0,
356 self, 'dbg_stop_script')
357 self.stopAct.setStatusTip(self.tr("""Stop the running script."""))
358 self.stopAct.setWhatsThis(self.tr(
359 """<b>Stop</b>"""
360 """<p>This stops the script running in the debugger backend.</p>"""
361 ))
362 self.stopAct.triggered.connect(self.__stopScript)
363 self.actions.append(self.stopAct)
364
365 self.debugActGrp = createActionGroup(self)
366
367 act = E5Action(
368 self.tr('Continue'),
369 UI.PixmapCache.getIcon("continue"),
370 self.tr('&Continue'), Qt.Key.Key_F6, 0,
371 self.debugActGrp, 'dbg_continue')
372 act.setStatusTip(
373 self.tr('Continue running the program from the current line'))
374 act.setWhatsThis(self.tr(
375 """<b>Continue</b>"""
376 """<p>Continue running the program from the current line. The"""
377 """ program will stop when it terminates or when a breakpoint"""
378 """ is reached.</p>"""
379 ))
380 act.triggered.connect(self.__continue)
381 self.actions.append(act)
382
383 act = E5Action(
384 self.tr('Continue to Cursor'),
385 UI.PixmapCache.getIcon("continueToCursor"),
386 self.tr('Continue &To Cursor'),
387 Qt.Modifier.SHIFT + Qt.Key.Key_F6,
388 0, self.debugActGrp, 'dbg_continue_to_cursor')
389 act.setStatusTip(self.tr(
390 """Continue running the program from the"""
391 """ current line to the current cursor position"""))
392 act.setWhatsThis(self.tr(
393 """<b>Continue To Cursor</b>"""
394 """<p>Continue running the program from the current line to the"""
395 """ current cursor position.</p>"""
396 ))
397 act.triggered.connect(self.__runToCursor)
398 self.actions.append(act)
399
400 act = E5Action(
401 self.tr('Continue Until'),
402 UI.PixmapCache.getIcon("continueUntil"),
403 self.tr('Continue &Until'), Qt.Modifier.CTRL + Qt.Key.Key_F6, 0,
404 self.debugActGrp, 'dbg_continue_until')
405 act.setStatusTip(self.tr(
406 """Continue running the program from the current line to the"""
407 """ current cursor position or until leaving the current frame"""))
408 act.setWhatsThis(self.tr(
409 """<b>Continue Until</b>"""
410 """<p>Continue running the program from the current line to the"""
411 """ cursor position greater than the current line or until"""
412 """ leaving the current frame.</p>"""
413 ))
414 act.triggered.connect(self.__runUntil)
415 self.actions.append(act)
416
417 act = E5Action(
418 self.tr('Move Instruction Pointer to Cursor'),
419 UI.PixmapCache.getIcon("moveInstructionPointer"),
420 self.tr('&Jump To Cursor'), Qt.Key.Key_F12, 0,
421 self.debugActGrp, 'dbg_jump_to_cursor')
422 act.setStatusTip(self.tr(
423 """Skip the code from the"""
424 """ current line to the current cursor position"""))
425 act.setWhatsThis(self.tr(
426 """<b>Move Instruction Pointer to Cursor</b>"""
427 """<p>Move the Python internal instruction pointer to the"""
428 """ current cursor position without executing the code in"""
429 """ between.</p>"""
430 """<p>It's not possible to jump out of a function or jump"""
431 """ in a code block, e.g. a loop. In these cases, a error"""
432 """ message is printed to the log window.</p>"""
433 ))
434 act.triggered.connect(self.__moveInstructionPointer)
435 self.actions.append(act)
436
437 act = E5Action(
438 self.tr('Single Step'),
439 UI.PixmapCache.getIcon("step"),
440 self.tr('Sin&gle Step'), Qt.Key.Key_F7, 0,
441 self.debugActGrp, 'dbg_single_step')
442 act.setStatusTip(self.tr('Execute a single Python statement'))
443 act.setWhatsThis(self.tr(
444 """<b>Single Step</b>"""
445 """<p>Execute a single Python statement. If the statement"""
446 """ is an <tt>import</tt> statement, a class constructor, or a"""
447 """ method or function call then control is returned to the"""
448 """ debugger at the next statement.</p>"""
449 ))
450 act.triggered.connect(self.__step)
451 self.actions.append(act)
452
453 act = E5Action(
454 self.tr('Step Over'),
455 UI.PixmapCache.getIcon("stepOver"),
456 self.tr('Step &Over'), Qt.Key.Key_F8, 0,
457 self.debugActGrp, 'dbg_step_over')
458 act.setStatusTip(self.tr(
459 """Execute a single Python statement staying"""
460 """ in the current frame"""))
461 act.setWhatsThis(self.tr(
462 """<b>Step Over</b>"""
463 """<p>Execute a single Python statement staying in the same"""
464 """ frame. If the statement is an <tt>import</tt> statement,"""
465 """ a class constructor, or a method or function call then"""
466 """ control is returned to the debugger after the statement"""
467 """ has completed.</p>"""
468 ))
469 act.triggered.connect(self.__stepOver)
470 self.actions.append(act)
471
472 act = E5Action(
473 self.tr('Step Out'),
474 UI.PixmapCache.getIcon("stepOut"),
475 self.tr('Step Ou&t'), Qt.Key.Key_F9, 0,
476 self.debugActGrp, 'dbg_step_out')
477 act.setStatusTip(self.tr(
478 """Execute Python statements until leaving"""
479 """ the current frame"""))
480 act.setWhatsThis(self.tr(
481 """<b>Step Out</b>"""
482 """<p>Execute Python statements until leaving the current"""
483 """ frame. If the statements are inside an <tt>import</tt>"""
484 """ statement, a class constructor, or a method or function"""
485 """ call then control is returned to the debugger after the"""
486 """ current frame has been left.</p>"""
487 ))
488 act.triggered.connect(self.__stepOut)
489 self.actions.append(act)
490
491 act = E5Action(
492 self.tr('Stop'),
493 UI.PixmapCache.getIcon("stepQuit"),
494 self.tr('&Stop'), Qt.Key.Key_F10, 0,
495 self.debugActGrp, 'dbg_stop')
496 act.setStatusTip(self.tr('Stop debugging'))
497 act.setWhatsThis(self.tr(
498 """<b>Stop</b>"""
499 """<p>Stop the running debugging session.</p>"""
500 ))
501 act.triggered.connect(self.__stepQuit)
502 self.actions.append(act)
503
504 self.dbgFilterAct = E5Action(
505 self.tr('Variables Type Filter'),
506 self.tr('Varia&bles Type Filter...'), 0, 0, self,
507 'dbg_variables_filter')
508 self.dbgFilterAct.setStatusTip(self.tr(
509 'Configure variables type filter'))
510 self.dbgFilterAct.setWhatsThis(self.tr(
511 """<b>Variables Type Filter</b>"""
512 """<p>Configure the variables type filter. Only variable types"""
513 """ that are not selected are displayed in the global or local"""
514 """ variables window during a debugging session.</p>"""
515 ))
516 self.dbgFilterAct.triggered.connect(
517 self.__configureVariablesFilters)
518 self.actions.append(self.dbgFilterAct)
519
520 self.excFilterAct = E5Action(
521 self.tr('Exceptions Filter'),
522 self.tr('&Exceptions Filter...'), 0, 0, self,
523 'dbg_exceptions_filter')
524 self.excFilterAct.setStatusTip(self.tr(
525 'Configure exceptions filter'))
526 self.excFilterAct.setWhatsThis(self.tr(
527 """<b>Exceptions Filter</b>"""
528 """<p>Configure the exceptions filter. Only exception types"""
529 """ that are listed are highlighted during a debugging"""
530 """ session.</p><p>Please note, that all unhandled exceptions"""
531 """ are highlighted indepent from the filter list.</p>"""
532 ))
533 self.excFilterAct.triggered.connect(
534 self.__configureExceptionsFilter)
535 self.actions.append(self.excFilterAct)
536
537 self.excIgnoreFilterAct = E5Action(
538 self.tr('Ignored Exceptions'),
539 self.tr('&Ignored Exceptions...'), 0, 0,
540 self, 'dbg_ignored_exceptions')
541 self.excIgnoreFilterAct.setStatusTip(self.tr(
542 'Configure ignored exceptions'))
543 self.excIgnoreFilterAct.setWhatsThis(self.tr(
544 """<b>Ignored Exceptions</b>"""
545 """<p>Configure the ignored exceptions. Only exception types"""
546 """ that are not listed are highlighted during a debugging"""
547 """ session.</p><p>Please note, that unhandled exceptions"""
548 """ cannot be ignored.</p>"""
549 ))
550 self.excIgnoreFilterAct.triggered.connect(
551 self.__configureIgnoredExceptions)
552 self.actions.append(self.excIgnoreFilterAct)
553
554 self.dbgSetBpActGrp = createActionGroup(self)
555
556 self.dbgToggleBpAct = E5Action(
557 self.tr('Toggle Breakpoint'),
558 UI.PixmapCache.getIcon("breakpointToggle"),
559 self.tr('Toggle Breakpoint'),
560 QKeySequence(self.tr("Shift+F11", "Debug|Toggle Breakpoint")),
561 0, self.dbgSetBpActGrp, 'dbg_toggle_breakpoint')
562 self.dbgToggleBpAct.setStatusTip(self.tr('Toggle Breakpoint'))
563 self.dbgToggleBpAct.setWhatsThis(self.tr(
564 """<b>Toggle Breakpoint</b>"""
565 """<p>Toggles a breakpoint at the current line of the"""
566 """ current editor.</p>"""
567 ))
568 self.dbgToggleBpAct.triggered.connect(self.__toggleBreakpoint)
569 self.actions.append(self.dbgToggleBpAct)
570
571 self.dbgEditBpAct = E5Action(
572 self.tr('Edit Breakpoint'),
573 UI.PixmapCache.getIcon("cBreakpointToggle"),
574 self.tr('Edit Breakpoint...'),
575 QKeySequence(self.tr("Shift+F12", "Debug|Edit Breakpoint")), 0,
576 self.dbgSetBpActGrp, 'dbg_edit_breakpoint')
577 self.dbgEditBpAct.setStatusTip(self.tr('Edit Breakpoint'))
578 self.dbgEditBpAct.setWhatsThis(self.tr(
579 """<b>Edit Breakpoint</b>"""
580 """<p>Opens a dialog to edit the breakpoints properties."""
581 """ It works at the current line of the current editor.</p>"""
582 ))
583 self.dbgEditBpAct.triggered.connect(self.__editBreakpoint)
584 self.actions.append(self.dbgEditBpAct)
585
586 self.dbgNextBpAct = E5Action(
587 self.tr('Next Breakpoint'),
588 UI.PixmapCache.getIcon("breakpointNext"),
589 self.tr('Next Breakpoint'),
590 QKeySequence(
591 self.tr("Ctrl+Shift+PgDown", "Debug|Next Breakpoint")), 0,
592 self.dbgSetBpActGrp, 'dbg_next_breakpoint')
593 self.dbgNextBpAct.setStatusTip(self.tr('Next Breakpoint'))
594 self.dbgNextBpAct.setWhatsThis(self.tr(
595 """<b>Next Breakpoint</b>"""
596 """<p>Go to next breakpoint of the current editor.</p>"""
597 ))
598 self.dbgNextBpAct.triggered.connect(self.__nextBreakpoint)
599 self.actions.append(self.dbgNextBpAct)
600
601 self.dbgPrevBpAct = E5Action(
602 self.tr('Previous Breakpoint'),
603 UI.PixmapCache.getIcon("breakpointPrevious"),
604 self.tr('Previous Breakpoint'),
605 QKeySequence(
606 self.tr("Ctrl+Shift+PgUp", "Debug|Previous Breakpoint")),
607 0, self.dbgSetBpActGrp, 'dbg_previous_breakpoint')
608 self.dbgPrevBpAct.setStatusTip(self.tr('Previous Breakpoint'))
609 self.dbgPrevBpAct.setWhatsThis(self.tr(
610 """<b>Previous Breakpoint</b>"""
611 """<p>Go to previous breakpoint of the current editor.</p>"""
612 ))
613 self.dbgPrevBpAct.triggered.connect(self.__previousBreakpoint)
614 self.actions.append(self.dbgPrevBpAct)
615
616 act = E5Action(
617 self.tr('Clear Breakpoints'),
618 self.tr('Clear Breakpoints'),
619 0, 0,
620 self.dbgSetBpActGrp, 'dbg_clear_breakpoint')
621 act.setStatusTip(self.tr('Clear Breakpoints'))
622 act.setWhatsThis(self.tr(
623 """<b>Clear Breakpoints</b>"""
624 """<p>Clear breakpoints of all editors.</p>"""
625 ))
626 act.triggered.connect(self.__clearBreakpoints)
627 self.actions.append(act)
628
629 self.debugActGrp.setEnabled(False)
630 self.dbgSetBpActGrp.setEnabled(False)
631 self.runAct.setEnabled(False)
632 self.runProjectAct.setEnabled(False)
633 self.profileAct.setEnabled(False)
634 self.profileProjectAct.setEnabled(False)
635 self.coverageAct.setEnabled(False)
636 self.coverageProjectAct.setEnabled(False)
637 self.debugAct.setEnabled(False)
638 self.debugProjectAct.setEnabled(False)
639 self.restartAct.setEnabled(False)
640 self.stopAct.setEnabled(False)
641
642 def initMenus(self):
643 """
644 Public slot to initialize the project menu.
645
646 @return the generated menu
647 """
648 dmenu = QMenu(self.tr('&Debug'), self.parent())
649 dmenu.setTearOffEnabled(True)
650 smenu = QMenu(self.tr('Sta&rt'), self.parent())
651 smenu.setTearOffEnabled(True)
652 self.breakpointsMenu = QMenu(self.tr('&Breakpoints'), dmenu)
653
654 smenu.addAction(self.restartAct)
655 smenu.addAction(self.stopAct)
656 smenu.addSeparator()
657 smenu.addAction(self.runAct)
658 smenu.addAction(self.runProjectAct)
659 smenu.addSeparator()
660 smenu.addAction(self.debugAct)
661 smenu.addAction(self.debugProjectAct)
662 smenu.addSeparator()
663 smenu.addAction(self.profileAct)
664 smenu.addAction(self.profileProjectAct)
665 smenu.addSeparator()
666 smenu.addAction(self.coverageAct)
667 smenu.addAction(self.coverageProjectAct)
668
669 dmenu.addActions(self.debugActGrp.actions())
670 dmenu.addSeparator()
671 dmenu.addActions(self.dbgSetBpActGrp.actions())
672 self.menuBreakpointsAct = dmenu.addMenu(self.breakpointsMenu)
673 dmenu.addSeparator()
674 dmenu.addAction(self.dbgFilterAct)
675 dmenu.addAction(self.excFilterAct)
676 dmenu.addAction(self.excIgnoreFilterAct)
677
678 self.breakpointsMenu.aboutToShow.connect(self.__showBreakpointsMenu)
679 self.breakpointsMenu.triggered.connect(self.__breakpointSelected)
680 dmenu.aboutToShow.connect(self.__showDebugMenu)
681
682 return smenu, dmenu
683
684 def initToolbars(self, toolbarManager):
685 """
686 Public slot to initialize the debug toolbars.
687
688 @param toolbarManager reference to a toolbar manager object
689 (E5ToolBarManager)
690 @return the generated toolbars (list of QToolBar)
691 """
692 starttb = QToolBar(self.tr("Start"), self.ui)
693 starttb.setIconSize(UI.Config.ToolBarIconSize)
694 starttb.setObjectName("StartToolbar")
695 starttb.setToolTip(self.tr('Start'))
696
697 starttb.addAction(self.restartAct)
698 starttb.addAction(self.stopAct)
699 starttb.addSeparator()
700 starttb.addAction(self.runAct)
701 starttb.addAction(self.runProjectAct)
702 starttb.addSeparator()
703 starttb.addAction(self.debugAct)
704 starttb.addAction(self.debugProjectAct)
705
706 debugtb = QToolBar(self.tr("Debug"), self.ui)
707 debugtb.setIconSize(UI.Config.ToolBarIconSize)
708 debugtb.setObjectName("DebugToolbar")
709 debugtb.setToolTip(self.tr('Debug'))
710
711 debugtb.addActions(self.debugActGrp.actions())
712 debugtb.addSeparator()
713 debugtb.addAction(self.dbgToggleBpAct)
714 debugtb.addAction(self.dbgEditBpAct)
715 debugtb.addAction(self.dbgNextBpAct)
716 debugtb.addAction(self.dbgPrevBpAct)
717
718 toolbarManager.addToolBar(starttb, starttb.windowTitle())
719 toolbarManager.addToolBar(debugtb, debugtb.windowTitle())
720 toolbarManager.addAction(self.profileAct, starttb.windowTitle())
721 toolbarManager.addAction(self.profileProjectAct, starttb.windowTitle())
722 toolbarManager.addAction(self.coverageAct, starttb.windowTitle())
723 toolbarManager.addAction(self.coverageProjectAct,
724 starttb.windowTitle())
725
726 return [starttb, debugtb]
727
728 def setArgvHistory(self, argsStr, clearHistories=False, history=None):
729 """
730 Public slot to initialize the argv history.
731
732 @param argsStr the commandline arguments (string)
733 @param clearHistories flag indicating, that the list should
734 be cleared (boolean)
735 @param history list of history entries to be set (list of strings)
736 """
737 if clearHistories:
738 del self.argvHistory[1:]
739 elif history is not None:
740 self.argvHistory = history[:]
741 else:
742 if argsStr in self.argvHistory:
743 self.argvHistory.remove(argsStr)
744 self.argvHistory.insert(0, argsStr)
745
746 def setWdHistory(self, wdStr, clearHistories=False, history=None):
747 """
748 Public slot to initialize the wd history.
749
750 @param wdStr the working directory (string)
751 @param clearHistories flag indicating, that the list should
752 be cleared (boolean)
753 @param history list of history entries to be set (list of strings)
754 """
755 if clearHistories:
756 del self.wdHistory[1:]
757 elif history is not None:
758 self.wdHistory = history[:]
759 else:
760 if wdStr in self.wdHistory:
761 self.wdHistory.remove(wdStr)
762 self.wdHistory.insert(0, wdStr)
763
764 def setEnvHistory(self, envStr, clearHistories=False, history=None):
765 """
766 Public slot to initialize the env history.
767
768 @param envStr the environment settings (string)
769 @param clearHistories flag indicating, that the list should
770 be cleared (boolean)
771 @param history list of history entries to be set (list of strings)
772 """
773 if clearHistories:
774 del self.envHistory[1:]
775 elif history is not None:
776 self.envHistory = history[:]
777 else:
778 if envStr in self.envHistory:
779 self.envHistory.remove(envStr)
780 self.envHistory.insert(0, envStr)
781
782 def setExceptionReporting(self, exceptions):
783 """
784 Public slot to initialize the exception reporting flag.
785
786 @param exceptions flag indicating exception reporting status (boolean)
787 """
788 self.exceptions = exceptions
789
790 def setExcList(self, excList):
791 """
792 Public slot to initialize the exceptions type list.
793
794 @param excList list of exception types (list of strings)
795 """
796 self.excList = excList[:] # keep a copy
797
798 def setExcIgnoreList(self, excIgnoreList):
799 """
800 Public slot to initialize the ignored exceptions type list.
801
802 @param excIgnoreList list of ignored exception types (list of strings)
803 """
804 self.excIgnoreList = excIgnoreList[:] # keep a copy
805
806 def setAutoClearShell(self, autoClearShell):
807 """
808 Public slot to initialize the autoClearShell flag.
809
810 @param autoClearShell flag indicating, that the interpreter window
811 should be cleared (boolean)
812 """
813 self.autoClearShell = autoClearShell
814
815 def setTracePython(self, tracePython):
816 """
817 Public slot to initialize the trace Python flag.
818
819 @param tracePython flag indicating if the Python library should be
820 traced as well (boolean)
821 """
822 self.tracePython = tracePython
823
824 def setAutoContinue(self, autoContinue):
825 """
826 Public slot to initialize the autoContinue flag.
827
828 @param autoContinue flag indicating, that the debugger should not
829 stop at the first executable line (boolean)
830 """
831 self.autoContinue = autoContinue
832
833 def __editorOpened(self, fn):
834 """
835 Private slot to handle the editorOpened signal.
836
837 @param fn filename of the opened editor
838 """
839 self.editorOpen = True
840
841 editor = self.viewmanager.getOpenEditor(fn) if fn else None
842 self.__checkActions(editor)
843
844 def __lastEditorClosed(self):
845 """
846 Private slot to handle the closeProgram signal.
847 """
848 self.editorOpen = False
849 self.debugAct.setEnabled(False)
850 self.runAct.setEnabled(False)
851 self.profileAct.setEnabled(False)
852 self.coverageAct.setEnabled(False)
853 self.debugActGrp.setEnabled(False)
854 self.dbgSetBpActGrp.setEnabled(False)
855 self.lastAction = -1
856 if not self.projectOpen:
857 self.restartAct.setEnabled(False)
858 self.lastDebuggedFile = None
859 self.lastStartAction = 0
860 self.clientType = ""
861
862 def __checkActions(self, editor):
863 """
864 Private slot to check some actions for their enable/disable status.
865
866 @param editor editor window
867 """
868 fn = editor.getFileName() if editor else None
869
870 cap = 0
871 if fn:
872 for language in self.debugServer.getSupportedLanguages():
873 exts = self.debugServer.getExtensions(language)
874 if fn.endswith(exts):
875 cap = self.debugServer.getClientCapabilities(language)
876 break
877 else:
878 if editor.isPy3File():
879 cap = self.debugServer.getClientCapabilities('Python3')
880 elif editor.isRubyFile():
881 cap = self.debugServer.getClientCapabilities('Ruby')
882
883 if not self.passive:
884 self.runAct.setEnabled(cap & HasInterpreter)
885 self.coverageAct.setEnabled(cap & HasCoverage)
886 self.profileAct.setEnabled(cap & HasProfiler)
887 self.debugAct.setEnabled(cap & HasDebugger)
888 self.dbgSetBpActGrp.setEnabled(cap & HasDebugger)
889 if editor.curLineHasBreakpoint():
890 self.dbgEditBpAct.setEnabled(True)
891 else:
892 self.dbgEditBpAct.setEnabled(False)
893 if editor.hasBreakpoints():
894 self.dbgNextBpAct.setEnabled(True)
895 self.dbgPrevBpAct.setEnabled(True)
896 else:
897 self.dbgNextBpAct.setEnabled(False)
898 self.dbgPrevBpAct.setEnabled(False)
899 else:
900 self.runAct.setEnabled(False)
901 self.coverageAct.setEnabled(False)
902 self.profileAct.setEnabled(False)
903 self.debugAct.setEnabled(False)
904 self.dbgSetBpActGrp.setEnabled(False)
905
906 def __cursorChanged(self, editor):
907 """
908 Private slot handling the cursorChanged signal of the viewmanager.
909
910 @param editor editor window
911 """
912 if editor is None:
913 return
914
915 if editor.isPyFile() or editor.isRubyFile():
916 if editor.curLineHasBreakpoint():
917 self.dbgEditBpAct.setEnabled(True)
918 else:
919 self.dbgEditBpAct.setEnabled(False)
920 if editor.hasBreakpoints():
921 self.dbgNextBpAct.setEnabled(True)
922 self.dbgPrevBpAct.setEnabled(True)
923 else:
924 self.dbgNextBpAct.setEnabled(False)
925 self.dbgPrevBpAct.setEnabled(False)
926
927 def __projectOpened(self):
928 """
929 Private slot to handle the projectOpened signal.
930 """
931 self.projectOpen = True
932 cap = self.debugServer.getClientCapabilities(
933 self.project.getProjectLanguage())
934 if not self.passive:
935 self.debugProjectAct.setEnabled(cap & HasDebugger)
936 self.runProjectAct.setEnabled(cap & HasInterpreter)
937 self.profileProjectAct.setEnabled(cap & HasProfiler)
938 self.coverageProjectAct.setEnabled(cap & HasCoverage)
939
940 def __projectClosed(self):
941 """
942 Private slot to handle the projectClosed signal.
943 """
944 self.projectOpen = False
945 self.runProjectAct.setEnabled(False)
946 self.profileProjectAct.setEnabled(False)
947 self.coverageProjectAct.setEnabled(False)
948 self.debugProjectAct.setEnabled(False)
949
950 if not self.editorOpen:
951 self.restartAct.setEnabled(False)
952 self.lastDebuggedFile = None
953 self.lastStartAction = 0
954 self.clientType = ""
955
956 def clearHistories(self):
957 """
958 Public method to clear the various debug histories.
959 """
960 self.argvHistory = []
961 self.wdHistory = []
962 self.envHistory = []
963 self.multiprocessNoDebugHistory = []
964
965 Preferences.Prefs.settings.setValue(
966 'DebugInfo/ArgumentsHistory', self.argvHistory)
967 Preferences.Prefs.settings.setValue(
968 'DebugInfo/WorkingDirectoryHistory', self.wdHistory)
969 Preferences.Prefs.settings.setValue(
970 'DebugInfo/EnvironmentHistory', self.envHistory)
971 Preferences.Prefs.settings.setValue(
972 'DebugInfo/MultiprocessNoDebugHistory',
973 self.multiprocessNoDebugHistory)
974
975 def shutdown(self):
976 """
977 Public method to perform shutdown actions.
978 """
979 # Just save the 10 most recent entries
980 del self.argvHistory[10:]
981 del self.wdHistory[10:]
982 del self.envHistory[10:]
983
984 Preferences.Prefs.settings.setValue(
985 'DebugInfo/VirtualEnvironment', self.lastUsedVenvName)
986 Preferences.Prefs.settings.setValue(
987 'DebugInfo/ArgumentsHistory', self.argvHistory)
988 Preferences.Prefs.settings.setValue(
989 'DebugInfo/WorkingDirectoryHistory', self.wdHistory)
990 Preferences.Prefs.settings.setValue(
991 'DebugInfo/EnvironmentHistory', self.envHistory)
992 Preferences.Prefs.settings.setValue(
993 'DebugInfo/Exceptions', self.excList)
994 Preferences.Prefs.settings.setValue(
995 'DebugInfo/IgnoredExceptions', self.excIgnoreList)
996 Preferences.Prefs.settings.setValue(
997 'DebugInfo/ReportExceptions', self.exceptions)
998 Preferences.Prefs.settings.setValue(
999 'DebugInfo/AutoClearShell', self.autoClearShell)
1000 Preferences.Prefs.settings.setValue(
1001 'DebugInfo/TracePython', self.tracePython)
1002 Preferences.Prefs.settings.setValue(
1003 'DebugInfo/AutoContinue', self.autoContinue)
1004 Preferences.Prefs.settings.setValue(
1005 'DebugInfo/EnableMultiprocess', self.enableMultiprocess)
1006 Preferences.Prefs.settings.setValue(
1007 'DebugInfo/MultiprocessNoDebugHistory',
1008 self.multiprocessNoDebugHistory)
1009 Preferences.Prefs.settings.setValue(
1010 'DebugInfo/OverrideGlobal',
1011 self.overrideGlobalConfig["enable"])
1012 Preferences.Prefs.settings.setValue(
1013 'DebugInfo/RedirectStdinStdout',
1014 self.overrideGlobalConfig["redirect"])
1015
1016 def shutdownServer(self):
1017 """
1018 Public method to shut down the debug server.
1019
1020 This is needed to cleanly close the sockets on Win OS.
1021
1022 @return always true
1023 """
1024 self.debugServer.shutdownServer()
1025 return True
1026
1027 def __resetUI(self, fullReset=True):
1028 """
1029 Private slot to reset the user interface.
1030
1031 @param fullReset flag indicating a full reset is required
1032 @type bool
1033 """
1034 self.lastAction = -1
1035 self.debugActGrp.setEnabled(False)
1036 self.__clientDebuggerIds.clear()
1037
1038 if not self.passive:
1039 if self.editorOpen:
1040 editor = self.viewmanager.activeWindow()
1041 else:
1042 editor = None
1043 self.__checkActions(editor)
1044
1045 self.debugProjectAct.setEnabled(self.projectOpen)
1046 self.runProjectAct.setEnabled(self.projectOpen)
1047 self.profileProjectAct.setEnabled(self.projectOpen)
1048 self.coverageProjectAct.setEnabled(self.projectOpen)
1049 if (
1050 self.lastDebuggedFile is not None and
1051 (self.editorOpen or self.projectOpen)
1052 ):
1053 self.restartAct.setEnabled(True)
1054 else:
1055 self.restartAct.setEnabled(False)
1056 self.stopAct.setEnabled(False)
1057
1058 self.resetUI.emit(fullReset)
1059
1060 def __clientDebuggerId(self, debuggerId):
1061 """
1062 Private slot to track the list of connected debuggers.
1063
1064 @param debuggerId ID of the debugger backend
1065 @type str
1066 """
1067 self.__clientDebuggerIds.add(debuggerId)
1068
1069 def __clientLine(self, fn, line, debuggerId, threadName, forStack):
1070 """
1071 Private method to handle a change to the current line.
1072
1073 @param fn filename
1074 @type str
1075 @param line linenumber
1076 @type int
1077 @param debuggerId ID of the debugger backend
1078 @type str
1079 @param threadName name of the thread signaling the event
1080 @type str
1081 @param forStack flag indicating this is for a stack dump
1082 @type bool
1083 """
1084 self.ui.raise_()
1085 self.ui.activateWindow()
1086 if self.ui.getViewProfile() != "debug":
1087 self.ui.setDebugProfile()
1088 self.viewmanager.setFileLine(fn, line)
1089 if not forStack:
1090 self.__getThreadList(debuggerId)
1091 self.__getClientVariables(debuggerId)
1092
1093 self.debugActGrp.setEnabled(True)
1094
1095 @pyqtSlot(str)
1096 def __clientDisconnected(self, debuggerId):
1097 """
1098 Private slot to handle a debug client disconnecting its control
1099 socket.
1100
1101 @param debuggerId ID of the debugger backend
1102 @type str
1103 """
1104 self.__clientDebuggerIds.discard(debuggerId)
1105
1106 if len(self.__clientDebuggerIds) == 0:
1107 self.viewmanager.exit()
1108 self.__resetUI(fullReset=False)
1109
1110 @pyqtSlot(str, int, str, bool, str)
1111 def __clientExit(self, program, status, message, quiet, debuggerId):
1112 """
1113 Private slot to handle the debugged program terminating.
1114
1115 @param program name of the exited program
1116 @type str
1117 @param status exit code of the debugged program
1118 @type int
1119 @param message exit message of the debugged program
1120 @type str
1121 @param quiet flag indicating to suppress exit info display
1122 @type bool
1123 @param debuggerId ID of the debugger backend
1124 @type str
1125 """
1126 self.__clientDisconnected(debuggerId)
1127
1128 if not quiet:
1129 if not program:
1130 program = self.ui.currentProg
1131
1132 if message:
1133 info = self.tr("Message: {0}").format(
1134 Utilities.html_uencode(message))
1135 else:
1136 info = ""
1137 if program is None:
1138 msg = self.tr(
1139 '<p>The program has terminated with an exit status of'
1140 ' {0}.</p><p>{1}</p>').format(status, info)
1141 else:
1142 msg = self.tr(
1143 '<p><b>{0}</b> has terminated with an exit status of'
1144 ' {1}.</p><p>{2}</p>').format(
1145 os.path.basename(program), status, info)
1146 if status != 0:
1147 timeout = 0
1148 kind = NotificationTypes.WARNING
1149 else:
1150 timeout = None
1151 kind = NotificationTypes.INFORMATION
1152 self.ui.showNotification(
1153 UI.PixmapCache.getPixmap("debug48"),
1154 self.tr("Program terminated"), msg, kind=kind,
1155 timeout=timeout)
1156
1157 def __lastClientExited(self):
1158 """
1159 Private slot handling the exit of the last client.
1160 """
1161 self.viewmanager.exit()
1162 self.__resetUI()
1163
1164 def __clientSyntaxError(self, message, filename, lineNo, characterNo):
1165 """
1166 Private method to handle a syntax error in the debugged program.
1167
1168 @param message message of the syntax error (string)
1169 @param filename translated filename of the syntax error position
1170 (string)
1171 @param lineNo line number of the syntax error position (integer)
1172 @param characterNo character number of the syntax error position
1173 (integer)
1174 """
1175 self.__resetUI()
1176 self.ui.raise_()
1177 self.ui.activateWindow()
1178
1179 if message is None:
1180 E5MessageBox.critical(
1181 self.ui, Program,
1182 self.tr(
1183 'The program being debugged contains an unspecified'
1184 ' syntax error.'))
1185 return
1186
1187 if not os.path.isabs(filename):
1188 if os.path.exists(os.path.join(self.project.getProjectPath(),
1189 filename)):
1190 filename = os.path.join(self.project.getProjectPath(),
1191 filename)
1192 else:
1193 ms = self.project.getMainScript(normalized=True)
1194 if ms is not None:
1195 d = os.path.dirname(ms)
1196 if os.path.exists(os.path.join(d, filename)):
1197 filename = os.path.join(d, filename)
1198 self.viewmanager.setFileLine(filename, lineNo, True, True)
1199 E5MessageBox.critical(
1200 self.ui, Program,
1201 self.tr('<p>The file <b>{0}</b> contains the syntax error'
1202 ' <b>{1}</b> at line <b>{2}</b>, character <b>{3}</b>.'
1203 '</p>')
1204 .format(filename, message, lineNo, characterNo))
1205
1206 def __clientException(self, exceptionType, exceptionMessage, stackTrace,
1207 debuggerId):
1208 """
1209 Private method to handle an exception of the debugged program.
1210
1211 @param exceptionType type of exception raised
1212 @type str
1213 @param exceptionMessage message given by the exception
1214 @type (str
1215 @param stackTrace list of stack entries
1216 @type list of str
1217 @param debuggerId ID of the debugger backend
1218 @type str
1219 """
1220 self.ui.raise_()
1221 QApplication.processEvents()
1222 if not exceptionType:
1223 E5MessageBox.critical(
1224 self.ui, Program,
1225 self.tr('An unhandled exception occured.'
1226 ' See the shell window for details.'))
1227 return
1228
1229 if (
1230 (self.exceptions and
1231 exceptionType not in self.excIgnoreList and
1232 (not len(self.excList) or
1233 (len(self.excList) and exceptionType in self.excList)
1234 )
1235 ) or exceptionType.startswith('unhandled')
1236 ):
1237 res = None
1238 if stackTrace:
1239 with contextlib.suppress(UnicodeError, OSError):
1240 file, line = stackTrace[0][:2]
1241 source, encoding = Utilities.readEncodedFile(file)
1242 source = source.splitlines(True)
1243 if len(source) >= line:
1244 lineFlags = Utilities.extractLineFlags(
1245 source[line - 1].strip())
1246 with contextlib.suppress(IndexError):
1247 lineFlags += Utilities.extractLineFlags(
1248 source[line].strip(), flagsLine=True)
1249 if "__IGNORE_EXCEPTION__" in lineFlags:
1250 res = E5MessageBox.No
1251 if res != E5MessageBox.No:
1252 self.viewmanager.setFileLine(
1253 stackTrace[0][0], stackTrace[0][1], True)
1254 if res != E5MessageBox.No:
1255 self.ui.activateWindow()
1256 if Preferences.getDebugger("BreakAlways"):
1257 res = E5MessageBox.Yes
1258 else:
1259 if stackTrace:
1260 if exceptionType.startswith('unhandled'):
1261 buttons = E5MessageBox.StandardButtons(
1262 E5MessageBox.No |
1263 E5MessageBox.Yes)
1264 else:
1265 buttons = E5MessageBox.StandardButtons(
1266 E5MessageBox.No |
1267 E5MessageBox.Yes |
1268 E5MessageBox.Ignore)
1269 res = E5MessageBox.critical(
1270 self.ui, Program,
1271 self.tr(
1272 '<p>The debugged program raised the exception'
1273 ' <b>{0}</b><br>"<b>{1}</b>"<br>'
1274 'File: <b>{2}</b>, Line: <b>{3}</b></p>'
1275 '<p>Break here?</p>')
1276 .format(
1277 exceptionType,
1278 Utilities.html_encode(exceptionMessage),
1279 stackTrace[0][0],
1280 stackTrace[0][1]),
1281 buttons,
1282 E5MessageBox.No)
1283 else:
1284 res = E5MessageBox.critical(
1285 self.ui, Program,
1286 self.tr(
1287 '<p>The debugged program raised the exception'
1288 ' <b>{0}</b><br>"<b>{1}</b>"</p>')
1289 .format(
1290 exceptionType,
1291 Utilities.html_encode(exceptionMessage)))
1292 if res == E5MessageBox.Yes:
1293 self.debugServer.setDebugging(True)
1294 self.exceptionInterrupt.emit()
1295 stack = []
1296 for fn, ln, func, args in stackTrace:
1297 stack.append((fn, ln, func, args))
1298 self.clientStack.emit(stack, debuggerId)
1299 self.__getClientVariables(debuggerId)
1300 self.__getClientDisassembly(debuggerId)
1301 self.ui.setDebugProfile()
1302 self.debugActGrp.setEnabled(True)
1303 return
1304 elif (
1305 res == E5MessageBox.Ignore and
1306 exceptionType not in self.excIgnoreList
1307 ):
1308 self.excIgnoreList.append(exceptionType)
1309
1310 if self.lastAction != -1:
1311 if self.lastAction == 2:
1312 self.__specialContinue(debuggerId)
1313 else:
1314 self.debugActions[self.lastAction](debuggerId)
1315 else:
1316 self.__continue(debuggerId)
1317
1318 def __clientSignal(self, message, filename, lineNo, funcName, funcArgs,
1319 debuggerId):
1320 """
1321 Private method to handle a signal generated on the client side.
1322
1323 @param message message of the syntax error
1324 @type str
1325 @param filename translated filename of the syntax error position
1326 @type str
1327 @param lineNo line number of the syntax error position
1328 @type int
1329 @param funcName name of the function causing the signal
1330 @type str
1331 @param funcArgs function arguments
1332 @type str
1333 @param debuggerId ID of the debugger backend
1334 @type str
1335 """
1336 self.ui.raise_()
1337 self.ui.activateWindow()
1338 QApplication.processEvents()
1339 self.viewmanager.setFileLine(filename, lineNo, True)
1340 E5MessageBox.critical(
1341 self.ui, Program,
1342 self.tr("""<p>The program generate the signal "{0}".<br/>"""
1343 """File: <b>{1}</b>, Line: <b>{2}</b></p>""").format(
1344 message, filename, lineNo))
1345
1346 def __clientGone(self, unplanned):
1347 """
1348 Private method to handle the disconnection of the debugger client.
1349
1350 @param unplanned True if the client died, False otherwise
1351 """
1352 self.__resetUI()
1353 if unplanned:
1354 E5MessageBox.information(
1355 self.ui, Program,
1356 self.tr('The program being debugged has terminated'
1357 ' unexpectedly.'))
1358
1359 def __getThreadList(self, debuggerId):
1360 """
1361 Private method to get the list of threads from the client.
1362
1363 @param debuggerId ID of the debugger backend
1364 @type str
1365 """
1366 self.debugServer.remoteThreadList(debuggerId)
1367
1368 def __clientThreadSet(self, debuggerId):
1369 """
1370 Private method to handle a change of the client's current thread.
1371
1372 @param debuggerId ID of the debugger backend
1373 @type str
1374 """
1375 self.debugServer.remoteClientVariables(
1376 debuggerId, 0, self.__localsVarFilterList)
1377
1378 def __getClientVariables(self, debuggerId):
1379 """
1380 Private method to request the global and local variables.
1381
1382 In the first step, the global variables are requested from the client.
1383 Once these have been received, the local variables are requested.
1384 This happens in the method '__clientVariables'.
1385
1386 @param debuggerId ID of the debugger backend
1387 @type str
1388 """
1389 # get globals first
1390 self.debugServer.remoteClientVariables(
1391 debuggerId, 1, self.__globalsVarFilterList)
1392 # the local variables are requested once we have received the globals
1393
1394 def __clientVariables(self, scope, variables, debuggerId):
1395 """
1396 Private method to write the clients variables to the user interface.
1397
1398 @param scope scope of the variables (-1 = empty locals, 1 = global,
1399 0 = local)
1400 @type int
1401 @param variables the list of variables from the client
1402 @type list
1403 @param debuggerId ID of the debugger backend
1404 @type str
1405 """
1406 if debuggerId == self.getSelectedDebuggerId():
1407 self.ui.activateDebugViewer()
1408 if scope > 0:
1409 self.debugViewer.showVariables(variables, True)
1410 if scope == 1:
1411 # now get the local variables
1412 self.debugServer.remoteClientVariables(
1413 self.getSelectedDebuggerId(),
1414 0, self.__localsVarFilterList)
1415 elif scope == 0:
1416 self.debugViewer.showVariables(variables, False)
1417 elif scope == -1:
1418 vlist = [(self.tr('No locals available.'), '', '')]
1419 self.debugViewer.showVariables(vlist, False)
1420
1421 def __clientVariable(self, scope, variables, debuggerId):
1422 """
1423 Private method to write the contents of a clients classvariable to
1424 the user interface.
1425
1426 @param scope scope of the variables (-1 = empty locals, 1 = global,
1427 0 = local)
1428 @type int
1429 @param variables the list of variables from the client
1430 @type list
1431 @param debuggerId ID of the debugger backend
1432 @type str
1433 """
1434 if debuggerId == self.getSelectedDebuggerId():
1435 self.ui.activateDebugViewer()
1436 if scope == 1:
1437 self.debugViewer.showVariable(variables, True)
1438 elif scope == 0:
1439 self.debugViewer.showVariable(variables, False)
1440
1441 def __getClientDisassembly(self, debuggerId):
1442 """
1443 Private method to ask the client for the latest traceback disassembly.
1444
1445 @param debuggerId ID of the debugger backend
1446 @type str
1447 """
1448 self.debugServer.remoteClientDisassembly(debuggerId)
1449
1450 def __clientBreakConditionError(self, filename, lineno, debuggerId):
1451 """
1452 Private method to handle a condition error of a breakpoint.
1453
1454 @param filename filename of the breakpoint
1455 @type str
1456 @param lineno line umber of the breakpoint
1457 @type int
1458 @param debuggerId ID of the debugger backend
1459 @type str
1460 """
1461 E5MessageBox.critical(
1462 self.ui,
1463 self.tr("Breakpoint Condition Error"),
1464 self.tr(
1465 """<p>The condition of the breakpoint <b>{0}, {1}</b>"""
1466 """ contains a syntax error.</p>""")
1467 .format(filename, lineno))
1468
1469 model = self.debugServer.getBreakPointModel()
1470 index = model.getBreakPointIndex(filename, lineno)
1471 if not index.isValid():
1472 return
1473
1474 bp = model.getBreakPointByIndex(index)
1475 if not bp:
1476 return
1477
1478 fn, line, cond, temp, enabled, count = bp[:6]
1479
1480 from .EditBreakpointDialog import EditBreakpointDialog
1481 dlg = EditBreakpointDialog(
1482 (fn, line), (cond, temp, enabled, count),
1483 [], self.ui, modal=True)
1484 if dlg.exec() == QDialog.DialogCode.Accepted:
1485 cond, temp, enabled, count = dlg.getData()
1486 model.setBreakPointByIndex(index, fn, line,
1487 (cond, temp, enabled, count))
1488
1489 def __clientWatchConditionError(self, cond, debuggerId):
1490 """
1491 Private method to handle a expression error of a watch expression.
1492
1493 Note: This can only happen for normal watch expressions
1494
1495 @param cond expression of the watch expression
1496 @type str
1497 @param debuggerId ID of the debugger backend
1498 @type str
1499 """
1500 E5MessageBox.critical(
1501 self.ui,
1502 self.tr("Watch Expression Error"),
1503 self.tr("""<p>The watch expression <b>{0}</b>"""
1504 """ contains a syntax error.</p>""")
1505 .format(cond))
1506
1507 model = self.debugServer.getWatchPointModel()
1508 index = model.getWatchPointIndex(cond)
1509 if not index.isValid():
1510 return
1511
1512 wp = model.getWatchPointByIndex(index)
1513 if not wp:
1514 return
1515
1516 cond, special, temp, enabled, count = wp[:5]
1517
1518 from .EditWatchpointDialog import EditWatchpointDialog
1519 dlg = EditWatchpointDialog(
1520 (cond, temp, enabled, count, special), self.ui)
1521 if dlg.exec() == QDialog.DialogCode.Accepted:
1522 cond, temp, enabled, count, special = dlg.getData()
1523
1524 # check for duplicates
1525 idx = model.getWatchPointIndex(cond, special)
1526 duplicate = (idx.isValid() and
1527 idx.internalPointer() != index.internalPointer())
1528 if duplicate:
1529 if not special:
1530 msg = self.tr(
1531 """<p>A watch expression '<b>{0}</b>'"""
1532 """ already exists.</p>"""
1533 ).format(Utilities.html_encode(cond))
1534 else:
1535 msg = self.tr(
1536 """<p>A watch expression '<b>{0}</b>'"""
1537 """ for the variable <b>{1}</b> already"""
1538 """ exists.</p>"""
1539 ).format(special, Utilities.html_encode(cond))
1540 E5MessageBox.warning(
1541 self.ui,
1542 self.tr("Watch expression already exists"),
1543 msg)
1544 model.deleteWatchPointByIndex(index)
1545 else:
1546 model.setWatchPointByIndex(index, cond, special,
1547 (temp, enabled, count))
1548
1549 def __configureVariablesFilters(self):
1550 """
1551 Private slot for displaying the variables filter configuration dialog.
1552 """
1553 from .VariablesFilterDialog import VariablesFilterDialog
1554 dlg = VariablesFilterDialog(self.ui, 'Filter Dialog', True)
1555 dlg.setSelection(self.__localsVarFilterList,
1556 self.__globalsVarFilterList)
1557 if dlg.exec() == QDialog.DialogCode.Accepted:
1558 self.__localsVarFilterList, self.__globalsVarFilterList = (
1559 dlg.getSelection()
1560 )
1561 self.debugViewer.setVariablesFilter(
1562 self.__globalsVarFilterList, self.__localsVarFilterList)
1563
1564 def __configureExceptionsFilter(self):
1565 """
1566 Private slot for displaying the exception filter dialog.
1567 """
1568 from .ExceptionsFilterDialog import ExceptionsFilterDialog
1569 dlg = ExceptionsFilterDialog(self.excList, ignore=False)
1570 if dlg.exec() == QDialog.DialogCode.Accepted:
1571 self.excList = dlg.getExceptionsList()[:] # keep a copy
1572
1573 def __configureIgnoredExceptions(self):
1574 """
1575 Private slot for displaying the ignored exceptions dialog.
1576 """
1577 from .ExceptionsFilterDialog import ExceptionsFilterDialog
1578 dlg = ExceptionsFilterDialog(self.excIgnoreList, ignore=True)
1579 if dlg.exec() == QDialog.DialogCode.Accepted:
1580 self.excIgnoreList = dlg.getExceptionsList()[:] # keep a copy
1581
1582 def __toggleBreakpoint(self):
1583 """
1584 Private slot to handle the 'Set/Reset breakpoint' action.
1585 """
1586 self.viewmanager.activeWindow().menuToggleBreakpoint()
1587
1588 def __editBreakpoint(self):
1589 """
1590 Private slot to handle the 'Edit breakpoint' action.
1591 """
1592 self.viewmanager.activeWindow().menuEditBreakpoint()
1593
1594 def __nextBreakpoint(self):
1595 """
1596 Private slot to handle the 'Next breakpoint' action.
1597 """
1598 self.viewmanager.activeWindow().menuNextBreakpoint()
1599
1600 def __previousBreakpoint(self):
1601 """
1602 Private slot to handle the 'Previous breakpoint' action.
1603 """
1604 self.viewmanager.activeWindow().menuPreviousBreakpoint()
1605
1606 def __clearBreakpoints(self):
1607 """
1608 Private slot to handle the 'Clear breakpoints' action.
1609 """
1610 self.debugServer.getBreakPointModel().deleteAll()
1611
1612 def __showDebugMenu(self):
1613 """
1614 Private method to set up the debug menu.
1615 """
1616 bpCount = self.debugServer.getBreakPointModel().rowCount()
1617 self.menuBreakpointsAct.setEnabled(bpCount > 0)
1618
1619 def __showBreakpointsMenu(self):
1620 """
1621 Private method to handle the show breakpoints menu signal.
1622 """
1623 self.breakpointsMenu.clear()
1624
1625 model = self.debugServer.getBreakPointModel()
1626 for row in range(model.rowCount()):
1627 index = model.index(row, 0)
1628 filename, line, cond = model.getBreakPointByIndex(index)[:3]
1629 formattedCond = " : {0}".format(cond[:20]) if cond else ""
1630 bpSuffix = " : {0:d}{1}".format(line, formattedCond)
1631 act = self.breakpointsMenu.addAction(
1632 "{0}{1}".format(
1633 Utilities.compactPath(
1634 filename,
1635 self.ui.maxMenuFilePathLen - len(bpSuffix)),
1636 bpSuffix))
1637 act.setData([filename, line])
1638
1639 def __breakpointSelected(self, act):
1640 """
1641 Private method to handle the breakpoint selected signal.
1642
1643 @param act reference to the action that triggered (QAction)
1644 """
1645 qvList = act.data()
1646 filename = qvList[0]
1647 line = qvList[1]
1648 self.viewmanager.openSourceFile(filename, line)
1649
1650 def __compileChangedProjectFiles(self):
1651 """
1652 Private method to signal compilation of changed forms and resources
1653 is wanted.
1654 """
1655 if Preferences.getProject("AutoCompileForms"):
1656 self.compileForms.emit()
1657 if Preferences.getProject("AutoCompileResources"):
1658 self.compileResources.emit()
1659 if Preferences.getProject("AutoExecuteMake"):
1660 self.executeMake.emit()
1661 QApplication.processEvents()
1662
1663 def __coverageScript(self):
1664 """
1665 Private slot to handle the coverage of script action.
1666 """
1667 self.__doCoverage(False)
1668
1669 def __coverageProject(self):
1670 """
1671 Private slot to handle the coverage of project action.
1672 """
1673 self.__compileChangedProjectFiles()
1674 self.__doCoverage(True)
1675
1676 def __doCoverage(self, runProject):
1677 """
1678 Private method to handle the coverage actions.
1679
1680 @param runProject flag indicating coverage of the current project
1681 (True) or script (false)
1682 """
1683 from .StartDialog import StartDialog
1684
1685 self.__resetUI()
1686 doNotStart = False
1687
1688 # Get the command line arguments, the working directory and the
1689 # exception reporting flag.
1690 cap = (
1691 self.tr("Coverage of Project")
1692 if runProject else
1693 self.tr("Coverage of Script")
1694 )
1695 dlg = StartDialog(
1696 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
1697 self.envHistory, self.exceptions, self.ui, 2,
1698 autoClearShell=self.autoClearShell,
1699 configOverride=self.overrideGlobalConfig)
1700 if dlg.exec() == QDialog.DialogCode.Accepted:
1701 (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
1702 console) = dlg.getData()
1703 configOverride = dlg.getGlobalOverrideData()
1704 eraseCoverage = dlg.getCoverageData()
1705
1706 if runProject:
1707 fn = self.project.getMainScript(True)
1708 if fn is None:
1709 E5MessageBox.critical(
1710 self.ui,
1711 self.tr("Coverage of Project"),
1712 self.tr(
1713 "There is no main script defined for the"
1714 " current project. Aborting"))
1715 return
1716
1717 if (
1718 Preferences.getDebugger("Autosave") and
1719 not self.project.saveAllScripts(reportSyntaxErrors=True)
1720 ):
1721 doNotStart = True
1722
1723 # save the info for later use
1724 self.project.setDbgInfo(
1725 lastUsedVenvName, argv, wd, env, exceptions, self.excList,
1726 self.excIgnoreList, clearShell,
1727 configOverride=configOverride
1728 )
1729
1730 self.lastStartAction = 6
1731 self.clientType = self.project.getProjectLanguage()
1732 else:
1733 editor = self.viewmanager.activeWindow()
1734 if editor is None:
1735 return
1736
1737 if (
1738 not self.viewmanager.checkDirty(
1739 editor, Preferences.getDebugger("Autosave")) or
1740 editor.getFileName() is None
1741 ):
1742 return
1743
1744 fn = editor.getFileName()
1745 self.lastStartAction = 5
1746 self.clientType = editor.determineFileType()
1747
1748 # save the filename for use by the restart method
1749 self.lastDebuggedFile = fn
1750 self.restartAct.setEnabled(True)
1751
1752 # save the most recently used virtual environment
1753 self.lastUsedVenvName = lastUsedVenvName
1754
1755 # This moves any previous occurrence of these arguments to the head
1756 # of the list.
1757 self.setArgvHistory(argv)
1758 self.setWdHistory(wd)
1759 self.setEnvHistory(env)
1760
1761 # Save the exception flags
1762 self.exceptions = exceptions
1763
1764 # Save the erase coverage flag
1765 self.eraseCoverage = eraseCoverage
1766
1767 # Save the clear interpreter flag
1768 self.autoClearShell = clearShell
1769
1770 # Save the run in console flag
1771 self.runInConsole = console
1772
1773 # Save the global config override data
1774 self.overrideGlobalConfig = copy.deepcopy(configOverride)
1775
1776 # Hide all error highlights
1777 self.viewmanager.unhighlight()
1778
1779 if not doNotStart:
1780 if runProject and self.project.getProjectType() in [
1781 "E6Plugin"]:
1782 argv = '--plugin="{0}" {1}'.format(fn, argv)
1783 fn = os.path.join(getConfig('ericDir'), "eric6.py")
1784
1785 self.debugViewer.initCallStackViewer(runProject)
1786
1787 # Ask the client to open the new program.
1788 self.debugServer.remoteCoverage(
1789 lastUsedVenvName, fn, argv, wd, env,
1790 autoClearShell=self.autoClearShell, erase=eraseCoverage,
1791 forProject=runProject, runInConsole=console,
1792 clientType=self.clientType,
1793 configOverride=self.overrideGlobalConfig)
1794
1795 self.stopAct.setEnabled(True)
1796
1797 if dlg.clearHistories():
1798 self.setArgvHistory("", clearHistories=True)
1799 self.setWdHistory("", clearHistories=True)
1800 self.setEnvHistory("", clearHistories=True)
1801 self.setMultiprocessNoDebugHistory("", clearHistories=True)
1802 elif dlg.historiesModified():
1803 argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
1804 self.setArgvHistory("", history=argvHistory)
1805 self.setWdHistory("", history=wdHistory)
1806 self.setEnvHistory("", history=envHistory)
1807
1808 def __profileScript(self):
1809 """
1810 Private slot to handle the profile script action.
1811 """
1812 self.__doProfile(False)
1813
1814 def __profileProject(self):
1815 """
1816 Private slot to handle the profile project action.
1817 """
1818 self.__compileChangedProjectFiles()
1819 self.__doProfile(True)
1820
1821 def __doProfile(self, runProject):
1822 """
1823 Private method to handle the profile actions.
1824
1825 @param runProject flag indicating profiling of the current project
1826 (True) or script (False)
1827 """
1828 from .StartDialog import StartDialog
1829
1830 self.__resetUI()
1831 doNotStart = False
1832
1833 # Get the command line arguments, the working directory and the
1834 # exception reporting flag.
1835 cap = (
1836 self.tr("Profile of Project")
1837 if runProject else
1838 self.tr("Profile of Script")
1839 )
1840 dlg = StartDialog(
1841 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
1842 self.envHistory, self.exceptions, self.ui, 3,
1843 autoClearShell=self.autoClearShell,
1844 configOverride=self.overrideGlobalConfig)
1845 if dlg.exec() == QDialog.DialogCode.Accepted:
1846 (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
1847 console) = dlg.getData()
1848 configOverride = dlg.getGlobalOverrideData()
1849 eraseTimings = dlg.getProfilingData()
1850
1851 if runProject:
1852 fn = self.project.getMainScript(True)
1853 if fn is None:
1854 E5MessageBox.critical(
1855 self.ui,
1856 self.tr("Profile of Project"),
1857 self.tr(
1858 "There is no main script defined for the"
1859 " current project. Aborting"))
1860 return
1861
1862 if (
1863 Preferences.getDebugger("Autosave") and
1864 not self.project.saveAllScripts(reportSyntaxErrors=True)
1865 ):
1866 doNotStart = True
1867
1868 # save the info for later use
1869 self.project.setDbgInfo(
1870 lastUsedVenvName, argv, wd, env, exceptions, self.excList,
1871 self.excIgnoreList, clearShell,
1872 configOverride=configOverride
1873 )
1874
1875 self.lastStartAction = 8
1876 self.clientType = self.project.getProjectLanguage()
1877 else:
1878 editor = self.viewmanager.activeWindow()
1879 if editor is None:
1880 return
1881
1882 if (
1883 not self.viewmanager.checkDirty(
1884 editor, Preferences.getDebugger("Autosave")) or
1885 editor.getFileName() is None
1886 ):
1887 return
1888
1889 fn = editor.getFileName()
1890 self.lastStartAction = 7
1891 self.clientType = editor.determineFileType()
1892
1893 # save the filename for use by the restart method
1894 self.lastDebuggedFile = fn
1895 self.restartAct.setEnabled(True)
1896
1897 # save the most recently used virtual environment
1898 self.lastUsedVenvName = lastUsedVenvName
1899
1900 # This moves any previous occurrence of these arguments to the head
1901 # of the list.
1902 self.setArgvHistory(argv)
1903 self.setWdHistory(wd)
1904 self.setEnvHistory(env)
1905
1906 # Save the exception flags
1907 self.exceptions = exceptions
1908
1909 # Save the erase timing flag
1910 self.eraseTimings = eraseTimings
1911
1912 # Save the clear interpreter flag
1913 self.autoClearShell = clearShell
1914
1915 # Save the run in console flag
1916 self.runInConsole = console
1917
1918 # Save the global config override data
1919 self.overrideGlobalConfig = copy.deepcopy(configOverride)
1920
1921 # Hide all error highlights
1922 self.viewmanager.unhighlight()
1923
1924 if not doNotStart:
1925 if runProject and self.project.getProjectType() in [
1926 "E6Plugin"]:
1927 argv = '--plugin="{0}" {1}'.format(fn, argv)
1928 fn = os.path.join(getConfig('ericDir'), "eric6.py")
1929
1930 self.debugViewer.initCallStackViewer(runProject)
1931
1932 # Ask the client to open the new program.
1933 self.debugServer.remoteProfile(
1934 lastUsedVenvName, fn, argv, wd, env,
1935 autoClearShell=self.autoClearShell, erase=eraseTimings,
1936 forProject=runProject, runInConsole=console,
1937 clientType=self.clientType,
1938 configOverride=self.overrideGlobalConfig)
1939
1940 self.stopAct.setEnabled(True)
1941
1942 if dlg.clearHistories():
1943 self.setArgvHistory("", clearHistories=True)
1944 self.setWdHistory("", clearHistories=True)
1945 self.setEnvHistory("", clearHistories=True)
1946 self.setMultiprocessNoDebugHistory("", clearHistories=True)
1947 elif dlg.historiesModified():
1948 argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
1949 self.setArgvHistory("", history=argvHistory)
1950 self.setWdHistory("", history=wdHistory)
1951 self.setEnvHistory("", history=envHistory)
1952
1953 def __runScript(self):
1954 """
1955 Private slot to handle the run script action.
1956 """
1957 self.__doRun(False)
1958
1959 def __runProject(self):
1960 """
1961 Private slot to handle the run project action.
1962 """
1963 self.__compileChangedProjectFiles()
1964 self.__doRun(True)
1965
1966 def __doRun(self, runProject):
1967 """
1968 Private method to handle the run actions.
1969
1970 @param runProject flag indicating running the current project (True)
1971 or script (False)
1972 """
1973 from .StartDialog import StartDialog
1974
1975 self.__resetUI()
1976 doNotStart = False
1977
1978 # Get the command line arguments, the working directory and the
1979 # exception reporting flag.
1980 cap = (
1981 self.tr("Run Project")
1982 if runProject else
1983 self.tr("Run Script")
1984 )
1985 dlg = StartDialog(
1986 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
1987 self.envHistory, self.exceptions, self.ui, 1,
1988 autoClearShell=self.autoClearShell,
1989 configOverride=self.overrideGlobalConfig)
1990 if dlg.exec() == QDialog.DialogCode.Accepted:
1991 (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
1992 console) = dlg.getData()
1993 configOverride = dlg.getGlobalOverrideData()
1994
1995 if runProject:
1996 fn = self.project.getMainScript(True)
1997 if fn is None:
1998 E5MessageBox.critical(
1999 self.ui,
2000 self.tr("Run Project"),
2001 self.tr(
2002 "There is no main script defined for the"
2003 " current project. Aborting"))
2004 return
2005
2006 if (
2007 Preferences.getDebugger("Autosave") and
2008 not self.project.saveAllScripts(reportSyntaxErrors=True)
2009 ):
2010 doNotStart = True
2011
2012 # save the info for later use
2013 self.project.setDbgInfo(
2014 lastUsedVenvName, argv, wd, env, exceptions, self.excList,
2015 self.excIgnoreList, clearShell,
2016 configOverride=configOverride
2017 )
2018
2019 self.lastStartAction = 4
2020 self.clientType = self.project.getProjectLanguage()
2021 else:
2022 editor = self.viewmanager.activeWindow()
2023 if editor is None:
2024 return
2025
2026 if (
2027 not self.viewmanager.checkDirty(
2028 editor,
2029 Preferences.getDebugger("Autosave")) or
2030 editor.getFileName() is None
2031 ):
2032 return
2033
2034 fn = editor.getFileName()
2035 self.lastStartAction = 3
2036 self.clientType = editor.determineFileType()
2037
2038 # save the filename for use by the restart method
2039 self.lastDebuggedFile = fn
2040 self.restartAct.setEnabled(True)
2041
2042 # save the most recently used virtual environment
2043 self.lastUsedVenvName = lastUsedVenvName
2044
2045 # This moves any previous occurrence of these arguments to the head
2046 # of the list.
2047 self.setArgvHistory(argv)
2048 self.setWdHistory(wd)
2049 self.setEnvHistory(env)
2050
2051 # Save the exception flags
2052 self.exceptions = exceptions
2053
2054 # Save the clear interpreter flag
2055 self.autoClearShell = clearShell
2056
2057 # Save the run in console flag
2058 self.runInConsole = console
2059
2060 # Save the global config override data
2061 self.overrideGlobalConfig = copy.deepcopy(configOverride)
2062
2063 # Hide all error highlights
2064 self.viewmanager.unhighlight()
2065
2066 if not doNotStart:
2067 if runProject and self.project.getProjectType() in [
2068 "E6Plugin"]:
2069 argv = '--plugin="{0}" {1}'.format(fn, argv)
2070 fn = os.path.join(getConfig('ericDir'), "eric6.py")
2071
2072 self.debugViewer.initCallStackViewer(runProject)
2073
2074 # Ask the client to open the new program.
2075 self.debugServer.remoteRun(
2076 lastUsedVenvName, fn, argv, wd, env,
2077 autoClearShell=self.autoClearShell, forProject=runProject,
2078 runInConsole=console, clientType=self.clientType,
2079 configOverride=self.overrideGlobalConfig)
2080
2081 self.stopAct.setEnabled(True)
2082
2083 if dlg.clearHistories():
2084 self.setArgvHistory("", clearHistories=True)
2085 self.setWdHistory("", clearHistories=True)
2086 self.setEnvHistory("", clearHistories=True)
2087 self.setMultiprocessNoDebugHistory("", clearHistories=True)
2088 elif dlg.historiesModified():
2089 argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
2090 self.setArgvHistory("", history=argvHistory)
2091 self.setWdHistory("", history=wdHistory)
2092 self.setEnvHistory("", history=envHistory)
2093
2094 def __debugScript(self):
2095 """
2096 Private slot to handle the debug script action.
2097 """
2098 self.__doDebug(False)
2099
2100 def __debugProject(self):
2101 """
2102 Private slot to handle the debug project action.
2103 """
2104 self.__compileChangedProjectFiles()
2105 self.__doDebug(True)
2106
2107 def __doDebug(self, debugProject):
2108 """
2109 Private method to handle the debug actions.
2110
2111 @param debugProject flag indicating debugging the current project
2112 (True) or script (False)
2113 """
2114 from .StartDialog import StartDialog
2115
2116 self.__resetUI()
2117 doNotStart = False
2118
2119 # Get the command line arguments, the working directory and the
2120 # exception reporting flag.
2121 cap = (
2122 self.tr("Debug Project")
2123 if debugProject else
2124 self.tr("Debug Script")
2125 )
2126 dlg = StartDialog(
2127 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
2128 self.envHistory, self.exceptions, self.ui, 0,
2129 tracePython=self.tracePython, autoClearShell=self.autoClearShell,
2130 autoContinue=self.autoContinue,
2131 enableMultiprocess=self.enableMultiprocess,
2132 multiprocessNoDebugHistory=self.multiprocessNoDebugHistory,
2133 configOverride=self.overrideGlobalConfig)
2134 if dlg.exec() == QDialog.DialogCode.Accepted:
2135 (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
2136 console) = dlg.getData()
2137 configOverride = dlg.getGlobalOverrideData()
2138 (tracePython, autoContinue, enableMultiprocess,
2139 multiprocessNoDebug) = dlg.getDebugData()
2140
2141 if debugProject:
2142 fn = self.project.getMainScript(True)
2143 if fn is None:
2144 E5MessageBox.critical(
2145 self.ui,
2146 self.tr("Debug Project"),
2147 self.tr(
2148 "There is no main script defined for the"
2149 " current project. No debugging possible."))
2150 return
2151
2152 if (
2153 Preferences.getDebugger("Autosave") and
2154 not self.project.saveAllScripts(reportSyntaxErrors=True)
2155 ):
2156 doNotStart = True
2157
2158 # save the info for later use
2159 self.project.setDbgInfo(
2160 lastUsedVenvName, argv, wd, env, exceptions, self.excList,
2161 self.excIgnoreList, clearShell, tracePython=tracePython,
2162 autoContinue=autoContinue,
2163 enableMultiprocess=enableMultiprocess,
2164 multiprocessNoDebug=multiprocessNoDebug,
2165 configOverride=configOverride
2166 )
2167
2168 self.lastStartAction = 2
2169 self.clientType = self.project.getProjectLanguage()
2170 else:
2171 editor = self.viewmanager.activeWindow()
2172 if editor is None:
2173 return
2174
2175 if (
2176 not self.viewmanager.checkDirty(
2177 editor, Preferences.getDebugger("Autosave")) or
2178 editor.getFileName() is None
2179 ):
2180 return
2181
2182 fn = editor.getFileName()
2183 self.lastStartAction = 1
2184 self.clientType = editor.determineFileType()
2185
2186 # save the filename for use by the restart method
2187 self.lastDebuggedFile = fn
2188 self.restartAct.setEnabled(True)
2189
2190 # save the most recently used virtual environment
2191 self.lastUsedVenvName = lastUsedVenvName
2192
2193 # This moves any previous occurrence of these arguments to the head
2194 # of the list.
2195 self.setArgvHistory(argv)
2196 self.setWdHistory(wd)
2197 self.setEnvHistory(env)
2198
2199 # Save the exception flags
2200 self.exceptions = exceptions
2201
2202 # Save the tracePython flag
2203 self.tracePython = tracePython
2204
2205 # Save the clear interpreter flag
2206 self.autoClearShell = clearShell
2207
2208 # Save the run in console flag
2209 self.runInConsole = console
2210
2211 # Save the auto continue flag
2212 self.autoContinue = autoContinue
2213
2214 # Save the multiprocess debugging data
2215 self.enableMultiprocess = enableMultiprocess
2216 self.setMultiprocessNoDebugHistory(multiprocessNoDebug)
2217
2218 # Save the global config override data
2219 self.overrideGlobalConfig = copy.deepcopy(configOverride)
2220
2221 # Hide all error highlights
2222 self.viewmanager.unhighlight()
2223
2224 if not doNotStart:
2225 if debugProject and self.project.getProjectType() in [
2226 "E6Plugin"]:
2227 argv = '--plugin="{0}" {1}'.format(fn, argv)
2228 fn = os.path.join(getConfig('ericDir'), "eric6.py")
2229 tracePython = True # override flag because it must be true
2230
2231 self.debugViewer.initCallStackViewer(debugProject)
2232
2233 # Ask the client to send call trace info
2234 enableCallTrace = self.debugViewer.isCallTraceEnabled()
2235 self.debugViewer.clearCallTrace()
2236 self.debugViewer.setCallTraceToProjectMode(debugProject)
2237
2238 # Ask the client to open the new program.
2239 self.debugServer.remoteLoad(
2240 lastUsedVenvName, fn, argv, wd, env,
2241 autoClearShell=self.autoClearShell,
2242 tracePython=tracePython,
2243 autoContinue=autoContinue, forProject=debugProject,
2244 runInConsole=console, clientType=self.clientType,
2245 enableCallTrace=enableCallTrace,
2246 enableMultiprocess=enableMultiprocess,
2247 multiprocessNoDebug=multiprocessNoDebug,
2248 configOverride=self.overrideGlobalConfig)
2249
2250 if (
2251 self.debugServer.isClientProcessUp() and
2252 self.debugServer.getClientType() == self.clientType
2253 ):
2254 # Signal that we have started a debugging session
2255 self.debuggingStarted.emit(fn)
2256
2257 self.stopAct.setEnabled(True)
2258
2259 if dlg.clearHistories():
2260 self.setArgvHistory("", clearHistories=True)
2261 self.setWdHistory("", clearHistories=True)
2262 self.setEnvHistory("", clearHistories=True)
2263 self.setMultiprocessNoDebugHistory("", clearHistories=True)
2264 elif dlg.historiesModified():
2265 (argvHistory, wdHistory, envHistory,
2266 noDebugHistory) = dlg.getHistories()
2267 self.setArgvHistory("", history=argvHistory)
2268 self.setWdHistory("", history=wdHistory)
2269 self.setEnvHistory("", history=envHistory)
2270 self.setMultiprocessNoDebugHistory("", history=noDebugHistory)
2271
2272 def __doRestart(self):
2273 """
2274 Private slot to handle the restart action to restart the last
2275 debugged file.
2276 """
2277 self.__resetUI()
2278 doNotStart = False
2279
2280 # first save any changes
2281 if self.lastStartAction in [1, 3, 5, 7, 9]:
2282 editor = self.viewmanager.getOpenEditor(self.lastDebuggedFile)
2283 if (
2284 editor and
2285 not self.viewmanager.checkDirty(
2286 editor, Preferences.getDebugger("Autosave"))
2287 ):
2288 return
2289 forProject = False
2290 elif self.lastStartAction in [2, 4, 6, 8, 10]:
2291 if (
2292 Preferences.getDebugger("Autosave") and
2293 not self.project.saveAllScripts(reportSyntaxErrors=True)
2294 ):
2295 doNotStart = True
2296 self.__compileChangedProjectFiles()
2297 forProject = True
2298 else:
2299 return # should not happen
2300
2301 # get the saved stuff
2302 venvName = self.lastUsedVenvName
2303 wd = self.wdHistory[0]
2304 argv = self.argvHistory[0]
2305 fn = self.lastDebuggedFile
2306 env = self.envHistory[0]
2307
2308 # Hide all error highlights
2309 self.viewmanager.unhighlight()
2310
2311 if not doNotStart:
2312 if forProject and self.project.getProjectType() in [
2313 "E6Plugin"]:
2314 argv = '--plugin="{0}" {1}'.format(fn, argv)
2315 fn = os.path.join(getConfig('ericDir'), "eric6.py")
2316
2317 self.debugViewer.initCallStackViewer(forProject)
2318
2319 if self.lastStartAction in [1, 2]:
2320 # Ask the client to send call trace info
2321 enableCallTrace = self.debugViewer.isCallTraceEnabled()
2322 self.debugViewer.clearCallTrace()
2323 self.debugViewer.setCallTraceToProjectMode(forProject)
2324 multiprocessNoDebug = self.multiprocessNoDebugHistory[0]
2325
2326 # Ask the client to debug the new program.
2327 self.debugServer.remoteLoad(
2328 venvName, fn, argv, wd, env,
2329 autoClearShell=self.autoClearShell,
2330 tracePython=self.tracePython,
2331 autoContinue=self.autoContinue,
2332 forProject=forProject,
2333 runInConsole=self.runInConsole,
2334 clientType=self.clientType,
2335 enableCallTrace=enableCallTrace,
2336 enableMultiprocess=self.enableMultiprocess,
2337 multiprocessNoDebug=multiprocessNoDebug,
2338 configOverride=self.overrideGlobalConfig)
2339
2340 # Signal that we have started a debugging session
2341 self.debuggingStarted.emit(fn)
2342
2343 elif self.lastStartAction in [3, 4]:
2344 # Ask the client to run the new program.
2345 self.debugServer.remoteRun(
2346 venvName, fn, argv, wd, env,
2347 autoClearShell=self.autoClearShell,
2348 forProject=forProject,
2349 runInConsole=self.runInConsole,
2350 clientType=self.clientType,
2351 configOverride=self.overrideGlobalConfig)
2352
2353 elif self.lastStartAction in [5, 6]:
2354 # Ask the client to coverage run the new program.
2355 self.debugServer.remoteCoverage(
2356 venvName, fn, argv, wd, env,
2357 autoClearShell=self.autoClearShell,
2358 erase=self.eraseCoverage,
2359 forProject=forProject,
2360 runInConsole=self.runInConsole,
2361 clientType=self.clientType,
2362 configOverride=self.overrideGlobalConfig)
2363
2364 elif self.lastStartAction in [7, 8]:
2365 # Ask the client to profile run the new program.
2366 self.debugServer.remoteProfile(
2367 venvName, fn, argv, wd, env,
2368 autoClearShell=self.autoClearShell,
2369 erase=self.eraseTimings,
2370 forProject=forProject,
2371 runInConsole=self.runInConsole,
2372 clientType=self.clientType,
2373 configOverride=self.overrideGlobalConfig)
2374
2375 self.stopAct.setEnabled(True)
2376
2377 def __stopScript(self):
2378 """
2379 Private slot to stop the running script.
2380 """
2381 self.debugServer.startClient(False)
2382
2383 def __passiveDebugStarted(self, fn, exc):
2384 """
2385 Private slot to handle a passive debug session start.
2386
2387 @param fn filename of the debugged script
2388 @param exc flag to enable exception reporting of the IDE (boolean)
2389 """
2390 # Hide all error highlights
2391 self.viewmanager.unhighlight()
2392
2393 # Set filename of script being debugged
2394 self.ui.currentProg = fn
2395
2396 # Set exception reporting
2397 self.setExceptionReporting(exc)
2398
2399 # Signal that we have started a debugging session
2400 self.debuggingStarted.emit(fn)
2401
2402 # Initialize the call stack viewer
2403 self.debugViewer.initCallStackViewer(False)
2404
2405 def __continue(self, debuggerId=""):
2406 """
2407 Private method to handle the Continue action.
2408
2409 @param debuggerId ID of the debugger backend
2410 @type str
2411 """
2412 if not debuggerId:
2413 debuggerId = self.getSelectedDebuggerId()
2414
2415 self.lastAction = 0
2416 self.__enterRemote()
2417 self.debugServer.remoteContinue(debuggerId)
2418
2419 def __specialContinue(self, debuggerId=""):
2420 """
2421 Private method to handle the Special Continue action.
2422
2423 @param debuggerId ID of the debugger backend
2424 @type str
2425 """
2426 if not debuggerId:
2427 debuggerId = self.getSelectedDebuggerId()
2428
2429 self.lastAction = 2
2430 self.__enterRemote()
2431 self.debugServer.remoteContinue(debuggerId, 1)
2432
2433 def __step(self, debuggerId=""):
2434 """
2435 Private method to handle the Step action.
2436
2437 @param debuggerId ID of the debugger backend
2438 @type str
2439 """
2440 if not debuggerId:
2441 debuggerId = self.getSelectedDebuggerId()
2442
2443 self.lastAction = 1
2444 self.__enterRemote()
2445 self.debugServer.remoteStep(debuggerId)
2446
2447 def __stepOver(self, debuggerId=""):
2448 """
2449 Private method to handle the Step Over action.
2450
2451 @param debuggerId ID of the debugger backend
2452 @type str
2453 """
2454 if not debuggerId:
2455 debuggerId = self.getSelectedDebuggerId()
2456
2457 self.lastAction = 2
2458 self.__enterRemote()
2459 self.debugServer.remoteStepOver(debuggerId)
2460
2461 def __stepOut(self, debuggerId=""):
2462 """
2463 Private method to handle the Step Out action.
2464
2465 @param debuggerId ID of the debugger backend
2466 @type str
2467 """
2468 if not debuggerId:
2469 debuggerId = self.getSelectedDebuggerId()
2470
2471 self.lastAction = 3
2472 self.__enterRemote()
2473 self.debugServer.remoteStepOut(debuggerId)
2474
2475 def __stepQuit(self, debuggerId=""):
2476 """
2477 Private method to handle the Step Quit action.
2478
2479 @param debuggerId ID of the debugger backend
2480 @type str
2481 """
2482 if not debuggerId:
2483 debuggerId = self.getSelectedDebuggerId()
2484
2485 self.lastAction = 4
2486 self.__enterRemote()
2487 self.debugServer.remoteStepQuit(debuggerId)
2488 self.__resetUI()
2489
2490 def __runToCursor(self, debuggerId=""):
2491 """
2492 Private method to handle the Run to Cursor action.
2493
2494 @param debuggerId ID of the debugger backend
2495 @type str
2496 """
2497 if not debuggerId:
2498 debuggerId = self.getSelectedDebuggerId()
2499
2500 self.lastAction = 0
2501 aw = self.viewmanager.activeWindow()
2502 line = aw.getCursorPosition()[0] + 1
2503 self.__enterRemote()
2504 self.debugServer.remoteBreakpoint(
2505 self.getSelectedDebuggerId(),
2506 aw.getFileName(), line, 1, None, 1)
2507 self.debugServer.remoteContinue(debuggerId)
2508
2509 def __runUntil(self, debuggerId=""):
2510 """
2511 Private method to handle the Run Until action.
2512
2513 @param debuggerId ID of the debugger backend
2514 @type str
2515 """
2516 if not debuggerId:
2517 debuggerId = self.getSelectedDebuggerId()
2518
2519 self.lastAction = 0
2520 aw = self.viewmanager.activeWindow()
2521 line = aw.getCursorPosition()[0] + 1
2522 self.__enterRemote()
2523 self.debugServer.remoteContinueUntil(debuggerId, line)
2524
2525 def __moveInstructionPointer(self, debuggerId=""):
2526 """
2527 Private method to move the instruction pointer to a different line.
2528
2529 @param debuggerId ID of the debugger backend
2530 @type str
2531 """
2532 if not debuggerId:
2533 debuggerId = self.getSelectedDebuggerId()
2534
2535 self.lastAction = 0
2536 aw = self.viewmanager.activeWindow()
2537 line = aw.getCursorPosition()[0] + 1
2538 self.debugServer.remoteMoveIP(debuggerId, line)
2539
2540 def __enterRemote(self):
2541 """
2542 Private method to update the user interface.
2543
2544 This method is called just prior to executing some of
2545 the program being debugged.
2546 """
2547 # Disable further debug commands from the user.
2548 self.debugActGrp.setEnabled(False)
2549
2550 self.viewmanager.unhighlight(True)
2551
2552 def getActions(self):
2553 """
2554 Public method to get a list of all actions.
2555
2556 @return list of all actions (list of E5Action)
2557 """
2558 return self.actions[:]
2559
2560 def getSelectedDebuggerId(self):
2561 """
2562 Public method to get the currently selected debugger ID.
2563
2564 @return selected debugger ID
2565 @rtype str
2566 """
2567 return self.debugViewer.getSelectedDebuggerId()
2568
2569 def setDebugActionsEnabled(self, enable):
2570 """
2571 Public method to set the enabled state of the debug actions.
2572
2573 @param enable enable state to be set
2574 @type bool
2575 """
2576 self.debugActGrp.setEnabled(enable)
2577
2578 def setMultiprocessNoDebugHistory(self, noDebugList, clearHistories=False,
2579 history=None):
2580 """
2581 Public slot to initialize the no debug list history.
2582
2583 @param noDebugList whitespace separated list of programs not to be
2584 debugged
2585 @type str
2586 @param clearHistories flag indicating, that the list should be cleared
2587 @type bool
2588 @param history list of history entries to be set
2589 @type list of str
2590 """
2591 if clearHistories:
2592 del self.multiprocessNoDebugHistory[1:]
2593 elif history is not None:
2594 self.multiprocessNoDebugHistory = history[:]
2595 else:
2596 if noDebugList in self.multiprocessNoDebugHistory:
2597 self.multiprocessNoDebugHistory.remove(noDebugList)
2598 self.multiprocessNoDebugHistory.insert(0, noDebugList)
2599
2600 def setEnableMultiprocess(self, enableMultiprocess):
2601 """
2602 Public slot to initialize the enableMultiprocess flag.
2603
2604 @param enableMultiprocess flag indicating, that the debugger should be
2605 run in multi process mode
2606 @type bool
2607 """
2608 self.enableMultiprocess = enableMultiprocess
2609
2610 def setEnableGlobalConfigOverride(self, overrideData):
2611 """
2612 Public method to initialize the global config override data.
2613
2614 @param overrideData dictionary containing a flag indicating to enable
2615 global config override and a flag indicating to redirect
2616 stdin/stdout/stderr
2617 @type dict
2618 """
2619 self.overrideGlobalConfig = copy.deepcopy(overrideData)

eric ide

mercurial