summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcmd2.py44
-rw-r--r--tests/test_cmd2.py106
2 files changed, 136 insertions, 14 deletions
diff --git a/cmd2.py b/cmd2.py
index d22ce41c..dc34df6b 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -717,8 +717,9 @@ class Cmd(cmd.Cmd):
"""
if not sys.platform.startswith('win'):
# Fix those annoying problems that occur with terminal programs like "less" when you pipe to them
- proc = subprocess.Popen(shlex.split('stty sane'))
- proc.communicate()
+ if self.stdin.isatty():
+ proc = subprocess.Popen(shlex.split('stty sane'))
+ proc.communicate()
return stop
def parseline(self, line):
@@ -969,25 +970,46 @@ class Cmd(cmd.Cmd):
return result
def pseudo_raw_input(self, prompt):
- """copied from cmd's cmdloop; like raw_input, but accounts for changed stdin, stdout"""
+ """
+ began life as a copy of cmd's cmdloop; like raw_input but
+
+ - accounts for changed stdin, stdout
+ - if input is a pipe (instead of a tty), look at self.echo
+ to decide whether to print the prompt and the input
+ """
# Deal with the vagaries of readline and ANSI escape codes
safe_prompt = self._surround_ansi_escapes(prompt)
if self.use_rawinput:
try:
- line = sm.input(safe_prompt)
+ if sys.stdin.isatty():
+ line = sm.input(safe_prompt)
+ else:
+ line = sm.input()
+ if self.echo:
+ sys.stdout.write('{}{}\n'.format(safe_prompt,line))
except EOFError:
line = 'eof'
else:
- self.poutput(safe_prompt, end='')
- self.stdout.flush()
- line = self.stdin.readline()
- if not len(line):
- line = 'eof'
+ if self.stdin.isatty():
+ # on a tty, print the prompt first, then read the line
+ self.poutput(safe_prompt, end='')
+ self.stdout.flush()
+ line = self.stdin.readline()
+ if len(line) == 0:
+ line = 'eof'
else:
- line = line.rstrip('\r\n')
-
+ # we are reading from a pipe, read the line to see if there is
+ # anything there, if so, then decide whether to print the
+ # prompt or not
+ line = self.stdin.readline()
+ if len(line):
+ # we read something, output the prompt and the something
+ if self.echo:
+ self.poutput('{}{}'.format(safe_prompt, line))
+ else:
+ line = 'eof'
return line.strip()
def _cmdloop(self):
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index dcbb99f7..6e1e6ef5 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -7,6 +7,7 @@ Released under MIT license, see LICENSE file
"""
import os
import sys
+import io
import tempfile
import mock
@@ -849,6 +850,7 @@ def test_cmdloop_without_rawinput():
# Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
app = cmd2.Cmd()
app.use_rawinput = False
+ app.echo = False
app.intro = 'Hello World, this is an intro ...'
app.stdout = StdOut()
@@ -858,7 +860,7 @@ def test_cmdloop_without_rawinput():
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog"]
- expected = app.intro + '\n{}'.format(app.prompt)
+ expected = app.intro + '\n'
with mock.patch.object(sys, 'argv', testargs):
# Run the command loop
app.cmdloop()
@@ -1367,7 +1369,6 @@ def test_eos(base_app):
# And make sure it reduced the length of the script dir list
assert len(base_app._script_dir) == 0
-
def test_echo(capsys):
app = cmd2.Cmd()
# Turn echo on and pre-stage some commands in the queue, simulating like we are in the middle of a script
@@ -1388,7 +1389,106 @@ def test_echo(capsys):
assert app._current_script_dir is None
assert out.startswith('{}{}\n'.format(app.prompt, command) + 'history [arg]: lists past commands issued')
-
+def test_pseudo_raw_input_tty_rawinput_true():
+ # use context managers so original functions get put back when we are done
+ # we dont use decorators because we need m_input for the assertion
+ with mock.patch('sys.stdin.isatty',
+ mock.MagicMock(name='isatty', return_value=True)):
+ with mock.patch('six.moves.input',
+ mock.MagicMock(name='input', side_effect=['set', EOFError])) as m_input:
+ # run the cmdloop, which should pull input from our mocks
+ app = cmd2.Cmd()
+ app.use_rawinput = True
+ app._cmdloop()
+ # because we mocked the input() call, we won't get the prompt
+ # or the name of the command in the output, so we can't check
+ # if its there. We assume that if input got called twice, once
+ # for the 'set' command, and once for the 'quit' command,
+ # that the rest of it worked
+ assert m_input.call_count == 2
+
+def test_pseudo_raw_input_tty_rawinput_false():
+ # gin up some input like it's coming from a tty
+ fakein = io.StringIO(u'{}'.format('set\n'))
+ mtty = mock.MagicMock(name='isatty', return_value=True)
+ fakein.isatty = mtty
+ mreadline = mock.MagicMock(name='readline', wraps=fakein.readline)
+ fakein.readline = mreadline
+
+ # run the cmdloop, telling it where to get input from
+ app = cmd2.Cmd(stdin=fakein)
+ app.use_rawinput = False
+ app._cmdloop()
+
+ # because we mocked the readline() call, we won't get the prompt
+ # or the name of the command in the output, so we can't check
+ # if its there. We assume that if readline() got called twice, once
+ # for the 'set' command, and once for the 'quit' command,
+ # that the rest of it worked
+ assert mreadline.call_count == 2
+
+# the next helper function and two tests check for piped
+# input when use_rawinput is True.
+def piped_rawinput_true(capsys, echo, command):
+ app = cmd2.Cmd()
+ app.use_rawinput = True
+ app.echo = echo
+ # run the cmdloop, which should pull input from our mock
+ app._cmdloop()
+ out, err = capsys.readouterr()
+ return (app, out)
+
+# using the decorator puts the original function at six.moves.input
+# back when this method returns
+@mock.patch('six.moves.input',
+ mock.MagicMock(name='input', side_effect=['set', EOFError]))
+def test_pseudo_raw_input_piped_rawinput_true_echo_true(capsys):
+ command = 'set'
+ app, out = piped_rawinput_true(capsys, True, command)
+ out = out.splitlines()
+ assert out[0] == '{}{}'.format(app.prompt, command)
+ assert out[1] == 'abbrev: False'
+
+# using the decorator puts the original function at six.moves.input
+# back when this method returns
+@mock.patch('six.moves.input',
+ mock.MagicMock(name='input', side_effect=['set', EOFError]))
+def test_pseudo_raw_input_piped_rawinput_true_echo_false(capsys):
+ command = 'set'
+ app, out = piped_rawinput_true(capsys, False, command)
+ firstline = out.splitlines()[0]
+ assert firstline == 'abbrev: False'
+ assert not '{}{}'.format(app.prompt, command) in out
+
+# the next helper function and two tests check for piped
+# input when use_rawinput=False
+def piped_rawinput_false(capsys, echo, command):
+ fakein = io.StringIO(u'{}'.format(command))
+ # run the cmdloop, telling it where to get input from
+ app = cmd2.Cmd(stdin=fakein)
+ app.use_rawinput = False
+ app.echo = echo
+ app.abbrev = False
+ app._cmdloop()
+ out, err = capsys.readouterr()
+ return (app, out)
+
+def test_pseudo_raw_input_piped_rawinput_false_echo_true(capsys):
+ command = 'set'
+ app, out = piped_rawinput_false(capsys, True, command)
+ out = out.splitlines()
+ assert out[0] == '{}{}'.format(app.prompt, command)
+ assert out[1] == 'abbrev: False'
+
+def test_pseudo_raw_input_piped_rawinput_false_echo_false(capsys):
+ command = 'set'
+ app, out = piped_rawinput_false(capsys, False, command)
+ firstline = out.splitlines()[0]
+ assert firstline == 'abbrev: False'
+ assert not '{}{}'.format(app.prompt, command) in out
+
+#
+# other input tests
def test_raw_input(base_app):
base_app.use_raw_input = True
fake_input = 'quit'