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)