Allow console to display any frame stack (even without an exception)

This commit is contained in:
Luke Campagnola 2017-09-13 20:50:31 -07:00
parent 8d730f07d9
commit 0517665473
4 changed files with 71 additions and 27 deletions

View File

@ -53,6 +53,7 @@ class ConsoleWidget(QtGui.QWidget):
self.editor = editor self.editor = editor
self.multiline = None self.multiline = None
self.inCmd = False self.inCmd = False
self.frames = [] # stack frames to access when an item in the stack list is selected
self.ui = template.Ui_Form() self.ui = template.Ui_Form()
self.ui.setupUi(self) self.ui.setupUi(self)
@ -133,14 +134,14 @@ class ConsoleWidget(QtGui.QWidget):
def globals(self): def globals(self):
frame = self.currentFrame() frame = self.currentFrame()
if frame is not None and self.ui.runSelectedFrameCheck.isChecked(): if frame is not None and self.ui.runSelectedFrameCheck.isChecked():
return self.currentFrame().tb_frame.f_globals return self.currentFrame().f_globals
else: else:
return self.localNamespace return self.localNamespace
def locals(self): def locals(self):
frame = self.currentFrame() frame = self.currentFrame()
if frame is not None and self.ui.runSelectedFrameCheck.isChecked(): if frame is not None and self.ui.runSelectedFrameCheck.isChecked():
return self.currentFrame().tb_frame.f_locals return self.currentFrame().f_locals
else: else:
return self.localNamespace return self.localNamespace
@ -149,10 +150,7 @@ class ConsoleWidget(QtGui.QWidget):
if self.currentTraceback is None: if self.currentTraceback is None:
return None return None
index = self.ui.exceptionStackList.currentRow() index = self.ui.exceptionStackList.currentRow()
tb = self.currentTraceback return self.frames[index]
for i in range(index):
tb = tb.tb_next
return tb
def execSingle(self, cmd): def execSingle(self, cmd):
try: try:
@ -171,7 +169,6 @@ class ConsoleWidget(QtGui.QWidget):
except: except:
self.displayException() self.displayException()
def execMulti(self, nextLine): def execMulti(self, nextLine):
#self.stdout.write(nextLine+"\n") #self.stdout.write(nextLine+"\n")
if nextLine.strip() != '': if nextLine.strip() != '':
@ -202,6 +199,10 @@ class ConsoleWidget(QtGui.QWidget):
self.multiline = None self.multiline = None
def write(self, strn, html=False): def write(self, strn, html=False):
isGuiThread = QtCore.QThread.currentThread() == QtCore.QCoreApplication.instance().thread()
if not isGuiThread:
self.stdout.write(strn)
return
self.output.moveCursor(QtGui.QTextCursor.End) self.output.moveCursor(QtGui.QTextCursor.End)
if html: if html:
self.output.textCursor().insertHtml(strn) self.output.textCursor().insertHtml(strn)
@ -293,14 +294,6 @@ class ConsoleWidget(QtGui.QWidget):
fileName = tb.tb_frame.f_code.co_filename fileName = tb.tb_frame.f_code.co_filename
subprocess.Popen(self.editor.format(fileName=fileName, lineNum=lineNum), shell=True) subprocess.Popen(self.editor.format(fileName=fileName, lineNum=lineNum), shell=True)
#def allExceptionsHandler(self, *args):
#self.exceptionHandler(*args)
#def nextExceptionHandler(self, *args):
#self.ui.catchNextExceptionBtn.setChecked(False)
#self.exceptionHandler(*args)
def updateSysTrace(self): def updateSysTrace(self):
## Install or uninstall sys.settrace handler ## Install or uninstall sys.settrace handler
@ -319,7 +312,7 @@ class ConsoleWidget(QtGui.QWidget):
else: else:
sys.settrace(self.systrace) sys.settrace(self.systrace)
def exceptionHandler(self, excType, exc, tb): def exceptionHandler(self, excType, exc, tb, systrace=False):
if self.ui.catchNextExceptionBtn.isChecked(): if self.ui.catchNextExceptionBtn.isChecked():
self.ui.catchNextExceptionBtn.setChecked(False) self.ui.catchNextExceptionBtn.setChecked(False)
elif not self.ui.catchAllExceptionsBtn.isChecked(): elif not self.ui.catchAllExceptionsBtn.isChecked():
@ -330,13 +323,60 @@ class ConsoleWidget(QtGui.QWidget):
excMessage = ''.join(traceback.format_exception_only(excType, exc)) excMessage = ''.join(traceback.format_exception_only(excType, exc))
self.ui.exceptionInfoLabel.setText(excMessage) self.ui.exceptionInfoLabel.setText(excMessage)
if systrace:
# exceptions caught using systrace don't need the usual
# call stack + traceback handling
self.setStack(sys._getframe().f_back.f_back)
else:
self.setStack(frame=sys._getframe().f_back, tb=tb)
def setStack(self, frame=None, tb=None):
"""Display a call stack and exception traceback.
This allows the user to probe the contents of any frame in the given stack.
*frame* may either be a Frame instance or None, in which case the current
frame is retrieved from ``sys._getframe()``.
If *tb* is provided then the frames in the traceback will be appended to
the end of the stack list. If *tb* is None, then sys.exc_info() will
be checked instead.
"""
if frame is None:
frame = sys._getframe().f_back
if tb is None:
tb = sys.exc_info()[2]
self.ui.exceptionStackList.clear() self.ui.exceptionStackList.clear()
self.frames = []
# Build stack up to this point
for index, line in enumerate(traceback.extract_stack(frame)):
self.ui.exceptionStackList.addItem('File "%s", line %s, in %s()\n %s' % line)
while frame is not None:
self.frames.insert(0, frame)
frame = frame.f_back
if tb is None:
return
self.ui.exceptionStackList.addItem('-- exception caught here: --')
item = self.ui.exceptionStackList.item(self.ui.exceptionStackList.count()-1)
item.setBackground(QtGui.QBrush(QtGui.QColor(200, 200, 200)))
self.frames.append(None)
# And finish the rest of the stack up to the exception
for index, line in enumerate(traceback.extract_tb(tb)): for index, line in enumerate(traceback.extract_tb(tb)):
self.ui.exceptionStackList.addItem('File "%s", line %s, in %s()\n %s' % line) self.ui.exceptionStackList.addItem('File "%s", line %s, in %s()\n %s' % line)
while tb is not None:
self.frames.append(tb.tb_frame)
tb = tb.tb_next
def systrace(self, frame, event, arg): def systrace(self, frame, event, arg):
if event == 'exception' and self.checkException(*arg): if event == 'exception' and self.checkException(*arg):
self.exceptionHandler(*arg) self.exceptionHandler(*arg, systrace=True)
return self.systrace return self.systrace
def checkException(self, excType, exc, tb): def checkException(self, excType, exc, tb):

View File

@ -86,7 +86,10 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="spacing"> <property name="horizontalSpacing">
<number>2</number>
</property>
<property name="verticalSpacing">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="6"> <item row="0" column="6">
@ -95,7 +98,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Clear Exception</string> <string>Clear Stack</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -149,7 +152,7 @@
<item row="1" column="0" colspan="7"> <item row="1" column="0" colspan="7">
<widget class="QLabel" name="exceptionInfoLabel"> <widget class="QLabel" name="exceptionInfoLabel">
<property name="text"> <property name="text">
<string>Exception Info</string> <string>Stack Trace</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'template.ui' # Form implementation generated from reading ui file 'template.ui'
# #
# Created: Fri May 02 18:55:28 2014 # Created: Wed Apr 08 16:28:53 2015
# by: PyQt4 UI code generator 4.10.4 # by: PyQt4 UI code generator 4.10.4
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -68,8 +68,9 @@ class Ui_Form(object):
self.exceptionGroup = QtGui.QGroupBox(self.splitter) self.exceptionGroup = QtGui.QGroupBox(self.splitter)
self.exceptionGroup.setObjectName(_fromUtf8("exceptionGroup")) self.exceptionGroup.setObjectName(_fromUtf8("exceptionGroup"))
self.gridLayout_2 = QtGui.QGridLayout(self.exceptionGroup) self.gridLayout_2 = QtGui.QGridLayout(self.exceptionGroup)
self.gridLayout_2.setSpacing(0)
self.gridLayout_2.setContentsMargins(-1, 0, -1, 0) self.gridLayout_2.setContentsMargins(-1, 0, -1, 0)
self.gridLayout_2.setHorizontalSpacing(2)
self.gridLayout_2.setVerticalSpacing(0)
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
self.clearExceptionBtn = QtGui.QPushButton(self.exceptionGroup) self.clearExceptionBtn = QtGui.QPushButton(self.exceptionGroup)
self.clearExceptionBtn.setEnabled(False) self.clearExceptionBtn.setEnabled(False)
@ -116,12 +117,12 @@ class Ui_Form(object):
self.historyBtn.setText(_translate("Form", "History..", None)) self.historyBtn.setText(_translate("Form", "History..", None))
self.exceptionBtn.setText(_translate("Form", "Exceptions..", None)) self.exceptionBtn.setText(_translate("Form", "Exceptions..", None))
self.exceptionGroup.setTitle(_translate("Form", "Exception Handling", None)) self.exceptionGroup.setTitle(_translate("Form", "Exception Handling", None))
self.clearExceptionBtn.setText(_translate("Form", "Clear Exception", None)) self.clearExceptionBtn.setText(_translate("Form", "Clear Stack", None))
self.catchAllExceptionsBtn.setText(_translate("Form", "Show All Exceptions", None)) self.catchAllExceptionsBtn.setText(_translate("Form", "Show All Exceptions", None))
self.catchNextExceptionBtn.setText(_translate("Form", "Show Next Exception", None)) self.catchNextExceptionBtn.setText(_translate("Form", "Show Next Exception", None))
self.onlyUncaughtCheck.setText(_translate("Form", "Only Uncaught Exceptions", None)) self.onlyUncaughtCheck.setText(_translate("Form", "Only Uncaught Exceptions", None))
self.runSelectedFrameCheck.setText(_translate("Form", "Run commands in selected stack frame", None)) self.runSelectedFrameCheck.setText(_translate("Form", "Run commands in selected stack frame", None))
self.exceptionInfoLabel.setText(_translate("Form", "Exception Info", None)) self.exceptionInfoLabel.setText(_translate("Form", "Stack Trace", None))
self.label.setText(_translate("Form", "Filter (regex):", None)) self.label.setText(_translate("Form", "Filter (regex):", None))
from .CmdInput import CmdInput from .CmdInput import CmdInput

View File

@ -100,7 +100,7 @@ class Ui_Form(object):
self.catchNextExceptionBtn.setText(QtGui.QApplication.translate("Form", "Show Next Exception", None, QtGui.QApplication.UnicodeUTF8)) self.catchNextExceptionBtn.setText(QtGui.QApplication.translate("Form", "Show Next Exception", None, QtGui.QApplication.UnicodeUTF8))
self.onlyUncaughtCheck.setText(QtGui.QApplication.translate("Form", "Only Uncaught Exceptions", None, QtGui.QApplication.UnicodeUTF8)) self.onlyUncaughtCheck.setText(QtGui.QApplication.translate("Form", "Only Uncaught Exceptions", None, QtGui.QApplication.UnicodeUTF8))
self.runSelectedFrameCheck.setText(QtGui.QApplication.translate("Form", "Run commands in selected stack frame", None, QtGui.QApplication.UnicodeUTF8)) self.runSelectedFrameCheck.setText(QtGui.QApplication.translate("Form", "Run commands in selected stack frame", None, QtGui.QApplication.UnicodeUTF8))
self.exceptionInfoLabel.setText(QtGui.QApplication.translate("Form", "Exception Info", None, QtGui.QApplication.UnicodeUTF8)) self.exceptionInfoLabel.setText(QtGui.QApplication.translate("Form", "Stack Trace", None, QtGui.QApplication.UnicodeUTF8))
self.clearExceptionBtn.setText(QtGui.QApplication.translate("Form", "Clear Exception", None, QtGui.QApplication.UnicodeUTF8)) self.clearExceptionBtn.setText(QtGui.QApplication.translate("Form", "Clear Stack", None, QtGui.QApplication.UnicodeUTF8))
from .CmdInput import CmdInput from .CmdInput import CmdInput