Skip to content

Commit b053e30

Browse files
committed
Fixed a bug with duplicate options
1 parent 339eafe commit b053e30

File tree

4 files changed

+58
-14
lines changed

4 files changed

+58
-14
lines changed

awsshell/interaction.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,12 @@ def execute(self, data):
7575
raise InteractionException('SimpleSelect expects a non-empty list')
7676
if self._model.get('Path') is not None:
7777
display_data = jmespath.search(self._model['Path'], data)
78-
option_dict = dict(zip(display_data, data))
79-
selected = self._prompter('%s ' % self.prompt, display_data)
80-
return option_dict[selected]
78+
result = self._prompter('%s ' % self.prompt, display_data)
79+
(selected, index) = result
80+
return data[index]
8181
else:
82-
return self._prompter('%s ' % self.prompt, data)
82+
(selected, index) = self._prompter('%s ' % self.prompt, data)
83+
return selected
8384

8485

8586
class SimplePrompt(Interaction):

awsshell/selectmenu.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def __init__(self, options):
5050
self.height = len(options)
5151
self._selection = None
5252

53+
def get_index(self):
54+
return self._selection
55+
5356
def get_selection(self):
5457
"""Return the currently selected option, if there is one."""
5558
if self._selection is not None:
@@ -77,6 +80,7 @@ def select_up(self, event=None):
7780

7881
def _insert_text(self, event):
7982
if event is not None:
83+
event.current_buffer.reset()
8084
event.current_buffer.document = Document(self.get_selection())
8185

8286
def preferred_width(self, cli, max_available_width):
@@ -121,7 +125,27 @@ def _get_menu_item_tokens(self, option, is_current):
121125
def create_select_menu_layout(msg, menu_control,
122126
show_meta=False,
123127
reserve_space_for_menu=True):
124-
"""Construct a layout for the given message and menu control."""
128+
"""Construct a layout for the given message and menu control.
129+
130+
:type msg: str
131+
:param msg: The message to be used when showing the prompt.
132+
133+
:type msg: :class:`SelectMenuControl`
134+
:param msg: The menu controller that manages the state and rendering of the
135+
currently selected option.
136+
137+
:type show_meta: bool
138+
:param show_meta: (Optional) Whether or not the meta information should be
139+
displayed below the prompt.
140+
141+
:type reserve_space_for_menu: bool
142+
:param reserve_space_for_menu: (Optional) Whether or not the prompt should
143+
force that there be enough lines for the completion menu to completely
144+
render.
145+
146+
:rtype: :class:`prompt_toolkit.layout.containers.Container`
147+
:return: The layout to be used for a select menu prompt.
148+
"""
125149
def get_prompt_tokens(cli):
126150
return [(Token.Prompt, msg)]
127151

@@ -201,7 +225,7 @@ def __init__(self, message, options, *args, **kwargs):
201225

202226
# create and apply the default and info buffers
203227
options_meta = kwargs.pop('options_meta', None)
204-
kwargs['buffers'] = self._initialize_buffers(options_meta)
228+
kwargs['buffers'] = self._initialize_buffers(options, options_meta)
205229

206230
# create and apply the new layout
207231
kwargs['layout'] = create_select_menu_layout(
@@ -212,10 +236,12 @@ def __init__(self, message, options, *args, **kwargs):
212236

213237
super(SelectMenuApplication, self).__init__(*args, **kwargs)
214238

215-
def _initialize_buffers(self, options_meta):
239+
def _initialize_buffers(self, options, options_meta):
216240
# Return the currently selected option
217241
def return_selection(cli, buf):
218-
cli.set_return_value(self.menu_control.get_selection())
242+
selection = self.menu_control.get_selection()
243+
index = self.menu_control.get_index()
244+
cli.set_return_value((selection, index))
219245

220246
buffers = {}
221247

@@ -228,11 +254,13 @@ def return_selection(cli, buf):
228254

229255
# Optionally show meta information if present
230256
if options_meta is not None:
257+
assert len(options) == len(options_meta)
231258
info_buf = Buffer(is_multiline=True)
232259
buffers['INFO'] = info_buf
233260

234261
def selection_changed(cli):
235-
info = options_meta[buffers[DEFAULT_BUFFER].text]
262+
index = self.menu_control.get_index()
263+
info = options_meta[index]
236264
formatted_info = json.dumps(info, indent=4, sort_keys=True,
237265
ensure_ascii=False)
238266
buffers['INFO'].text = formatted_info
@@ -277,7 +305,22 @@ def _(_):
277305

278306

279307
def select_prompt(message, options, *args, **kwargs):
280-
"""Construct and run the select menu application, returning the result."""
308+
"""Construct and run the select menu application, returning the result.
309+
310+
:type message: str
311+
:param message: The message to be used when showing the prompt.
312+
313+
:type options: list of str
314+
:param options: The options to be displayed in the drop down list.
315+
316+
:type options_meta: list of dict
317+
:param options_meta: (Optional) List of detailed objects for each option in
318+
the list. This list is parallel to options and must equal in length.
319+
320+
:rtype: tuple of (str, int)
321+
:return: The tuple containing the selected option, and its index in the
322+
list of options.
323+
"""
281324
runner = kwargs.pop('runner', run_application)
282325
app = SelectMenuApplication(message, options, *args, **kwargs)
283326
return runner(app)

tests/integration/test_selectmenu.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def test_select_menu_application_accept(cli):
8888
# move selection down then accept
8989
feed_key(cli, Keys.Down)
9090
feed_key(cli, Keys.ControlJ)
91-
assert cli.return_value() == '1'
91+
assert cli.return_value() == ('1', 0)
9292

9393

9494
def test_select_menu_application_any(cli):
@@ -104,7 +104,7 @@ def test_select_menu_application_any(cli):
104104

105105
def test_select_menu_application_with_meta(pipe_input, make_cli):
106106
# test that selecting an option when theres info will render it
107-
meta = {'opt': {'key': u'val'}}
107+
meta = [{'key': u'val'}]
108108
app = SelectMenuApplication(u'prompt', [u'opt'], options_meta=meta)
109109
cli = make_cli(app=app, cli_input=pipe_input)
110110
feed_key(cli, Keys.Down)

tests/unit/test_interaction.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def test_simple_select():
7979
prompt = mock.Mock()
8080
selector = SimpleSelect({}, 'one or two?', prompt)
8181
options = ['one', 'two']
82-
prompt.return_value = options[1]
82+
prompt.return_value = (options[1], 1)
8383
xformed = selector.execute(options)
8484
assert prompt.call_count == 1
8585
assert xformed == options[1]
@@ -92,7 +92,7 @@ def test_simple_select_with_path():
9292
model = {'Path': '[].a'}
9393
simple_selector = SimpleSelect(model, 'Promptingu', prompt)
9494
options = [{'a': '1', 'b': 'one'}, {'a': '2', 'b': 'two'}]
95-
prompt.return_value = '2'
95+
prompt.return_value = ('2', 1)
9696
xformed = simple_selector.execute(options)
9797
assert prompt.call_count == 1
9898
assert xformed == options[1]

0 commit comments

Comments
 (0)