summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcmd2.py34
-rwxr-xr-xsetup.py6
-rw-r--r--tests/test_argparse.py40
-rw-r--r--tests/test_cmd2.py30
-rw-r--r--tests/test_completion.py6
5 files changed, 58 insertions, 58 deletions
diff --git a/cmd2.py b/cmd2.py
index aee6f958..c397bb7c 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -92,8 +92,12 @@ except ImportError:
# BrokenPipeError is only in Python 3. Use IOError for Python 2.
if six.PY3:
BROKEN_PIPE_ERROR = BrokenPipeError
+
+ # redirect_stdout and redirect_stderr weren't added to contextlib until Python 3.4
+ from contextlib import redirect_stdout, redirect_stderr
else:
BROKEN_PIPE_ERROR = IOError
+ from contextlib2 import redirect_stdout, redirect_stderr
# On some systems, pyperclip will import gtk for its clipboard functionality.
# The following code is a workaround for gtk interfering with printing from a background
@@ -668,6 +672,9 @@ class Cmd(cmd.Cmd):
# Used when piping command output to a shell command
self.pipe_proc = None
+ # Used by complete() for readline tab completion
+ self.completion_matches = []
+
# ----- Methods related to presenting output to the user -----
@property
@@ -771,13 +778,14 @@ class Cmd(cmd.Cmd):
return cmd_completion
+ # noinspection PyUnusedLocal
def complete_subcommand(self, text, line, begidx, endidx):
"""Readline tab-completion method for completing argparse sub-command names."""
- cmd, args, foo = self.parseline(line)
+ command, args, foo = self.parseline(line)
arglist = args.split()
- if len(arglist) <= 1 and cmd + ' ' + args == line:
- funcname = self._func_named(cmd)
+ if len(arglist) <= 1 and command + ' ' + args == line:
+ funcname = self._func_named(command)
if funcname:
# Check to see if this function was decorated with an argparse ArgumentParser
func = getattr(self, funcname)
@@ -799,7 +807,7 @@ class Cmd(cmd.Cmd):
return []
def complete(self, text, state):
- """Override of cmd method which returns the next possible completion for 'text'.
+ """Override of command method which returns the next possible completion for 'text'.
If a command has not been entered, then complete against command list.
Otherwise try to call complete_<command> to get list of completions.
@@ -819,17 +827,17 @@ class Cmd(cmd.Cmd):
stripped = len(origline) - len(line)
begidx = readline.get_begidx() - stripped
endidx = readline.get_endidx() - stripped
- if begidx>0:
- cmd, args, foo = self.parseline(line)
- if cmd == '':
+ if begidx > 0:
+ command, args, foo = self.parseline(line)
+ if command == '':
compfunc = self.completedefault
else:
arglist = args.split()
compfunc = None
# If the user has entered no more than a single argument after the command name
- if len(arglist) <= 1 and cmd + ' ' + args == line:
- funcname = self._func_named(cmd)
+ if len(arglist) <= 1 and command + ' ' + args == line:
+ funcname = self._func_named(command)
if funcname:
# Check to see if this function was decorated with an argparse ArgumentParser
func = getattr(self, funcname)
@@ -842,7 +850,7 @@ class Cmd(cmd.Cmd):
if compfunc is None:
# This command either doesn't have sub-commands or the user is past the point of entering one
try:
- compfunc = getattr(self, 'complete_' + cmd)
+ compfunc = getattr(self, 'complete_' + command)
except AttributeError:
compfunc = self.completedefault
else:
@@ -1319,7 +1327,11 @@ class Cmd(cmd.Cmd):
# Function has an argparser, so get help based on all the arguments in case there are sub-commands
new_arglist = arglist[1:]
new_arglist.append('-h')
- func(new_arglist)
+
+ # Temporarily redirect all argparse output to both sys.stdout and sys.stderr to self.stdout
+ with redirect_stdout(self.stdout):
+ with redirect_stderr(self.stdout):
+ func(new_arglist)
else:
# No special behavior needed, delegate to cmd base class do_help()
cmd.Cmd.do_help(self, funcname[3:])
diff --git a/setup.py b/setup.py
index f56555db..aea5ab92 100755
--- a/setup.py
+++ b/setup.py
@@ -62,9 +62,15 @@ Topic :: Software Development :: Libraries :: Python Modules
""".splitlines())))
INSTALL_REQUIRES = ['pyparsing >= 2.0.1', 'pyperclip', 'six']
+
+# Windows also requires pyreadline to ensure tab completion works
if sys.platform.startswith('win'):
INSTALL_REQUIRES += ['pyreadline']
+# Python 2.7 also requires contextlib2 for temporarily redirecting stdout and stderr and subprocess32
+if sys.version_info < (3, 0):
+ INSTALL_REQUIRES += ['contextlib2', 'subprocess32']
+
# unittest.mock was added in Python 3.3. mock is a backport of unittest.mock to all versions of Python
TESTS_REQUIRE = ['mock', 'pytest']
DOCS_REQUIRE = ['sphinx', 'sphinx_rtd_theme', 'pyparsing', 'pyperclip', 'six']
diff --git a/tests/test_argparse.py b/tests/test_argparse.py
index ecaa1049..d3646046 100644
--- a/tests/test_argparse.py
+++ b/tests/test_argparse.py
@@ -139,26 +139,20 @@ def test_argparse_quoted_arguments_posix_multiple(argparse_app):
out = run_cmd(argparse_app, 'tag strong this "should be" loud')
assert out == ['<strong>this should be loud</strong>']
-def test_argparse_help_docstring(argparse_app, capsys):
- run_cmd(argparse_app, 'help say')
- out, err = capsys.readouterr()
- out = out.splitlines()
+def test_argparse_help_docstring(argparse_app):
+ out = run_cmd(argparse_app, 'help say')
assert out[0].startswith('usage: say')
assert out[1] == ''
assert out[2] == 'Repeat what you tell me to.'
-def test_argparse_help_description(argparse_app, capsys):
- run_cmd(argparse_app, 'help tag')
- out, err = capsys.readouterr()
- out = out.splitlines()
+def test_argparse_help_description(argparse_app):
+ out = run_cmd(argparse_app, 'help tag')
assert out[0].startswith('usage: tag')
assert out[1] == ''
assert out[2] == 'create a html tag'
-def test_argparse_prog(argparse_app, capsys):
- run_cmd(argparse_app, 'help tag')
- out, err = capsys.readouterr()
- out = out.splitlines()
+def test_argparse_prog(argparse_app):
+ out = run_cmd(argparse_app, 'help tag')
progname = out[0].split(' ')[1]
assert progname == 'tag'
@@ -237,26 +231,20 @@ def test_subcommand_invalid(subcommand_app, capsys):
assert err[0].startswith('usage: base')
assert err[1].startswith("base: error: invalid choice: 'baz'")
-def test_subcommand_base_help(subcommand_app, capsys):
- run_cmd(subcommand_app, 'help base')
- out, err = capsys.readouterr()
- out = out.splitlines()
+def test_subcommand_base_help(subcommand_app):
+ out = run_cmd(subcommand_app, 'help base')
assert out[0].startswith('usage: base')
assert out[1] == ''
assert out[2] == 'Base command help'
-def test_subcommand_help(subcommand_app, capsys):
- run_cmd(subcommand_app, 'help base foo')
- out, err = capsys.readouterr()
- out = out.splitlines()
+def test_subcommand_help(subcommand_app):
+ out = run_cmd(subcommand_app, 'help base foo')
assert out[0].startswith('usage: base foo')
assert out[1] == ''
assert out[2] == 'positional arguments:'
-def test_subcommand_invalid_help(subcommand_app, capsys):
- run_cmd(subcommand_app, 'help base baz')
- out, err = capsys.readouterr()
- err = err.splitlines()
- assert err[0].startswith('usage: base')
- assert err[1].startswith("base: error: invalid choice: 'baz'")
+def test_subcommand_invalid_help(subcommand_app):
+ out = run_cmd(subcommand_app, 'help base baz')
+ assert out[0].startswith('usage: base')
+ assert out[1].startswith("base: error: invalid choice: 'baz'")
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 9fb9f2d9..186def65 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -39,26 +39,22 @@ def test_base_help(base_app):
assert out == expected
-def test_base_help_history(base_app, capsys):
- run_cmd(base_app, 'help history')
- out, err = capsys.readouterr()
- assert out == HELP_HISTORY
- assert err == ''
+def test_base_help_history(base_app):
+ out = run_cmd(base_app, 'help history')
+ assert out == normalize(HELP_HISTORY)
def test_base_argparse_help(base_app, capsys):
# Verify that "set -h" gives the same output as "help set" and that it starts in a way that makes sense
run_cmd(base_app, 'set -h')
- out1, err1 = capsys.readouterr()
+ out, err = capsys.readouterr()
+ out1 = out.splitlines()
- run_cmd(base_app, 'help set')
- out2, err2 = capsys.readouterr()
+ out2 = run_cmd(base_app, 'help set')
assert out1 == out2
- assert err1 == err2
- out = out1.splitlines()
- assert out[0].startswith('usage: set')
- assert out[1] == ''
- assert out[2].startswith('Sets a settable parameter')
+ assert out1[0].startswith('usage: set')
+ assert out1[1] == ''
+ assert out1[2].startswith('Sets a settable parameter')
def test_base_invalid_option(base_app, capsys):
run_cmd(base_app, 'set -z')
@@ -606,17 +602,15 @@ def test_allow_redirection(base_app):
assert not os.path.exists(filename)
-def test_input_redirection(base_app, request, capsys):
+def test_input_redirection(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
filename = os.path.join(test_dir, 'redirect.txt')
# NOTE: File 'redirect.txt" contains 1 word "history"
# Verify that redirecting input ffom a file works
- run_cmd(base_app, 'help < {}'.format(filename))
- out, err = capsys.readouterr()
- assert out == HELP_HISTORY
- assert err == ''
+ out = run_cmd(base_app, 'help < {}'.format(filename))
+ assert out == normalize(HELP_HISTORY)
def test_pipe_to_shell(base_app, capsys):
diff --git a/tests/test_completion.py b/tests/test_completion.py
index 05774df9..70f77d0a 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -80,9 +80,9 @@ def test_complete_command_invalid_state(cmd2_app):
with mock.patch.object(readline, 'get_line_buffer', get_line):
with mock.patch.object(readline, 'get_begidx', get_begidx):
with mock.patch.object(readline, 'get_endidx', get_endidx):
- with pytest.raises(AttributeError):
- # Run the readline tab-completion function with readline mocks in place and cause an exception
- completion = cmd2_app.complete(text, state)
+ # Run the readline tab-completion function with readline mocks in place get None
+ completion = cmd2_app.complete(text, state)
+ assert completion is None
def test_complete_empty_arg(cmd2_app):
text = ''