Skip to content

Commit 400ab57

Browse files
committed
Made it so default_to_shell results in do_shell being called so that output can be captured
1 parent c3a7380 commit 400ab57

File tree

3 files changed

+48
-64
lines changed

3 files changed

+48
-64
lines changed

cmd2/cmd2.py

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ def perror(self, err: Union[str, Exception], traceback_war: bool=True, err_color
632632
err_msg = err_color + err_msg + Fore.RESET
633633
self.decolorized_write(sys.stderr, err_msg)
634634

635-
if traceback_war:
635+
if traceback_war and not self.debug:
636636
war = "To enable full traceback, run the following command: 'set debug true'\n"
637637
war = war_color + war + Fore.RESET
638638
self.decolorized_write(sys.stderr, war)
@@ -1893,6 +1893,8 @@ def _redirect_output(self, statement: Statement) -> None:
18931893
try:
18941894
self.pipe_proc = subprocess.Popen(statement.pipe_to, stdin=subproc_stdin)
18951895
except Exception as ex:
1896+
self.perror('Not piping because - {}'.format(ex), traceback_war=False)
1897+
18961898
# Restore stdout to what it was and close the pipe
18971899
self.stdout.close()
18981900
subproc_stdin.close()
@@ -1901,8 +1903,6 @@ def _redirect_output(self, statement: Statement) -> None:
19011903
self.kept_state = None
19021904
self.redirecting = False
19031905

1904-
# Re-raise the exception
1905-
raise ex
19061906
elif statement.output:
19071907
import tempfile
19081908
if (not statement.output_to) and (not self.can_clip):
@@ -1920,7 +1920,7 @@ def _redirect_output(self, statement: Statement) -> None:
19201920
try:
19211921
sys.stdout = self.stdout = open(statement.output_to, mode)
19221922
except OSError as ex:
1923-
self.perror('Not Redirecting because - {}'.format(ex), traceback_war=False)
1923+
self.perror('Not redirecting because - {}'.format(ex), traceback_war=False)
19241924
self.redirecting = False
19251925
else:
19261926
# going to a paste buffer
@@ -1998,18 +1998,29 @@ def onecmd(self, statement: Union[Statement, str]) -> bool:
19981998
if statement.command in self.macros:
19991999
stop = self._run_macro(statement)
20002000
else:
2001-
func = self.cmd_func(statement.command)
2001+
command = statement.command
2002+
func = self.cmd_func(command)
2003+
func_arg = statement.args
2004+
2005+
if not func and self.default_to_shell:
2006+
command = 'shell'
2007+
func = self.cmd_func(command)
2008+
func_arg = statement.command_and_args
2009+
20022010
if func:
2003-
# Since we have a valid command store it in the history
2004-
if statement.command not in self.exclude_from_history:
2011+
# Check if this command should be stored in the history
2012+
if command not in self.exclude_from_history:
20052013
self.history.append(statement.raw)
20062014

2007-
stop = func(statement)
2015+
stop = func(func_arg)
20082016

20092017
else:
20102018
self.default(statement)
20112019
stop = False
20122020

2021+
if stop is None:
2022+
stop = False
2023+
20132024
return stop
20142025

20152026
def _run_macro(self, statement: Statement) -> bool:
@@ -2057,18 +2068,11 @@ def _run_macro(self, statement: Statement) -> bool:
20572068
return self.onecmd_plus_hooks(resolved)
20582069

20592070
def default(self, statement: Statement) -> None:
2060-
"""Executed when the command given isn't a recognized command implemented by a do_* method.
2071+
"""Called on an input line when the command prefix is not recognized.
20612072
20622073
:param statement: Statement object with parsed input
20632074
"""
2064-
if self.default_to_shell:
2065-
result = os.system(statement.command_and_args)
2066-
# If os.system() succeeded, then don't print warning about unknown command
2067-
if not result:
2068-
return
2069-
2070-
# Print out a message stating this is an unknown command
2071-
self.poutput('*** Unknown syntax: {}\n'.format(statement.command_and_args))
2075+
self.poutput('*** {} is not a recognized command, alias, or macro\n'.format(statement.command))
20722076

20732077
def pseudo_raw_input(self, prompt: str) -> str:
20742078
"""Began life as a copy of cmd's cmdloop; like raw_input but
@@ -2803,7 +2807,7 @@ def show(self, args: argparse.Namespace, parameter: str='') -> None:
28032807
:param args: argparse parsed arguments from the set command
28042808
:param parameter: optional search parameter
28052809
"""
2806-
param = parameter.strip().lower()
2810+
param = utils.norm_fold(parameter.strip())
28072811
result = {}
28082812
maxlen = 0
28092813

@@ -2844,7 +2848,7 @@ def do_set(self, args: argparse.Namespace) -> None:
28442848
# Check if param was passed in
28452849
if not args.param:
28462850
return self.show(args)
2847-
param = args.param.strip().lower()
2851+
param = utils.norm_fold(args.param.strip())
28482852

28492853
# Check if value was passed in
28502854
if not args.value:
@@ -3156,15 +3160,19 @@ def load_ipy(app):
31563160
history_parser_group.add_argument('-e', '--edit', action='store_true',
31573161
help='edit and then run selected history items')
31583162
history_parser_group.add_argument('-s', '--script', action='store_true', help='output commands in script format')
3159-
history_parser_group.add_argument('-o', '--output-file', metavar='FILE', help='output commands to a script file')
3160-
history_parser_group.add_argument('-t', '--transcript', help='output commands and results to a transcript file')
3163+
setattr(history_parser_group.add_argument('-o', '--output-file', metavar='FILE',
3164+
help='output commands to a script file'),
3165+
ACTION_ARG_CHOICES, ('path_complete',))
3166+
setattr(history_parser_group.add_argument('-t', '--transcript',
3167+
help='output commands and results to a transcript file'),
3168+
ACTION_ARG_CHOICES, ('path_complete',))
31613169
history_parser_group.add_argument('-c', '--clear', action="store_true", help='clear all history')
3162-
_history_arg_help = """empty all history items
3163-
a one history item by number
3164-
a..b, a:b, a:, ..b items by indices (inclusive)
3165-
[string] items containing string
3166-
/regex/ items matching regular expression"""
3167-
history_parser.add_argument('arg', nargs='?', help=_history_arg_help)
3170+
history_arg_help = ("empty all history items\n"
3171+
"a one history item by number\n"
3172+
"a..b, a:b, a:, ..b items by indices (inclusive)\n"
3173+
"string items containing string\n"
3174+
"/regex/ items matching regular expression")
3175+
history_parser.add_argument('arg', nargs='?', help=history_arg_help)
31683176

31693177
@with_argparser(history_parser)
31703178
def do_history(self, args: argparse.Namespace) -> None:
@@ -3851,7 +3859,6 @@ def get(self, getme: Optional[Union[int, str]]=None) -> List[HistoryItem]:
38513859
end = int(end)
38523860
return self[start:end]
38533861

3854-
# noinspection PyUnresolvedReferences
38553862
getme = getme.strip()
38563863

38573864
if getme.startswith(r'/') and getme.endswith(r'/'):
@@ -3871,7 +3878,7 @@ def isin(hi):
38713878
:param hi: HistoryItem
38723879
:return: bool - True if search matches
38733880
"""
3874-
return getme.lower() in hi.lowercase
3881+
return utils.norm_fold(getme) in utils.norm_fold(hi)
38753882
return [itm for itm in self if isin(itm)]
38763883

38773884

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
arg empty all history items
6060
a one history item by number
6161
a..b, a:b, a:, ..b items by indices (inclusive)
62-
[string] items containing string
62+
string items containing string
6363
/regex/ items matching regular expression
6464
6565
optional arguments:

tests/test_cmd2.py

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def test_pyscript_requires_an_argument(base_app, capsys):
298298

299299
def test_base_error(base_app):
300300
out = run_cmd(base_app, 'meow')
301-
assert out == ["*** Unknown syntax: meow"]
301+
assert "is not a recognized command" in out[0]
302302

303303

304304
@pytest.fixture
@@ -803,8 +803,7 @@ def test_pipe_to_shell_error(base_app, capsys):
803803
run_cmd(base_app, 'help | foobarbaz.this_does_not_exist')
804804
out, err = capsys.readouterr()
805805
assert not out
806-
expected_error = 'FileNotFoundError'
807-
assert err.startswith("EXCEPTION of type '{}' occurred with message:".format(expected_error))
806+
assert err.startswith("ERROR: Not piping because")
808807

809808

810809
@pytest.mark.skipif(not clipboard.can_clip,
@@ -1042,12 +1041,12 @@ def hook_failure():
10421041

10431042
def test_precmd_hook_success(base_app):
10441043
out = base_app.onecmd_plus_hooks('help')
1045-
assert out is None
1044+
assert out is False
10461045

10471046

10481047
def test_precmd_hook_failure(hook_failure):
10491048
out = hook_failure.onecmd_plus_hooks('help')
1050-
assert out == True
1049+
assert out is True
10511050

10521051

10531052
class SayApp(cmd2.Cmd):
@@ -1098,40 +1097,18 @@ def __init__(self, *args, **kwargs):
10981097
super().__init__(*args, **kwargs)
10991098
self.default_to_shell = True
11001099

1101-
@pytest.fixture
1102-
def shell_app():
1103-
app = ShellApp()
1104-
app.stdout = utils.StdSim(app.stdout)
1105-
return app
1106-
1107-
def test_default_to_shell_unknown(shell_app):
1108-
unknown_command = 'zyxcw23'
1109-
out = run_cmd(shell_app, unknown_command)
1110-
assert out == ["*** Unknown syntax: {}".format(unknown_command)]
1111-
1112-
def test_default_to_shell_good(capsys):
1113-
app = cmd2.Cmd()
1114-
app.default_to_shell = True
1100+
def test_default_to_shell(base_app, monkeypatch):
11151101
if sys.platform.startswith('win'):
11161102
line = 'dir'
11171103
else:
11181104
line = 'ls'
1119-
statement = app.statement_parser.parse(line)
1120-
retval = app.default(statement)
1121-
assert not retval
1122-
out, err = capsys.readouterr()
1123-
assert out == ''
1124-
1125-
def test_default_to_shell_failure(capsys):
1126-
app = cmd2.Cmd()
1127-
app.default_to_shell = True
1128-
line = 'ls does_not_exist.xyz'
1129-
statement = app.statement_parser.parse(line)
1130-
retval = app.default(statement)
1131-
assert not retval
1132-
out, err = capsys.readouterr()
1133-
assert out == "*** Unknown syntax: {}\n".format(line)
11341105

1106+
base_app.default_to_shell = True
1107+
m = mock.Mock()
1108+
monkeypatch.setattr("{}.Popen".format('subprocess'), m)
1109+
out = run_cmd(base_app, line)
1110+
assert out == []
1111+
assert m.called
11351112

11361113
def test_ansi_prompt_not_esacped(base_app):
11371114
from cmd2.rl_utils import rl_make_safe_prompt

0 commit comments

Comments
 (0)