[Python-checkins] bpo-23216: IDLE: Add docstrings to search modules (GH-12141)

Miss Islington (bot) webhook-mailer at python.org
Sat Mar 16 19:47:32 EDT 2019


https://github.com/python/cpython/commit/b34f1aa81433d60aee7bd744352b347dd650ca84
commit: b34f1aa81433d60aee7bd744352b347dd650ca84
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-03-16T16:47:28-07:00
summary:

bpo-23216: IDLE: Add docstrings to search modules (GH-12141)

(cherry picked from commit 0bb5e75cf8bc9b197ffb91cba6f30543ed502708)

Co-authored-by: Cheryl Sabella <cheryl.sabella at gmail.com>

files:
A Misc/NEWS.d/next/IDLE/2019-03-02-19-39-53.bpo-23216.ZA7H8H.rst
M Lib/idlelib/grep.py
M Lib/idlelib/replace.py
M Lib/idlelib/search.py

diff --git a/Lib/idlelib/grep.py b/Lib/idlelib/grep.py
index 873233ec1543..6068d7e4dfce 100644
--- a/Lib/idlelib/grep.py
+++ b/Lib/idlelib/grep.py
@@ -14,11 +14,16 @@
 from idlelib import searchengine
 
 # Importing OutputWindow here fails due to import loop
-# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow
+# EditorWindow -> GrepDialog -> OutputWindow -> EditorWindow
 
 
 def grep(text, io=None, flist=None):
-    """Create or find singleton GrepDialog instance.
+    """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
@@ -26,7 +31,6 @@ def grep(text, io=None, flist=None):
         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"):
@@ -50,17 +54,29 @@ def __init__(self, root, engine, flist):
         searchengine instance to prepare the search.
 
         Attributes:
-            globvar: Value of Text Entry widget for path to search.
-            recvar: Boolean value of Checkbutton widget
-                    for traversing through subdirectories.
+            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.
         """
-        SearchDialogBase.__init__(self, root, engine)
+        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."
+        """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 ""
@@ -85,9 +101,9 @@ def create_other_buttons(self):
         btn.pack(side="top", fill="both")
 
     def create_command_buttons(self):
-        "Create base command buttons and add button for search."
+        "Create base command buttons and add button for Search Files."
         SearchDialogBase.create_command_buttons(self)
-        self.make_button("Search Files", self.default_command, 1)
+        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
@@ -119,6 +135,10 @@ def grep_it(self, prog, path):
         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.
         """
         dir, base = os.path.split(path)
         list = self.findfiles(dir, base, self.recvar.get())
@@ -149,7 +169,13 @@ def grep_it(self, prog, path):
     def findfiles(self, dir, base, rec):
         """Return list of files in the dir that match the base pattern.
 
+        Use the current directory if dir has no value.
         If rec is True, recursively iterate through subdirectories.
+
+        Args:
+            dir: Directory path to search.
+            base: File search pattern.
+            rec: Boolean for recursive search through subdirectories.
         """
         try:
             names = os.listdir(dir or os.curdir)
diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py
index 4a834eb7901e..6be034af9626 100644
--- a/Lib/idlelib/replace.py
+++ b/Lib/idlelib/replace.py
@@ -1,7 +1,7 @@
 """Replace dialog for IDLE. Inherits SearchDialogBase for GUI.
-Uses idlelib.SearchEngine for search capability.
+Uses idlelib.searchengine.SearchEngine for search capability.
 Defines various replace related functions like replace, replace all,
-replace+find.
+and replace+find.
 """
 import re
 
@@ -10,9 +10,16 @@
 from idlelib.searchbase import SearchDialogBase
 from idlelib import searchengine
 
+
 def replace(text):
-    """Returns a singleton ReplaceDialog instance.The single dialog
-     saves user entries and preferences across instances."""
+    """Create or reuse a singleton ReplaceDialog instance.
+
+    The singleton dialog saves user entries and preferences
+    across instances.
+
+    Args:
+        text: Text widget containing the text to be searched.
+    """
     root = text._root()
     engine = searchengine.get(root)
     if not hasattr(engine, "_replacedialog"):
@@ -22,16 +29,36 @@ def replace(text):
 
 
 class ReplaceDialog(SearchDialogBase):
+    "Dialog for finding and replacing a pattern in text."
 
     title = "Replace Dialog"
     icon = "Replace"
 
     def __init__(self, root, engine):
-        SearchDialogBase.__init__(self, root, engine)
+        """Create search dialog for finding and replacing text.
+
+        Uses SearchDialogBase as the basis for the GUI and a
+        searchengine instance to prepare the search.
+
+        Attributes:
+            replvar: StringVar containing 'Replace with:' value.
+            replent: Entry widget for replvar.  Created in
+                create_entries().
+            ok: Boolean used in searchengine.search_text to indicate
+                whether the search includes the selection.
+        """
+        super().__init__(root, engine)
         self.replvar = StringVar(root)
 
     def open(self, text):
-        """Display the replace dialog"""
+        """Make dialog visible on top of others and ready to use.
+
+        Also, highlight the currently selected text and set the
+        search to include the current selection (self.ok).
+
+        Args:
+            text: Text widget being searched.
+        """
         SearchDialogBase.open(self, text)
         try:
             first = text.index("sel.first")
@@ -44,37 +71,50 @@ def open(self, text):
         first = first or text.index("insert")
         last = last or first
         self.show_hit(first, last)
-        self.ok = 1
+        self.ok = True
 
     def create_entries(self):
-        """Create label and text entry widgets"""
+        "Create base and additional label and text entry widgets."
         SearchDialogBase.create_entries(self)
         self.replent = self.make_entry("Replace with:", self.replvar)[0]
 
     def create_command_buttons(self):
+        """Create base and additional command buttons.
+
+        The additional buttons are for Find, Replace,
+        Replace+Find, and Replace All.
+        """
         SearchDialogBase.create_command_buttons(self)
         self.make_button("Find", self.find_it)
         self.make_button("Replace", self.replace_it)
-        self.make_button("Replace+Find", self.default_command, 1)
+        self.make_button("Replace+Find", self.default_command, isdef=True)
         self.make_button("Replace All", self.replace_all)
 
     def find_it(self, event=None):
-        self.do_find(0)
+        "Handle the Find button."
+        self.do_find(False)
 
     def replace_it(self, event=None):
+        """Handle the Replace button.
+
+        If the find is successful, then perform replace.
+        """
         if self.do_find(self.ok):
             self.do_replace()
 
     def default_command(self, event=None):
-        "Replace and find next."
+        """Handle the Replace+Find button as the default command.
+
+        First performs a replace and then, if the replace was
+        successful, a find next.
+        """
         if self.do_find(self.ok):
             if self.do_replace():  # Only find next match if replace succeeded.
                                    # A bad re can cause it to fail.
-                self.do_find(0)
+                self.do_find(False)
 
     def _replace_expand(self, m, repl):
-        """ Helper function for expanding a regular expression
-            in the replace field, if needed. """
+        "Expand replacement text if regular expression."
         if self.engine.isre():
             try:
                 new = m.expand(repl)
@@ -87,7 +127,15 @@ def _replace_expand(self, m, repl):
         return new
 
     def replace_all(self, event=None):
-        """Replace all instances of patvar with replvar in text"""
+        """Handle the Replace All button.
+
+        Search text for occurrences of the Find value and replace
+        each of them.  The 'wrap around' value controls the start
+        point for searching.  If wrap isn't set, then the searching
+        starts at the first occurrence after the current selection;
+        if wrap is set, the replacement starts at the first line.
+        The replacement is always done top-to-bottom in the text.
+        """
         prog = self.engine.getprog()
         if not prog:
             return
@@ -104,12 +152,13 @@ def replace_all(self, event=None):
         if self.engine.iswrap():
             line = 1
             col = 0
-        ok = 1
+        ok = True
         first = last = None
         # XXX ought to replace circular instead of top-to-bottom when wrapping
         text.undo_block_start()
-        while 1:
-            res = self.engine.search_forward(text, prog, line, col, 0, ok)
+        while True:
+            res = self.engine.search_forward(text, prog, line, col,
+                                             wrap=False, ok=ok)
             if not res:
                 break
             line, m = res
@@ -130,13 +179,17 @@ def replace_all(self, event=None):
                 if new:
                     text.insert(first, new)
             col = i + len(new)
-            ok = 0
+            ok = False
         text.undo_block_stop()
         if first and last:
             self.show_hit(first, last)
         self.close()
 
-    def do_find(self, ok=0):
+    def do_find(self, ok=False):
+        """Search for and highlight next occurrence of pattern in text.
+
+        No text replacement is done with this option.
+        """
         if not self.engine.getprog():
             return False
         text = self.text
@@ -149,10 +202,11 @@ def do_find(self, ok=0):
         first = "%d.%d" % (line, i)
         last = "%d.%d" % (line, j)
         self.show_hit(first, last)
-        self.ok = 1
+        self.ok = True
         return True
 
     def do_replace(self):
+        "Replace search pattern in text with replacement value."
         prog = self.engine.getprog()
         if not prog:
             return False
@@ -180,12 +234,20 @@ def do_replace(self):
             text.insert(first, new)
         text.undo_block_stop()
         self.show_hit(first, text.index("insert"))
-        self.ok = 0
+        self.ok = False
         return True
 
     def show_hit(self, first, last):
-        """Highlight text from 'first' to 'last'.
-        'first', 'last' - Text indices"""
+        """Highlight text between first and last indices.
+
+        Text is highlighted via the 'hit' tag and the marked
+        section is brought into view.
+
+        The colors from the 'hit' tag aren't currently shown
+        when the text is displayed.  This is due to the 'sel'
+        tag being added first, so the colors in the 'sel'
+        config are seen instead of the colors for 'hit'.
+        """
         text = self.text
         text.mark_set("insert", first)
         text.tag_remove("sel", "1.0", "end")
@@ -199,6 +261,7 @@ def show_hit(self, first, last):
         text.update_idletasks()
 
     def close(self, event=None):
+        "Close the dialog and remove hit tags."
         SearchDialogBase.close(self, event)
         self.text.tag_remove("hit", "1.0", "end")
 
diff --git a/Lib/idlelib/search.py b/Lib/idlelib/search.py
index 6e5a0c7973c7..5bbe9d6b5dc8 100644
--- a/Lib/idlelib/search.py
+++ b/Lib/idlelib/search.py
@@ -1,10 +1,23 @@
+"""Search dialog for Find, Find Again, and Find Selection
+   functionality.
+
+   Inherits from SearchDialogBase for GUI and uses searchengine
+   to prepare search pattern.
+"""
 from tkinter import TclError
 
 from idlelib import searchengine
 from idlelib.searchbase import SearchDialogBase
 
 def _setup(text):
-    "Create or find the singleton SearchDialog instance."
+    """Return the new or existing singleton SearchDialog instance.
+
+    The singleton dialog saves user entries and preferences
+    across instances.
+
+    Args:
+        text: Text widget containing the text to be searched.
+    """
     root = text._root()
     engine = searchengine.get(root)
     if not hasattr(engine, "_searchdialog"):
@@ -12,31 +25,71 @@ def _setup(text):
     return engine._searchdialog
 
 def find(text):
-    "Handle the editor edit menu item and corresponding event."
+    """Open the search dialog.
+
+    Module-level function to access the singleton SearchDialog
+    instance and open the dialog.  If text is selected, it is
+    used as the search phrase; otherwise, the previous entry
+    is used.  No search is done with this command.
+    """
     pat = text.get("sel.first", "sel.last")
     return _setup(text).open(text, pat)  # Open is inherited from SDBase.
 
 def find_again(text):
-    "Handle the editor edit menu item and corresponding event."
+    """Repeat the search for the last pattern and preferences.
+
+    Module-level function to access the singleton SearchDialog
+    instance to search again using the user entries and preferences
+    from the last dialog.  If there was no prior search, open the
+    search dialog; otherwise, perform the search without showing the
+    dialog.
+    """
     return _setup(text).find_again(text)
 
 def find_selection(text):
-    "Handle the editor edit menu item and corresponding event."
+    """Search for the selected pattern in the text.
+
+    Module-level function to access the singleton SearchDialog
+    instance to search using the selected text.  With a text
+    selection, perform the search without displaying the dialog.
+    Without a selection, use the prior entry as the search phrase
+    and don't display the dialog.  If there has been no prior
+    search, open the search dialog.
+    """
     return _setup(text).find_selection(text)
 
 
 class SearchDialog(SearchDialogBase):
+    "Dialog for finding a pattern in text."
 
     def create_widgets(self):
+        "Create the base search dialog and add a button for Find Next."
         SearchDialogBase.create_widgets(self)
-        self.make_button("Find Next", self.default_command, 1)
+        # TODO - why is this here and not in a create_command_buttons?
+        self.make_button("Find Next", self.default_command, isdef=True)
 
     def default_command(self, event=None):
+        "Handle the Find Next button as the default command."
         if not self.engine.getprog():
             return
         self.find_again(self.text)
 
     def find_again(self, text):
+        """Repeat the last search.
+
+        If no search was previously run, open a new search dialog.  In
+        this case, no search is done.
+
+        If a seach was previously run, the search dialog won't be
+        shown and the options from the previous search (including the
+        search pattern) will be used to find the next occurrence
+        of the pattern.  Next is relative based on direction.
+
+        Position the window to display the located occurrence in the
+        text.
+
+        Return True if the search was successful and False otherwise.
+        """
         if not self.engine.getpat():
             self.open(text)
             return False
@@ -66,6 +119,13 @@ def find_again(self, text):
             return False
 
     def find_selection(self, text):
+        """Search for selected text with previous dialog preferences.
+
+        Instead of using the same pattern for searching (as Find
+        Again does), this first resets the pattern to the currently
+        selected text.  If the selected text isn't changed, then use
+        the prior search phrase.
+        """
         pat = text.get("sel.first", "sel.last")
         if pat:
             self.engine.setcookedpat(pat)
diff --git a/Misc/NEWS.d/next/IDLE/2019-03-02-19-39-53.bpo-23216.ZA7H8H.rst b/Misc/NEWS.d/next/IDLE/2019-03-02-19-39-53.bpo-23216.ZA7H8H.rst
new file mode 100644
index 000000000000..ec091615a190
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2019-03-02-19-39-53.bpo-23216.ZA7H8H.rst
@@ -0,0 +1 @@
+Add docstrings to IDLE search modules.



More information about the Python-checkins mailing list