Spaces:
Runtime error
Runtime error
| """Grep dialog for Find in Files functionality. | |
| Inherits from SearchDialogBase for GUI and uses searchengine | |
| to prepare search pattern. | |
| """ | |
| import fnmatch | |
| import os | |
| import sys | |
| from tkinter import StringVar, BooleanVar | |
| from tkinter.ttk import Checkbutton # Frame imported in ...Base | |
| from idlelib.searchbase import SearchDialogBase | |
| from idlelib import searchengine | |
| # Importing OutputWindow here fails due to import loop | |
| # EditorWindow -> GrepDialog -> OutputWindow -> EditorWindow | |
| def grep(text, io=None, flist=None): | |
| """Open the Find in Files dialog. | |
| Module-level function to access the singleton GrepDialog | |
| instance and open the dialog. If text is selected, it is | |
| used as the search phrase; otherwise, the previous entry | |
| is used. | |
| Args: | |
| text: Text widget that contains the selected text for | |
| default search phrase. | |
| io: iomenu.IOBinding instance with default path to search. | |
| flist: filelist.FileList instance for OutputWindow parent. | |
| """ | |
| root = text._root() | |
| engine = searchengine.get(root) | |
| if not hasattr(engine, "_grepdialog"): | |
| engine._grepdialog = GrepDialog(root, engine, flist) | |
| dialog = engine._grepdialog | |
| searchphrase = text.get("sel.first", "sel.last") | |
| dialog.open(text, searchphrase, io) | |
| def walk_error(msg): | |
| "Handle os.walk error." | |
| print(msg) | |
| def findfiles(folder, pattern, recursive): | |
| """Generate file names in dir that match pattern. | |
| Args: | |
| folder: Root directory to search. | |
| pattern: File pattern to match. | |
| recursive: True to include subdirectories. | |
| """ | |
| for dirpath, _, filenames in os.walk(folder, onerror=walk_error): | |
| yield from (os.path.join(dirpath, name) | |
| for name in filenames | |
| if fnmatch.fnmatch(name, pattern)) | |
| if not recursive: | |
| break | |
| class GrepDialog(SearchDialogBase): | |
| "Dialog for searching multiple files." | |
| title = "Find in Files Dialog" | |
| icon = "Grep" | |
| needwrapbutton = 0 | |
| def __init__(self, root, engine, flist): | |
| """Create search dialog for searching for a phrase in the file system. | |
| Uses SearchDialogBase as the basis for the GUI and a | |
| searchengine instance to prepare the search. | |
| Attributes: | |
| flist: filelist.Filelist instance for OutputWindow parent. | |
| globvar: String value of Entry widget for path to search. | |
| globent: Entry widget for globvar. Created in | |
| create_entries(). | |
| recvar: Boolean value of Checkbutton widget for | |
| traversing through subdirectories. | |
| """ | |
| super().__init__(root, engine) | |
| self.flist = flist | |
| self.globvar = StringVar(root) | |
| self.recvar = BooleanVar(root) | |
| def open(self, text, searchphrase, io=None): | |
| """Make dialog visible on top of others and ready to use. | |
| Extend the SearchDialogBase open() to set the initial value | |
| for globvar. | |
| Args: | |
| text: Multicall object containing the text information. | |
| searchphrase: String phrase to search. | |
| io: iomenu.IOBinding instance containing file path. | |
| """ | |
| SearchDialogBase.open(self, text, searchphrase) | |
| if io: | |
| path = io.filename or "" | |
| else: | |
| path = "" | |
| dir, base = os.path.split(path) | |
| head, tail = os.path.splitext(base) | |
| if not tail: | |
| tail = ".py" | |
| self.globvar.set(os.path.join(dir, "*" + tail)) | |
| def create_entries(self): | |
| "Create base entry widgets and add widget for search path." | |
| SearchDialogBase.create_entries(self) | |
| self.globent = self.make_entry("In files:", self.globvar)[0] | |
| def create_other_buttons(self): | |
| "Add check button to recurse down subdirectories." | |
| btn = Checkbutton( | |
| self.make_frame()[0], variable=self.recvar, | |
| text="Recurse down subdirectories") | |
| btn.pack(side="top", fill="both") | |
| def create_command_buttons(self): | |
| "Create base command buttons and add button for Search Files." | |
| SearchDialogBase.create_command_buttons(self) | |
| self.make_button("Search Files", self.default_command, isdef=True) | |
| def default_command(self, event=None): | |
| """Grep for search pattern in file path. The default command is bound | |
| to <Return>. | |
| If entry values are populated, set OutputWindow as stdout | |
| and perform search. The search dialog is closed automatically | |
| when the search begins. | |
| """ | |
| prog = self.engine.getprog() | |
| if not prog: | |
| return | |
| path = self.globvar.get() | |
| if not path: | |
| self.top.bell() | |
| return | |
| from idlelib.outwin import OutputWindow # leave here! | |
| save = sys.stdout | |
| try: | |
| sys.stdout = OutputWindow(self.flist) | |
| self.grep_it(prog, path) | |
| finally: | |
| sys.stdout = save | |
| def grep_it(self, prog, path): | |
| """Search for prog within the lines of the files in path. | |
| For the each file in the path directory, open the file and | |
| search each line for the matching pattern. If the pattern is | |
| found, write the file and line information to stdout (which | |
| is an OutputWindow). | |
| Args: | |
| prog: The compiled, cooked search pattern. | |
| path: String containing the search path. | |
| """ | |
| folder, filepat = os.path.split(path) | |
| if not folder: | |
| folder = os.curdir | |
| filelist = sorted(findfiles(folder, filepat, self.recvar.get())) | |
| self.close() | |
| pat = self.engine.getpat() | |
| print(f"Searching {pat!r} in {path} ...") | |
| hits = 0 | |
| try: | |
| for fn in filelist: | |
| try: | |
| with open(fn, errors='replace') as f: | |
| for lineno, line in enumerate(f, 1): | |
| if line[-1:] == '\n': | |
| line = line[:-1] | |
| if prog.search(line): | |
| sys.stdout.write(f"{fn}: {lineno}: {line}\n") | |
| hits += 1 | |
| except OSError as msg: | |
| print(msg) | |
| print(f"Hits found: {hits}\n(Hint: right-click to open locations.)" | |
| if hits else "No hits.") | |
| except AttributeError: | |
| # Tk window has been closed, OutputWindow.text = None, | |
| # so in OW.write, OW.text.insert fails. | |
| pass | |
| def _grep_dialog(parent): # htest # | |
| from tkinter import Toplevel, Text, SEL, END | |
| from tkinter.ttk import Frame, Button | |
| from idlelib.pyshell import PyShellFileList | |
| top = Toplevel(parent) | |
| top.title("Test GrepDialog") | |
| x, y = map(int, parent.geometry().split('+')[1:]) | |
| top.geometry(f"+{x}+{y + 175}") | |
| flist = PyShellFileList(top) | |
| frame = Frame(top) | |
| frame.pack() | |
| text = Text(frame, height=5) | |
| text.pack() | |
| def show_grep_dialog(): | |
| text.tag_add(SEL, "1.0", END) | |
| grep(text, flist=flist) | |
| text.tag_remove(SEL, "1.0", END) | |
| button = Button(frame, text="Show GrepDialog", command=show_grep_dialog) | |
| button.pack() | |
| if __name__ == "__main__": | |
| from unittest import main | |
| main('idlelib.idle_test.test_grep', verbosity=2, exit=False) | |
| from idlelib.idle_test.htest import run | |
| run(_grep_dialog) | |