Mercurial > repos > rliterman > csp2
comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/browser.py @ 68:5028fdace37b
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 16:23:26 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
67:0e9998148a16 | 68:5028fdace37b |
---|---|
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) |