@@ -41,13 +41,15 @@ def __bool__(self):
4141
4242class CopyStream (object ):
4343 """Copies all data written to a stream"""
44- def __init__ (self , inner_stream ):
44+ def __init__ (self , inner_stream , echo ):
4545 self .buffer = ''
4646 self .inner_stream = inner_stream
47+ self .echo = echo
4748
4849 def write (self , s ):
4950 self .buffer += s
50- self .inner_stream .write (s )
51+ if self .echo :
52+ self .inner_stream .write (s )
5153
5254 def read (self ):
5355 raise NotImplementedError
@@ -62,12 +64,12 @@ def __getattr__(self, item):
6264 return getattr (self .inner_stream , item )
6365
6466
65- def _exec_cmd (cmd2_app , func ):
67+ def _exec_cmd (cmd2_app , func , echo ):
6668 """Helper to encapsulate executing a command and capturing the results"""
67- copy_stdout = CopyStream (sys .stdout )
68- copy_stderr = CopyStream (sys .stderr )
69+ copy_stdout = CopyStream (sys .stdout , echo )
70+ copy_stderr = CopyStream (sys .stderr , echo )
6971
70- copy_cmd_stdout = CopyStream (cmd2_app .stdout )
72+ copy_cmd_stdout = CopyStream (cmd2_app .stdout , echo )
7173
7274 cmd2_app ._last_result = None
7375
@@ -80,7 +82,7 @@ def _exec_cmd(cmd2_app, func):
8082 cmd2_app .stdout = copy_cmd_stdout .inner_stream
8183
8284 # if stderr is empty, set it to None
83- stderr = copy_stderr if copy_stderr .buffer else None
85+ stderr = copy_stderr . buffer if copy_stderr .buffer else None
8486
8587 outbuf = copy_cmd_stdout .buffer if copy_cmd_stdout .buffer else copy_stdout .buffer
8688 result = CommandResult (stdout = outbuf , stderr = stderr , data = cmd2_app ._last_result )
@@ -91,7 +93,8 @@ class ArgparseFunctor:
9193 """
9294 Encapsulates translating python object traversal
9395 """
94- def __init__ (self , cmd2_app , item , parser ):
96+ def __init__ (self , echo : bool , cmd2_app , item , parser ):
97+ self ._echo = echo
9598 self ._cmd2_app = cmd2_app
9699 self ._item = item
97100 self ._parser = parser
@@ -101,6 +104,14 @@ def __init__(self, cmd2_app, item, parser):
101104 # argparse object for the current command layer
102105 self .__current_subcommand_parser = parser
103106
107+ def __dir__ (self ):
108+ """Returns a custom list of attribute names to match the sub-commands"""
109+ commands = []
110+ for action in self .__current_subcommand_parser ._actions :
111+ if not action .option_strings and isinstance (action , argparse ._SubParsersAction ):
112+ commands .extend (action .choices )
113+ return commands
114+
104115 def __getattr__ (self , item ):
105116 """Search for a subcommand matching this item and update internal state to track the traversal"""
106117 # look for sub-command under the current command/sub-command layer
@@ -114,7 +125,6 @@ def __getattr__(self, item):
114125 return self
115126
116127 raise AttributeError (item )
117- # return super().__getattr__(item)
118128
119129 def __call__ (self , * args , ** kwargs ):
120130 """
@@ -251,16 +261,16 @@ def traverse_parser(parser):
251261
252262 traverse_parser (self ._parser )
253263
254- # print('Command: {}'.format( cmd_str[0]))
264+ return _exec_cmd ( self . _cmd2_app , functools . partial ( func , cmd_str [0 ]), self . _echo )
255265
256- return _exec_cmd (self ._cmd2_app , functools .partial (func , cmd_str [0 ]))
257266
258267class PyscriptBridge (object ):
259268 """Preserves the legacy 'cmd' interface for pyscript while also providing a new python API wrapper for
260269 application commands."""
261270 def __init__ (self , cmd2_app ):
262271 self ._cmd2_app = cmd2_app
263272 self ._last_result = None
273+ self .cmd_echo = False
264274
265275 def __getattr__ (self , item : str ):
266276 """Check if the attribute is a command. If so, return a callable."""
@@ -274,13 +284,19 @@ def __getattr__(self, item: str):
274284 except AttributeError :
275285 # Command doesn't, we will accept parameters in the form of a command string
276286 def wrap_func (args = '' ):
277- return _exec_cmd (self ._cmd2_app , functools .partial (func , args ))
287+ return _exec_cmd (self ._cmd2_app , functools .partial (func , args ), self . cmd_echo )
278288 return wrap_func
279289 else :
280290 # Command does use argparse, return an object that can traverse the argparse subcommands and arguments
281- return ArgparseFunctor (self ._cmd2_app , item , parser )
291+ return ArgparseFunctor (self .cmd_echo , self . _cmd2_app , item , parser )
282292
283- raise AttributeError (item )
293+ return super ().__getattr__ (item )
294+
295+ def __dir__ (self ):
296+ """Return a custom set of attribute names to match the available commands"""
297+ commands = list (self ._cmd2_app .get_all_commands ())
298+ commands .insert (0 , 'cmd_echo' )
299+ return commands
284300
285301 def __call__ (self , args ):
286- return _exec_cmd (self ._cmd2_app , functools .partial (self ._cmd2_app .onecmd_plus_hooks , args + '\n ' ))
302+ return _exec_cmd (self ._cmd2_app , functools .partial (self ._cmd2_app .onecmd_plus_hooks , args + '\n ' ), self . cmd_echo )
0 commit comments