[Python-checkins] bpo-33628: IDLE: Minor code cleanup of codecontext.py and its tests (GH-7085)

Miss Islington (bot) webhook-mailer at python.org
Wed May 23 23:26:57 EDT 2018


https://github.com/python/cpython/commit/b2ab5dc72b36d074a86f2b1d91ae804e5a7210f8
commit: b2ab5dc72b36d074a86f2b1d91ae804e5a7210f8
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2018-05-23T20:26:54-07:00
summary:

bpo-33628: IDLE: Minor code cleanup of codecontext.py and its tests  (GH-7085)

(cherry picked from commit 8506016f904ba6bc27bf5261f27a0bdd5945cd26)

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

files:
A Misc/NEWS.d/next/IDLE/2018-05-23-19-51-07.bpo-33628.sLlFLO.rst
M Lib/idlelib/NEWS.txt
M Lib/idlelib/codecontext.py
M Lib/idlelib/idle_test/test_codecontext.py

diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 786378e2a632..2f8237d2395d 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -3,6 +3,8 @@ Released on 2018-06-18?
 ======================================
 
 
+bpo-33628: Cleanup codecontext.py and its test.
+
 bpo-32831: Add docstrings and tests for codecontext.py.
 Coverage is 100%.  Patch by Cheryl Sabella.
 
diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py
index efd163ed265e..635f68c86e1f 100644
--- a/Lib/idlelib/codecontext.py
+++ b/Lib/idlelib/codecontext.py
@@ -23,9 +23,23 @@
 FONTUPDATEINTERVAL = 1000 # millisec
 
 
-def getspacesfirstword(s, c=re.compile(r"^(\s*)(\w*)")):
-    "Extract the beginning whitespace and first word from s."
-    return c.match(s).groups()
+def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")):
+    "Extract the beginning whitespace and first word from codeline."
+    return c.match(codeline).groups()
+
+
+def get_line_info(codeline):
+    """Return tuple of (line indent value, codeline, block start keyword).
+
+    The indentation of empty lines (or comment lines) is INFINITY.
+    If the line does not start a block, the keyword value is False.
+    """
+    spaces, firstword = get_spaces_firstword(codeline)
+    indent = len(spaces)
+    if len(codeline) == indent or codeline[indent] == '#':
+        indent = INFINITY
+    opener = firstword in BLOCKOPENERS and firstword
+    return indent, codeline, opener
 
 
 class CodeContext:
@@ -42,15 +56,15 @@ def __init__(self, editwin):
         self.textfont is the editor window font.
 
         self.label displays the code context text above the editor text.
-          Initially None it is toggled via <<toggle-code-context>>.
+          Initially None, it is toggled via <<toggle-code-context>>.
         self.topvisible is the number of the top text line displayed.
         self.info is a list of (line number, indent level, line text,
           block keyword) tuples for the block structure above topvisible.
-          s self.info[0] is initialized a 'dummy' line which
-        # starts the toplevel 'block' of the module.
+          self.info[0] is initialized with a 'dummy' line which
+          starts the toplevel 'block' of the module.
 
         self.t1 and self.t2 are two timer events on the editor text widget to
-        monitor for changes to the context text or editor font.
+          monitor for changes to the context text or editor font.
         """
         self.editwin = editwin
         self.text = editwin.text
@@ -94,23 +108,21 @@ def toggle_code_context_event(self, event=None):
             # All values are passed through getint(), since some
             # values may be pixel objects, which can't simply be added to ints.
             widgets = self.editwin.text, self.editwin.text_frame
-            # Calculate the required vertical padding
+            # Calculate the required horizontal padding and border width.
             padx = 0
+            border = 0
             for widget in widgets:
                 padx += widget.tk.getint(widget.pack_info()['padx'])
                 padx += widget.tk.getint(widget.cget('padx'))
-            # Calculate the required border width
-            border = 0
-            for widget in widgets:
                 border += widget.tk.getint(widget.cget('border'))
             self.label = tkinter.Label(
                     self.editwin.top, text="\n" * (self.context_depth - 1),
                     anchor=W, justify=LEFT, font=self.textfont,
                     bg=self.bgcolor, fg=self.fgcolor,
-                    width=1, #don't request more than we get
+                    width=1,  # Don't request more than we get.
                     padx=padx, border=border, relief=SUNKEN)
             # Pack the label widget before and above the text_frame widget,
-            # thus ensuring that it will appear directly above text_frame
+            # thus ensuring that it will appear directly above text_frame.
             self.label.pack(side=TOP, fill=X, expand=False,
                             before=self.editwin.text_frame)
         else:
@@ -118,21 +130,6 @@ def toggle_code_context_event(self, event=None):
             self.label = None
         return "break"
 
-    def get_line_info(self, linenum):
-        """Return tuple of (line indent value, text, and block start keyword).
-
-        If the line does not start a block, the keyword value is False.
-        The indentation of empty lines (or comment lines) is INFINITY.
-        """
-        text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
-        spaces, firstword = getspacesfirstword(text)
-        opener = firstword in BLOCKOPENERS and firstword
-        if len(text) == len(spaces) or text[len(spaces)] == '#':
-            indent = INFINITY
-        else:
-            indent = len(spaces)
-        return indent, text, opener
-
     def get_context(self, new_topvisible, stopline=1, stopindent=0):
         """Return a list of block line tuples and the 'last' indent.
 
@@ -144,16 +141,17 @@ def get_context(self, new_topvisible, stopline=1, stopindent=0):
         """
         assert stopline > 0
         lines = []
-        # The indentation level we are currently in:
+        # The indentation level we are currently in.
         lastindent = INFINITY
         # For a line to be interesting, it must begin with a block opening
         # keyword, and have less indentation than lastindent.
         for linenum in range(new_topvisible, stopline-1, -1):
-            indent, text, opener = self.get_line_info(linenum)
+            codeline = self.text.get(f'{linenum}.0', f'{linenum}.end')
+            indent, text, opener = get_line_info(codeline)
             if indent < lastindent:
                 lastindent = indent
                 if opener in ("else", "elif"):
-                    # We also show the if statement
+                    # Also show the if statement.
                     lastindent += 1
                 if opener and linenum < new_topvisible and indent >= stopindent:
                     lines.append((linenum, indent, text, opener))
@@ -172,19 +170,19 @@ def update_code_context(self):
         the context label.
         """
         new_topvisible = int(self.text.index("@0,0").split('.')[0])
-        if self.topvisible == new_topvisible:      # haven't scrolled
+        if self.topvisible == new_topvisible:      # Haven't scrolled.
             return
-        if self.topvisible < new_topvisible:       # scroll down
+        if self.topvisible < new_topvisible:       # Scroll down.
             lines, lastindent = self.get_context(new_topvisible,
                                                  self.topvisible)
-            # retain only context info applicable to the region
-            # between topvisible and new_topvisible:
+            # Retain only context info applicable to the region
+            # between topvisible and new_topvisible.
             while self.info[-1][1] >= lastindent:
                 del self.info[-1]
-        else:  # self.topvisible > new_topvisible:     # scroll up
+        else:  # self.topvisible > new_topvisible: # Scroll up.
             stopindent = self.info[-1][1] + 1
-            # retain only context info associated
-            # with lines above new_topvisible:
+            # Retain only context info associated
+            # with lines above new_topvisible.
             while self.info[-1][0] >= new_topvisible:
                 stopindent = self.info[-1][1]
                 del self.info[-1]
@@ -193,9 +191,9 @@ def update_code_context(self):
                                                  stopindent)
         self.info.extend(lines)
         self.topvisible = new_topvisible
-        # empty lines in context pane:
+        # Empty lines in context pane.
         context_strings = [""] * max(0, self.context_depth - len(self.info))
-        # followed by the context hint lines:
+        # Followed by the context hint lines.
         context_strings += [x[2] for x in self.info[-self.context_depth:]]
         self.label["text"] = '\n'.join(context_strings)
 
diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py
index 448094eda7ef..ed45828641b4 100644
--- a/Lib/idlelib/idle_test/test_codecontext.py
+++ b/Lib/idlelib/idle_test/test_codecontext.py
@@ -96,8 +96,6 @@ def test_init(self):
         eq(self.root.tk.call('after', 'info', self.cc.t2)[1], 'timer')
 
     def test_del(self):
-        self.root.tk.call('after', 'info', self.cc.t1)
-        self.root.tk.call('after', 'info', self.cc.t2)
         self.cc.__del__()
         with self.assertRaises(TclError) as msg:
             self.root.tk.call('after', 'info', self.cc.t1)
@@ -135,21 +133,6 @@ def test_toggle_code_context_event(self):
         eq(toggle(), 'break')
         self.assertIsNone(cc.label)
 
-    def test_get_line_info(self):
-        eq = self.assertEqual
-        gli = self.cc.get_line_info
-
-        # Line 1 is not a BLOCKOPENER.
-        eq(gli(1), (codecontext.INFINITY, '', False))
-        # Line 2 is a BLOCKOPENER without an indent.
-        eq(gli(2), (0, 'class C1():', 'class'))
-        # Line 3 is not a BLOCKOPENER and does not return the indent level.
-        eq(gli(3), (codecontext.INFINITY, '    # Class comment.', False))
-        # Line 4 is a BLOCKOPENER and is indented.
-        eq(gli(4), (4, '    def __init__(self, a, b):', 'def'))
-        # Line 8 is a different BLOCKOPENER and is indented.
-        eq(gli(8), (8, '        if a > b:', 'if'))
-
     def test_get_context(self):
         eq = self.assertEqual
         gc = self.cc.get_context
@@ -323,8 +306,8 @@ def test_font_timer_event(self):
 
 class HelperFunctionText(unittest.TestCase):
 
-    def test_getspacesfirstword(self):
-        get = codecontext.getspacesfirstword
+    def test_get_spaces_firstword(self):
+        get = codecontext.get_spaces_firstword
         test_lines = (
             ('    first word', ('    ', 'first')),
             ('\tfirst word', ('\t', 'first')),
@@ -342,6 +325,24 @@ def test_getspacesfirstword(self):
                              c=re.compile(r'^(\s*)([^\s]*)')),
                          ('    ', '(continuation)'))
 
+    def test_get_line_info(self):
+        eq = self.assertEqual
+        gli = codecontext.get_line_info
+        lines = code_sample.splitlines()
+
+        # Line 1 is not a BLOCKOPENER.
+        eq(gli(lines[0]), (codecontext.INFINITY, '', False))
+        # Line 2 is a BLOCKOPENER without an indent.
+        eq(gli(lines[1]), (0, 'class C1():', 'class'))
+        # Line 3 is not a BLOCKOPENER and does not return the indent level.
+        eq(gli(lines[2]), (codecontext.INFINITY, '    # Class comment.', False))
+        # Line 4 is a BLOCKOPENER and is indented.
+        eq(gli(lines[3]), (4, '    def __init__(self, a, b):', 'def'))
+        # Line 8 is a different BLOCKOPENER and is indented.
+        eq(gli(lines[7]), (8, '        if a > b:', 'if'))
+        # Test tab.
+        eq(gli('\tif a == b:'), (1, '\tif a == b:', 'if'))
+
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/Misc/NEWS.d/next/IDLE/2018-05-23-19-51-07.bpo-33628.sLlFLO.rst b/Misc/NEWS.d/next/IDLE/2018-05-23-19-51-07.bpo-33628.sLlFLO.rst
new file mode 100644
index 000000000000..f0b13a21c346
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2018-05-23-19-51-07.bpo-33628.sLlFLO.rst
@@ -0,0 +1,2 @@
+IDLE: Cleanup codecontext.py and its test.
+



More information about the Python-checkins mailing list