Skip to content

Commit 7905d61

Browse files
committed
#8620: Cmd no longer truncates last character if stdin ends without newline
Cmd used to blindly chop off the last character of every input line. If the input reached EOF and there was no final new line, it would truncate the last character of the last command. This fix instead strips trailing \r\n from the input lines. While this is a small behavior change, it should not break any working code, since feeding a '\r\n' terminated file to Cmd would previously leave the \r's on the lines, resulting in failed command execution. I wrote the unit test in preparation for a PyOhio TeachMe session run by Catherine Devlin, and we can thank Catherine and the PyOhio session attendees for the fix. I've added Catherine to the Acks file for organizing and leading the TeachMe session, out of which we will hopefully get some new contributors.
1 parent f0f2e65 commit 7905d61

4 files changed

Lines changed: 31 additions & 2 deletions

File tree

Lib/cmd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def cmdloop(self, intro=None):
133133
if not len(line):
134134
line = 'EOF'
135135
else:
136-
line = line[:-1] # chop \n
136+
line = line.rstrip('\r\n')
137137
line = self.precmd(line)
138138
stop = self.onecmd(line)
139139
stop = self.postcmd(stop, line)

Lib/test/test_cmd.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
import cmd
99
import sys
1010
import re
11-
from io import StringIO
11+
import unittest
12+
import io
1213
from test import support
1314

1415
class samplecmdclass(cmd.Cmd):
@@ -168,9 +169,33 @@ def help_add(self):
168169
def do_exit(self, arg):
169170
return True
170171

172+
173+
class TestAlternateInput(unittest.TestCase):
174+
175+
class simplecmd(cmd.Cmd):
176+
177+
def do_print(self, args):
178+
print(args, file=self.stdout)
179+
180+
def do_EOF(self, args):
181+
return True
182+
183+
def test_file_with_missing_final_nl(self):
184+
input = io.StringIO("print test\nprint test2")
185+
output = io.StringIO()
186+
cmd = self.simplecmd(stdin=input, stdout=output)
187+
cmd.use_rawinput = False
188+
cmd.cmdloop()
189+
self.assertMultiLineEqual(output.getvalue(),
190+
("(Cmd) test\n"
191+
"(Cmd) test2\n"
192+
"(Cmd) "))
193+
194+
171195
def test_main(verbose=None):
172196
from test import test_cmd
173197
support.run_doctest(test_cmd, verbose)
198+
support.run_unittest(TestAlternateInput)
174199

175200
def test_coverage(coverdir):
176201
trace = support.import_module('trace')

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ Vincent Delft
195195
Arnaud Delobelle
196196
Erik Demaine
197197
Roger Dev
198+
Catherine Devlin
198199
Raghuram Devarakonda
199200
Caleb Deveraux
200201
Toby Dickenson

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Core and Builtins
1515
Library
1616
-------
1717

18+
- Issue #8620: when a Cmd is fed input that reaches EOF without a final
19+
newline, it no longer truncates the last character of the last command line.
20+
1821
- Issue #5146: Handle UID THREAD command correctly in imaplib.
1922

2023
- Issue #5147: Fix the header generated for cookie files written by

0 commit comments

Comments
 (0)