Skip to content

Commit dde0b69

Browse files
committed
Prompt and line now properly echoed on tty and pipe
if we are on a pipe, we have to echo the prompt only after we read and are not at EOF.
1 parent 64e1933 commit dde0b69

File tree

2 files changed

+79
-10
lines changed

2 files changed

+79
-10
lines changed

cmd2.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -970,25 +970,45 @@ def _surround_ansi_escapes(prompt, start="\x01", end="\x02"):
970970
return result
971971

972972
def pseudo_raw_input(self, prompt):
973-
"""copied from cmd's cmdloop; like raw_input, but accounts for changed stdin, stdout"""
973+
"""
974+
began life as a copy of cmd's cmdloop; like raw_input but
975+
976+
- accounts for changed stdin, stdout
977+
- if input is a pipe (instead of a tty), look at self.echo
978+
to decide whether to print the prompt and the input
979+
"""
974980

975981
# Deal with the vagaries of readline and ANSI escape codes
976982
safe_prompt = self._surround_ansi_escapes(prompt)
977983

978984
if self.use_rawinput:
979985
try:
980-
line = sm.input(safe_prompt)
986+
if sys.stdin.isatty():
987+
sys.stdout.write(safe_prompt)
988+
line = sm.input()
989+
else:
990+
line = sm.input()
991+
if self.echo:
992+
sys.stdout.write('{}{}\n'.format(safe_prompt,line))
981993
except EOFError:
982994
line = 'eof'
983995
else:
984-
self.poutput(safe_prompt, end='')
985-
self.stdout.flush()
986-
line = self.stdin.readline()
987-
if not len(line):
988-
line = 'eof'
996+
if self.stdin.isatty():
997+
# on a tty, print the prompt first, then read the line
998+
self.poutput(safe_prompt, end='')
999+
self.stdout.flush()
1000+
line = self.stdin.readline()
9891001
else:
990-
line = line.rstrip('\r\n')
991-
1002+
# we are reading from a pipe, read the line to see if there is
1003+
# anything there, if so, then decide whether to print the
1004+
# prompt or not
1005+
line = self.stdin.readline()
1006+
if len(line):
1007+
# we read something, output the prompt and the something
1008+
if self.echo:
1009+
self.poutput('{}{}'.format(safe_prompt, line))
1010+
else:
1011+
line = 'eof'
9921012
return line.strip()
9931013

9941014
def _cmdloop(self):

tests/test_cmd2.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88
import os
99
import sys
10+
import io
1011
import tempfile
1112

1213
import mock
@@ -849,6 +850,7 @@ def test_cmdloop_without_rawinput():
849850
# Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
850851
app = cmd2.Cmd()
851852
app.use_rawinput = False
853+
app.echo = False
852854
app.intro = 'Hello World, this is an intro ...'
853855
app.stdout = StdOut()
854856

@@ -858,7 +860,7 @@ def test_cmdloop_without_rawinput():
858860

859861
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
860862
testargs = ["prog"]
861-
expected = app.intro + '\n{}'.format(app.prompt)
863+
expected = app.intro + '\n'
862864
with mock.patch.object(sys, 'argv', testargs):
863865
# Run the command loop
864866
app.cmdloop()
@@ -1388,7 +1390,54 @@ def test_echo(capsys):
13881390
assert app._current_script_dir is None
13891391
assert out.startswith('{}{}\n'.format(app.prompt, command) + 'history [arg]: lists past commands issued')
13901392

1393+
#@pytest.mark.parametrize('rawinput', [True, False])
1394+
def test_piped_input_echo_false(capsys):
1395+
command = 'set'
1396+
1397+
# hack up stdin
1398+
fakein = io.StringIO(command)
1399+
#realin = sys.stdin
1400+
#sys.stdin = fakein
1401+
1402+
# run the cmdloop, which should pull input from stdin
1403+
app = cmd2.Cmd(stdin=fakein)
1404+
app.use_rawinput = False
1405+
app.echo = False
1406+
app.abbrev = False
1407+
app._cmdloop()
1408+
out, err = capsys.readouterr()
1409+
1410+
# put stdin back
1411+
#sys.stdin = realin
1412+
1413+
firstline = out.splitlines()[0]
1414+
assert firstline == 'abbrev: False'
1415+
assert not '{}{}'.format(app.prompt, command) in out
1416+
1417+
#@pytest.mark.parametrize('rawinput', [True, False])
1418+
def test_piped_input_echo_true(capsys):
1419+
command = 'set'
1420+
1421+
# hack up stdin
1422+
fakein = io.StringIO(command)
1423+
# realin = sys.stdin
1424+
# sys.stdin = fakein
1425+
1426+
# run the cmdloop, which should pull input from stdin
1427+
app = cmd2.Cmd(stdin=fakein)
1428+
app.use_rawinput = False
1429+
app.echo = True
1430+
app.abbrev = False
1431+
app._cmdloop()
1432+
out, err = capsys.readouterr()
1433+
1434+
# put stdin back
1435+
# sys.stdin = realin
13911436

1437+
out = out.splitlines()
1438+
assert out[0] == '{}{}'.format(app.prompt, command)
1439+
assert out[1] == 'abbrev: False'
1440+
13921441
def test_raw_input(base_app):
13931442
base_app.use_raw_input = True
13941443
fake_input = 'quit'

0 commit comments

Comments
 (0)