1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing the debugger UI. 8""" 9 10import os 11import copy 12import contextlib 13 14from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt 15from PyQt5.QtGui import QKeySequence 16from PyQt5.QtWidgets import QMenu, QToolBar, QApplication, QDialog 17 18from E5Gui.E5Action import E5Action, createActionGroup 19from E5Gui import E5MessageBox 20 21from UI.Info import Program 22from UI.NotificationWidget import NotificationTypes 23 24from .DebugClientCapabilities import ( 25 HasDebugger, HasInterpreter, HasProfiler, HasCoverage 26) 27 28import Preferences 29import Utilities 30import UI.PixmapCache 31import UI.Config 32 33from eric6config import getConfig 34 35 36class 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 if self.debugServer.isDebugging(): 1376 self.debugServer.remoteClientVariables( 1377 debuggerId, 0, self.__localsVarFilterList) 1378 1379 def __getClientVariables(self, debuggerId): 1380 """ 1381 Private method to request the global and local variables. 1382 1383 In the first step, the global variables are requested from the client. 1384 Once these have been received, the local variables are requested. 1385 This happens in the method '__clientVariables'. 1386 1387 @param debuggerId ID of the debugger backend 1388 @type str 1389 """ 1390 # get globals first 1391 self.debugServer.remoteClientVariables( 1392 debuggerId, 1, self.__globalsVarFilterList) 1393 # the local variables are requested once we have received the globals 1394 1395 def __clientVariables(self, scope, variables, debuggerId): 1396 """ 1397 Private method to write the clients variables to the user interface. 1398 1399 @param scope scope of the variables 1400 (-2 = no frame found, -1 = empty locals, 1 = global, 0 = local) 1401 @type int 1402 @param variables the list of variables from the client 1403 @type list 1404 @param debuggerId ID of the debugger backend 1405 @type str 1406 """ 1407 if debuggerId == self.getSelectedDebuggerId() and scope > -2: 1408 self.ui.activateDebugViewer() 1409 if scope > 0: 1410 self.debugViewer.showVariables(variables, True) 1411 if scope == 1: 1412 # now get the local variables 1413 self.debugServer.remoteClientVariables( 1414 self.getSelectedDebuggerId(), 1415 0, self.__localsVarFilterList) 1416 elif scope == 0: 1417 self.debugViewer.showVariables(variables, False) 1418 elif scope == -1: 1419 vlist = [(self.tr('No locals available.'), '', '')] 1420 self.debugViewer.showVariables(vlist, False) 1421 1422 def __clientVariable(self, scope, variables, debuggerId): 1423 """ 1424 Private method to write the contents of a clients classvariable to 1425 the user interface. 1426 1427 @param scope scope of the variables (-1 = empty locals, 1 = global, 1428 0 = local) 1429 @type int 1430 @param variables the list of variables from the client 1431 @type list 1432 @param debuggerId ID of the debugger backend 1433 @type str 1434 """ 1435 if debuggerId == self.getSelectedDebuggerId(): 1436 self.ui.activateDebugViewer() 1437 if scope == 1: 1438 self.debugViewer.showVariable(variables, True) 1439 elif scope == 0: 1440 self.debugViewer.showVariable(variables, False) 1441 1442 def __getClientDisassembly(self, debuggerId): 1443 """ 1444 Private method to ask the client for the latest traceback disassembly. 1445 1446 @param debuggerId ID of the debugger backend 1447 @type str 1448 """ 1449 self.debugServer.remoteClientDisassembly(debuggerId) 1450 1451 def __clientBreakConditionError(self, filename, lineno, debuggerId): 1452 """ 1453 Private method to handle a condition error of a breakpoint. 1454 1455 @param filename filename of the breakpoint 1456 @type str 1457 @param lineno line umber of the breakpoint 1458 @type int 1459 @param debuggerId ID of the debugger backend 1460 @type str 1461 """ 1462 E5MessageBox.critical( 1463 self.ui, 1464 self.tr("Breakpoint Condition Error"), 1465 self.tr( 1466 """<p>The condition of the breakpoint <b>{0}, {1}</b>""" 1467 """ contains a syntax error.</p>""") 1468 .format(filename, lineno)) 1469 1470 model = self.debugServer.getBreakPointModel() 1471 index = model.getBreakPointIndex(filename, lineno) 1472 if not index.isValid(): 1473 return 1474 1475 bp = model.getBreakPointByIndex(index) 1476 if not bp: 1477 return 1478 1479 fn, line, cond, temp, enabled, count = bp[:6] 1480 1481 from .EditBreakpointDialog import EditBreakpointDialog 1482 dlg = EditBreakpointDialog( 1483 (fn, line), (cond, temp, enabled, count), 1484 [], self.ui, modal=True) 1485 if dlg.exec() == QDialog.DialogCode.Accepted: 1486 cond, temp, enabled, count = dlg.getData() 1487 model.setBreakPointByIndex(index, fn, line, 1488 (cond, temp, enabled, count)) 1489 1490 def __clientWatchConditionError(self, cond, debuggerId): 1491 """ 1492 Private method to handle a expression error of a watch expression. 1493 1494 Note: This can only happen for normal watch expressions 1495 1496 @param cond expression of the watch expression 1497 @type str 1498 @param debuggerId ID of the debugger backend 1499 @type str 1500 """ 1501 E5MessageBox.critical( 1502 self.ui, 1503 self.tr("Watch Expression Error"), 1504 self.tr("""<p>The watch expression <b>{0}</b>""" 1505 """ contains a syntax error.</p>""") 1506 .format(cond)) 1507 1508 model = self.debugServer.getWatchPointModel() 1509 index = model.getWatchPointIndex(cond) 1510 if not index.isValid(): 1511 return 1512 1513 wp = model.getWatchPointByIndex(index) 1514 if not wp: 1515 return 1516 1517 cond, special, temp, enabled, count = wp[:5] 1518 1519 from .EditWatchpointDialog import EditWatchpointDialog 1520 dlg = EditWatchpointDialog( 1521 (cond, temp, enabled, count, special), self.ui) 1522 if dlg.exec() == QDialog.DialogCode.Accepted: 1523 cond, temp, enabled, count, special = dlg.getData() 1524 1525 # check for duplicates 1526 idx = model.getWatchPointIndex(cond, special) 1527 duplicate = (idx.isValid() and 1528 idx.internalPointer() != index.internalPointer()) 1529 if duplicate: 1530 if not special: 1531 msg = self.tr( 1532 """<p>A watch expression '<b>{0}</b>'""" 1533 """ already exists.</p>""" 1534 ).format(Utilities.html_encode(cond)) 1535 else: 1536 msg = self.tr( 1537 """<p>A watch expression '<b>{0}</b>'""" 1538 """ for the variable <b>{1}</b> already""" 1539 """ exists.</p>""" 1540 ).format(special, Utilities.html_encode(cond)) 1541 E5MessageBox.warning( 1542 self.ui, 1543 self.tr("Watch expression already exists"), 1544 msg) 1545 model.deleteWatchPointByIndex(index) 1546 else: 1547 model.setWatchPointByIndex(index, cond, special, 1548 (temp, enabled, count)) 1549 1550 def __configureVariablesFilters(self): 1551 """ 1552 Private slot for displaying the variables filter configuration dialog. 1553 """ 1554 from .VariablesFilterDialog import VariablesFilterDialog 1555 dlg = VariablesFilterDialog(self.ui, 'Filter Dialog', True) 1556 dlg.setSelection(self.__localsVarFilterList, 1557 self.__globalsVarFilterList) 1558 if dlg.exec() == QDialog.DialogCode.Accepted: 1559 self.__localsVarFilterList, self.__globalsVarFilterList = ( 1560 dlg.getSelection() 1561 ) 1562 self.debugViewer.setVariablesFilter( 1563 self.__globalsVarFilterList, self.__localsVarFilterList) 1564 1565 def __configureExceptionsFilter(self): 1566 """ 1567 Private slot for displaying the exception filter dialog. 1568 """ 1569 from .ExceptionsFilterDialog import ExceptionsFilterDialog 1570 dlg = ExceptionsFilterDialog(self.excList, ignore=False) 1571 if dlg.exec() == QDialog.DialogCode.Accepted: 1572 self.excList = dlg.getExceptionsList()[:] # keep a copy 1573 1574 def __configureIgnoredExceptions(self): 1575 """ 1576 Private slot for displaying the ignored exceptions dialog. 1577 """ 1578 from .ExceptionsFilterDialog import ExceptionsFilterDialog 1579 dlg = ExceptionsFilterDialog(self.excIgnoreList, ignore=True) 1580 if dlg.exec() == QDialog.DialogCode.Accepted: 1581 self.excIgnoreList = dlg.getExceptionsList()[:] # keep a copy 1582 1583 def __toggleBreakpoint(self): 1584 """ 1585 Private slot to handle the 'Set/Reset breakpoint' action. 1586 """ 1587 self.viewmanager.activeWindow().menuToggleBreakpoint() 1588 1589 def __editBreakpoint(self): 1590 """ 1591 Private slot to handle the 'Edit breakpoint' action. 1592 """ 1593 self.viewmanager.activeWindow().menuEditBreakpoint() 1594 1595 def __nextBreakpoint(self): 1596 """ 1597 Private slot to handle the 'Next breakpoint' action. 1598 """ 1599 self.viewmanager.activeWindow().menuNextBreakpoint() 1600 1601 def __previousBreakpoint(self): 1602 """ 1603 Private slot to handle the 'Previous breakpoint' action. 1604 """ 1605 self.viewmanager.activeWindow().menuPreviousBreakpoint() 1606 1607 def __clearBreakpoints(self): 1608 """ 1609 Private slot to handle the 'Clear breakpoints' action. 1610 """ 1611 self.debugServer.getBreakPointModel().deleteAll() 1612 1613 def __showDebugMenu(self): 1614 """ 1615 Private method to set up the debug menu. 1616 """ 1617 bpCount = self.debugServer.getBreakPointModel().rowCount() 1618 self.menuBreakpointsAct.setEnabled(bpCount > 0) 1619 1620 def __showBreakpointsMenu(self): 1621 """ 1622 Private method to handle the show breakpoints menu signal. 1623 """ 1624 self.breakpointsMenu.clear() 1625 1626 model = self.debugServer.getBreakPointModel() 1627 for row in range(model.rowCount()): 1628 index = model.index(row, 0) 1629 filename, line, cond = model.getBreakPointByIndex(index)[:3] 1630 formattedCond = " : {0}".format(cond[:20]) if cond else "" 1631 bpSuffix = " : {0:d}{1}".format(line, formattedCond) 1632 act = self.breakpointsMenu.addAction( 1633 "{0}{1}".format( 1634 Utilities.compactPath( 1635 filename, 1636 self.ui.maxMenuFilePathLen - len(bpSuffix)), 1637 bpSuffix)) 1638 act.setData([filename, line]) 1639 1640 def __breakpointSelected(self, act): 1641 """ 1642 Private method to handle the breakpoint selected signal. 1643 1644 @param act reference to the action that triggered (QAction) 1645 """ 1646 qvList = act.data() 1647 filename = qvList[0] 1648 line = qvList[1] 1649 self.viewmanager.openSourceFile(filename, line) 1650 1651 def __compileChangedProjectFiles(self): 1652 """ 1653 Private method to signal compilation of changed forms and resources 1654 is wanted. 1655 """ 1656 if Preferences.getProject("AutoCompileForms"): 1657 self.compileForms.emit() 1658 if Preferences.getProject("AutoCompileResources"): 1659 self.compileResources.emit() 1660 if Preferences.getProject("AutoExecuteMake"): 1661 self.executeMake.emit() 1662 QApplication.processEvents() 1663 1664 def __coverageScript(self): 1665 """ 1666 Private slot to handle the coverage of script action. 1667 """ 1668 self.__doCoverage(False) 1669 1670 def __coverageProject(self): 1671 """ 1672 Private slot to handle the coverage of project action. 1673 """ 1674 self.__compileChangedProjectFiles() 1675 self.__doCoverage(True) 1676 1677 def __doCoverage(self, runProject): 1678 """ 1679 Private method to handle the coverage actions. 1680 1681 @param runProject flag indicating coverage of the current project 1682 (True) or script (false) 1683 """ 1684 from .StartDialog import StartDialog 1685 1686 self.__resetUI() 1687 doNotStart = False 1688 1689 # Get the command line arguments, the working directory and the 1690 # exception reporting flag. 1691 cap = ( 1692 self.tr("Coverage of Project") 1693 if runProject else 1694 self.tr("Coverage of Script") 1695 ) 1696 dlg = StartDialog( 1697 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory, 1698 self.envHistory, self.exceptions, self.ui, 2, 1699 autoClearShell=self.autoClearShell, 1700 configOverride=self.overrideGlobalConfig) 1701 if dlg.exec() == QDialog.DialogCode.Accepted: 1702 (lastUsedVenvName, argv, wd, env, exceptions, clearShell, 1703 console) = dlg.getData() 1704 configOverride = dlg.getGlobalOverrideData() 1705 eraseCoverage = dlg.getCoverageData() 1706 1707 if runProject: 1708 fn = self.project.getMainScript(True) 1709 if fn is None: 1710 E5MessageBox.critical( 1711 self.ui, 1712 self.tr("Coverage of Project"), 1713 self.tr( 1714 "There is no main script defined for the" 1715 " current project. Aborting")) 1716 return 1717 1718 if ( 1719 Preferences.getDebugger("Autosave") and 1720 not self.project.saveAllScripts(reportSyntaxErrors=True) 1721 ): 1722 doNotStart = True 1723 1724 # save the info for later use 1725 self.project.setDbgInfo( 1726 lastUsedVenvName, argv, wd, env, exceptions, self.excList, 1727 self.excIgnoreList, clearShell, 1728 configOverride=configOverride 1729 ) 1730 1731 self.lastStartAction = 6 1732 self.clientType = self.project.getProjectLanguage() 1733 else: 1734 editor = self.viewmanager.activeWindow() 1735 if editor is None: 1736 return 1737 1738 if ( 1739 not self.viewmanager.checkDirty( 1740 editor, Preferences.getDebugger("Autosave")) or 1741 editor.getFileName() is None 1742 ): 1743 return 1744 1745 fn = editor.getFileName() 1746 self.lastStartAction = 5 1747 self.clientType = editor.determineFileType() 1748 1749 # save the filename for use by the restart method 1750 self.lastDebuggedFile = fn 1751 self.restartAct.setEnabled(True) 1752 1753 # save the most recently used virtual environment 1754 self.lastUsedVenvName = lastUsedVenvName 1755 1756 # This moves any previous occurrence of these arguments to the head 1757 # of the list. 1758 self.setArgvHistory(argv) 1759 self.setWdHistory(wd) 1760 self.setEnvHistory(env) 1761 1762 # Save the exception flags 1763 self.exceptions = exceptions 1764 1765 # Save the erase coverage flag 1766 self.eraseCoverage = eraseCoverage 1767 1768 # Save the clear interpreter flag 1769 self.autoClearShell = clearShell 1770 1771 # Save the run in console flag 1772 self.runInConsole = console 1773 1774 # Save the global config override data 1775 self.overrideGlobalConfig = copy.deepcopy(configOverride) 1776 1777 # Hide all error highlights 1778 self.viewmanager.unhighlight() 1779 1780 if not doNotStart: 1781 if runProject and self.project.getProjectType() in [ 1782 "E6Plugin"]: 1783 argv = '--plugin="{0}" {1}'.format(fn, argv) 1784 fn = os.path.join(getConfig('ericDir'), "eric6.py") 1785 1786 self.debugViewer.initCallStackViewer(runProject) 1787 1788 # Ask the client to open the new program. 1789 self.debugServer.remoteCoverage( 1790 lastUsedVenvName, fn, argv, wd, env, 1791 autoClearShell=self.autoClearShell, erase=eraseCoverage, 1792 forProject=runProject, runInConsole=console, 1793 clientType=self.clientType, 1794 configOverride=self.overrideGlobalConfig) 1795 1796 self.stopAct.setEnabled(True) 1797 1798 if dlg.clearHistories(): 1799 self.setArgvHistory("", clearHistories=True) 1800 self.setWdHistory("", clearHistories=True) 1801 self.setEnvHistory("", clearHistories=True) 1802 self.setMultiprocessNoDebugHistory("", clearHistories=True) 1803 elif dlg.historiesModified(): 1804 argvHistory, wdHistory, envHistory, _ = dlg.getHistories() 1805 self.setArgvHistory("", history=argvHistory) 1806 self.setWdHistory("", history=wdHistory) 1807 self.setEnvHistory("", history=envHistory) 1808 1809 def __profileScript(self): 1810 """ 1811 Private slot to handle the profile script action. 1812 """ 1813 self.__doProfile(False) 1814 1815 def __profileProject(self): 1816 """ 1817 Private slot to handle the profile project action. 1818 """ 1819 self.__compileChangedProjectFiles() 1820 self.__doProfile(True) 1821 1822 def __doProfile(self, runProject): 1823 """ 1824 Private method to handle the profile actions. 1825 1826 @param runProject flag indicating profiling of the current project 1827 (True) or script (False) 1828 """ 1829 from .StartDialog import StartDialog 1830 1831 self.__resetUI() 1832 doNotStart = False 1833 1834 # Get the command line arguments, the working directory and the 1835 # exception reporting flag. 1836 cap = ( 1837 self.tr("Profile of Project") 1838 if runProject else 1839 self.tr("Profile of Script") 1840 ) 1841 dlg = StartDialog( 1842 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory, 1843 self.envHistory, self.exceptions, self.ui, 3, 1844 autoClearShell=self.autoClearShell, 1845 configOverride=self.overrideGlobalConfig) 1846 if dlg.exec() == QDialog.DialogCode.Accepted: 1847 (lastUsedVenvName, argv, wd, env, exceptions, clearShell, 1848 console) = dlg.getData() 1849 configOverride = dlg.getGlobalOverrideData() 1850 eraseTimings = dlg.getProfilingData() 1851 1852 if runProject: 1853 fn = self.project.getMainScript(True) 1854 if fn is None: 1855 E5MessageBox.critical( 1856 self.ui, 1857 self.tr("Profile of Project"), 1858 self.tr( 1859 "There is no main script defined for the" 1860 " current project. Aborting")) 1861 return 1862 1863 if ( 1864 Preferences.getDebugger("Autosave") and 1865 not self.project.saveAllScripts(reportSyntaxErrors=True) 1866 ): 1867 doNotStart = True 1868 1869 # save the info for later use 1870 self.project.setDbgInfo( 1871 lastUsedVenvName, argv, wd, env, exceptions, self.excList, 1872 self.excIgnoreList, clearShell, 1873 configOverride=configOverride 1874 ) 1875 1876 self.lastStartAction = 8 1877 self.clientType = self.project.getProjectLanguage() 1878 else: 1879 editor = self.viewmanager.activeWindow() 1880 if editor is None: 1881 return 1882 1883 if ( 1884 not self.viewmanager.checkDirty( 1885 editor, Preferences.getDebugger("Autosave")) or 1886 editor.getFileName() is None 1887 ): 1888 return 1889 1890 fn = editor.getFileName() 1891 self.lastStartAction = 7 1892 self.clientType = editor.determineFileType() 1893 1894 # save the filename for use by the restart method 1895 self.lastDebuggedFile = fn 1896 self.restartAct.setEnabled(True) 1897 1898 # save the most recently used virtual environment 1899 self.lastUsedVenvName = lastUsedVenvName 1900 1901 # This moves any previous occurrence of these arguments to the head 1902 # of the list. 1903 self.setArgvHistory(argv) 1904 self.setWdHistory(wd) 1905 self.setEnvHistory(env) 1906 1907 # Save the exception flags 1908 self.exceptions = exceptions 1909 1910 # Save the erase timing flag 1911 self.eraseTimings = eraseTimings 1912 1913 # Save the clear interpreter flag 1914 self.autoClearShell = clearShell 1915 1916 # Save the run in console flag 1917 self.runInConsole = console 1918 1919 # Save the global config override data 1920 self.overrideGlobalConfig = copy.deepcopy(configOverride) 1921 1922 # Hide all error highlights 1923 self.viewmanager.unhighlight() 1924 1925 if not doNotStart: 1926 if runProject and self.project.getProjectType() in [ 1927 "E6Plugin"]: 1928 argv = '--plugin="{0}" {1}'.format(fn, argv) 1929 fn = os.path.join(getConfig('ericDir'), "eric6.py") 1930 1931 self.debugViewer.initCallStackViewer(runProject) 1932 1933 # Ask the client to open the new program. 1934 self.debugServer.remoteProfile( 1935 lastUsedVenvName, fn, argv, wd, env, 1936 autoClearShell=self.autoClearShell, erase=eraseTimings, 1937 forProject=runProject, runInConsole=console, 1938 clientType=self.clientType, 1939 configOverride=self.overrideGlobalConfig) 1940 1941 self.stopAct.setEnabled(True) 1942 1943 if dlg.clearHistories(): 1944 self.setArgvHistory("", clearHistories=True) 1945 self.setWdHistory("", clearHistories=True) 1946 self.setEnvHistory("", clearHistories=True) 1947 self.setMultiprocessNoDebugHistory("", clearHistories=True) 1948 elif dlg.historiesModified(): 1949 argvHistory, wdHistory, envHistory, _ = dlg.getHistories() 1950 self.setArgvHistory("", history=argvHistory) 1951 self.setWdHistory("", history=wdHistory) 1952 self.setEnvHistory("", history=envHistory) 1953 1954 def __runScript(self): 1955 """ 1956 Private slot to handle the run script action. 1957 """ 1958 self.__doRun(False) 1959 1960 def __runProject(self): 1961 """ 1962 Private slot to handle the run project action. 1963 """ 1964 self.__compileChangedProjectFiles() 1965 self.__doRun(True) 1966 1967 def __doRun(self, runProject): 1968 """ 1969 Private method to handle the run actions. 1970 1971 @param runProject flag indicating running the current project (True) 1972 or script (False) 1973 """ 1974 from .StartDialog import StartDialog 1975 1976 self.__resetUI() 1977 doNotStart = False 1978 1979 # Get the command line arguments, the working directory and the 1980 # exception reporting flag. 1981 cap = ( 1982 self.tr("Run Project") 1983 if runProject else 1984 self.tr("Run Script") 1985 ) 1986 dlg = StartDialog( 1987 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory, 1988 self.envHistory, self.exceptions, self.ui, 1, 1989 autoClearShell=self.autoClearShell, 1990 configOverride=self.overrideGlobalConfig) 1991 if dlg.exec() == QDialog.DialogCode.Accepted: 1992 (lastUsedVenvName, argv, wd, env, exceptions, clearShell, 1993 console) = dlg.getData() 1994 configOverride = dlg.getGlobalOverrideData() 1995 1996 if runProject: 1997 fn = self.project.getMainScript(True) 1998 if fn is None: 1999 E5MessageBox.critical( 2000 self.ui, 2001 self.tr("Run Project"), 2002 self.tr( 2003 "There is no main script defined for the" 2004 " current project. Aborting")) 2005 return 2006 2007 if ( 2008 Preferences.getDebugger("Autosave") and 2009 not self.project.saveAllScripts(reportSyntaxErrors=True) 2010 ): 2011 doNotStart = True 2012 2013 # save the info for later use 2014 self.project.setDbgInfo( 2015 lastUsedVenvName, argv, wd, env, exceptions, self.excList, 2016 self.excIgnoreList, clearShell, 2017 configOverride=configOverride 2018 ) 2019 2020 self.lastStartAction = 4 2021 self.clientType = self.project.getProjectLanguage() 2022 else: 2023 editor = self.viewmanager.activeWindow() 2024 if editor is None: 2025 return 2026 2027 if ( 2028 not self.viewmanager.checkDirty( 2029 editor, 2030 Preferences.getDebugger("Autosave")) or 2031 editor.getFileName() is None 2032 ): 2033 return 2034 2035 fn = editor.getFileName() 2036 self.lastStartAction = 3 2037 self.clientType = editor.determineFileType() 2038 2039 # save the filename for use by the restart method 2040 self.lastDebuggedFile = fn 2041 self.restartAct.setEnabled(True) 2042 2043 # save the most recently used virtual environment 2044 self.lastUsedVenvName = lastUsedVenvName 2045 2046 # This moves any previous occurrence of these arguments to the head 2047 # of the list. 2048 self.setArgvHistory(argv) 2049 self.setWdHistory(wd) 2050 self.setEnvHistory(env) 2051 2052 # Save the exception flags 2053 self.exceptions = exceptions 2054 2055 # Save the clear interpreter flag 2056 self.autoClearShell = clearShell 2057 2058 # Save the run in console flag 2059 self.runInConsole = console 2060 2061 # Save the global config override data 2062 self.overrideGlobalConfig = copy.deepcopy(configOverride) 2063 2064 # Hide all error highlights 2065 self.viewmanager.unhighlight() 2066 2067 if not doNotStart: 2068 if runProject and self.project.getProjectType() in [ 2069 "E6Plugin"]: 2070 argv = '--plugin="{0}" {1}'.format(fn, argv) 2071 fn = os.path.join(getConfig('ericDir'), "eric6.py") 2072 2073 self.debugViewer.initCallStackViewer(runProject) 2074 2075 # Ask the client to open the new program. 2076 self.debugServer.remoteRun( 2077 lastUsedVenvName, fn, argv, wd, env, 2078 autoClearShell=self.autoClearShell, forProject=runProject, 2079 runInConsole=console, clientType=self.clientType, 2080 configOverride=self.overrideGlobalConfig) 2081 2082 self.stopAct.setEnabled(True) 2083 2084 if dlg.clearHistories(): 2085 self.setArgvHistory("", clearHistories=True) 2086 self.setWdHistory("", clearHistories=True) 2087 self.setEnvHistory("", clearHistories=True) 2088 self.setMultiprocessNoDebugHistory("", clearHistories=True) 2089 elif dlg.historiesModified(): 2090 argvHistory, wdHistory, envHistory, _ = dlg.getHistories() 2091 self.setArgvHistory("", history=argvHistory) 2092 self.setWdHistory("", history=wdHistory) 2093 self.setEnvHistory("", history=envHistory) 2094 2095 def __debugScript(self): 2096 """ 2097 Private slot to handle the debug script action. 2098 """ 2099 self.__doDebug(False) 2100 2101 def __debugProject(self): 2102 """ 2103 Private slot to handle the debug project action. 2104 """ 2105 self.__compileChangedProjectFiles() 2106 self.__doDebug(True) 2107 2108 def __doDebug(self, debugProject): 2109 """ 2110 Private method to handle the debug actions. 2111 2112 @param debugProject flag indicating debugging the current project 2113 (True) or script (False) 2114 """ 2115 from .StartDialog import StartDialog 2116 2117 self.__resetUI() 2118 doNotStart = False 2119 2120 # Get the command line arguments, the working directory and the 2121 # exception reporting flag. 2122 cap = ( 2123 self.tr("Debug Project") 2124 if debugProject else 2125 self.tr("Debug Script") 2126 ) 2127 dlg = StartDialog( 2128 cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory, 2129 self.envHistory, self.exceptions, self.ui, 0, 2130 tracePython=self.tracePython, autoClearShell=self.autoClearShell, 2131 autoContinue=self.autoContinue, 2132 enableMultiprocess=self.enableMultiprocess, 2133 multiprocessNoDebugHistory=self.multiprocessNoDebugHistory, 2134 configOverride=self.overrideGlobalConfig) 2135 if dlg.exec() == QDialog.DialogCode.Accepted: 2136 (lastUsedVenvName, argv, wd, env, exceptions, clearShell, 2137 console) = dlg.getData() 2138 configOverride = dlg.getGlobalOverrideData() 2139 (tracePython, autoContinue, enableMultiprocess, 2140 multiprocessNoDebug) = dlg.getDebugData() 2141 2142 if debugProject: 2143 fn = self.project.getMainScript(True) 2144 if fn is None: 2145 E5MessageBox.critical( 2146 self.ui, 2147 self.tr("Debug Project"), 2148 self.tr( 2149 "There is no main script defined for the" 2150 " current project. No debugging possible.")) 2151 return 2152 2153 if ( 2154 Preferences.getDebugger("Autosave") and 2155 not self.project.saveAllScripts(reportSyntaxErrors=True) 2156 ): 2157 doNotStart = True 2158 2159 # save the info for later use 2160 self.project.setDbgInfo( 2161 lastUsedVenvName, argv, wd, env, exceptions, self.excList, 2162 self.excIgnoreList, clearShell, tracePython=tracePython, 2163 autoContinue=autoContinue, 2164 enableMultiprocess=enableMultiprocess, 2165 multiprocessNoDebug=multiprocessNoDebug, 2166 configOverride=configOverride 2167 ) 2168 2169 self.lastStartAction = 2 2170 self.clientType = self.project.getProjectLanguage() 2171 else: 2172 editor = self.viewmanager.activeWindow() 2173 if editor is None: 2174 return 2175 2176 if ( 2177 not self.viewmanager.checkDirty( 2178 editor, Preferences.getDebugger("Autosave")) or 2179 editor.getFileName() is None 2180 ): 2181 return 2182 2183 fn = editor.getFileName() 2184 self.lastStartAction = 1 2185 self.clientType = editor.determineFileType() 2186 2187 # save the filename for use by the restart method 2188 self.lastDebuggedFile = fn 2189 self.restartAct.setEnabled(True) 2190 2191 # save the most recently used virtual environment 2192 self.lastUsedVenvName = lastUsedVenvName 2193 2194 # This moves any previous occurrence of these arguments to the head 2195 # of the list. 2196 self.setArgvHistory(argv) 2197 self.setWdHistory(wd) 2198 self.setEnvHistory(env) 2199 2200 # Save the exception flags 2201 self.exceptions = exceptions 2202 2203 # Save the tracePython flag 2204 self.tracePython = tracePython 2205 2206 # Save the clear interpreter flag 2207 self.autoClearShell = clearShell 2208 2209 # Save the run in console flag 2210 self.runInConsole = console 2211 2212 # Save the auto continue flag 2213 self.autoContinue = autoContinue 2214 2215 # Save the multiprocess debugging data 2216 self.enableMultiprocess = enableMultiprocess 2217 self.setMultiprocessNoDebugHistory(multiprocessNoDebug) 2218 2219 # Save the global config override data 2220 self.overrideGlobalConfig = copy.deepcopy(configOverride) 2221 2222 # Hide all error highlights 2223 self.viewmanager.unhighlight() 2224 2225 if not doNotStart: 2226 if debugProject and self.project.getProjectType() in [ 2227 "E6Plugin"]: 2228 argv = '--plugin="{0}" {1}'.format(fn, argv) 2229 fn = os.path.join(getConfig('ericDir'), "eric6.py") 2230 tracePython = True # override flag because it must be true 2231 2232 self.debugViewer.initCallStackViewer(debugProject) 2233 2234 # Ask the client to send call trace info 2235 enableCallTrace = self.debugViewer.isCallTraceEnabled() 2236 self.debugViewer.clearCallTrace() 2237 self.debugViewer.setCallTraceToProjectMode(debugProject) 2238 2239 # Ask the client to open the new program. 2240 self.debugServer.remoteLoad( 2241 lastUsedVenvName, fn, argv, wd, env, 2242 autoClearShell=self.autoClearShell, 2243 tracePython=tracePython, 2244 autoContinue=autoContinue, forProject=debugProject, 2245 runInConsole=console, clientType=self.clientType, 2246 enableCallTrace=enableCallTrace, 2247 enableMultiprocess=enableMultiprocess, 2248 multiprocessNoDebug=multiprocessNoDebug, 2249 configOverride=self.overrideGlobalConfig) 2250 2251 if ( 2252 self.debugServer.isClientProcessUp() and 2253 self.debugServer.getClientType() == self.clientType 2254 ): 2255 # Signal that we have started a debugging session 2256 self.debuggingStarted.emit(fn) 2257 2258 self.stopAct.setEnabled(True) 2259 2260 if dlg.clearHistories(): 2261 self.setArgvHistory("", clearHistories=True) 2262 self.setWdHistory("", clearHistories=True) 2263 self.setEnvHistory("", clearHistories=True) 2264 self.setMultiprocessNoDebugHistory("", clearHistories=True) 2265 elif dlg.historiesModified(): 2266 (argvHistory, wdHistory, envHistory, 2267 noDebugHistory) = dlg.getHistories() 2268 self.setArgvHistory("", history=argvHistory) 2269 self.setWdHistory("", history=wdHistory) 2270 self.setEnvHistory("", history=envHistory) 2271 self.setMultiprocessNoDebugHistory("", history=noDebugHistory) 2272 2273 def __doRestart(self): 2274 """ 2275 Private slot to handle the restart action to restart the last 2276 debugged file. 2277 """ 2278 self.__resetUI() 2279 doNotStart = False 2280 2281 # first save any changes 2282 if self.lastStartAction in [1, 3, 5, 7, 9]: 2283 editor = self.viewmanager.getOpenEditor(self.lastDebuggedFile) 2284 if ( 2285 editor and 2286 not self.viewmanager.checkDirty( 2287 editor, Preferences.getDebugger("Autosave")) 2288 ): 2289 return 2290 forProject = False 2291 elif self.lastStartAction in [2, 4, 6, 8, 10]: 2292 if ( 2293 Preferences.getDebugger("Autosave") and 2294 not self.project.saveAllScripts(reportSyntaxErrors=True) 2295 ): 2296 doNotStart = True 2297 self.__compileChangedProjectFiles() 2298 forProject = True 2299 else: 2300 return # should not happen 2301 2302 # get the saved stuff 2303 venvName = self.lastUsedVenvName 2304 wd = self.wdHistory[0] 2305 argv = self.argvHistory[0] 2306 fn = self.lastDebuggedFile 2307 env = self.envHistory[0] 2308 2309 # Hide all error highlights 2310 self.viewmanager.unhighlight() 2311 2312 if not doNotStart: 2313 if forProject and self.project.getProjectType() in [ 2314 "E6Plugin"]: 2315 argv = '--plugin="{0}" {1}'.format(fn, argv) 2316 fn = os.path.join(getConfig('ericDir'), "eric6.py") 2317 2318 self.debugViewer.initCallStackViewer(forProject) 2319 2320 if self.lastStartAction in [1, 2]: 2321 # Ask the client to send call trace info 2322 enableCallTrace = self.debugViewer.isCallTraceEnabled() 2323 self.debugViewer.clearCallTrace() 2324 self.debugViewer.setCallTraceToProjectMode(forProject) 2325 multiprocessNoDebug = self.multiprocessNoDebugHistory[0] 2326 2327 # Ask the client to debug the new program. 2328 self.debugServer.remoteLoad( 2329 venvName, fn, argv, wd, env, 2330 autoClearShell=self.autoClearShell, 2331 tracePython=self.tracePython, 2332 autoContinue=self.autoContinue, 2333 forProject=forProject, 2334 runInConsole=self.runInConsole, 2335 clientType=self.clientType, 2336 enableCallTrace=enableCallTrace, 2337 enableMultiprocess=self.enableMultiprocess, 2338 multiprocessNoDebug=multiprocessNoDebug, 2339 configOverride=self.overrideGlobalConfig) 2340 2341 # Signal that we have started a debugging session 2342 self.debuggingStarted.emit(fn) 2343 2344 elif self.lastStartAction in [3, 4]: 2345 # Ask the client to run the new program. 2346 self.debugServer.remoteRun( 2347 venvName, fn, argv, wd, env, 2348 autoClearShell=self.autoClearShell, 2349 forProject=forProject, 2350 runInConsole=self.runInConsole, 2351 clientType=self.clientType, 2352 configOverride=self.overrideGlobalConfig) 2353 2354 elif self.lastStartAction in [5, 6]: 2355 # Ask the client to coverage run the new program. 2356 self.debugServer.remoteCoverage( 2357 venvName, fn, argv, wd, env, 2358 autoClearShell=self.autoClearShell, 2359 erase=self.eraseCoverage, 2360 forProject=forProject, 2361 runInConsole=self.runInConsole, 2362 clientType=self.clientType, 2363 configOverride=self.overrideGlobalConfig) 2364 2365 elif self.lastStartAction in [7, 8]: 2366 # Ask the client to profile run the new program. 2367 self.debugServer.remoteProfile( 2368 venvName, fn, argv, wd, env, 2369 autoClearShell=self.autoClearShell, 2370 erase=self.eraseTimings, 2371 forProject=forProject, 2372 runInConsole=self.runInConsole, 2373 clientType=self.clientType, 2374 configOverride=self.overrideGlobalConfig) 2375 2376 self.stopAct.setEnabled(True) 2377 2378 def __stopScript(self): 2379 """ 2380 Private slot to stop the running script. 2381 """ 2382 self.debugServer.startClient(False) 2383 2384 def __passiveDebugStarted(self, fn, exc): 2385 """ 2386 Private slot to handle a passive debug session start. 2387 2388 @param fn filename of the debugged script 2389 @param exc flag to enable exception reporting of the IDE (boolean) 2390 """ 2391 # Hide all error highlights 2392 self.viewmanager.unhighlight() 2393 2394 # Set filename of script being debugged 2395 self.ui.currentProg = fn 2396 2397 # Set exception reporting 2398 self.setExceptionReporting(exc) 2399 2400 # Signal that we have started a debugging session 2401 self.debuggingStarted.emit(fn) 2402 2403 # Initialize the call stack viewer 2404 self.debugViewer.initCallStackViewer(False) 2405 2406 def __continue(self, debuggerId=""): 2407 """ 2408 Private method to handle the Continue action. 2409 2410 @param debuggerId ID of the debugger backend 2411 @type str 2412 """ 2413 if not debuggerId: 2414 debuggerId = self.getSelectedDebuggerId() 2415 2416 self.lastAction = 0 2417 self.__enterRemote() 2418 self.debugServer.remoteContinue(debuggerId) 2419 2420 def __specialContinue(self, debuggerId=""): 2421 """ 2422 Private method to handle the Special Continue action. 2423 2424 @param debuggerId ID of the debugger backend 2425 @type str 2426 """ 2427 if not debuggerId: 2428 debuggerId = self.getSelectedDebuggerId() 2429 2430 self.lastAction = 2 2431 self.__enterRemote() 2432 self.debugServer.remoteContinue(debuggerId, 1) 2433 2434 def __step(self, debuggerId=""): 2435 """ 2436 Private method to handle the Step action. 2437 2438 @param debuggerId ID of the debugger backend 2439 @type str 2440 """ 2441 if not debuggerId: 2442 debuggerId = self.getSelectedDebuggerId() 2443 2444 self.lastAction = 1 2445 self.__enterRemote() 2446 self.debugServer.remoteStep(debuggerId) 2447 2448 def __stepOver(self, debuggerId=""): 2449 """ 2450 Private method to handle the Step Over action. 2451 2452 @param debuggerId ID of the debugger backend 2453 @type str 2454 """ 2455 if not debuggerId: 2456 debuggerId = self.getSelectedDebuggerId() 2457 2458 self.lastAction = 2 2459 self.__enterRemote() 2460 self.debugServer.remoteStepOver(debuggerId) 2461 2462 def __stepOut(self, debuggerId=""): 2463 """ 2464 Private method to handle the Step Out action. 2465 2466 @param debuggerId ID of the debugger backend 2467 @type str 2468 """ 2469 if not debuggerId: 2470 debuggerId = self.getSelectedDebuggerId() 2471 2472 self.lastAction = 3 2473 self.__enterRemote() 2474 self.debugServer.remoteStepOut(debuggerId) 2475 2476 def __stepQuit(self, debuggerId=""): 2477 """ 2478 Private method to handle the Step Quit action. 2479 2480 @param debuggerId ID of the debugger backend 2481 @type str 2482 """ 2483 if not debuggerId: 2484 debuggerId = self.getSelectedDebuggerId() 2485 2486 self.lastAction = 4 2487 self.__enterRemote() 2488 self.debugServer.remoteStepQuit(debuggerId) 2489 self.__resetUI() 2490 2491 def __runToCursor(self, debuggerId=""): 2492 """ 2493 Private method to handle the Run to Cursor action. 2494 2495 @param debuggerId ID of the debugger backend 2496 @type str 2497 """ 2498 if not debuggerId: 2499 debuggerId = self.getSelectedDebuggerId() 2500 2501 self.lastAction = 0 2502 aw = self.viewmanager.activeWindow() 2503 line = aw.getCursorPosition()[0] + 1 2504 self.__enterRemote() 2505 self.debugServer.remoteBreakpoint( 2506 self.getSelectedDebuggerId(), 2507 aw.getFileName(), line, 1, None, 1) 2508 self.debugServer.remoteContinue(debuggerId) 2509 2510 def __runUntil(self, debuggerId=""): 2511 """ 2512 Private method to handle the Run Until action. 2513 2514 @param debuggerId ID of the debugger backend 2515 @type str 2516 """ 2517 if not debuggerId: 2518 debuggerId = self.getSelectedDebuggerId() 2519 2520 self.lastAction = 0 2521 aw = self.viewmanager.activeWindow() 2522 line = aw.getCursorPosition()[0] + 1 2523 self.__enterRemote() 2524 self.debugServer.remoteContinueUntil(debuggerId, line) 2525 2526 def __moveInstructionPointer(self, debuggerId=""): 2527 """ 2528 Private method to move the instruction pointer to a different line. 2529 2530 @param debuggerId ID of the debugger backend 2531 @type str 2532 """ 2533 if not debuggerId: 2534 debuggerId = self.getSelectedDebuggerId() 2535 2536 self.lastAction = 0 2537 aw = self.viewmanager.activeWindow() 2538 line = aw.getCursorPosition()[0] + 1 2539 self.debugServer.remoteMoveIP(debuggerId, line) 2540 2541 def __enterRemote(self): 2542 """ 2543 Private method to update the user interface. 2544 2545 This method is called just prior to executing some of 2546 the program being debugged. 2547 """ 2548 # Disable further debug commands from the user. 2549 self.debugActGrp.setEnabled(False) 2550 2551 self.viewmanager.unhighlight(True) 2552 2553 def getActions(self): 2554 """ 2555 Public method to get a list of all actions. 2556 2557 @return list of all actions (list of E5Action) 2558 """ 2559 return self.actions[:] 2560 2561 def getSelectedDebuggerId(self): 2562 """ 2563 Public method to get the currently selected debugger ID. 2564 2565 @return selected debugger ID 2566 @rtype str 2567 """ 2568 return self.debugViewer.getSelectedDebuggerId() 2569 2570 def setDebugActionsEnabled(self, enable): 2571 """ 2572 Public method to set the enabled state of the debug actions. 2573 2574 @param enable enable state to be set 2575 @type bool 2576 """ 2577 self.debugActGrp.setEnabled(enable) 2578 2579 def setMultiprocessNoDebugHistory(self, noDebugList, clearHistories=False, 2580 history=None): 2581 """ 2582 Public slot to initialize the no debug list history. 2583 2584 @param noDebugList whitespace separated list of programs not to be 2585 debugged 2586 @type str 2587 @param clearHistories flag indicating, that the list should be cleared 2588 @type bool 2589 @param history list of history entries to be set 2590 @type list of str 2591 """ 2592 if clearHistories: 2593 del self.multiprocessNoDebugHistory[1:] 2594 elif history is not None: 2595 self.multiprocessNoDebugHistory = history[:] 2596 else: 2597 if noDebugList in self.multiprocessNoDebugHistory: 2598 self.multiprocessNoDebugHistory.remove(noDebugList) 2599 self.multiprocessNoDebugHistory.insert(0, noDebugList) 2600 2601 def setEnableMultiprocess(self, enableMultiprocess): 2602 """ 2603 Public slot to initialize the enableMultiprocess flag. 2604 2605 @param enableMultiprocess flag indicating, that the debugger should be 2606 run in multi process mode 2607 @type bool 2608 """ 2609 self.enableMultiprocess = enableMultiprocess 2610 2611 def setEnableGlobalConfigOverride(self, overrideData): 2612 """ 2613 Public method to initialize the global config override data. 2614 2615 @param overrideData dictionary containing a flag indicating to enable 2616 global config override and a flag indicating to redirect 2617 stdin/stdout/stderr 2618 @type dict 2619 """ 2620 self.overrideGlobalConfig = copy.deepcopy(overrideData) 2621