1# Rename to stackbrowser or possibly consolidate with browser. 2 3import linecache 4import os 5 6import tkinter as tk 7 8from idlelib.debugobj import ObjectTreeItem, make_objecttreeitem 9from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas 10 11def StackBrowser(root, exc, flist=None, top=None): 12 global sc, item, node # For testing. 13 if top is None: 14 top = tk.Toplevel(root) 15 sc = ScrolledCanvas(top, bg="white", highlightthickness=0) 16 sc.frame.pack(expand=1, fill="both") 17 item = StackTreeItem(exc, flist) 18 node = TreeNode(sc.canvas, None, item) 19 node.expand() 20 21 22class StackTreeItem(TreeItem): 23 24 def __init__(self, exc, flist=None): 25 self.flist = flist 26 self.stack = self.get_stack(None if exc is None else exc.__traceback__) 27 self.text = f"{type(exc).__name__}: {str(exc)}" 28 29 def get_stack(self, tb): 30 stack = [] 31 if tb and tb.tb_frame is None: 32 tb = tb.tb_next 33 while tb is not None: 34 stack.append((tb.tb_frame, tb.tb_lineno)) 35 tb = tb.tb_next 36 return stack 37 38 def GetText(self): # Titlecase names are overrides. 39 return self.text 40 41 def GetSubList(self): 42 sublist = [] 43 for info in self.stack: 44 item = FrameTreeItem(info, self.flist) 45 sublist.append(item) 46 return sublist 47 48 49class FrameTreeItem(TreeItem): 50 51 def __init__(self, info, flist): 52 self.info = info 53 self.flist = flist 54 55 def GetText(self): 56 frame, lineno = self.info 57 try: 58 modname = frame.f_globals["__name__"] 59 except: 60 modname = "?" 61 code = frame.f_code 62 filename = code.co_filename 63 funcname = code.co_name 64 sourceline = linecache.getline(filename, lineno) 65 sourceline = sourceline.strip() 66 if funcname in ("?", "", None): 67 item = "%s, line %d: %s" % (modname, lineno, sourceline) 68 else: 69 item = "%s.%s(...), line %d: %s" % (modname, funcname, 70 lineno, sourceline) 71 return item 72 73 def GetSubList(self): 74 frame, lineno = self.info 75 sublist = [] 76 if frame.f_globals is not frame.f_locals: 77 item = VariablesTreeItem("<locals>", frame.f_locals, self.flist) 78 sublist.append(item) 79 item = VariablesTreeItem("<globals>", frame.f_globals, self.flist) 80 sublist.append(item) 81 return sublist 82 83 def OnDoubleClick(self): 84 if self.flist: 85 frame, lineno = self.info 86 filename = frame.f_code.co_filename 87 if os.path.isfile(filename): 88 self.flist.gotofileline(filename, lineno) 89 90 91class VariablesTreeItem(ObjectTreeItem): 92 93 def GetText(self): 94 return self.labeltext 95 96 def GetLabelText(self): 97 return None 98 99 def IsExpandable(self): 100 return len(self.object) > 0 101 102 def GetSubList(self): 103 sublist = [] 104 for key in self.object.keys(): # self.object not necessarily dict. 105 try: 106 value = self.object[key] 107 except KeyError: 108 continue 109 def setfunction(value, key=key, object_=self.object): 110 object_[key] = value 111 item = make_objecttreeitem(key + " =", value, setfunction) 112 sublist.append(item) 113 return sublist 114 115 116def _stackbrowser(parent): # htest # 117 from idlelib.pyshell import PyShellFileList 118 top = tk.Toplevel(parent) 119 top.title("Test StackViewer") 120 x, y = map(int, parent.geometry().split('+')[1:]) 121 top.geometry("+%d+%d" % (x + 50, y + 175)) 122 flist = PyShellFileList(top) 123 try: # to obtain a traceback object 124 intentional_name_error 125 except NameError as e: 126 StackBrowser(top, e, flist=flist, top=top) 127 128 129if __name__ == '__main__': 130 from unittest import main 131 main('idlelib.idle_test.test_stackviewer', verbosity=2, exit=False) 132 133 from idlelib.idle_test.htest import run 134 run(_stackbrowser) 135