summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Lin <anselor@gmail.com>2018-05-17 18:08:52 -0400
committerEric Lin <anselor@gmail.com>2018-05-17 18:08:52 -0400
commitff89bad1b0dd2a608081db5a8fa299ef43d66bc5 (patch)
tree5b5ba5f355d4026e563af3491ee6758642833f10
parent371284d20370a8e85dd8527d9bbcc6267b335cde (diff)
downloadcmd2-git-ff89bad1b0dd2a608081db5a8fa299ef43d66bc5.tar.gz
Suppresses stdout and stderr output by default when calling an application command from pyscript.
Added support for tab completing application commands in ipython shell Updated unit tests scripts to set cmd_echo to True to validate command output.
-rw-r--r--cmd2/pyscript_bridge.py46
-rw-r--r--tests/pyscript/bar1.py1
-rw-r--r--tests/pyscript/custom_echo.py2
-rw-r--r--tests/pyscript/foo1.py1
-rw-r--r--tests/pyscript/foo2.py1
-rw-r--r--tests/pyscript/foo3.py1
-rw-r--r--tests/pyscript/foo4.py1
-rw-r--r--tests/pyscript/help.py3
-rw-r--r--tests/pyscript/help_media.py1
-rw-r--r--tests/pyscript/media_movies_add1.py1
-rw-r--r--tests/pyscript/media_movies_add2.py1
-rw-r--r--tests/pyscript/media_movies_list1.py3
-rw-r--r--tests/pyscript/media_movies_list2.py3
-rw-r--r--tests/pyscript/media_movies_list3.py3
-rw-r--r--tests/pyscript/media_movies_list4.py1
-rw-r--r--tests/pyscript/media_movies_list5.py1
-rw-r--r--tests/pyscript/media_movies_list6.py1
-rw-r--r--tests/pyscript/media_movies_list7.py1
-rw-r--r--tests/pyscript/pyscript_dir1.py3
-rw-r--r--tests/pyscript/pyscript_dir2.py3
-rw-r--r--tests/scripts/recursive.py1
-rw-r--r--tests/test_pyscript.py36
22 files changed, 91 insertions, 24 deletions
diff --git a/cmd2/pyscript_bridge.py b/cmd2/pyscript_bridge.py
index ecd2b622..a1c367e2 100644
--- a/cmd2/pyscript_bridge.py
+++ b/cmd2/pyscript_bridge.py
@@ -41,13 +41,15 @@ class CommandResult(namedtuple_with_defaults('CmdResult', ['stdout', 'stderr', '
class CopyStream(object):
"""Copies all data written to a stream"""
- def __init__(self, inner_stream):
+ def __init__(self, inner_stream, echo):
self.buffer = ''
self.inner_stream = inner_stream
+ self.echo = echo
def write(self, s):
self.buffer += s
- self.inner_stream.write(s)
+ if self.echo:
+ self.inner_stream.write(s)
def read(self):
raise NotImplementedError
@@ -62,12 +64,12 @@ class CopyStream(object):
return getattr(self.inner_stream, item)
-def _exec_cmd(cmd2_app, func):
+def _exec_cmd(cmd2_app, func, echo):
"""Helper to encapsulate executing a command and capturing the results"""
- copy_stdout = CopyStream(sys.stdout)
- copy_stderr = CopyStream(sys.stderr)
+ copy_stdout = CopyStream(sys.stdout, echo)
+ copy_stderr = CopyStream(sys.stderr, echo)
- copy_cmd_stdout = CopyStream(cmd2_app.stdout)
+ copy_cmd_stdout = CopyStream(cmd2_app.stdout, echo)
cmd2_app._last_result = None
@@ -80,7 +82,7 @@ def _exec_cmd(cmd2_app, func):
cmd2_app.stdout = copy_cmd_stdout.inner_stream
# if stderr is empty, set it to None
- stderr = copy_stderr if copy_stderr.buffer else None
+ stderr = copy_stderr.buffer if copy_stderr.buffer else None
outbuf = copy_cmd_stdout.buffer if copy_cmd_stdout.buffer else copy_stdout.buffer
result = CommandResult(stdout=outbuf, stderr=stderr, data=cmd2_app._last_result)
@@ -91,7 +93,8 @@ class ArgparseFunctor:
"""
Encapsulates translating python object traversal
"""
- def __init__(self, cmd2_app, item, parser):
+ def __init__(self, echo: bool, cmd2_app, item, parser):
+ self._echo = echo
self._cmd2_app = cmd2_app
self._item = item
self._parser = parser
@@ -101,6 +104,14 @@ class ArgparseFunctor:
# argparse object for the current command layer
self.__current_subcommand_parser = parser
+ def __dir__(self):
+ """Returns a custom list of attribute names to match the sub-commands"""
+ commands = []
+ for action in self.__current_subcommand_parser._actions:
+ if not action.option_strings and isinstance(action, argparse._SubParsersAction):
+ commands.extend(action.choices)
+ return commands
+
def __getattr__(self, item):
"""Search for a subcommand matching this item and update internal state to track the traversal"""
# look for sub-command under the current command/sub-command layer
@@ -114,7 +125,6 @@ class ArgparseFunctor:
return self
raise AttributeError(item)
- # return super().__getattr__(item)
def __call__(self, *args, **kwargs):
"""
@@ -251,9 +261,8 @@ class ArgparseFunctor:
traverse_parser(self._parser)
- # print('Command: {}'.format(cmd_str[0]))
+ return _exec_cmd(self._cmd2_app, functools.partial(func, cmd_str[0]), self._echo)
- return _exec_cmd(self._cmd2_app, functools.partial(func, cmd_str[0]))
class PyscriptBridge(object):
"""Preserves the legacy 'cmd' interface for pyscript while also providing a new python API wrapper for
@@ -261,6 +270,7 @@ class PyscriptBridge(object):
def __init__(self, cmd2_app):
self._cmd2_app = cmd2_app
self._last_result = None
+ self.cmd_echo = False
def __getattr__(self, item: str):
"""Check if the attribute is a command. If so, return a callable."""
@@ -274,13 +284,19 @@ class PyscriptBridge(object):
except AttributeError:
# Command doesn't, we will accept parameters in the form of a command string
def wrap_func(args=''):
- return _exec_cmd(self._cmd2_app, functools.partial(func, args))
+ return _exec_cmd(self._cmd2_app, functools.partial(func, args), self.cmd_echo)
return wrap_func
else:
# Command does use argparse, return an object that can traverse the argparse subcommands and arguments
- return ArgparseFunctor(self._cmd2_app, item, parser)
+ return ArgparseFunctor(self.cmd_echo, self._cmd2_app, item, parser)
- raise AttributeError(item)
+ return super().__getattr__(item)
+
+ def __dir__(self):
+ """Return a custom set of attribute names to match the available commands"""
+ commands = list(self._cmd2_app.get_all_commands())
+ commands.insert(0, 'cmd_echo')
+ return commands
def __call__(self, args):
- return _exec_cmd(self._cmd2_app, functools.partial(self._cmd2_app.onecmd_plus_hooks, args + '\n'))
+ return _exec_cmd(self._cmd2_app, functools.partial(self._cmd2_app.onecmd_plus_hooks, args + '\n'), self.cmd_echo)
diff --git a/tests/pyscript/bar1.py b/tests/pyscript/bar1.py
index c6276a87..521e2c29 100644
--- a/tests/pyscript/bar1.py
+++ b/tests/pyscript/bar1.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.bar('11', '22')
diff --git a/tests/pyscript/custom_echo.py b/tests/pyscript/custom_echo.py
new file mode 100644
index 00000000..14040e4c
--- /dev/null
+++ b/tests/pyscript/custom_echo.py
@@ -0,0 +1,2 @@
+custom.cmd_echo = True
+custom.echo('blah!')
diff --git a/tests/pyscript/foo1.py b/tests/pyscript/foo1.py
index 6e345d95..d9345354 100644
--- a/tests/pyscript/foo1.py
+++ b/tests/pyscript/foo1.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.foo('aaa', 'bbb', counter=3, trueval=True, constval=True)
diff --git a/tests/pyscript/foo2.py b/tests/pyscript/foo2.py
index d4df7616..d3600a60 100644
--- a/tests/pyscript/foo2.py
+++ b/tests/pyscript/foo2.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.foo('11', '22', '33', '44', counter=3, trueval=True, constval=True)
diff --git a/tests/pyscript/foo3.py b/tests/pyscript/foo3.py
index db69edaf..fc0e084a 100644
--- a/tests/pyscript/foo3.py
+++ b/tests/pyscript/foo3.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.foo('11', '22', '33', '44', '55', '66', counter=3, trueval=False, constval=False)
diff --git a/tests/pyscript/foo4.py b/tests/pyscript/foo4.py
index 88fd3ce8..e4b7d01c 100644
--- a/tests/pyscript/foo4.py
+++ b/tests/pyscript/foo4.py
@@ -1,3 +1,4 @@
+app.cmd_echo = True
result = app.foo('aaa', 'bbb', counter=3)
out_text = 'Fail'
if result:
diff --git a/tests/pyscript/help.py b/tests/pyscript/help.py
index 3f67793c..664c0488 100644
--- a/tests/pyscript/help.py
+++ b/tests/pyscript/help.py
@@ -1 +1,2 @@
-app.help() \ No newline at end of file
+app.cmd_echo = True
+app.help()
diff --git a/tests/pyscript/help_media.py b/tests/pyscript/help_media.py
index 78025bdd..d8d97c42 100644
--- a/tests/pyscript/help_media.py
+++ b/tests/pyscript/help_media.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.help('media')
diff --git a/tests/pyscript/media_movies_add1.py b/tests/pyscript/media_movies_add1.py
index a9139cb1..7249c0ef 100644
--- a/tests/pyscript/media_movies_add1.py
+++ b/tests/pyscript/media_movies_add1.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.media.movies.add('My Movie', 'PG-13', director=('George Lucas', 'J. J. Abrams'))
diff --git a/tests/pyscript/media_movies_add2.py b/tests/pyscript/media_movies_add2.py
index 5c4617ae..681095d7 100644
--- a/tests/pyscript/media_movies_add2.py
+++ b/tests/pyscript/media_movies_add2.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.media.movies.add('My Movie', 'PG-13', actor=('Mark Hamill'), director=('George Lucas', 'J. J. Abrams'))
diff --git a/tests/pyscript/media_movies_list1.py b/tests/pyscript/media_movies_list1.py
index 0124bbcb..edbc2021 100644
--- a/tests/pyscript/media_movies_list1.py
+++ b/tests/pyscript/media_movies_list1.py
@@ -1 +1,2 @@
-app.media.movies.list() \ No newline at end of file
+app.cmd_echo = True
+app.media.movies.list()
diff --git a/tests/pyscript/media_movies_list2.py b/tests/pyscript/media_movies_list2.py
index 83f6c8ff..5ad01b7b 100644
--- a/tests/pyscript/media_movies_list2.py
+++ b/tests/pyscript/media_movies_list2.py
@@ -1 +1,2 @@
-app.media().movies().list() \ No newline at end of file
+app.cmd_echo = True
+app.media().movies().list()
diff --git a/tests/pyscript/media_movies_list3.py b/tests/pyscript/media_movies_list3.py
index 4fcf1288..bdbdfceb 100644
--- a/tests/pyscript/media_movies_list3.py
+++ b/tests/pyscript/media_movies_list3.py
@@ -1 +1,2 @@
-app('media movies list') \ No newline at end of file
+app.cmd_echo = True
+app('media movies list')
diff --git a/tests/pyscript/media_movies_list4.py b/tests/pyscript/media_movies_list4.py
index 1165b0c5..5f7bdaa9 100644
--- a/tests/pyscript/media_movies_list4.py
+++ b/tests/pyscript/media_movies_list4.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.media.movies.list(actor='Mark Hamill')
diff --git a/tests/pyscript/media_movies_list5.py b/tests/pyscript/media_movies_list5.py
index 962b1516..fa4efa5b 100644
--- a/tests/pyscript/media_movies_list5.py
+++ b/tests/pyscript/media_movies_list5.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.media.movies.list(actor=('Mark Hamill', 'Carrie Fisher'))
diff --git a/tests/pyscript/media_movies_list6.py b/tests/pyscript/media_movies_list6.py
index 5f8d3654..ef1851cd 100644
--- a/tests/pyscript/media_movies_list6.py
+++ b/tests/pyscript/media_movies_list6.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.media.movies.list(rating='PG')
diff --git a/tests/pyscript/media_movies_list7.py b/tests/pyscript/media_movies_list7.py
index bb0e28bb..7c827b7f 100644
--- a/tests/pyscript/media_movies_list7.py
+++ b/tests/pyscript/media_movies_list7.py
@@ -1 +1,2 @@
+app.cmd_echo = True
app.media.movies.list(rating=('PG', 'PG-13'))
diff --git a/tests/pyscript/pyscript_dir1.py b/tests/pyscript/pyscript_dir1.py
new file mode 100644
index 00000000..14a70a31
--- /dev/null
+++ b/tests/pyscript/pyscript_dir1.py
@@ -0,0 +1,3 @@
+out = dir(app)
+out.sort()
+print(out)
diff --git a/tests/pyscript/pyscript_dir2.py b/tests/pyscript/pyscript_dir2.py
new file mode 100644
index 00000000..28c61c8e
--- /dev/null
+++ b/tests/pyscript/pyscript_dir2.py
@@ -0,0 +1,3 @@
+out = dir(app.media)
+out.sort()
+print(out)
diff --git a/tests/scripts/recursive.py b/tests/scripts/recursive.py
index 32c981b6..4c29d317 100644
--- a/tests/scripts/recursive.py
+++ b/tests/scripts/recursive.py
@@ -3,4 +3,5 @@
"""
Example demonstrating that running a Python script recursively inside another Python script isn't allowed
"""
+app.cmd_echo = True
app('pyscript ../script.py')
diff --git a/tests/test_pyscript.py b/tests/test_pyscript.py
index 8d0cefd8..73c1a62a 100644
--- a/tests/test_pyscript.py
+++ b/tests/test_pyscript.py
@@ -101,7 +101,14 @@ class PyscriptExample(Cmd):
@with_argparser(bar_parser)
def do_bar(self, args):
- print('bar ' + str(args.__dict__))
+ out = 'bar '
+ arg_dict = args.__dict__
+ keys = list(arg_dict.keys())
+ keys.sort()
+ out += '{'
+ for key in keys:
+ out += "'{}':'{}'".format(key, arg_dict[key])
+ print(out)
@pytest.fixture
@@ -160,7 +167,7 @@ def test_pyscript_help(ps_app, capsys, request, command, pyscript_file):
('foo aaa bbb -ccc -t -n', 'foo1.py'),
('foo 11 22 33 44 -ccc -t -n', 'foo2.py'),
('foo 11 22 33 44 55 66 -ccc', 'foo3.py'),
- ('bar 11 22', 'bar1.py')
+ ('bar 11 22', 'bar1.py'),
])
def test_pyscript_out(ps_app, capsys, request, command, pyscript_file):
test_dir = os.path.dirname(request.module.__file__)
@@ -204,11 +211,30 @@ def test_pyscript_results(ps_app, capsys, request, pyscript_file, exp_out):
assert exp_out in expected
-def test_pyscript_custom_name(ps_echo, capsys):
+@pytest.mark.parametrize('expected, pyscript_file', [
+ ("['_relative_load', 'alias', 'bar', 'cmd_echo', 'edit', 'eof', 'eos', 'foo', 'help', 'history', 'load', 'media', 'py', 'pyscript', 'quit', 'set', 'shell', 'shortcuts', 'unalias']",
+ 'pyscript_dir1.py'),
+ ("['movies', 'shows']", 'pyscript_dir2.py')
+])
+def test_pyscript_dir(ps_app, capsys, request, expected, pyscript_file):
+ test_dir = os.path.dirname(request.module.__file__)
+ python_script = os.path.join(test_dir, 'pyscript', pyscript_file)
+
+ run_cmd(ps_app, 'pyscript {}'.format(python_script))
+ out, _ = capsys.readouterr()
+ out = out.strip()
+ assert len(out) > 0
+ assert out == expected
+
+
+def test_pyscript_custom_name(ps_echo, capsys, request):
message = 'blah!'
- run_cmd(ps_echo, 'py custom.echo("{}")'.format(message))
+
+ test_dir = os.path.dirname(request.module.__file__)
+ python_script = os.path.join(test_dir, 'pyscript', 'custom_echo.py')
+
+ run_cmd(ps_echo, 'pyscript {}'.format(python_script))
expected, _ = capsys.readouterr()
assert len(expected) > 0
expected = expected.splitlines()
assert message == expected[0]
-