(root)/
Python-3.11.7/
Lib/
idlelib/
grep.py
       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 ESC[4;38;5;81mGrepDialog(ESC[4;38;5;149mSearchDialogBase):
      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      text.insert('1.0', 'import grep')
     208  
     209      def show_grep_dialog():
     210          text.tag_add(SEL, "1.0", '1.end')
     211          grep(text, flist=flist)
     212          text.tag_remove(SEL, "1.0", '1.end')
     213  
     214      button = Button(frame, text="Show GrepDialog", command=show_grep_dialog)
     215      button.pack()
     216  
     217  
     218  if __name__ == "__main__":
     219      from unittest import main
     220      main('idlelib.idle_test.test_grep', verbosity=2, exit=False)
     221  
     222      from idlelib.idle_test.htest import run
     223      run(_grep_dialog)