browser.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. # basic module browser.
  2. # usage:
  3. # >>> import browser
  4. # >>> browser.Browse()
  5. # or
  6. # >>> browser.Browse(your_module)
  7. import sys
  8. import types
  9. import __main__
  10. import win32ui
  11. from pywin.mfc import dialog
  12. from . import hierlist
  13. special_names = [ '__doc__', '__name__', '__self__' ]
  14. #
  15. # HierList items
  16. class HLIPythonObject(hierlist.HierListItem):
  17. def __init__(self, myobject=None, name=None ):
  18. hierlist.HierListItem.__init__(self)
  19. self.myobject = myobject
  20. self.knownExpandable = None
  21. if name:
  22. self.name=name
  23. else:
  24. try:
  25. self.name=myobject.__name__
  26. except (AttributeError, TypeError):
  27. try:
  28. r = repr(myobject)
  29. if len(r)>20:
  30. r = r[:20] + "..."
  31. self.name=r
  32. except (AttributeError, TypeError):
  33. self.name="???"
  34. def __lt__(self, other):
  35. return self.name < other.name
  36. def __eq__(self, other):
  37. return self.name == other.name
  38. def __repr__(self):
  39. try:
  40. type = self.GetHLIType()
  41. except:
  42. type = "Generic"
  43. return "HLIPythonObject("+type+") - name: "+ self.name + " object: " + repr(self.myobject)
  44. def GetText(self):
  45. try:
  46. return str(self.name) + ' (' + self.GetHLIType() + ')'
  47. except AttributeError:
  48. return str(self.name) + ' = ' + repr(self.myobject)
  49. def InsertDocString(self, lst):
  50. ob = None
  51. try:
  52. ob = self.myobject.__doc__
  53. except (AttributeError, TypeError):
  54. pass
  55. # I don't quite grok descriptors enough to know how to
  56. # best hook them up. Eg:
  57. # >>> object.__getattribute__.__class__.__doc__
  58. # <attribute '__doc__' of 'wrapper_descriptor' objects>
  59. if ob and isinstance(ob, str):
  60. lst.insert(0, HLIDocString( ob, "Doc" ))
  61. def GetSubList(self):
  62. ret = []
  63. try:
  64. for (key, ob) in self.myobject.__dict__.items():
  65. if key not in special_names:
  66. ret.append(MakeHLI( ob, key ) )
  67. except (AttributeError, TypeError):
  68. pass
  69. try:
  70. for name in self.myobject.__methods__:
  71. ret.append(HLIMethod( name )) # no MakeHLI, as cant auto detect
  72. except (AttributeError, TypeError):
  73. pass
  74. try:
  75. for member in self.myobject.__members__:
  76. if not member in special_names:
  77. ret.append(MakeHLI(getattr(self.myobject, member), member))
  78. except (AttributeError, TypeError):
  79. pass
  80. ret.sort()
  81. self.InsertDocString(ret)
  82. return ret
  83. # if the has a dict, it is expandable.
  84. def IsExpandable(self):
  85. if self.knownExpandable is None:
  86. self.knownExpandable = self.CalculateIsExpandable()
  87. return self.knownExpandable
  88. def CalculateIsExpandable(self):
  89. if hasattr(self.myobject, '__doc__'):
  90. return 1
  91. try:
  92. for key in self.myobject.__dict__.keys():
  93. if key not in special_names:
  94. return 1
  95. except (AttributeError, TypeError):
  96. pass
  97. try:
  98. self.myobject.__methods__
  99. return 1
  100. except (AttributeError, TypeError):
  101. pass
  102. try:
  103. for item in self.myobject.__members__:
  104. if item not in special_names:
  105. return 1
  106. except (AttributeError, TypeError):
  107. pass
  108. return 0
  109. def GetBitmapColumn(self):
  110. if self.IsExpandable():
  111. return 0
  112. else:
  113. return 4
  114. def TakeDefaultAction(self):
  115. ShowObject(self.myobject, self.name)
  116. class HLIDocString(HLIPythonObject):
  117. def GetHLIType(self):
  118. return "DocString"
  119. def GetText(self):
  120. return self.myobject.strip()
  121. def IsExpandable(self):
  122. return 0
  123. def GetBitmapColumn(self):
  124. return 6
  125. class HLIModule(HLIPythonObject):
  126. def GetHLIType(self):
  127. return "Module"
  128. class HLIFrame(HLIPythonObject):
  129. def GetHLIType(self):
  130. return "Stack Frame"
  131. class HLITraceback(HLIPythonObject):
  132. def GetHLIType(self):
  133. return "Traceback"
  134. class HLIClass(HLIPythonObject):
  135. def GetHLIType(self):
  136. return "Class"
  137. def GetSubList(self):
  138. ret = []
  139. for base in self.myobject.__bases__:
  140. ret.append( MakeHLI(base, 'Base class: ' + base.__name__ ) )
  141. ret = ret + HLIPythonObject.GetSubList(self)
  142. return ret
  143. class HLIMethod(HLIPythonObject):
  144. # myobject is just a string for methods.
  145. def GetHLIType(self):
  146. return "Method"
  147. def GetText(self):
  148. return "Method: " + self.myobject + '()'
  149. class HLICode(HLIPythonObject):
  150. def GetHLIType(self):
  151. return "Code"
  152. def IsExpandable(self):
  153. return self.myobject
  154. def GetSubList(self):
  155. ret = []
  156. ret.append( MakeHLI( self.myobject.co_consts, "Constants (co_consts)" ))
  157. ret.append( MakeHLI( self.myobject.co_names, "Names (co_names)" ))
  158. ret.append( MakeHLI( self.myobject.co_filename, "Filename (co_filename)" ))
  159. ret.append( MakeHLI( self.myobject.co_argcount, "Number of args (co_argcount)"))
  160. ret.append( MakeHLI( self.myobject.co_varnames, "Param names (co_varnames)"))
  161. return ret
  162. class HLIInstance(HLIPythonObject):
  163. def GetHLIType(self):
  164. return "Instance"
  165. def GetText(self):
  166. return str(self.name) + ' (Instance of class ' + str(self.myobject.__class__.__name__) + ')'
  167. def IsExpandable(self):
  168. return 1
  169. def GetSubList(self):
  170. ret = []
  171. ret.append( MakeHLI( self.myobject.__class__) )
  172. ret = ret + HLIPythonObject.GetSubList(self)
  173. return ret
  174. class HLIBuiltinFunction(HLIPythonObject):
  175. def GetHLIType(self):
  176. return "Builtin Function"
  177. class HLIFunction(HLIPythonObject):
  178. def GetHLIType(self):
  179. return "Function"
  180. def IsExpandable(self):
  181. return 1
  182. def GetSubList(self):
  183. ret = []
  184. # ret.append( MakeHLI( self.myobject.func_argcount, "Arg Count" ))
  185. try:
  186. ret.append( MakeHLI( self.myobject.func_argdefs, "Arg Defs" ))
  187. except AttributeError:
  188. pass
  189. try:
  190. code = self.myobject.__code__
  191. globs = self.myobject.__globals__
  192. except AttributeError:
  193. # must be py2.5 or earlier...
  194. code = self.myobject.func_code
  195. globs = self.myobject.func_globals
  196. ret.append(MakeHLI(code, "Code" ))
  197. ret.append(MakeHLI(globs, "Globals" ))
  198. self.InsertDocString(ret)
  199. return ret
  200. class HLISeq(HLIPythonObject):
  201. def GetHLIType(self):
  202. return "Sequence (abstract!)"
  203. def IsExpandable(self):
  204. return len(self.myobject)>0
  205. def GetSubList(self):
  206. ret = []
  207. pos=0
  208. for item in self.myobject:
  209. ret.append(MakeHLI( item, '['+str(pos)+']' ) )
  210. pos=pos+1
  211. self.InsertDocString(ret)
  212. return ret
  213. class HLIList(HLISeq):
  214. def GetHLIType(self):
  215. return "List"
  216. class HLITuple(HLISeq):
  217. def GetHLIType(self):
  218. return "Tuple"
  219. class HLIDict(HLIPythonObject):
  220. def GetHLIType(self):
  221. return "Dict"
  222. def IsExpandable(self):
  223. try:
  224. self.myobject.__doc__
  225. return 1
  226. except (AttributeError, TypeError):
  227. return len(self.myobject) > 0
  228. def GetSubList(self):
  229. ret = []
  230. keys = list(self.myobject.keys())
  231. keys.sort()
  232. for key in keys:
  233. ob = self.myobject[key]
  234. ret.append(MakeHLI( ob, str(key) ) )
  235. self.InsertDocString(ret)
  236. return ret
  237. # In Python 1.6, strings and Unicode have builtin methods, but we dont really want to see these
  238. class HLIString(HLIPythonObject):
  239. def IsExpandable(self):
  240. return 0
  241. TypeMap = { type : HLIClass,
  242. types.FunctionType: HLIFunction,
  243. tuple: HLITuple,
  244. dict: HLIDict,
  245. list: HLIList,
  246. types.ModuleType: HLIModule,
  247. types.CodeType : HLICode,
  248. types.BuiltinFunctionType : HLIBuiltinFunction,
  249. types.FrameType : HLIFrame,
  250. types.TracebackType : HLITraceback,
  251. str : HLIString,
  252. int: HLIPythonObject,
  253. bool: HLIPythonObject,
  254. float: HLIPythonObject,
  255. }
  256. def MakeHLI( ob, name=None ):
  257. try:
  258. cls = TypeMap[type(ob)]
  259. except KeyError:
  260. # hrmph - this check gets more and more bogus as Python
  261. # improves. Its possible we should just *always* use
  262. # HLIInstance?
  263. if hasattr(ob, '__class__'): # 'new style' class
  264. cls = HLIInstance
  265. else:
  266. cls = HLIPythonObject
  267. return cls( ob, name )
  268. #########################################
  269. #
  270. # Dialog related.
  271. class DialogShowObject(dialog.Dialog):
  272. def __init__(self, object, title):
  273. self.object = object
  274. self.title = title
  275. dialog.Dialog.__init__(self, win32ui.IDD_LARGE_EDIT)
  276. def OnInitDialog(self):
  277. import re
  278. self.SetWindowText(self.title)
  279. self.edit = self.GetDlgItem(win32ui.IDC_EDIT1)
  280. try:
  281. strval = str(self.object)
  282. except:
  283. t, v, tb = sys.exc_info()
  284. strval = "Exception getting object value\n\n%s:%s" % (t, v)
  285. tb = None
  286. strval = re.sub('\n','\r\n', strval)
  287. self.edit.ReplaceSel(strval)
  288. def ShowObject(object, title):
  289. dlg = DialogShowObject(object, title)
  290. dlg.DoModal()
  291. # And some mods for a sizable dialog from Sam Rushing!
  292. import win32con
  293. import win32api
  294. import commctrl
  295. class dynamic_browser (dialog.Dialog):
  296. style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE
  297. cs = (
  298. win32con.WS_CHILD |
  299. win32con.WS_VISIBLE |
  300. commctrl.TVS_HASLINES |
  301. commctrl.TVS_LINESATROOT |
  302. commctrl.TVS_HASBUTTONS
  303. )
  304. dt = [
  305. ["Python Object Browser", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")],
  306. ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs]
  307. ]
  308. def __init__ (self, hli_root):
  309. dialog.Dialog.__init__ (self, self.dt)
  310. self.hier_list = hierlist.HierListWithItems (
  311. hli_root,
  312. win32ui.IDB_BROWSER_HIER
  313. )
  314. self.HookMessage (self.on_size, win32con.WM_SIZE)
  315. def OnInitDialog (self):
  316. self.hier_list.HierInit (self)
  317. return dialog.Dialog.OnInitDialog (self)
  318. def OnOK(self):
  319. self.hier_list.HierTerm()
  320. self.hier_list = None
  321. return self._obj_.OnOK()
  322. def OnCancel(self):
  323. self.hier_list.HierTerm()
  324. self.hier_list = None
  325. return self._obj_.OnCancel()
  326. def on_size (self, params):
  327. lparam = params[3]
  328. w = win32api.LOWORD(lparam)
  329. h = win32api.HIWORD(lparam)
  330. self.GetDlgItem (win32ui.IDC_LIST1).MoveWindow((0,0,w,h))
  331. def Browse (ob=__main__):
  332. " Browse the argument, or the main dictionary "
  333. root = MakeHLI (ob, 'root')
  334. if not root.IsExpandable():
  335. raise TypeError("Browse() argument must have __dict__ attribute, or be a Browser supported type")
  336. dlg = dynamic_browser (root)
  337. dlg.CreateWindow()
  338. #
  339. #
  340. # Classes for using the browser in an MDI window, rather than a dialog
  341. #
  342. from pywin.mfc import docview
  343. class BrowserTemplate(docview.DocTemplate):
  344. def __init__(self):
  345. docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView)
  346. def OpenObject(self, root): # Use this instead of OpenDocumentFile.
  347. # Look for existing open document
  348. for doc in self.GetDocumentList():
  349. if doc.root==root:
  350. doc.GetFirstView().ActivateFrame()
  351. return doc
  352. # not found - new one.
  353. doc = BrowserDocument(self, root)
  354. frame = self.CreateNewFrame(doc)
  355. doc.OnNewDocument()
  356. self.InitialUpdateFrame(frame, doc, 1)
  357. return doc
  358. class BrowserDocument (docview.Document):
  359. def __init__(self, template, root):
  360. docview.Document.__init__(self, template)
  361. self.root = root
  362. self.SetTitle("Browser: " + root.name)
  363. def OnOpenDocument (self, name):
  364. raise TypeError("This template can not open files")
  365. return 0
  366. class BrowserView(docview.TreeView):
  367. def OnInitialUpdate(self):
  368. import commctrl
  369. rc = self._obj_.OnInitialUpdate()
  370. list=hierlist.HierListWithItems( self.GetDocument().root, win32ui.IDB_BROWSER_HIER, win32ui.AFX_IDW_PANE_FIRST)
  371. list.HierInit(self.GetParent())
  372. list.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
  373. return rc
  374. template = None
  375. def MakeTemplate():
  376. global template
  377. if template is None:
  378. template = BrowserTemplate() #win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView)
  379. def BrowseMDI(ob=__main__):
  380. """Browse an object using an MDI window.
  381. """
  382. MakeTemplate()
  383. root = MakeHLI(ob, repr(ob))
  384. if not root.IsExpandable():
  385. raise TypeError("Browse() argument must have __dict__ attribute, or be a Browser supported type")
  386. template.OpenObject(root)