resolved RESOLVED UNSUBMITTED PASS · None tool calls · 0 s · Textualize/rich
Case insensitive prompt choices
## Type of changes
- [ ] Bug fix
- [x] New feature
- [ ] Documentation / docstrings
- [ ] Tests
- [ ] Other
## Checklist
- [x] I've run the latest [black](https://github.com/psf/black) with default args on new code.
- [x] I've updated CHANGELOG.md and CONTRIBUTORS.md where appropriate.
- [x] I've added tests for new code.
- [x] I accept that @willmcgugan may be pedantic in the code review.
## Description
Optional flag for `Prompt.ask` allowing case-insensitive responses. Just add `case_sensitive=False` to the command options:
```python
from rich.prompt import Prompt
name = Prompt.ask("Enter your name", choices=["Paul", "Jessica", "Duncan"], default="Paul", case_sensitive=False)
```
Now, it would accept "paul" or "Paul" as valid responses. Defaults to True, which is the usual result.
| # | Tool | Arguments | Result |
|---|---|---|---|
| No trace captured. | |||
--- a/rich/prompt.py
+++ b/rich/prompt.py
@@ -36,6 +36,7 @@ class PromptBase(Generic[PromptType]):
console (Console, optional): A Console instance or None to use global console. Defaults to None.
password (bool, optional): Enable password input. Defaults to False.
choices (List[str], optional): A list of valid choices. Defaults to None.
+ case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True.
show_default (bool, optional): Show default in prompt. Defaults to True.
show_choices (bool, optional): Show choices in prompt. Defaults to True.
"""
@@ -57,6 +58,7 @@ def __init__(
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
+ case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
) -> None:
@@ -69,6 +71,7 @@ def __init__(
self.password = password
if choices is not None:
self.choices = choices
+ self.case_sensitive = case_sensitive
self.show_default = show_default
self.show_choices = show_choices
@@ -81,6 +84,7 @@ def ask(
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
+ case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
default: DefaultType,
@@ -97,6 +101,7 @@ def ask(
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
+ case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
stream: Optional[TextIO] = None,
@@ -111,6 +116,7 @@ def ask(
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
+ case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
default: Any = ...,
@@ -126,6 +132,7 @@ def ask(
console (Console, optional): A Console instance or None to use global console. Defaults to None.
password (bool, optional): Enable password input. Defaults to False.
choices (List[str], optional): A list of valid choices. Defaults to None.
+ case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True.
show_default (bool, optional): Show default in prompt. Defaults to True.
show_choices (bool, optional): Show choices in prompt. Defaults to True.
stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None.
@@ -135,6 +142,7 @@ def ask(
console=console,
password=password,
choices=choices,
+ case_sensitive=case_sensitive,
show_default=show_default,
show_choices=show_choices,
)
@@ -212,7 +220,9 @@ def check_choice(self, value: str) -> bool:
bool: True if choice was valid, otherwise False.
"""
assert self.choices is not None
- return value.strip() in self.choices
+ if self.case_sensitive:
+ return value.strip() in self.choices
+ return value.strip().lower() in [choice.lower() for choice in self.choices]
def process_response(self, value: str) -> PromptType:
"""Process response from user, convert to prompt type.
@@ -232,9 +242,17 @@ def process_response(self, value: str) -> PromptType:
except ValueError:
raise InvalidResponse(self.validate_error_message)
- if self.choices is not None and not self.check_choice(value):
- raise InvalidResponse(self.illegal_choice_message)
-
+ if self.choices is not None:
+ if not self.check_choice(value):
+ raise InvalidResponse(self.illegal_choice_message)
+
+ if not self.case_sensitive:
+ # return the original choice, not the lower case version
+ return_value = self.response_type(
+ self.choices[
+ [choice.lower() for choice in self.choices].index(value.lower())
+ ]
+ )
return return_value
def on_validate_error(self, value: str, error: InvalidResponse) -> None:
@@ -346,7 +364,6 @@ def process_response(self, value: str) -> bool:
if __name__ == "__main__": # pragma: no cover
-
from rich import print
if Confirm.ask("Run [i]prompt[/i] tests?", default=True):
@@ -372,5 +389,12 @@ def process_response(self, value: str) -> bool:
fruit = Prompt.ask("Enter a fruit", choices=["apple", "orange", "pear"])
print(f"fruit={fruit!r}")
+ doggie = Prompt.ask(
+ "What's the best Dog? (Case INSENSITIVE)",
+ choices=["Border Terrier", "Collie", "Labradoodle"],
+ case_sensitive=False,
+ )
+ print(f"doggie={doggie!r}")
+
else:
print("[b]OK :loudly_crying_face:")....... [100%] 7 passed in 0.10s