diff -r 74144b0e7858 Lib/idlelib/ClassBrowser.py --- a/Lib/idlelib/ClassBrowser.py Sun Mar 16 13:55:19 2014 +1000 +++ b/Lib/idlelib/ClassBrowser.py Sun Mar 16 18:41:01 2014 +0530 @@ -5,7 +5,6 @@ - reparse when source changed (maybe just a button would be OK?) (or recheck on window popup) - add popup menu with more options (e.g. doc strings, base classes, imports) -- show function argument list? (have to do pattern matching on source) - should the classes and methods lists also be in the module's menu bar? - add base classes to class browser tree """ @@ -13,6 +12,8 @@ import os import sys import pyclbr +import imp +import inspect from idlelib import PyShell from idlelib.WindowList import ListedToplevel @@ -21,13 +22,19 @@ class ClassBrowser: - def __init__(self, flist, name, path): + def __init__(self, flist, name, path, source): # XXX This API should change, if the file doesn't end in ".py" # XXX the code here is bogus! self.name = name self.file = os.path.join(path[0], self.name + ".py") + #The current text in EditorWindow. Enables ClassBrowser to work on latest + #text. + self.source = source + self.module = imp.new_module(self.name) + exec(self.source, self.module.__dict__) self.init(flist) + def close(self, event=None): self.top.destroy() self.node.destroy() @@ -57,12 +64,17 @@ self.top.wm_iconname("Class Browser") def rootnode(self): - return ModuleBrowserTreeItem(self.file) + return ModuleBrowserTreeItem(self.file, self.source, self.module) + + def destroy(self): + self.event_generate("") class ModuleBrowserTreeItem(TreeItem): - def __init__(self, file): + def __init__(self, file, source, module): self.file = file + self.source = source + self.module = module def GetText(self): return os.path.basename(self.file) @@ -73,7 +85,7 @@ def GetSubList(self): sublist = [] for name in self.listclasses(): - item = ClassBrowserTreeItem(name, self.classes, self.file) + item = ClassBrowserTreeItem(name, self.classes, self.file, self.module) sublist.append(item) return sublist @@ -93,7 +105,7 @@ if os.path.normcase(ext) != ".py": return [] try: - dict = pyclbr.readmodule_ex(name, [dir] + sys.path) + dict = pyclbr.readmodule_ex(name, [dir] + sys.path, self.source) except ImportError as msg: return [] items = [] @@ -122,10 +134,12 @@ class ClassBrowserTreeItem(TreeItem): - def __init__(self, name, classes, file): + def __init__(self, name, classes, file, module): self.name = name self.classes = classes self.file = file + self.module = module + try: self.cl = self.classes[self.name] except (IndexError, KeyError): @@ -134,7 +148,8 @@ def GetText(self): if self.isfunction: - return "def " + self.name + "(...)" + function = getattr(self.module, self.name) + return "def " + self.name + str(inspect.signature(function)) else: return "class " + self.name @@ -156,7 +171,7 @@ return [] sublist = [] for name in self.listmethods(): - item = MethodBrowserTreeItem(name, self.cl, self.file) + item = MethodBrowserTreeItem(name, self.cl, self.file, self.module) sublist.append(item) return sublist @@ -182,13 +197,16 @@ class MethodBrowserTreeItem(TreeItem): - def __init__(self, name, cl, file): + def __init__(self, name, cl, file, module): self.name = name self.cl = cl self.file = file + self.module = module + self.class_ = getattr(self.module, self.cl.name) def GetText(self): - return "def " + self.name + "(...)" + function = getattr(self.class_, self.name) + return "def " + self.name + str(inspect.signature(function)) def GetIconName(self): return "python" # XXX @@ -203,6 +221,9 @@ edit.gotoline(self.cl.methods[self.name]) def main(): + from idlelib.PyShell import PyShell, PyShellFileList + from tkinter import Tk, Button, Text + try: file = __file__ except NameError: @@ -211,11 +232,26 @@ file = sys.argv[1] else: file = sys.argv[0] + f = open(file, 'r') dir, file = os.path.split(file) name = os.path.splitext(file)[0] - ClassBrowser(PyShell.flist, name, [dir]) + root = Tk() + root.title('Class Browser Test Dialog') + root.geometry("%dx%d%+d%+d" % (275, 25, 50, 50)) + flist = PyShellFileList(root) + text = Text() + source = f.read() + text.insert('insert', source) + + + def run(): + ClassBrowser(flist, name, [dir], source) + + + Button(root, text='ClassBrowser', command=run,).pack() if sys.stdin is sys.__stdin__: - mainloop() + root.mainloop() + f.close() if __name__ == "__main__": main() diff -r 74144b0e7858 Lib/idlelib/EditorWindow.py --- a/Lib/idlelib/EditorWindow.py Sun Mar 16 13:55:19 2014 +1000 +++ b/Lib/idlelib/EditorWindow.py Sun Mar 16 18:41:01 2014 +0530 @@ -685,6 +685,7 @@ else: self.io.loadfile(file_path) + def open_class_browser(self, event=None): filename = self.io.filename if not filename: @@ -697,7 +698,15 @@ head, tail = os.path.split(filename) base, ext = os.path.splitext(tail) from idlelib import ClassBrowser - ClassBrowser.ClassBrowser(self.flist, base, [head]) + try: + if self.classbrowser: + self.classbrowser.close() + self.classbrowser = ClassBrowser.ClassBrowser( + self.flist, base, [head], self.text.get('1.0', 'end')) + except: + self.classbrowser = ClassBrowser.ClassBrowser(self.flist, base, [head], self.text.get('1.0', 'end')) + + def open_path_browser(self, event=None): from idlelib import PathBrowser diff -r 74144b0e7858 Lib/pyclbr.py --- a/Lib/pyclbr.py Sun Mar 16 13:55:19 2014 +1000 +++ b/Lib/pyclbr.py Sun Mar 16 18:41:01 2014 +0530 @@ -87,16 +87,16 @@ res[key] = value return res -def readmodule_ex(module, path=None): +def readmodule_ex(module, path=None, source=None): '''Read a module file and return a dictionary of classes. Search for MODULE in PATH and sys.path, read and parse the module and return a dictionary with one entry for each class found in the module. ''' - return _readmodule(module, path or []) + return _readmodule(module, path or [], source=source) -def _readmodule(module, path, inpackage=None): +def _readmodule(module, path,source=None, inpackage=None): '''Do the hard work for readmodule[_ex]. If INPACKAGE is given, it must be the dotted name of the package in @@ -146,13 +146,14 @@ _modules[fullmodule] = dict if spec.loader.is_package(fullmodule): dict['__path__'] = [os.path.dirname(fname)] - try: - source = spec.loader.get_source(fullmodule) - if source is None: + if source is None: + try: + source = spec.loader.get_source(fullmodule) + if source is None: + return dict + except (AttributeError, ImportError): + # not Python source, can't do anything with this module return dict - except (AttributeError, ImportError): - # not Python source, can't do anything with this module - return dict f = io.StringIO(source)