Skip to content

Commit dcb5be4

Browse files
authored
Merge pull request #578 from python-cmd2/func_plus_hooks
Func plus hooks
2 parents 3dcff64 + be82ee5 commit dcb5be4

File tree

3 files changed

+37
-27
lines changed

3 files changed

+37
-27
lines changed

.gitignore

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,5 @@ htmlcov
2626
dmypy.json
2727
dmypy.sock
2828

29-
# cmd2 history file used in main.py
29+
# cmd2 history file used in hello_cmd2.py
3030
cmd2_history.txt
31-
32-
# Virtual environment
33-
venv

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
## 0.9.6 (TBD)
1+
## 0.9.6 (October 13, 2018)
2+
* Bug Fixes
3+
* Fixed bug introduced in 0.9.5 caused by backing up and restoring `self.prompt` in `pseudo_raw_input`.
4+
As part of this fix, continuation prompts will not be redrawn with `async_update_prompt` or `async_alert`.
25
* Enhancements
36
* All platforms now depend on [wcwidth](https://pypi.python.org/pypi/wcwidth) to assist with asynchronous alerts.
47
* Macros now accept extra arguments when called. These will be tacked onto the resolved command.
8+
* All cmd2 commands run via py now go through onecmd_plus_hooks.
59

610
## 0.9.5 (October 11, 2018)
711
* Bug Fixes

cmd2/pyscript_bridge.py

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,14 @@ def __bool__(self) -> bool:
4545
return not self.stderr
4646

4747

48-
def _exec_cmd(cmd2_app, func: Callable, echo: bool) -> CommandResult:
49-
"""Helper to encapsulate executing a command and capturing the results"""
48+
def _exec_cmd(cmd2_app, command: str, echo: bool) -> CommandResult:
49+
"""
50+
Helper to encapsulate executing a command and capturing the results
51+
:param cmd2_app: cmd2 app that will run the command
52+
:param command: command line being run
53+
:param echo: if True, output will be echoed to stdout/stderr while the command runs
54+
:return: result of the command
55+
"""
5056
copy_stdout = StdSim(sys.stdout, echo)
5157
copy_stderr = StdSim(sys.stderr, echo)
5258

@@ -58,7 +64,8 @@ def _exec_cmd(cmd2_app, func: Callable, echo: bool) -> CommandResult:
5864
cmd2_app.stdout = copy_cmd_stdout
5965
with redirect_stdout(copy_stdout):
6066
with redirect_stderr(copy_stderr):
61-
func()
67+
# Include a newline in case it's a multiline command
68+
cmd2_app.onecmd_plus_hooks(command + '\n')
6269
finally:
6370
cmd2_app.stdout = copy_cmd_stdout.inner_stream
6471

@@ -199,28 +206,29 @@ def _run(self):
199206
self._command_name))
200207

201208
# reconstruct the cmd2 command from the python call
202-
cmd_str = ['']
209+
command = self._command_name
203210

204211
def process_argument(action, value):
212+
nonlocal command
205213
if isinstance(action, argparse._CountAction):
206214
if isinstance(value, int):
207215
for _ in range(value):
208-
cmd_str[0] += '{} '.format(action.option_strings[0])
216+
command += ' {}'.format(action.option_strings[0])
209217
return
210218
else:
211219
raise TypeError('Expected int for ' + action.dest)
212220
if isinstance(action, argparse._StoreConstAction) or isinstance(action, argparse._AppendConstAction):
213221
if value:
214222
# Nothing else to append to the command string, just the flag is enough.
215-
cmd_str[0] += '{} '.format(action.option_strings[0])
223+
command += ' {}'.format(action.option_strings[0])
216224
return
217225
else:
218226
# value is not True so we default to false, which means don't include the flag
219227
return
220228

221229
# was the argument a flag?
222230
if action.option_strings:
223-
cmd_str[0] += '{} '.format(action.option_strings[0])
231+
command += ' {}'.format(action.option_strings[0])
224232

225233
is_remainder_arg = action.dest == self._remainder_arg
226234

@@ -231,33 +239,34 @@ def process_argument(action, value):
231239
raise ValueError('{} appears to be a flag and should be supplied as a keyword argument '
232240
'to the function.'.format(item))
233241
item = quote_string_if_needed(item)
234-
cmd_str[0] += '{} '.format(item)
242+
command += ' {}'.format(item)
235243

236244
# If this is a flag parameter that can accept a variable number of arguments and we have not
237245
# reached the max number, add a list completion suffix to tell argparse to move to the next
238246
# parameter
239247
if action.option_strings and isinstance(action, _RangeAction) and action.nargs_max is not None and \
240248
action.nargs_max > len(value):
241-
cmd_str[0] += '{0}{0} '.format(self._parser.prefix_chars[0])
249+
command += ' {0}{0}'.format(self._parser.prefix_chars[0])
242250

243251
else:
244252
value = str(value).strip()
245253
if not is_remainder_arg and is_potential_flag(value, self._parser):
246254
raise ValueError('{} appears to be a flag and should be supplied as a keyword argument '
247255
'to the function.'.format(value))
248256
value = quote_string_if_needed(value)
249-
cmd_str[0] += '{} '.format(value)
257+
command += ' {}'.format(value)
250258

251259
# If this is a flag parameter that can accept a variable number of arguments and we have not
252260
# reached the max number, add a list completion suffix to tell argparse to move to the next
253261
# parameter
254262
if action.option_strings and isinstance(action, _RangeAction) and action.nargs_max is not None and \
255263
action.nargs_max > 1:
256-
cmd_str[0] += '{0}{0} '.format(self._parser.prefix_chars[0])
264+
command += ' {0}{0}'.format(self._parser.prefix_chars[0])
257265

258266
def process_action(action):
267+
nonlocal command
259268
if isinstance(action, argparse._SubParsersAction):
260-
cmd_str[0] += '{} '.format(self._args[action.dest])
269+
command += ' {}'.format(self._args[action.dest])
261270
traverse_parser(action.choices[self._args[action.dest]])
262271
elif isinstance(action, argparse._AppendAction):
263272
if isinstance(self._args[action.dest], list) or isinstance(self._args[action.dest], tuple):
@@ -284,8 +293,7 @@ def traverse_parser(parser):
284293
process_action(action)
285294

286295
traverse_parser(self._parser)
287-
288-
return _exec_cmd(self._cmd2_app, functools.partial(func, cmd_str[0]), self._echo)
296+
return _exec_cmd(self._cmd2_app, command, self._echo)
289297

290298

291299
class PyscriptBridge(object):
@@ -310,7 +318,10 @@ def __getattr__(self, item: str):
310318
else:
311319
# Command doesn't use argparse, we will accept parameters in the form of a command string
312320
def wrap_func(args=''):
313-
return _exec_cmd(self._cmd2_app, functools.partial(func, args), self.cmd_echo)
321+
command = item
322+
if args:
323+
command += ' ' + args
324+
return _exec_cmd(self._cmd2_app, command, self.cmd_echo)
314325

315326
return wrap_func
316327
else:
@@ -323,17 +334,15 @@ def __dir__(self):
323334
attributes.insert(0, 'cmd_echo')
324335
return attributes
325336

326-
def __call__(self, args: str, echo: Optional[bool]=None) -> CommandResult:
337+
def __call__(self, command: str, echo: Optional[bool]=None) -> CommandResult:
327338
"""
328339
Provide functionality to call application commands by calling PyscriptBridge
329340
ex: app('help')
330-
:param args: The string being passed to the command
331-
:param echo: If True, output will be echoed while the command runs
332-
This temporarily overrides the value of self.cmd_echo
341+
:param command: command line being run
342+
:param echo: if True, output will be echoed to stdout/stderr while the command runs
343+
this temporarily overrides the value of self.cmd_echo
333344
"""
334345
if echo is None:
335346
echo = self.cmd_echo
336347

337-
return _exec_cmd(self._cmd2_app,
338-
functools.partial(self._cmd2_app.onecmd_plus_hooks, args + '\n'),
339-
echo)
348+
return _exec_cmd(self._cmd2_app, command, echo)

0 commit comments

Comments
 (0)