Mercurial > repos > rliterman > csp2
comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/grep.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 """Grep dialog for Find in Files functionality. | |
2 | |
3 Inherits from SearchDialogBase for GUI and uses searchengine | |
4 to prepare search pattern. | |
5 """ | |
6 import fnmatch | |
7 import os | |
8 import sys | |
9 | |
10 from tkinter import StringVar, BooleanVar | |
11 from tkinter.ttk import Checkbutton # Frame imported in ...Base | |
12 | |
13 from idlelib.searchbase import SearchDialogBase | |
14 from idlelib import searchengine | |
15 | |
16 # Importing OutputWindow here fails due to import loop | |
17 # EditorWindow -> GrepDialog -> OutputWindow -> EditorWindow | |
18 | |
19 | |
20 def grep(text, io=None, flist=None): | |
21 """Open the Find in Files dialog. | |
22 | |
23 Module-level function to access the singleton GrepDialog | |
24 instance and open the dialog. If text is selected, it is | |
25 used as the search phrase; otherwise, the previous entry | |
26 is used. | |
27 | |
28 Args: | |
29 text: Text widget that contains the selected text for | |
30 default search phrase. | |
31 io: iomenu.IOBinding instance with default path to search. | |
32 flist: filelist.FileList instance for OutputWindow parent. | |
33 """ | |
34 root = text._root() | |
35 engine = searchengine.get(root) | |
36 if not hasattr(engine, "_grepdialog"): | |
37 engine._grepdialog = GrepDialog(root, engine, flist) | |
38 dialog = engine._grepdialog | |
39 searchphrase = text.get("sel.first", "sel.last") | |
40 dialog.open(text, searchphrase, io) | |
41 | |
42 | |
43 def walk_error(msg): | |
44 "Handle os.walk error." | |
45 print(msg) | |
46 | |
47 | |
48 def findfiles(folder, pattern, recursive): | |
49 """Generate file names in dir that match pattern. | |
50 | |
51 Args: | |
52 folder: Root directory to search. | |
53 pattern: File pattern to match. | |
54 recursive: True to include subdirectories. | |
55 """ | |
56 for dirpath, _, filenames in os.walk(folder, onerror=walk_error): | |
57 yield from (os.path.join(dirpath, name) | |
58 for name in filenames | |
59 if fnmatch.fnmatch(name, pattern)) | |
60 if not recursive: | |
61 break | |
62 | |
63 | |
64 class GrepDialog(SearchDialogBase): | |
65 "Dialog for searching multiple files." | |
66 | |
67 title = "Find in Files Dialog" | |
68 icon = "Grep" | |
69 needwrapbutton = 0 | |
70 | |
71 def __init__(self, root, engine, flist): | |
72 """Create search dialog for searching for a phrase in the file system. | |
73 | |
74 Uses SearchDialogBase as the basis for the GUI and a | |
75 searchengine instance to prepare the search. | |
76 | |
77 Attributes: | |
78 flist: filelist.Filelist instance for OutputWindow parent. | |
79 globvar: String value of Entry widget for path to search. | |
80 globent: Entry widget for globvar. Created in | |
81 create_entries(). | |
82 recvar: Boolean value of Checkbutton widget for | |
83 traversing through subdirectories. | |
84 """ | |
85 super().__init__(root, engine) | |
86 self.flist = flist | |
87 self.globvar = StringVar(root) | |
88 self.recvar = BooleanVar(root) | |
89 | |
90 def open(self, text, searchphrase, io=None): | |
91 """Make dialog visible on top of others and ready to use. | |
92 | |
93 Extend the SearchDialogBase open() to set the initial value | |
94 for globvar. | |
95 | |
96 Args: | |
97 text: Multicall object containing the text information. | |
98 searchphrase: String phrase to search. | |
99 io: iomenu.IOBinding instance containing file path. | |
100 """ | |
101 SearchDialogBase.open(self, text, searchphrase) | |
102 if io: | |
103 path = io.filename or "" | |
104 else: | |
105 path = "" | |
106 dir, base = os.path.split(path) | |
107 head, tail = os.path.splitext(base) | |
108 if not tail: | |
109 tail = ".py" | |
110 self.globvar.set(os.path.join(dir, "*" + tail)) | |
111 | |
112 def create_entries(self): | |
113 "Create base entry widgets and add widget for search path." | |
114 SearchDialogBase.create_entries(self) | |
115 self.globent = self.make_entry("In files:", self.globvar)[0] | |
116 | |
117 def create_other_buttons(self): | |
118 "Add check button to recurse down subdirectories." | |
119 btn = Checkbutton( | |
120 self.make_frame()[0], variable=self.recvar, | |
121 text="Recurse down subdirectories") | |
122 btn.pack(side="top", fill="both") | |
123 | |
124 def create_command_buttons(self): | |
125 "Create base command buttons and add button for Search Files." | |
126 SearchDialogBase.create_command_buttons(self) | |
127 self.make_button("Search Files", self.default_command, isdef=True) | |
128 | |
129 def default_command(self, event=None): | |
130 """Grep for search pattern in file path. The default command is bound | |
131 to <Return>. | |
132 | |
133 If entry values are populated, set OutputWindow as stdout | |
134 and perform search. The search dialog is closed automatically | |
135 when the search begins. | |
136 """ | |
137 prog = self.engine.getprog() | |
138 if not prog: | |
139 return | |
140 path = self.globvar.get() | |
141 if not path: | |
142 self.top.bell() | |
143 return | |
144 from idlelib.outwin import OutputWindow # leave here! | |
145 save = sys.stdout | |
146 try: | |
147 sys.stdout = OutputWindow(self.flist) | |
148 self.grep_it(prog, path) | |
149 finally: | |
150 sys.stdout = save | |
151 | |
152 def grep_it(self, prog, path): | |
153 """Search for prog within the lines of the files in path. | |
154 | |
155 For the each file in the path directory, open the file and | |
156 search each line for the matching pattern. If the pattern is | |
157 found, write the file and line information to stdout (which | |
158 is an OutputWindow). | |
159 | |
160 Args: | |
161 prog: The compiled, cooked search pattern. | |
162 path: String containing the search path. | |
163 """ | |
164 folder, filepat = os.path.split(path) | |
165 if not folder: | |
166 folder = os.curdir | |
167 filelist = sorted(findfiles(folder, filepat, self.recvar.get())) | |
168 self.close() | |
169 pat = self.engine.getpat() | |
170 print(f"Searching {pat!r} in {path} ...") | |
171 hits = 0 | |
172 try: | |
173 for fn in filelist: | |
174 try: | |
175 with open(fn, errors='replace') as f: | |
176 for lineno, line in enumerate(f, 1): | |
177 if line[-1:] == '\n': | |
178 line = line[:-1] | |
179 if prog.search(line): | |
180 sys.stdout.write(f"{fn}: {lineno}: {line}\n") | |
181 hits += 1 | |
182 except OSError as msg: | |
183 print(msg) | |
184 print(f"Hits found: {hits}\n(Hint: right-click to open locations.)" | |
185 if hits else "No hits.") | |
186 except AttributeError: | |
187 # Tk window has been closed, OutputWindow.text = None, | |
188 # so in OW.write, OW.text.insert fails. | |
189 pass | |
190 | |
191 | |
192 def _grep_dialog(parent): # htest # | |
193 from tkinter import Toplevel, Text, SEL, END | |
194 from tkinter.ttk import Frame, Button | |
195 from idlelib.pyshell import PyShellFileList | |
196 | |
197 top = Toplevel(parent) | |
198 top.title("Test GrepDialog") | |
199 x, y = map(int, parent.geometry().split('+')[1:]) | |
200 top.geometry(f"+{x}+{y + 175}") | |
201 | |
202 flist = PyShellFileList(top) | |
203 frame = Frame(top) | |
204 frame.pack() | |
205 text = Text(frame, height=5) | |
206 text.pack() | |
207 | |
208 def show_grep_dialog(): | |
209 text.tag_add(SEL, "1.0", END) | |
210 grep(text, flist=flist) | |
211 text.tag_remove(SEL, "1.0", END) | |
212 | |
213 button = Button(frame, text="Show GrepDialog", command=show_grep_dialog) | |
214 button.pack() | |
215 | |
216 if __name__ == "__main__": | |
217 from unittest import main | |
218 main('idlelib.idle_test.test_grep', verbosity=2, exit=False) | |
219 | |
220 from idlelib.idle_test.htest import run | |
221 run(_grep_dialog) |