comparison 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
comparison
equal deleted inserted replaced
67:0e9998148a16 69:33d812a61356
1 """Module browser.
2
3 XXX TO DO:
4
5 - reparse when source changed (maybe just a button would be OK?)
6 (or recheck on window popup)
7 - add popup menu with more options (e.g. doc strings, base classes, imports)
8 - add base classes to class browser tree
9 - finish removing limitation to x.py files (ModuleBrowserTreeItem)
10 """
11
12 import os
13 import pyclbr
14 import sys
15
16 from idlelib.config import idleConf
17 from idlelib import pyshell
18 from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas
19 from idlelib.window import ListedToplevel
20
21
22 file_open = None # Method...Item and Class...Item use this.
23 # Normally pyshell.flist.open, but there is no pyshell.flist for htest.
24
25
26 def transform_children(child_dict, modname=None):
27 """Transform a child dictionary to an ordered sequence of objects.
28
29 The dictionary maps names to pyclbr information objects.
30 Filter out imported objects.
31 Augment class names with bases.
32 The insertion order of the dictionary is assumed to have been in line
33 number order, so sorting is not necessary.
34
35 The current tree only calls this once per child_dict as it saves
36 TreeItems once created. A future tree and tests might violate this,
37 so a check prevents multiple in-place augmentations.
38 """
39 obs = [] # Use list since values should already be sorted.
40 for key, obj in child_dict.items():
41 if modname is None or obj.module == modname:
42 if hasattr(obj, 'super') and obj.super and obj.name == key:
43 # If obj.name != key, it has already been suffixed.
44 supers = []
45 for sup in obj.super:
46 if type(sup) is type(''):
47 sname = sup
48 else:
49 sname = sup.name
50 if sup.module != obj.module:
51 sname = f'{sup.module}.{sname}'
52 supers.append(sname)
53 obj.name += '({})'.format(', '.join(supers))
54 obs.append(obj)
55 return obs
56
57
58 class ModuleBrowser:
59 """Browse module classes and functions in IDLE.
60 """
61 # This class is also the base class for pathbrowser.PathBrowser.
62 # Init and close are inherited, other methods are overridden.
63 # PathBrowser.__init__ does not call __init__ below.
64
65 def __init__(self, master, path, *, _htest=False, _utest=False):
66 """Create a window for browsing a module's structure.
67
68 Args:
69 master: parent for widgets.
70 path: full path of file to browse.
71 _htest - bool; change box location when running htest.
72 -utest - bool; suppress contents when running unittest.
73
74 Global variables:
75 file_open: Function used for opening a file.
76
77 Instance variables:
78 name: Module name.
79 file: Full path and module with .py extension. Used in
80 creating ModuleBrowserTreeItem as the rootnode for
81 the tree and subsequently in the children.
82 """
83 self.master = master
84 self.path = path
85 self._htest = _htest
86 self._utest = _utest
87 self.init()
88
89 def close(self, event=None):
90 "Dismiss the window and the tree nodes."
91 self.top.destroy()
92 self.node.destroy()
93
94 def init(self):
95 "Create browser tkinter widgets, including the tree."
96 global file_open
97 root = self.master
98 flist = (pyshell.flist if not (self._htest or self._utest)
99 else pyshell.PyShellFileList(root))
100 file_open = flist.open
101 pyclbr._modules.clear()
102
103 # create top
104 self.top = top = ListedToplevel(root)
105 top.protocol("WM_DELETE_WINDOW", self.close)
106 top.bind("<Escape>", self.close)
107 if self._htest: # place dialog below parent if running htest
108 top.geometry("+%d+%d" %
109 (root.winfo_rootx(), root.winfo_rooty() + 200))
110 self.settitle()
111 top.focus_set()
112
113 # create scrolled canvas
114 theme = idleConf.CurrentTheme()
115 background = idleConf.GetHighlight(theme, 'normal')['background']
116 sc = ScrolledCanvas(top, bg=background, highlightthickness=0,
117 takefocus=1)
118 sc.frame.pack(expand=1, fill="both")
119 item = self.rootnode()
120 self.node = node = TreeNode(sc.canvas, None, item)
121 if not self._utest:
122 node.update()
123 node.expand()
124
125 def settitle(self):
126 "Set the window title."
127 self.top.wm_title("Module Browser - " + os.path.basename(self.path))
128 self.top.wm_iconname("Module Browser")
129
130 def rootnode(self):
131 "Return a ModuleBrowserTreeItem as the root of the tree."
132 return ModuleBrowserTreeItem(self.path)
133
134
135 class ModuleBrowserTreeItem(TreeItem):
136 """Browser tree for Python module.
137
138 Uses TreeItem as the basis for the structure of the tree.
139 Used by both browsers.
140 """
141
142 def __init__(self, file):
143 """Create a TreeItem for the file.
144
145 Args:
146 file: Full path and module name.
147 """
148 self.file = file
149
150 def GetText(self):
151 "Return the module name as the text string to display."
152 return os.path.basename(self.file)
153
154 def GetIconName(self):
155 "Return the name of the icon to display."
156 return "python"
157
158 def GetSubList(self):
159 "Return ChildBrowserTreeItems for children."
160 return [ChildBrowserTreeItem(obj) for obj in self.listchildren()]
161
162 def OnDoubleClick(self):
163 "Open a module in an editor window when double clicked."
164 if os.path.normcase(self.file[-3:]) != ".py":
165 return
166 if not os.path.exists(self.file):
167 return
168 file_open(self.file)
169
170 def IsExpandable(self):
171 "Return True if Python (.py) file."
172 return os.path.normcase(self.file[-3:]) == ".py"
173
174 def listchildren(self):
175 "Return sequenced classes and functions in the module."
176 dir, base = os.path.split(self.file)
177 name, ext = os.path.splitext(base)
178 if os.path.normcase(ext) != ".py":
179 return []
180 try:
181 tree = pyclbr.readmodule_ex(name, [dir] + sys.path)
182 except ImportError:
183 return []
184 return transform_children(tree, name)
185
186
187 class ChildBrowserTreeItem(TreeItem):
188 """Browser tree for child nodes within the module.
189
190 Uses TreeItem as the basis for the structure of the tree.
191 """
192
193 def __init__(self, obj):
194 "Create a TreeItem for a pyclbr class/function object."
195 self.obj = obj
196 self.name = obj.name
197 self.isfunction = isinstance(obj, pyclbr.Function)
198
199 def GetText(self):
200 "Return the name of the function/class to display."
201 name = self.name
202 if self.isfunction:
203 return "def " + name + "(...)"
204 else:
205 return "class " + name
206
207 def GetIconName(self):
208 "Return the name of the icon to display."
209 if self.isfunction:
210 return "python"
211 else:
212 return "folder"
213
214 def IsExpandable(self):
215 "Return True if self.obj has nested objects."
216 return self.obj.children != {}
217
218 def GetSubList(self):
219 "Return ChildBrowserTreeItems for children."
220 return [ChildBrowserTreeItem(obj)
221 for obj in transform_children(self.obj.children)]
222
223 def OnDoubleClick(self):
224 "Open module with file_open and position to lineno."
225 try:
226 edit = file_open(self.obj.file)
227 edit.gotoline(self.obj.lineno)
228 except (OSError, AttributeError):
229 pass
230
231
232 def _module_browser(parent): # htest #
233 if len(sys.argv) > 1: # If pass file on command line.
234 file = sys.argv[1]
235 else:
236 file = __file__
237 # Add nested objects for htest.
238 class Nested_in_func(TreeNode):
239 def nested_in_class(): pass
240 def closure():
241 class Nested_in_closure: pass
242 ModuleBrowser(parent, file, _htest=True)
243
244 if __name__ == "__main__":
245 if len(sys.argv) == 1: # If pass file on command line, unittest fails.
246 from unittest import main
247 main('idlelib.idle_test.test_browser', verbosity=2, exit=False)
248 from idlelib.idle_test.htest import run
249 run(_module_browser)