diff options
author | Eric Lin <anselor@gmail.com> | 2018-05-17 18:08:52 -0400 |
---|---|---|
committer | Eric Lin <anselor@gmail.com> | 2018-05-17 18:08:52 -0400 |
commit | ff89bad1b0dd2a608081db5a8fa299ef43d66bc5 (patch) | |
tree | 5b5ba5f355d4026e563af3491ee6758642833f10 /cmd2/pyscript_bridge.py | |
parent | 371284d20370a8e85dd8527d9bbcc6267b335cde (diff) | |
download | cmd2-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.
Diffstat (limited to 'cmd2/pyscript_bridge.py')
-rw-r--r-- | cmd2/pyscript_bridge.py | 46 |
1 files changed, 31 insertions, 15 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) |