17aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer"""Class browser.
27aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
37aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David SchererXXX TO DO:
47aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
57aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer- reparse when source changed (maybe just a button would be OK?)
67aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    (or recheck on window popup)
77aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer- add popup menu with more options (e.g. doc strings, base classes, imports)
87aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer- show function argument list? (have to do pattern matching on source)
97aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer- should the classes and methods lists also be in the module's menu bar?
107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer- add base classes to class browser tree
117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer"""
127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport os
147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport sys
157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport pyclbr
167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
17d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import PyShell
18d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib.WindowList import ListedToplevel
19d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
20d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib.configHandler import idleConf
217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
22cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedyfile_open = None  # Method...Item and Class...Item use this.
23cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy# Normally PyShell.flist.open, but there is no PyShell.flist for htest.
24cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy
257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererclass ClassBrowser:
267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2762012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy    def __init__(self, flist, name, path, _htest=False):
287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX This API should change, if the file doesn't end in ".py"
297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX the code here is bogus!
3062012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy        """
3162012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy        _htest - bool, change box when location running htest.
3262012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy        """
33cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy        global file_open
34cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy        if not _htest:
35cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy            file_open = PyShell.flist.open
367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.name = name
377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.file = os.path.join(path[0], self.name + ".py")
3862012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy        self._htest = _htest
397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.init(flist)
407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close(self, event=None):
427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.destroy()
437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.node.destroy()
447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def init(self, flist):
467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.flist = flist
477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # reset pyclbr
487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        pyclbr._modules.clear()
497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # create top
507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top = top = ListedToplevel(flist.root)
517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top.protocol("WM_DELETE_WINDOW", self.close)
527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top.bind("<Escape>", self.close)
5362012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy        if self._htest: # place dialog below parent if running htest
5462012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy            top.geometry("+%d+%d" %
5562012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy                (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200))
567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.settitle()
577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top.focus_set()
587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # create scrolled canvas
5935aa5d07a2865f34d178e7c35c1f1eeb73289eacTerry Jan Reedy        theme = idleConf.CurrentTheme()
6073360a3e61274ffcc4c9fc3d09746bd6603e92a5Kurt B. Kaiser        background = idleConf.GetHighlight(theme, 'normal')['background']
6173360a3e61274ffcc4c9fc3d09746bd6603e92a5Kurt B. Kaiser        sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        sc.frame.pack(expand=1, fill="both")
637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        item = self.rootnode()
647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.node = node = TreeNode(sc.canvas, None, item)
657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        node.update()
667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        node.expand()
677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def settitle(self):
697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_title("Class Browser - " + self.name)
707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_iconname("Class Browser")
717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def rootnode(self):
737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return ModuleBrowserTreeItem(self.file)
747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererclass ModuleBrowserTreeItem(TreeItem):
767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def __init__(self, file):
787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.file = file
797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetText(self):
817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return os.path.basename(self.file)
827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetIconName(self):
847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return "python"
857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetSubList(self):
877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        sublist = []
887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name in self.listclasses():
897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            item = ClassBrowserTreeItem(name, self.classes, self.file)
907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            sublist.append(item)
917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return sublist
927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def OnDoubleClick(self):
947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if os.path.normcase(self.file[-3:]) != ".py":
957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not os.path.exists(self.file):
977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        PyShell.flist.open(self.file)
997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def IsExpandable(self):
1017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return os.path.normcase(self.file[-3:]) == ".py"
102d6c4c9e846660cb501491ec0b98f5086ef3fcc7fKurt B. Kaiser
1037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def listclasses(self):
1047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        dir, file = os.path.split(self.file)
1057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        name, ext = os.path.splitext(file)
1067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if os.path.normcase(ext) != ".py":
1077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return []
1087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
1097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
1109bc505616b4bc88b29993f351218c3083bd3f3e8Terry Jan Reedy        except ImportError:
1117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return []
1127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        items = []
1137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.classes = {}
1147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for key, cl in dict.items():
1157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if cl.module == name:
1167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                s = key
1176550051691d604c728ed56e4acf90dc6535981f9Raymond Hettinger                if hasattr(cl, 'super') and cl.super:
1187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    supers = []
1197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    for sup in cl.super:
1207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        if type(sup) is type(''):
1217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            sname = sup
1227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        else:
1237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            sname = sup.name
1247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            if sup.module != cl.module:
1257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                                sname = "%s.%s" % (sup.module, sname)
1267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        supers.append(sname)
127a2876442047ac41abce63b5d0e987e9d807c694fKurt B. Kaiser                    s = s + "(%s)" % ", ".join(supers)
1287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                items.append((cl.lineno, s))
1297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                self.classes[s] = cl
1307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        items.sort()
1317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        list = []
1327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for item, s in items:
1337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            list.append(s)
1347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return list
1357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererclass ClassBrowserTreeItem(TreeItem):
1377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def __init__(self, name, classes, file):
1397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.name = name
1407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.classes = classes
1417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.file = file
1427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
1437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.cl = self.classes[self.name]
1447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except (IndexError, KeyError):
1457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.cl = None
1467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.isfunction = isinstance(self.cl, pyclbr.Function)
1477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetText(self):
1497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.isfunction:
1507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return "def " + self.name + "(...)"
1517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
1527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return "class " + self.name
1537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetIconName(self):
1557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.isfunction:
1567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return "python"
1577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
1587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return "folder"
1597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def IsExpandable(self):
1617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.cl:
1620b743441a60640824360bdff15780fc5d40489d6Kurt B. Kaiser            try:
1630b743441a60640824360bdff15780fc5d40489d6Kurt B. Kaiser                return not not self.cl.methods
1640b743441a60640824360bdff15780fc5d40489d6Kurt B. Kaiser            except AttributeError:
1650b743441a60640824360bdff15780fc5d40489d6Kurt B. Kaiser                return False
1667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetSubList(self):
1687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.cl:
1697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return []
1707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        sublist = []
1717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name in self.listmethods():
1727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            item = MethodBrowserTreeItem(name, self.cl, self.file)
1737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            sublist.append(item)
1747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return sublist
1757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def OnDoubleClick(self):
1777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not os.path.exists(self.file):
1787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
179cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy        edit = file_open(self.file)
1807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if hasattr(self.cl, 'lineno'):
1817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            lineno = self.cl.lineno
1827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            edit.gotoline(lineno)
1837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def listmethods(self):
1857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.cl:
1867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return []
1877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        items = []
1887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name, lineno in self.cl.methods.items():
1897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            items.append((lineno, name))
1907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        items.sort()
1917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        list = []
1927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for item, name in items:
1937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            list.append(name)
1947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return list
1957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererclass MethodBrowserTreeItem(TreeItem):
1977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def __init__(self, name, cl, file):
1997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.name = name
2007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.cl = cl
2017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.file = file
2027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetText(self):
2047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return "def " + self.name + "(...)"
2057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def GetIconName(self):
2077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return "python" # XXX
2087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def IsExpandable(self):
2107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return 0
2117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def OnDoubleClick(self):
2137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not os.path.exists(self.file):
2147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
215cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy        edit = file_open(self.file)
2167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        edit.gotoline(self.cl.methods[self.name])
2177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
21862012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedydef _class_browser(parent): #Wrapper for htest
2197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    try:
2207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        file = __file__
2217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    except NameError:
2227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        file = sys.argv[0]
2237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if sys.argv[1:]:
2247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            file = sys.argv[1]
2257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
2267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            file = sys.argv[0]
2277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    dir, file = os.path.split(file)
2287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    name = os.path.splitext(file)[0]
22962012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy    flist = PyShell.PyShellFileList(parent)
230cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy    global file_open
231cf834769e4f2c3b14c93091334f2334284c9d8c0Terry Jan Reedy    file_open = flist.open
23262012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy    ClassBrowser(flist, name, [dir], _htest=True)
2337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererif __name__ == "__main__":
23562012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy    from idlelib.idle_test.htest import run
23662012fc719818b6087b01c93fcc1cd0d2b4d8932Terry Jan Reedy    run(_class_browser)
237