diff CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/browser.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/browser.py	Tue Mar 18 17:55:14 2025 -0400
@@ -0,0 +1,249 @@
+"""Module browser.
+
+XXX TO DO:
+
+- 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)
+- add base classes to class browser tree
+- finish removing limitation to x.py files (ModuleBrowserTreeItem)
+"""
+
+import os
+import pyclbr
+import sys
+
+from idlelib.config import idleConf
+from idlelib import pyshell
+from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas
+from idlelib.window import ListedToplevel
+
+
+file_open = None  # Method...Item and Class...Item use this.
+# Normally pyshell.flist.open, but there is no pyshell.flist for htest.
+
+
+def transform_children(child_dict, modname=None):
+    """Transform a child dictionary to an ordered sequence of objects.
+
+    The dictionary maps names to pyclbr information objects.
+    Filter out imported objects.
+    Augment class names with bases.
+    The insertion order of the dictionary is assumed to have been in line
+    number order, so sorting is not necessary.
+
+    The current tree only calls this once per child_dict as it saves
+    TreeItems once created.  A future tree and tests might violate this,
+    so a check prevents multiple in-place augmentations.
+    """
+    obs = []  # Use list since values should already be sorted.
+    for key, obj in child_dict.items():
+        if modname is None or obj.module == modname:
+            if hasattr(obj, 'super') and obj.super and obj.name == key:
+                # If obj.name != key, it has already been suffixed.
+                supers = []
+                for sup in obj.super:
+                    if type(sup) is type(''):
+                        sname = sup
+                    else:
+                        sname = sup.name
+                        if sup.module != obj.module:
+                            sname = f'{sup.module}.{sname}'
+                    supers.append(sname)
+                obj.name += '({})'.format(', '.join(supers))
+            obs.append(obj)
+    return obs
+
+
+class ModuleBrowser:
+    """Browse module classes and functions in IDLE.
+    """
+    # This class is also the base class for pathbrowser.PathBrowser.
+    # Init and close are inherited, other methods are overridden.
+    # PathBrowser.__init__ does not call __init__ below.
+
+    def __init__(self, master, path, *, _htest=False, _utest=False):
+        """Create a window for browsing a module's structure.
+
+        Args:
+            master: parent for widgets.
+            path: full path of file to browse.
+            _htest - bool; change box location when running htest.
+            -utest - bool; suppress contents when running unittest.
+
+        Global variables:
+            file_open: Function used for opening a file.
+
+        Instance variables:
+            name: Module name.
+            file: Full path and module with .py extension.  Used in
+                creating ModuleBrowserTreeItem as the rootnode for
+                the tree and subsequently in the children.
+        """
+        self.master = master
+        self.path = path
+        self._htest = _htest
+        self._utest = _utest
+        self.init()
+
+    def close(self, event=None):
+        "Dismiss the window and the tree nodes."
+        self.top.destroy()
+        self.node.destroy()
+
+    def init(self):
+        "Create browser tkinter widgets, including the tree."
+        global file_open
+        root = self.master
+        flist = (pyshell.flist if not (self._htest or self._utest)
+                 else pyshell.PyShellFileList(root))
+        file_open = flist.open
+        pyclbr._modules.clear()
+
+        # create top
+        self.top = top = ListedToplevel(root)
+        top.protocol("WM_DELETE_WINDOW", self.close)
+        top.bind("<Escape>", self.close)
+        if self._htest: # place dialog below parent if running htest
+            top.geometry("+%d+%d" %
+                (root.winfo_rootx(), root.winfo_rooty() + 200))
+        self.settitle()
+        top.focus_set()
+
+        # create scrolled canvas
+        theme = idleConf.CurrentTheme()
+        background = idleConf.GetHighlight(theme, 'normal')['background']
+        sc = ScrolledCanvas(top, bg=background, highlightthickness=0,
+                            takefocus=1)
+        sc.frame.pack(expand=1, fill="both")
+        item = self.rootnode()
+        self.node = node = TreeNode(sc.canvas, None, item)
+        if not self._utest:
+            node.update()
+            node.expand()
+
+    def settitle(self):
+        "Set the window title."
+        self.top.wm_title("Module Browser - " + os.path.basename(self.path))
+        self.top.wm_iconname("Module Browser")
+
+    def rootnode(self):
+        "Return a ModuleBrowserTreeItem as the root of the tree."
+        return ModuleBrowserTreeItem(self.path)
+
+
+class ModuleBrowserTreeItem(TreeItem):
+    """Browser tree for Python module.
+
+    Uses TreeItem as the basis for the structure of the tree.
+    Used by both browsers.
+    """
+
+    def __init__(self, file):
+        """Create a TreeItem for the file.
+
+        Args:
+            file: Full path and module name.
+        """
+        self.file = file
+
+    def GetText(self):
+        "Return the module name as the text string to display."
+        return os.path.basename(self.file)
+
+    def GetIconName(self):
+        "Return the name of the icon to display."
+        return "python"
+
+    def GetSubList(self):
+        "Return ChildBrowserTreeItems for children."
+        return [ChildBrowserTreeItem(obj) for obj in self.listchildren()]
+
+    def OnDoubleClick(self):
+        "Open a module in an editor window when double clicked."
+        if os.path.normcase(self.file[-3:]) != ".py":
+            return
+        if not os.path.exists(self.file):
+            return
+        file_open(self.file)
+
+    def IsExpandable(self):
+        "Return True if Python (.py) file."
+        return os.path.normcase(self.file[-3:]) == ".py"
+
+    def listchildren(self):
+        "Return sequenced classes and functions in the module."
+        dir, base = os.path.split(self.file)
+        name, ext = os.path.splitext(base)
+        if os.path.normcase(ext) != ".py":
+            return []
+        try:
+            tree = pyclbr.readmodule_ex(name, [dir] + sys.path)
+        except ImportError:
+            return []
+        return transform_children(tree, name)
+
+
+class ChildBrowserTreeItem(TreeItem):
+    """Browser tree for child nodes within the module.
+
+    Uses TreeItem as the basis for the structure of the tree.
+    """
+
+    def __init__(self, obj):
+        "Create a TreeItem for a pyclbr class/function object."
+        self.obj = obj
+        self.name = obj.name
+        self.isfunction = isinstance(obj, pyclbr.Function)
+
+    def GetText(self):
+        "Return the name of the function/class to display."
+        name = self.name
+        if self.isfunction:
+            return "def " + name + "(...)"
+        else:
+            return "class " + name
+
+    def GetIconName(self):
+        "Return the name of the icon to display."
+        if self.isfunction:
+            return "python"
+        else:
+            return "folder"
+
+    def IsExpandable(self):
+        "Return True if self.obj has nested objects."
+        return self.obj.children != {}
+
+    def GetSubList(self):
+        "Return ChildBrowserTreeItems for children."
+        return [ChildBrowserTreeItem(obj)
+                for obj in transform_children(self.obj.children)]
+
+    def OnDoubleClick(self):
+        "Open module with file_open and position to lineno."
+        try:
+            edit = file_open(self.obj.file)
+            edit.gotoline(self.obj.lineno)
+        except (OSError, AttributeError):
+            pass
+
+
+def _module_browser(parent): # htest #
+    if len(sys.argv) > 1:  # If pass file on command line.
+        file = sys.argv[1]
+    else:
+        file = __file__
+        # Add nested objects for htest.
+        class Nested_in_func(TreeNode):
+            def nested_in_class(): pass
+        def closure():
+            class Nested_in_closure: pass
+    ModuleBrowser(parent, file, _htest=True)
+
+if __name__ == "__main__":
+    if len(sys.argv) == 1:  # If pass file on command line, unittest fails.
+        from unittest import main
+        main('idlelib.idle_test.test_browser', verbosity=2, exit=False)
+    from idlelib.idle_test.htest import run
+    run(_module_browser)