Skip to content

Commit f593588

Browse files
authored
Merge pull request #612 from python-cmd2/select_zero
Fixed bug in cmd2.Cmd.select() when user enters 0 or negative number
2 parents 3efb3f1 + 259ca0d commit f593588

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## 0.9.7 (TBD, 2018)
2+
* Bug Fixes
3+
* Fixed bug when user chooses a zero or negative index when calling ``Cmd.select()``
24
* Enhancements
35
* **cmdloop** now only attempts to register a custom signal handler for SIGINT if running in the main thread
46
* commands run as a result of ``default_to_shell`` being **True** now run via ``do_shell()`` and are saved

cmd2/cmd2.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2780,6 +2780,8 @@ def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]], p
27802780

27812781
try:
27822782
choice = int(response)
2783+
if choice < 1:
2784+
raise IndexError
27832785
result = fulloptions[choice - 1][0]
27842786
break
27852787
except (ValueError, IndexError):

tests/test_cmd2.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,11 +1344,11 @@ def test_select_options(select_app):
13441344
# And verify the expected output to stdout
13451345
assert out == expected
13461346

1347-
def test_select_invalid_option(select_app):
1347+
def test_select_invalid_option_too_big(select_app):
13481348
# Mock out the input call so we don't actually wait for a user's response on stdin
13491349
m = mock.MagicMock(name='input')
13501350
# If side_effect is an iterable then each call to the mock will return the next value from the iterable.
1351-
m.side_effect = ['3', '1'] # First pass and invalid selection, then pass a valid one
1351+
m.side_effect = ['3', '1'] # First pass an invalid selection, then pass a valid one
13521352
builtins.input = m
13531353

13541354
food = 'fish'
@@ -1368,6 +1368,30 @@ def test_select_invalid_option(select_app):
13681368
# And verify the expected output to stdout
13691369
assert out == expected
13701370

1371+
def test_select_invalid_option_too_small(select_app):
1372+
# Mock out the input call so we don't actually wait for a user's response on stdin
1373+
m = mock.MagicMock(name='input')
1374+
# If side_effect is an iterable then each call to the mock will return the next value from the iterable.
1375+
m.side_effect = ['0', '1'] # First pass an invalid selection, then pass a valid one
1376+
builtins.input = m
1377+
1378+
food = 'fish'
1379+
out = run_cmd(select_app, "eat {}".format(food))
1380+
expected = normalize("""
1381+
1. sweet
1382+
2. salty
1383+
'0' isn't a valid choice. Pick a number between 1 and 2:
1384+
{} with sweet sauce, yum!
1385+
""".format(food))
1386+
1387+
# Make sure our mock was called exactly twice with the expected arguments
1388+
arg = 'Sauce? '
1389+
calls = [mock.call(arg), mock.call(arg)]
1390+
m.assert_has_calls(calls)
1391+
1392+
# And verify the expected output to stdout
1393+
assert out == expected
1394+
13711395
def test_select_list_of_strings(select_app):
13721396
# Mock out the input call so we don't actually wait for a user's response on stdin
13731397
m = mock.MagicMock(name='input', return_value='2')

0 commit comments

Comments
 (0)