Skip to content

Commit 6bf644e

Browse files
authored
bpo-38862: IDLE Strip Trailing Whitespace fixes end newlines (GH-17366)
Extra newlines are removed at the end of non-shell files. If the file only has newlines after stripping other trailing whitespace, all are removed, as is done by patchcheck.py.
1 parent 6f03b23 commit 6bf644e

File tree

7 files changed

+69
-41
lines changed

7 files changed

+69
-41
lines changed

Doc/library/idle.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ Format Paragraph
199199
Strip trailing whitespace
200200
Remove trailing space and other whitespace characters after the last
201201
non-whitespace character of a line by applying str.rstrip to each line,
202-
including lines within multiline strings.
202+
including lines within multiline strings. Except for Shell windows,
203+
remove extra newlines at the end of the file.
203204

204205
.. index::
205206
single: Run script

Lib/idlelib/NEWS.txt

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Released on 2020-10-05?
33
======================================
44

55

6+
bpo-38862: 'Strip Trailing Whitespace' on the Format menu removes extra
7+
newlines at the end of non-shell files.
8+
69
bpo-38636: Fix IDLE Format menu tab toggle and file indent width. These
710
functions (default shortcuts Alt-T and Alt-U) were mistakenly disabled
811
in 3.7.5 and 3.8.0.

Lib/idlelib/format.py

+10
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,16 @@ def do_rstrip(self, event=None):
408408
if cut < raw:
409409
text.delete('%i.%i' % (cur, cut), '%i.end' % cur)
410410

411+
if (text.get('end-2c') == '\n' # File ends with at least 1 newline;
412+
and not hasattr(self.editwin, 'interp')): # & is not Shell.
413+
# Delete extra user endlines.
414+
while (text.index('end-1c') > '1.0' # Stop if file empty.
415+
and text.get('end-3c') == '\n'):
416+
text.delete('end-3c')
417+
# Because tk indexes are slice indexes and never raise,
418+
# a file with only newlines will be emptied.
419+
# patchcheck.py does the same.
420+
411421
undo.undo_block_stop()
412422

413423

Lib/idlelib/help.html

+13-12
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<html xmlns="http://www.w3.org/1999/xhtml">
55
<head>
66
<meta charset="utf-8" />
7-
<title>IDLE &#8212; Python 3.9.0a0 documentation</title>
7+
<title>IDLE &#8212; Python 3.9.0a1 documentation</title>
88
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
99
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
1010

@@ -17,14 +17,14 @@
1717
<script type="text/javascript" src="../_static/sidebar.js"></script>
1818

1919
<link rel="search" type="application/opensearchdescription+xml"
20-
title="Search within Python 3.9.0a0 documentation"
20+
title="Search within Python 3.9.0a1 documentation"
2121
href="../_static/opensearch.xml"/>
2222
<link rel="author" title="About these documents" href="../about.html" />
2323
<link rel="index" title="Index" href="../genindex.html" />
2424
<link rel="search" title="Search" href="../search.html" />
2525
<link rel="copyright" title="Copyright" href="../copyright.html" />
2626
<link rel="next" title="Other Graphical User Interface Packages" href="othergui.html" />
27-
<link rel="prev" title="tkinter.scrolledtextScrolled Text Widget" href="tkinter.scrolledtext.html" />
27+
<link rel="prev" title="tkinter.tixExtension widgets for Tk" href="tkinter.tix.html" />
2828
<link rel="canonical" href="https://docs.python.org/3/library/idle.html" />
2929

3030

@@ -62,7 +62,7 @@ <h3>Navigation</h3>
6262
<a href="othergui.html" title="Other Graphical User Interface Packages"
6363
accesskey="N">next</a> |</li>
6464
<li class="right" >
65-
<a href="tkinter.scrolledtext.html" title="tkinter.scrolledtextScrolled Text Widget"
65+
<a href="tkinter.tix.html" title="tkinter.tixExtension widgets for Tk"
6666
accesskey="P">previous</a> |</li>
6767

6868
<li><img src="../_static/py.png" alt=""
@@ -71,7 +71,7 @@ <h3>Navigation</h3>
7171

7272

7373
<li>
74-
<a href="../index.html">3.9.0a0 Documentation</a> &#187;
74+
<a href="../index.html">3.9.0a1 Documentation</a> &#187;
7575
</li>
7676

7777
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
@@ -240,7 +240,8 @@ <h3>Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and
240240
</dd>
241241
<dt>Strip trailing whitespace</dt><dd><p>Remove trailing space and other whitespace characters after the last
242242
non-whitespace character of a line by applying str.rstrip to each line,
243-
including lines within multiline strings.</p>
243+
including lines within multiline strings. Except for Shell windows,
244+
remove extra newlines at the end of the file.</p>
244245
</dd>
245246
</dl>
246247
</div>
@@ -886,8 +887,8 @@ <h3><a href="../contents.html">Table of Contents</a></h3>
886887
</ul>
887888

888889
<h4>Previous topic</h4>
889-
<p class="topless"><a href="tkinter.scrolledtext.html"
890-
title="previous chapter"><code class="xref py py-mod docutils literal notranslate"><span class="pre">tkinter.scrolledtext</span></code>Scrolled Text Widget</a></p>
890+
<p class="topless"><a href="tkinter.tix.html"
891+
title="previous chapter"><code class="xref py py-mod docutils literal notranslate"><span class="pre">tkinter.tix</span></code>Extension widgets for Tk</a></p>
891892
<h4>Next topic</h4>
892893
<p class="topless"><a href="othergui.html"
893894
title="next chapter">Other Graphical User Interface Packages</a></p>
@@ -919,7 +920,7 @@ <h3>Navigation</h3>
919920
<a href="othergui.html" title="Other Graphical User Interface Packages"
920921
>next</a> |</li>
921922
<li class="right" >
922-
<a href="tkinter.scrolledtext.html" title="tkinter.scrolledtextScrolled Text Widget"
923+
<a href="tkinter.tix.html" title="tkinter.tixExtension widgets for Tk"
923924
>previous</a> |</li>
924925

925926
<li><img src="../_static/py.png" alt=""
@@ -928,7 +929,7 @@ <h3>Navigation</h3>
928929

929930

930931
<li>
931-
<a href="../index.html">3.9.0a0 Documentation</a> &#187;
932+
<a href="../index.html">3.9.0a1 Documentation</a> &#187;
932933
</li>
933934

934935
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
@@ -959,11 +960,11 @@ <h3>Navigation</h3>
959960
<br />
960961
<br />
961962

962-
Last updated on Sep 01, 2019.
963+
Last updated on Nov 24, 2019.
963964
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
964965
<br />
965966

966-
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.1.2.
967+
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.1.1.
967968
</div>
968969

969970
</body>

Lib/idlelib/idle_test/mock_idle.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ def __call__(self, *args, **kwds):
4040
class Editor:
4141
'''Minimally imitate editor.EditorWindow class.
4242
'''
43-
def __init__(self, flist=None, filename=None, key=None, root=None):
44-
self.text = Text()
43+
def __init__(self, flist=None, filename=None, key=None, root=None,
44+
text=None): # Allow real Text with mock Editor.
45+
self.text = text or Text()
4546
self.undo = UndoDelegator()
4647

4748
def get_selection_indices(self):

Lib/idlelib/idle_test/test_format.py

+36-26
Original file line numberDiff line numberDiff line change
@@ -611,47 +611,57 @@ def test_change_indentwidth(self, askinteger):
611611

612612
class RstripTest(unittest.TestCase):
613613

614-
def test_rstrip_line(self):
615-
editor = MockEditor()
616-
text = editor.text
617-
do_rstrip = ft.Rstrip(editor).do_rstrip
618-
eq = self.assertEqual
614+
@classmethod
615+
def setUpClass(cls):
616+
requires('gui')
617+
cls.root = Tk()
618+
cls.root.withdraw()
619+
cls.text = Text(cls.root)
620+
cls.editor = MockEditor(text=cls.text)
621+
cls.do_rstrip = ft.Rstrip(cls.editor).do_rstrip
622+
623+
@classmethod
624+
def tearDownClass(cls):
625+
del cls.text, cls.do_rstrip, cls.editor
626+
cls.root.update_idletasks()
627+
cls.root.destroy()
628+
del cls.root
619629

620-
do_rstrip()
621-
eq(text.get('1.0', 'insert'), '')
622-
text.insert('1.0', ' ')
623-
do_rstrip()
624-
eq(text.get('1.0', 'insert'), '')
625-
text.insert('1.0', ' \n')
626-
do_rstrip()
627-
eq(text.get('1.0', 'insert'), '\n')
628-
629-
def test_rstrip_multiple(self):
630-
editor = MockEditor()
631-
# Comment above, uncomment 3 below to test with real Editor & Text.
632-
#from idlelib.editor import EditorWindow as Editor
633-
#from tkinter import Tk
634-
#editor = Editor(root=Tk())
635-
text = editor.text
636-
do_rstrip = ft.Rstrip(editor).do_rstrip
630+
def tearDown(self):
631+
self.text.delete('1.0', 'end-1c')
637632

633+
def test_rstrip_lines(self):
638634
original = (
639635
"Line with an ending tab \n"
640636
"Line ending in 5 spaces \n"
641637
"Linewithnospaces\n"
642638
" indented line\n"
643639
" indented line with trailing space \n"
644-
" ")
640+
" \n")
645641
stripped = (
646642
"Line with an ending tab\n"
647643
"Line ending in 5 spaces\n"
648644
"Linewithnospaces\n"
649645
" indented line\n"
650646
" indented line with trailing space\n")
651647

652-
text.insert('1.0', original)
653-
do_rstrip()
654-
self.assertEqual(text.get('1.0', 'insert'), stripped)
648+
self.text.insert('1.0', original)
649+
self.do_rstrip()
650+
self.assertEqual(self.text.get('1.0', 'insert'), stripped)
651+
652+
def test_rstrip_end(self):
653+
text = self.text
654+
for code in ('', '\n', '\n\n\n'):
655+
with self.subTest(code=code):
656+
text.insert('1.0', code)
657+
self.do_rstrip()
658+
self.assertEqual(text.get('1.0','end-1c'), '')
659+
for code in ('a\n', 'a\n\n', 'a\n\n\n'):
660+
with self.subTest(code=code):
661+
text.delete('1.0', 'end-1c')
662+
text.insert('1.0', code)
663+
self.do_rstrip()
664+
self.assertEqual(text.get('1.0','end-1c'), 'a\n')
655665

656666

657667
if __name__ == '__main__':
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
'Strip Trailing Whitespace' on the Format menu removes extra newlines
2+
at the end of non-shell files.

0 commit comments

Comments
 (0)