Index: Lib/pyclbr.py =================================================================== --- Lib/pyclbr.py (revision 74352) +++ Lib/pyclbr.py (working copy) @@ -45,34 +45,49 @@ from token import NAME, DEDENT, OP from operator import itemgetter -__all__ = ["readmodule", "readmodule_ex", "Class", "Function"] +__all__ = ["readmodule", "readmodule_ex", "Object", "Class", "Function"] _modules = {} # cache of modules we've seen +class Object: + def __init__(self, module, name, file, lineno, parent): + self.module = module + self.name = name + self.file = file + self.lineno = lineno + self.parent = parent + self.objects = {} + + def _addobject(self, name, obj): + self.objects[name] = obj + # each Python class is represented by an instance of this class -class Class: +class Class(Object): '''Class to represent a Python class.''' - def __init__(self, module, name, super, file, lineno): - self.module = module - self.name = name + def __init__(self, module, name, super, file, lineno, parent=None): + Object.__init__(self, module, name, file, lineno, parent) if super is None: super = [] self.super = super self.methods = {} - self.file = file - self.lineno = lineno def _addmethod(self, name, lineno): self.methods[name] = lineno -class Function: +class Function(Object): '''Class to represent a top-level Python function''' - def __init__(self, module, name, file, lineno): - self.module = module - self.name = name - self.file = file - self.lineno = lineno + def __init__(self, module, name, file, lineno, parent=None): + Object.__init__(self, module, name, file, lineno, parent) +def _newfunction(ob, name, lineno): + '''Helper function for creating a nested function or a method.''' + return Function(ob.module, name, ob.file, lineno, ob) + +def _newclass(ob, name, super, lineno): + '''Helper function for creating a nested class.''' + return Class(ob.module, name, super, ob.file, lineno, ob) + + def readmodule(module, path=None): '''Backwards compatible interface. @@ -164,17 +179,23 @@ tokentype, meth_name, start = g.next()[0:3] if tokentype != NAME: continue # Syntax error + cur_func = None if stack: - cur_class = stack[-1][0] - if isinstance(cur_class, Class): + cur_obj = stack[-1][0] + if isinstance(cur_obj, Object): + # it's a nested function or a method + cur_func = _newfunction(cur_obj, meth_name, lineno) + cur_obj._addobject(meth_name, cur_func) + + if isinstance(cur_obj, Class): # it's a method - cur_class._addmethod(meth_name, lineno) - # else it's a nested def + cur_func = _newfunction(cur_obj, meth_name, lineno) + cur_obj._addmethod(meth_name, lineno) else: # it's a function - dict[meth_name] = Function(fullmodule, meth_name, - fname, lineno) - stack.append((None, thisindent)) # Marker for nested fns + cur_func = Function(fullmodule, meth_name, fname, lineno) + dict[meth_name] = cur_func + stack.append((cur_func, thisindent)) # Marker for nested fns elif token == 'class': lineno, thisindent = start # close previous nested classes and defs @@ -225,9 +246,16 @@ super.append(token) # expressions in the base list are not supported inherit = names - cur_class = Class(fullmodule, class_name, inherit, - fname, lineno) - if not stack: + if stack: + cur_obj = stack[-1][0] + if isinstance(cur_obj, (Class, Function)): + # either a nested class or a class inside a function + cur_class = _newclass(cur_obj, class_name, inherit, + lineno) + cur_obj._addobject(class_name, cur_class) + else: + cur_class = Class(fullmodule, class_name, inherit, + fname, lineno) dict[class_name] = cur_class stack.append((cur_class, thisindent)) elif token == 'import' and start[1] == 0: @@ -325,18 +353,30 @@ else: path = [] dict = readmodule_ex(mod, path) + lineno_key = lambda a: getattr(a, 'lineno', 0) objs = dict.values() - objs.sort(lambda a, b: cmp(getattr(a, 'lineno', 0), - getattr(b, 'lineno', 0))) - for obj in objs: + objs.sort(key=lineno_key, reverse=True) + indent_level = 2 + while objs: + obj = objs.pop() + if isinstance(obj, str): + # Value of a __path__ key + continue + if not hasattr(obj, 'indent'): + obj.indent = 0 + + if isinstance(obj, Object): + new_objs = obj.objects.values() + new_objs.sort(key=lineno_key, reverse=True) + for ob in new_objs: + ob.indent = obj.indent + indent_level + objs.extend(new_objs) + if isinstance(obj, Class): - print "class", obj.name, obj.super, obj.lineno - methods = sorted(obj.methods.iteritems(), key=itemgetter(1)) - for name, lineno in methods: - if name != "__path__": - print " def", name, lineno + print "%sclass %s %s %s" % (' ' * obj.indent, obj.name, + obj.super, obj.lineno) elif isinstance(obj, Function): - print "def", obj.name, obj.lineno + print "%sdef %s %s" % (' ' * obj.indent, obj.name, obj.lineno) if __name__ == "__main__": _main() Index: Lib/idlelib/ClassBrowser.py =================================================================== --- Lib/idlelib/ClassBrowser.py (revision 74352) +++ Lib/idlelib/ClassBrowser.py (working copy) @@ -10,6 +10,8 @@ - add base classes to class browser tree """ +__all__ = ['ClassBrowser'] + import os import sys import pyclbr @@ -19,6 +21,27 @@ from TreeWidget import TreeNode, TreeItem, ScrolledCanvas from configHandler import idleConf +def collect_objects(d, name=None): + items = [] + objects = {} + for key, cl in d.items(): + if name is None or cl.module == name: + s = key + if hasattr(cl, 'super') and cl.super: + supers = [] + for sup in cl.super: + if type(sup) is type(''): + sname = sup + else: + sname = sup.name + if sup.module != cl.module: + sname = "%s.%s" % (sup.module, sname) + supers.append(sname) + s = s + "(%s)" % ", ".join(supers) + items.append((cl.lineno, s)) + objects[s] = cl + return objects, items + class ClassBrowser: def __init__(self, flist, name, path): @@ -72,8 +95,8 @@ def GetSubList(self): sublist = [] - for name in self.listclasses(): - item = ClassBrowserTreeItem(name, self.classes, self.file) + for name in self.listobjects(): + item = ObjectBrowserTreeItem(name, self.classes, self.file) sublist.append(item) return sublist @@ -87,7 +110,7 @@ def IsExpandable(self): return os.path.normcase(self.file[-3:]) == ".py" - def listclasses(self): + def listobjects(self): dir, file = os.path.split(self.file) name, ext = os.path.splitext(file) if os.path.normcase(ext) != ".py": @@ -96,31 +119,11 @@ dict = pyclbr.readmodule_ex(name, [dir] + sys.path) except ImportError, msg: return [] - items = [] - self.classes = {} - for key, cl in dict.items(): - if cl.module == name: - s = key - if hasattr(cl, 'super') and cl.super: - supers = [] - for sup in cl.super: - if type(sup) is type(''): - sname = sup - else: - sname = sup.name - if sup.module != cl.module: - sname = "%s.%s" % (sup.module, sname) - supers.append(sname) - s = s + "(%s)" % ", ".join(supers) - items.append((cl.lineno, s)) - self.classes[s] = cl + self.classes, items = collect_objects(dict, name) items.sort() - list = [] - for item, s in items: - list.append(s) - return list + return [s for item, s in items] -class ClassBrowserTreeItem(TreeItem): +class ObjectBrowserTreeItem(TreeItem): def __init__(self, name, classes, file): self.name = name @@ -147,7 +150,7 @@ def IsExpandable(self): if self.cl: try: - return not not self.cl.methods + return not not self.cl.objects except AttributeError: return False @@ -155,8 +158,9 @@ if not self.cl: return [] sublist = [] - for name in self.listmethods(): - item = MethodBrowserTreeItem(name, self.cl, self.file) + for obj in self.listobjects(): + classes, item_name = obj + item = ObjectBrowserTreeItem(item_name, classes, self.file) sublist.append(item) return sublist @@ -168,40 +172,18 @@ lineno = self.cl.lineno edit.gotoline(lineno) - def listmethods(self): + def listobjects(self): if not self.cl: return [] - items = [] - for name, lineno in self.cl.methods.items(): - items.append((lineno, name)) - items.sort() - list = [] - for item, name in items: - list.append(name) - return list -class MethodBrowserTreeItem(TreeItem): + result = [] + for name, ob in self.cl.objects.items(): + classes, items = collect_objects({name:ob}) + result.append((ob.lineno, classes, items[0][1])) + result.sort() + return [item[1:] for item in result] - def __init__(self, name, cl, file): - self.name = name - self.cl = cl - self.file = file - def GetText(self): - return "def " + self.name + "(...)" - - def GetIconName(self): - return "python" # XXX - - def IsExpandable(self): - return 0 - - def OnDoubleClick(self): - if not os.path.exists(self.file): - return - edit = PyShell.flist.open(self.file) - edit.gotoline(self.cl.methods[self.name]) - def main(): try: file = __file__