Skip to content

Commit

Permalink
Change Choice.shortcut_key to property with setter (#349)
Browse files Browse the repository at this point in the history
* Make `Choice.shortcut_key` property with setter

Ensures that `Choice.auto_shortcut` is set to `False`
whenever `Choice.shortcut_key` is set to a `str` value
and vice versa. Add tests for fix.

Fixes #340

Signed-off-by: Ross Williams <[email protected]>

* Make Choice.auto_shortcut a property attribute

`auto_shortcut` and `shortcut_key` interact in ways that requires logic
when setting either, to ensure that they do not conflict.

Signed-off-by: Ross Williams <[email protected]>

* Update questionary/prompts/common.py

---------

Signed-off-by: Ross Williams <[email protected]>
  • Loading branch information
overhacked authored Jul 23, 2024
1 parent c64e564 commit f6761e2
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 13 deletions.
58 changes: 45 additions & 13 deletions questionary/prompts/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ class Choice:
checked: Optional[bool]
"""Whether the choice is initially selected"""

shortcut_key: Optional[str]
"""A shortcut key for the choice"""
__shortcut_key: Optional[Union[str, bool]]

description: Optional[str]
"""Choice description"""
Expand All @@ -86,6 +85,8 @@ def __init__(
) -> None:
self.disabled = disabled
self.title = title
self.shortcut_key = shortcut_key
# self.auto_shortcut is set by the self.shortcut_key setter
self.checked = checked if checked is not None else False
self.description = description

Expand All @@ -96,17 +97,6 @@ def __init__(
else:
self.value = title

if shortcut_key is not None:
if isinstance(shortcut_key, bool):
self.auto_shortcut = shortcut_key
self.shortcut_key = None
else:
self.shortcut_key = str(shortcut_key)
self.auto_shortcut = False
else:
self.shortcut_key = None
self.auto_shortcut = True

@staticmethod
def build(c: Union[str, "Choice", Dict[str, Any]]) -> "Choice":
"""Create a choice object from different representations.
Expand Down Expand Up @@ -134,12 +124,54 @@ def build(c: Union[str, "Choice", Dict[str, Any]]) -> "Choice":
c.get("description", None),
)

@property
def shortcut_key(self) -> Optional[Union[str, bool]]:
"""A shortcut key for the choice"""
return self.__shortcut_key

@shortcut_key.setter
def shortcut_key(self, key: Optional[Union[str, bool]]):
if key is not None:
if isinstance(key, bool):
self.__auto_shortcut = key
self.__shortcut_key = None
else:
self.__shortcut_key = str(key)
self.__auto_shortcut = False
else:
self.__shortcut_key = None
self.__auto_shortcut = True

@shortcut_key.deleter
def shortcut_key(self):
self.__shortcut_key = None
self.__auto_shortcut = True

def get_shortcut_title(self):
if self.shortcut_key is None:
return "-) "
else:
return "{}) ".format(self.shortcut_key)

@property
def auto_shortcut(self) -> bool:
"""Whether to assign a shortcut key to the choice
Keys are assigned starting with numbers and proceeding
through the ASCII alphabet.
"""
return self.__auto_shortcut

@auto_shortcut.setter
def auto_shortcut(self, should_assign: bool):
self.__auto_shortcut = should_assign
if self.__auto_shortcut:
self.__shortcut_key = None

@auto_shortcut.deleter
def auto_shortcut(self):
self.__auto_shortcut = False


class Separator(Choice):
"""Used to space/separate choices group."""
Expand Down
25 changes: 25 additions & 0 deletions tests/prompts/test_select.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from copy import copy

import pytest

from questionary import Choice
Expand Down Expand Up @@ -199,6 +201,29 @@ def test_allow_shortcut_key_with_True():
assert result == "bazz"


def test_auto_shortcut_key_stable_in_loop():
message = "Foo message"
choices = [
Choice("foo"),
Choice("bar"),
]
kwargs = {
"choices": choices,
"use_shortcuts": True,
}
text = "\r"

result, cli = feed_cli_with_input("select", message, text, **kwargs)
assert result == "foo"
result_shortcut_keys = [copy(c.shortcut_key) for c in choices]
result2, cli = feed_cli_with_input("select", message, text, **kwargs)
assert result2 == "foo"
result2_shortcut_keys = [copy(c.shortcut_key) for c in choices]
assert (
result_shortcut_keys == result2_shortcut_keys
), "Shortcut keys changed across two runs of 'select'"


def test_select_initial_choice_with_value():
message = "Foo message"
choice = Choice(title="bazz", value="bar")
Expand Down

0 comments on commit f6761e2

Please sign in to comment.