Skip to content

Commit 933e328

Browse files
authored
Merge branch 'master' into verify_command_names
2 parents 021ae36 + cc3d5cd commit 933e328

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

cmd2/cmd2.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ def pexcept(self, msg: Any, *, end: str = '\n', apply_style: bool = True) -> Non
690690
final_msg = ansi.style_error(final_msg)
691691

692692
if not self.debug:
693-
warning = "\nTo enable full traceback, run the following command: 'set debug true'"
693+
warning = "\nTo enable full traceback, run the following command: 'set debug true'"
694694
final_msg += ansi.style_warning(warning)
695695

696696
# Set apply_style to False since style has already been applied
@@ -2896,6 +2896,28 @@ def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]],
28962896
| a list of tuples -> interpreted as (value, text), so
28972897
that the return value can differ from
28982898
the text advertised to the user """
2899+
2900+
completion_disabled = False
2901+
orig_completer = None
2902+
2903+
def disable_completion():
2904+
"""Turn off completion during the select input line"""
2905+
nonlocal orig_completer
2906+
nonlocal completion_disabled
2907+
2908+
if rl_type != RlType.NONE and not completion_disabled:
2909+
orig_completer = readline.get_completer()
2910+
readline.set_completer(lambda *args, **kwargs: None)
2911+
completion_disabled = True
2912+
2913+
def enable_completion():
2914+
"""Restore tab completion when select is done reading input"""
2915+
nonlocal completion_disabled
2916+
2917+
if rl_type != RlType.NONE and completion_disabled:
2918+
readline.set_completer(orig_completer)
2919+
completion_disabled = False
2920+
28992921
local_opts = opts
29002922
if isinstance(opts, str):
29012923
local_opts = list(zip(opts.split(), opts.split()))
@@ -2910,15 +2932,28 @@ def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]],
29102932
fulloptions.append((opt[0], opt[0]))
29112933
for (idx, (_, text)) in enumerate(fulloptions):
29122934
self.poutput(' %2d. %s' % (idx + 1, text))
2935+
29132936
while True:
29142937
safe_prompt = rl_make_safe_prompt(prompt)
2915-
response = input(safe_prompt)
2938+
2939+
try:
2940+
with self.sigint_protection:
2941+
disable_completion()
2942+
response = input(safe_prompt)
2943+
except EOFError:
2944+
response = ''
2945+
self.poutput('\n', end='')
2946+
finally:
2947+
with self.sigint_protection:
2948+
enable_completion()
2949+
2950+
if not response:
2951+
continue
29162952

29172953
if rl_type != RlType.NONE:
29182954
hlen = readline.get_current_history_length()
2919-
if hlen >= 1 and response != '':
2955+
if hlen >= 1:
29202956
readline.remove_history_item(hlen - 1)
2921-
29222957
try:
29232958
choice = int(response)
29242959
if choice < 1:
@@ -2928,6 +2963,7 @@ def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]],
29282963
except (ValueError, IndexError):
29292964
self.poutput("{!r} isn't a valid choice. Pick a number between 1 and {}:".format(
29302965
response, len(fulloptions)))
2966+
29312967
return result
29322968

29332969
def _get_read_only_settings(self) -> str:

tests/test_cmd2.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ def _expected_no_editor_error():
643643

644644
expected_text = normalize("""
645645
EXCEPTION of type '{}' occurred with message: 'Please use 'set editor' to specify your text editing program of choice.'
646-
To enable full traceback, run the following command: 'set debug true'
646+
To enable full traceback, run the following command: 'set debug true'
647647
""".format(expected_exception))
648648

649649
return expected_text
@@ -1102,6 +1102,7 @@ def test_select_invalid_option_too_big(select_app):
11021102
arg = 'Sauce? '
11031103
calls = [mock.call(arg), mock.call(arg)]
11041104
m.assert_has_calls(calls)
1105+
assert m.call_count == 2
11051106

11061107
# And verify the expected output to stdout
11071108
assert out == expected
@@ -1126,6 +1127,7 @@ def test_select_invalid_option_too_small(select_app):
11261127
arg = 'Sauce? '
11271128
calls = [mock.call(arg), mock.call(arg)]
11281129
m.assert_has_calls(calls)
1130+
assert m.call_count == 2
11291131

11301132
# And verify the expected output to stdout
11311133
assert out == expected
@@ -1185,6 +1187,19 @@ def test_select_uneven_list_of_tuples(select_app):
11851187
# And verify the expected output to stdout
11861188
assert out == expected
11871189

1190+
def test_select_eof(select_app):
1191+
# Ctrl-D during select causes an EOFError that just reprompts the user
1192+
m = mock.MagicMock(name='input', side_effect=[EOFError, 2])
1193+
builtins.input = m
1194+
1195+
food = 'fish'
1196+
out, err = run_cmd(select_app, "eat {}".format(food))
1197+
1198+
# Make sure our mock was called exactly twice with the expected arguments
1199+
arg = 'Sauce? '
1200+
calls = [mock.call(arg), mock.call(arg)]
1201+
m.assert_has_calls(calls)
1202+
assert m.call_count == 2
11881203

11891204
class HelpNoDocstringApp(cmd2.Cmd):
11901205
greet_parser = argparse.ArgumentParser()

0 commit comments

Comments
 (0)