Mercurial > repos > rliterman > csp2
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/grep.py Tue Mar 18 16:23:26 2025 -0400 @@ -0,0 +1,221 @@ +"""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)