diff -r 0c0f90e8df26 Lib/idlelib/ClassBrowser.py --- a/Lib/idlelib/ClassBrowser.py Mon Mar 17 07:36:51 2014 +0100 +++ b/Lib/idlelib/ClassBrowser.py Tue Mar 18 21:48:33 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,11 +22,16 @@ 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 + #current text instead of whats on disk. + self.source = source + self.module = imp.new_module(self.name) + exec(self.source, self.module.__dict__) self.init(flist) def close(self, event=None): @@ -57,12 +63,14 @@ self.top.wm_iconname("Class Browser") def rootnode(self): - return ModuleBrowserTreeItem(self.file) + return ModuleBrowserTreeItem(self.file, self.source, self.module) 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 +81,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 +101,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 +130,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 +144,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 +167,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 +193,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 +217,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 +228,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 0c0f90e8df26 Lib/idlelib/EditorWindow.py --- a/Lib/idlelib/EditorWindow.py Mon Mar 17 07:36:51 2014 +0100 +++ b/Lib/idlelib/EditorWindow.py Tue Mar 18 21:48:33 2014 +0530 @@ -697,7 +697,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 0c0f90e8df26 Lib/pyclbr.py --- a/Lib/pyclbr.py Mon Mar 17 07:36:51 2014 +0100 +++ b/Lib/pyclbr.py Tue Mar 18 21:48:33 2014 +0530 @@ -87,23 +87,31 @@ 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 or None, path or [], source=source) -def _readmodule(module, path, inpackage=None): +def _readmodule(module, path, inpackage=None, source=None): '''Do the hard work for readmodule[_ex]. If INPACKAGE is given, it must be the dotted name of the package in which we are searching for a submodule, and then PATH must be the package search path; otherwise, we are searching for a top-level module, and PATH is combined with sys.path. - ''' + + source - To handld when raw source is passed instead of module (and path.) + ''' + # Initialize the dict for this module's contents + dict = {} + + if module is None and source is None: + return dict + # Compute the full module name (prepending inpackage if set) if inpackage is not None: fullmodule = "%s.%s" % (inpackage, module) @@ -114,9 +122,6 @@ if fullmodule in _modules: return _modules[fullmodule] - # Initialize the dict for this module's contents - dict = {} - # Check if it is a built-in module; we don't do much for these if module in sys.builtin_module_names and inpackage is None: _modules[module] = dict @@ -146,13 +151,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) diff -r 0c0f90e8df26 Lib/test/test_pyclbr.py --- a/Lib/test/test_pyclbr.py Mon Mar 17 07:36:51 2014 +0100 +++ b/Lib/test/test_pyclbr.py Tue Mar 18 21:48:33 2014 +0530 @@ -172,6 +172,11 @@ # not a package self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncore.foo') + def test_string_source(self): + #Does not test working of pyclbr. + #Just make sure string source is well recieved + source = 'def foo():pass' + pyclbr.readmodule_ex('test_pyclbr', source=source) def test_main(): run_unittest(PyclbrTest)