# basic module browser. # usage: # >>> import browser # >>> browser.Browse() # or # >>> browser.Browse(your_module) import sys import types import __main__ import win32ui from pywin.mfc import dialog from . import hierlist special_names = ["__doc__", "__name__", "__self__"] # # HierList items class HLIPythonObject(hierlist.HierListItem): def __init__(self, myobject=None, name=None): hierlist.HierListItem.__init__(self) self.myobject = myobject self.knownExpandable = None if name: self.name = name else: try: self.name = myobject.__name__ except (AttributeError, TypeError): try: r = repr(myobject) if len(r) > 20: r = r[:20] + "..." self.name = r except (AttributeError, TypeError): self.name = "???" def __lt__(self, other): return self.name < other.name def __eq__(self, other): return self.name == other.name def __repr__(self): try: type = self.GetHLIType() except: type = "Generic" return ( "HLIPythonObject(" + type + ") - name: " + self.name + " object: " + repr(self.myobject) ) def GetText(self): try: return str(self.name) + " (" + self.GetHLIType() + ")" except AttributeError: return str(self.name) + " = " + repr(self.myobject) def InsertDocString(self, lst): ob = None try: ob = self.myobject.__doc__ except (AttributeError, TypeError): pass # I don't quite grok descriptors enough to know how to # best hook them up. Eg: # >>> object.__getattribute__.__class__.__doc__ # if ob and isinstance(ob, str): lst.insert(0, HLIDocString(ob, "Doc")) def GetSubList(self): ret = [] try: for key, ob in self.myobject.__dict__.items(): if key not in special_names: ret.append(MakeHLI(ob, key)) except (AttributeError, TypeError): pass try: for name in self.myobject.__methods__: ret.append(HLIMethod(name)) # no MakeHLI, as cant auto detect except (AttributeError, TypeError): pass try: for member in self.myobject.__members__: if not member in special_names: ret.append(MakeHLI(getattr(self.myobject, member), member)) except (AttributeError, TypeError): pass ret.sort() self.InsertDocString(ret) return ret # if the has a dict, it is expandable. def IsExpandable(self): if self.knownExpandable is None: self.knownExpandable = self.CalculateIsExpandable() return self.knownExpandable def CalculateIsExpandable(self): if hasattr(self.myobject, "__doc__"): return 1 try: for key in self.myobject.__dict__.keys(): if key not in special_names: return 1 except (AttributeError, TypeError): pass try: self.myobject.__methods__ return 1 except (AttributeError, TypeError): pass try: for item in self.myobject.__members__: if item not in special_names: return 1 except (AttributeError, TypeError): pass return 0 def GetBitmapColumn(self): if self.IsExpandable(): return 0 else: return 4 def TakeDefaultAction(self): ShowObject(self.myobject, self.name) class HLIDocString(HLIPythonObject): def GetHLIType(self): return "DocString" def GetText(self): return self.myobject.strip() def IsExpandable(self): return 0 def GetBitmapColumn(self): return 6 class HLIModule(HLIPythonObject): def GetHLIType(self): return "Module" class HLIFrame(HLIPythonObject): def GetHLIType(self): return "Stack Frame" class HLITraceback(HLIPythonObject): def GetHLIType(self): return "Traceback" class HLIClass(HLIPythonObject): def GetHLIType(self): return "Class" def GetSubList(self): ret = [] for base in self.myobject.__bases__: ret.append(MakeHLI(base, "Base class: " + base.__name__)) ret = ret + HLIPythonObject.GetSubList(self) return ret class HLIMethod(HLIPythonObject): # myobject is just a string for methods. def GetHLIType(self): return "Method" def GetText(self): return "Method: " + self.myobject + "()" class HLICode(HLIPythonObject): def GetHLIType(self): return "Code" def IsExpandable(self): return self.myobject def GetSubList(self): ret = [] ret.append(MakeHLI(self.myobject.co_consts, "Constants (co_consts)")) ret.append(MakeHLI(self.myobject.co_names, "Names (co_names)")) ret.append(MakeHLI(self.myobject.co_filename, "Filename (co_filename)")) ret.append(MakeHLI(self.myobject.co_argcount, "Number of args (co_argcount)")) ret.append(MakeHLI(self.myobject.co_varnames, "Param names (co_varnames)")) return ret class HLIInstance(HLIPythonObject): def GetHLIType(self): return "Instance" def GetText(self): return ( str(self.name) + " (Instance of class " + str(self.myobject.__class__.__name__) + ")" ) def IsExpandable(self): return 1 def GetSubList(self): ret = [] ret.append(MakeHLI(self.myobject.__class__)) ret = ret + HLIPythonObject.GetSubList(self) return ret class HLIBuiltinFunction(HLIPythonObject): def GetHLIType(self): return "Builtin Function" class HLIFunction(HLIPythonObject): def GetHLIType(self): return "Function" def IsExpandable(self): return 1 def GetSubList(self): ret = [] # ret.append( MakeHLI( self.myobject.func_argcount, "Arg Count" )) try: ret.append(MakeHLI(self.myobject.func_argdefs, "Arg Defs")) except AttributeError: pass try: code = self.myobject.__code__ globs = self.myobject.__globals__ except AttributeError: # must be py2.5 or earlier... code = self.myobject.func_code globs = self.myobject.func_globals ret.append(MakeHLI(code, "Code")) ret.append(MakeHLI(globs, "Globals")) self.InsertDocString(ret) return ret class HLISeq(HLIPythonObject): def GetHLIType(self): return "Sequence (abstract!)" def IsExpandable(self): return len(self.myobject) > 0 def GetSubList(self): ret = [] pos = 0 for item in self.myobject: ret.append(MakeHLI(item, "[" + str(pos) + "]")) pos = pos + 1 self.InsertDocString(ret) return ret class HLIList(HLISeq): def GetHLIType(self): return "List" class HLITuple(HLISeq): def GetHLIType(self): return "Tuple" class HLIDict(HLIPythonObject): def GetHLIType(self): return "Dict" def IsExpandable(self): try: self.myobject.__doc__ return 1 except (AttributeError, TypeError): return len(self.myobject) > 0 def GetSubList(self): ret = [] keys = list(self.myobject.keys()) keys.sort() for key in keys: ob = self.myobject[key] ret.append(MakeHLI(ob, str(key))) self.InsertDocString(ret) return ret # In Python 1.6, strings and Unicode have builtin methods, but we dont really want to see these class HLIString(HLIPythonObject): def IsExpandable(self): return 0 TypeMap = { type: HLIClass, types.FunctionType: HLIFunction, tuple: HLITuple, dict: HLIDict, list: HLIList, types.ModuleType: HLIModule, types.CodeType: HLICode, types.BuiltinFunctionType: HLIBuiltinFunction, types.FrameType: HLIFrame, types.TracebackType: HLITraceback, str: HLIString, int: HLIPythonObject, bool: HLIPythonObject, float: HLIPythonObject, } def MakeHLI(ob, name=None): try: cls = TypeMap[type(ob)] except KeyError: # hrmph - this check gets more and more bogus as Python # improves. Its possible we should just *always* use # HLIInstance? if hasattr(ob, "__class__"): # 'new style' class cls = HLIInstance else: cls = HLIPythonObject return cls(ob, name) ######################################### # # Dialog related. class DialogShowObject(dialog.Dialog): def __init__(self, object, title): self.object = object self.title = title dialog.Dialog.__init__(self, win32ui.IDD_LARGE_EDIT) def OnInitDialog(self): import re self.SetWindowText(self.title) self.edit = self.GetDlgItem(win32ui.IDC_EDIT1) try: strval = str(self.object) except: t, v, tb = sys.exc_info() strval = "Exception getting object value\n\n%s:%s" % (t, v) tb = None strval = re.sub("\n", "\r\n", strval) self.edit.ReplaceSel(strval) def ShowObject(object, title): dlg = DialogShowObject(object, title) dlg.DoModal() # And some mods for a sizable dialog from Sam Rushing! import commctrl import win32api import win32con class dynamic_browser(dialog.Dialog): style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE cs = ( win32con.WS_CHILD | win32con.WS_VISIBLE | commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS ) dt = [ ["Python Object Browser", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")], ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs], ] def __init__(self, hli_root): dialog.Dialog.__init__(self, self.dt) self.hier_list = hierlist.HierListWithItems(hli_root, win32ui.IDB_BROWSER_HIER) self.HookMessage(self.on_size, win32con.WM_SIZE) def OnInitDialog(self): self.hier_list.HierInit(self) return dialog.Dialog.OnInitDialog(self) def OnOK(self): self.hier_list.HierTerm() self.hier_list = None return self._obj_.OnOK() def OnCancel(self): self.hier_list.HierTerm() self.hier_list = None return self._obj_.OnCancel() def on_size(self, params): lparam = params[3] w = win32api.LOWORD(lparam) h = win32api.HIWORD(lparam) self.GetDlgItem(win32ui.IDC_LIST1).MoveWindow((0, 0, w, h)) def Browse(ob=__main__): "Browse the argument, or the main dictionary" root = MakeHLI(ob, "root") if not root.IsExpandable(): raise TypeError( "Browse() argument must have __dict__ attribute, or be a Browser supported type" ) dlg = dynamic_browser(root) dlg.CreateWindow() return dlg # # # Classes for using the browser in an MDI window, rather than a dialog # from pywin.mfc import docview class BrowserTemplate(docview.DocTemplate): def __init__(self): docview.DocTemplate.__init__( self, win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView ) def OpenObject(self, root): # Use this instead of OpenDocumentFile. # Look for existing open document for doc in self.GetDocumentList(): if doc.root == root: doc.GetFirstView().ActivateFrame() return doc # not found - new one. doc = BrowserDocument(self, root) frame = self.CreateNewFrame(doc) doc.OnNewDocument() self.InitialUpdateFrame(frame, doc, 1) return doc class BrowserDocument(docview.Document): def __init__(self, template, root): docview.Document.__init__(self, template) self.root = root self.SetTitle("Browser: " + root.name) def OnOpenDocument(self, name): raise TypeError("This template can not open files") return 0 class BrowserView(docview.TreeView): def OnInitialUpdate(self): import commctrl rc = self._obj_.OnInitialUpdate() list = hierlist.HierListWithItems( self.GetDocument().root, win32ui.IDB_BROWSER_HIER, win32ui.AFX_IDW_PANE_FIRST, ) list.HierInit(self.GetParent()) list.SetStyle( commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS ) return rc template = None def MakeTemplate(): global template if template is None: template = ( BrowserTemplate() ) # win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView) def BrowseMDI(ob=__main__): """Browse an object using an MDI window.""" MakeTemplate() root = MakeHLI(ob, repr(ob)) if not root.IsExpandable(): raise TypeError( "Browse() argument must have __dict__ attribute, or be a Browser supported type" ) template.OpenObject(root)