← pod_r3_v1g

rich_3052

failed WRONG_FIX wrong_fix · 12 tool calls · 116 s · Textualize/rich

Task input

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 calls (13)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"Prompt.ask\" rich/prompt.py"
}
{"status": "ok", "stdout": "rich/prompt.py:122: >>> filename = Prompt.ask(\"Enter a filename\")\nrich/prompt.py:290: >>> name = Prompt.ask(\"Enter your name\")\
full
{"status": "ok", "stdout": "rich/prompt.py:122:            >>> filename = Prompt.ask(\"Enter a filename\")\nrich/prompt.py:290:        >>> name = Prompt.ask(\"Enter your name\")\nrich/prompt.py:302:        >>> burrito_count = IntPrompt.ask(\"How many burritos do you want to order\")\nrich/prompt.py:314:        >>> temperature = FloatPrompt.ask(\"Enter desired temperature\")\nrich/prompt.py:354:            result = IntPrompt.ask(\nrich/prompt.py:363:            password = Prompt.ask(\nrich/prompt.py:372:        fruit = Prompt.ask(\"Enter a fruit\", choices=[\"apple\", \"orange\", \"pear\"])\n", "stderr": "", "exit_code": 0}
2run_command
{
 "command": "sed -n '1,300p' rich/prompt.py"
}
{"status": "ok", "stdout": "from typing import Any, Generic, List, Optional, TextIO, TypeVar, Union, overload\n\nfrom . import get_console\nfrom .console import
full
{"status": "ok", "stdout": "from typing import Any, Generic, List, Optional, TextIO, TypeVar, Union, overload\n\nfrom . import get_console\nfrom .console import Console\nfrom .text import Text, TextType\n\nPromptType = TypeVar(\"PromptType\")\nDefaultType = TypeVar(\"DefaultType\")\n\n\nclass PromptError(Exception):\n    \"\"\"Exception base class for prompt related errors.\"\"\"\n\n\nclass InvalidResponse(PromptError):\n    \"\"\"Exception to indicate a response was invalid. Raise this within process_response() to indicate an error\n    and provide an error message.\n\n    Args:\n        message (Union[str, Text]): Error message.\n    \"\"\"\n\n    def __init__(self, message: TextType) -> None:\n        self.message = message\n\n    def __rich__(self) -> TextType:\n        return self.message\n\n\nclass PromptBase(Generic[PromptType]):\n    \"\"\"Ask the user for input until a valid response is received. This is the base class, see one of\n    the concrete classes for examples.\n\n    Args:\n        prompt (TextType, optional): Prompt text. Defaults to \"\".\n        console (Console, optional): A Console instance or None to use global console. Defaults to None.\n        password (bool, optional): Enable password input. Defaults to False.\n        choices (List[str], optional): A list of valid choices. Defaults to None.\n        show_default (bool, optional): Show default in prompt. Defaults to True.\n        show_choices (bool, optional): Show choices in prompt. Defaults to True.\n    \"\"\"\n\n    response_type: type = str\n\n    validate_error_message = \"[prompt.invalid]Please enter a valid value\"\n    illegal_choice_message = (\n        \"[prompt.invalid.choice]Please select one of the available options\"\n    )\n    prompt_suffix = \": \"\n\n    choices: Optional[List[str]] = None\n\n    def __init__(\n        self,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Opt
3run_command
{
 "command": "sed -n '301,400p' rich/prompt.py"
}
{"status": "ok", "stdout": " Example:\n >>> burrito_count = IntPrompt.ask(\"How many burritos do you want to order\")\n\n \"\"\"\n\n response_type = int\n valid
full
{"status": "ok", "stdout": "    Example:\n        >>> burrito_count = IntPrompt.ask(\"How many burritos do you want to order\")\n\n    \"\"\"\n\n    response_type = int\n    validate_error_message = \"[prompt.invalid]Please enter a valid integer number\"\n\n\nclass FloatPrompt(PromptBase[int]):\n    \"\"\"A prompt that returns a float.\n\n    Example:\n        >>> temperature = FloatPrompt.ask(\"Enter desired temperature\")\n\n    \"\"\"\n\n    response_type = float\n    validate_error_message = \"[prompt.invalid]Please enter a number\"\n\n\nclass Confirm(PromptBase[bool]):\n    \"\"\"A yes / no confirmation prompt.\n\n    Example:\n        >>> if Confirm.ask(\"Continue\"):\n                run_job()\n\n    \"\"\"\n\n    response_type = bool\n    validate_error_message = \"[prompt.invalid]Please enter Y or N\"\n    choices: List[str] = [\"y\", \"n\"]\n\n    def render_default(self, default: DefaultType) -> Text:\n        \"\"\"Render the default as (y) or (n) rather than True/False.\"\"\"\n        yes, no = self.choices\n        return Text(f\"({yes})\" if default else f\"({no})\", style=\"prompt.default\")\n\n    def process_response(self, value: str) -> bool:\n        \"\"\"Convert choices to a bool.\"\"\"\n        value = value.strip().lower()\n        if value not in self.choices:\n            raise InvalidResponse(self.validate_error_message)\n        return value == self.choices[0]\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n\n    from rich import print\n\n    if Confirm.ask(\"Run [i]prompt[/i] tests?\", default=True):\n        while True:\n            result = IntPrompt.ask(\n                \":rocket: Enter a number between [b]1[/b] and [b]10[/b]\", default=5\n            )\n            if result >= 1 and result <= 10:\n                break\n            print(\":pile_of_poo: [prompt.invalid]Number must be between 1 and 10\")\n        print(f\"number={result}\")\n\n        while True:\n            password = Prompt.ask(\n                \"Please e
4run_command
{
 "command": "sed -n '100,200p' rich/prompt.py"
}
{"status": "ok", "stdout": " show_default: bool = True,\n show_choices: bool = True,\n stream: Optional[TextIO] = None,\n ) -> PromptType:\n ...\n\n @classmetho
full
{"status": "ok", "stdout": "        show_default: bool = True,\n        show_choices: bool = True,\n        stream: Optional[TextIO] = None,\n    ) -> PromptType:\n        ...\n\n    @classmethod\n    def ask(\n        cls,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Optional[List[str]] = None,\n        show_default: bool = True,\n        show_choices: bool = True,\n        default: Any = ...,\n        stream: Optional[TextIO] = None,\n    ) -> Any:\n        \"\"\"Shortcut to construct and run a prompt loop and return the result.\n\n        Example:\n            >>> filename = Prompt.ask(\"Enter a filename\")\n\n        Args:\n            prompt (TextType, optional): Prompt text. Defaults to \"\".\n            console (Console, optional): A Console instance or None to use global console. Defaults to None.\n            password (bool, optional): Enable password input. Defaults to False.\n            choices (List[str], optional): A list of valid choices. Defaults to None.\n            show_default (bool, optional): Show default in prompt. Defaults to True.\n            show_choices (bool, optional): Show choices in prompt. Defaults to True.\n            stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None.\n        \"\"\"\n        _prompt = cls(\n            prompt,\n            console=console,\n            password=password,\n            choices=choices,\n            show_default=show_default,\n            show_choices=show_choices,\n        )\n        return _prompt(default=default, stream=stream)\n\n    def render_default(self, default: DefaultType) -> Text:\n        \"\"\"Turn the supplied default in to a Text instance.\n\n        Args:\n            default (DefaultType): Default value.\n\n        Returns:\n            Text: Text containing rendering of default value.\n        \"\"\"\n        return Text(f\"({default})\", \"pr
5run_command
{
 "command": "sed -n '201,300p' rich/prompt.py"
}
{"status": "ok", "stdout": " str: String from user.\n \"\"\"\n return console.input(prompt, password=password, stream=stream)\n\n def check_choice(self, value:
full
{"status": "ok", "stdout": "            str: String from user.\n        \"\"\"\n        return console.input(prompt, password=password, stream=stream)\n\n    def check_choice(self, value: str) -> bool:\n        \"\"\"Check value is in the list of valid choices.\n\n        Args:\n            value (str): Value entered by user.\n\n        Returns:\n            bool: True if choice was valid, otherwise False.\n        \"\"\"\n        assert self.choices is not None\n        return value.strip() in self.choices\n\n    def process_response(self, value: str) -> PromptType:\n        \"\"\"Process response from user, convert to prompt type.\n\n        Args:\n            value (str): String typed by user.\n\n        Raises:\n            InvalidResponse: If ``value`` is invalid.\n\n        Returns:\n            PromptType: The value to be returned from ask method.\n        \"\"\"\n        value = value.strip()\n        try:\n            return_value: PromptType = self.response_type(value)\n        except ValueError:\n            raise InvalidResponse(self.validate_error_message)\n\n        if self.choices is not None and not self.check_choice(value):\n            raise InvalidResponse(self.illegal_choice_message)\n\n        return return_value\n\n    def on_validate_error(self, value: str, error: InvalidResponse) -> None:\n        \"\"\"Called to handle validation error.\n\n        Args:\n            value (str): String entered by user.\n            error (InvalidResponse): Exception instance the initiated the error.\n        \"\"\"\n        self.console.print(error)\n\n    def pre_prompt(self) -> None:\n        \"\"\"Hook to display something before the prompt.\"\"\"\n\n    @overload\n    def __call__(self, *, stream: Optional[TextIO] = None) -> PromptType:\n        ...\n\n    @overload\n    def __call__(\n        self, *, default: DefaultType, stream: Optional[TextIO] = None\n    ) -> Union[PromptType, DefaultType]:\n        ...\n\n    def __call__(self, *, default: Any = .
6run_command
{
 "command": "python3 - <<'PY'\nfrom rich.prompt import Prompt\nfrom unittest.mock import MagicMock\n\ndef test_case_sensitive_default():\n    # Mock console.input to return \"paul\"\n    console = MagicMock()\n    console.input.return_value = \"paul\"\n    \n    # Default case_sensitive should be True, so \"paul\" should not be in [\"Paul\", \"Jessica\", \"Duncan\"]\n    # But since we can't easily run the loop, we test check_choice\n    prompt = Prompt(choices=[\"Paul\", \"Jessica\", \"Duncan\"], console=console)\n    assert prompt.check_choice(\"paul\") is False\n    assert prompt.check_choice(\"Paul\") is True\n\ndef test_case_insensitive():\n    # Mock console.input to return \"paul\"\n    console = MagicMock()\n    console.input.return_value = \"paul\"\n    \n    # We need to implement case_sensitive=False in PromptBase first.\n    # For now, this test will fail because PromptBase doesn't have case_sensitive.\n    try:\n        prompt = Prompt(choices=[\"Paul\", \"Jessica\", \"Duncan\"], console=console, case_sensitive=False)\n        print(\"Prompt accepted case_sensitive=False\")\n    except TypeError as e:\n        print(f\"Prompt did not accept case_sensitive: {e}\")\n\nif __name__ == \"__main__\":\n    test_case_sensitive_default()\n    test_case_insensitive()\nPY\n"
}
{"status": "ok", "stdout": "Prompt did not accept case_sensitive: PromptBase.__init__() got an unexpected keyword argument 'case_sensitive'\n", "stderr": "", "e
full
{"status": "ok", "stdout": "Prompt did not accept case_sensitive: PromptBase.__init__() got an unexpected keyword argument 'case_sensitive'\n", "stderr": "", "exit_code": 0}
7run_command
{
 "command": "python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"rich/prompt.py\")\ns = p.read_text()\n\n# Update PromptBase.__init__ signature and docstring\nold_init_doc = \"\"\"        choices (List[str], optional): A list of valid choices. Defaults to None.\n        show_default (bool, optional): Show default in prompt. Defaults to True.\n        show_choices (bool, optional): Show choices in prompt. Defaults to True.\"\"\"\nnew_init_doc = \"\"\"        choices (List[str], optional): A list of valid choices. Defaults to None.\n        case_sensitive (bool, optional): Whether choices are case sensitive. Defaults to True.\n        show_default (bool, optional): Show default in prompt. Defaults to True.\n        show_choices (bool, optional): Show choices in prompt. Defaults to True.\"\"\"\n\nold_init_sig = \"\"\"    def __init__(\n        self,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Optional[List[str]] = None,\n        show_default: bool = True,\n        show_choices: bool = True,\n    ) -> None:\"\"\"\nnew_init_sig = \"\"\"    def __init__(\n        self,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Optional[List[str]] = None,\n        case_sensitive: bool = True,\n        show_default: bool = True,\n        show_choices: bool = True,\n    ) -> None:\"\"\"\n\n# Update PromptBase.__init__ body\nold_init_body = \"\"\"        if choices is not None:\n            self.choices = choices\n        self.show_default = show_default\"\"\"\nnew_init_body = \"\"\"        if choices is not None:\n            self.choices = choices\n        self.case_sensitive = case_sensitive\n        self.show_default = show_default\"\"\"\n\ns = s.replace(old_init_doc, new_init_doc)\ns = s.replace(old_init_sig, new_init_sig)\ns = s.replace(old_init_body, new_init_body)\n\n# Update PromptBase.ask overloads and implementation\n# Overload 1\nold_overload1 = \"\"\"    @classmethod\n    @overload\n    def ask(\n        cls,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Optional[List[str]] = None,\n        show_default: bool = True,\n        show_choices: bool = True,\n        default: DefaultType,\n        stream: Optional[TextIO] = None,\n    ) -> Union[DefaultType, PromptType]:\"\"\"\nnew_overload1 = \"\"\"    @classmethod\n    @overload\n    def ask(\n        cls,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Optional[List[str]] = None,\n        case_sensitive: bool = True,\n        show_default: bool = True,\n        show_choices: bool = True,\n        default: DefaultType,\n        stream: Optional[TextIO] = None,\n    ) -> Union[DefaultType, PromptType]:\"\"\"\n\n# Overload 2\nold_overload2 = \"\"\"    @classmethod\n    @overload\n    def ask(\n        cls,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Optional[List[str]] = None,\n        show_default: bool = True,\n        show_choices: bool = True,\n        stream: Optional[TextIO] = None,\n    ) -> PromptType:\"\"\"\nnew_overload2 = \"\"\"    @classmethod\n    @overload\n    def ask(\n        cls,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n        password: bool = False,\n        choices: Optional[List[str]] = None,\n        case_sensitive: bool = True,\n        show_default: bool = True,\n        show_choices: bool = True,\n        stream: Optional[TextIO] = None,\n    ) -> PromptType:\"\"\"\n\n# ask implementation\nold_ask_sig = \"\"\"    @classmethod\n    def ask(\n        cls,\n        prompt: TextType = \"\",\n        *,\n        console: Optional[Console] = None,\n  
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
8run_command
{
 "command": "python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"rich/prompt.py\")\ns = p.read_text()\n\n# Update check_choice method\nold_check_choice = \"\"\"    def check_choice(self, value: str) -> bool:\n        \\\"\\\"\\\"Check value is in the list of valid choices.\n\n        Args:\n            value (str): Value entered by user.\n\n        Returns:\n            bool: True if choice was valid, otherwise False.\n        \\\"\\\"\\\"\n        assert self.choices is not None\n        return value.strip() in self.choices\"\"\"\n\nnew_check_choice = \"\"\"    def check_choice(self, value: str) -> bool:\n        \\\"\\\"\\\"Check value is in the list of valid choices.\n\n        Args:\n            value (str): Value entered by user.\n\n        Returns:\n            bool: True if choice was valid, otherwise False.\n        \\\"\\\"\\\"\n        assert self.choices is not None\n        if self.case_sensitive:\n            return value.strip() in self.choices\n        return value.strip().lower() in [choice.lower() for choice in self.choices]\"\"\"\n\ns = s.replace(old_check_choice, new_check_choice)\np.write_text(s)\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
9run_command
{
 "command": "python3 - <<'PY'\nfrom rich.prompt import Prompt\nfrom unittest.mock import MagicMock\n\ndef test_case_sensitive_default():\n    console = MagicMock()\n    prompt = Prompt(choices=[\"Paul\", \"Jessica\", \"Duncan\"], console=console)\n    assert prompt.case_sensitive is True\n    assert prompt.check_choice(\"paul\") is False\n    assert prompt.check_choice(\"Paul\") is True\n\ndef test_case_insensitive():\n    console = MagicMock()\n    prompt = Prompt(choices=[\"Paul\", \"Jessica\", \"Duncan\"], console=console, case_sensitive=False)\n    assert prompt.case_sensitive is False\n    assert prompt.check_choice(\"paul\") is True\n    assert prompt.check_choice(\"Paul\") is True\n    assert prompt.check_choice(\"PAUL\") is True\n    assert prompt.check_choice(\"Jessica\") is True\n    assert prompt.check_choice(\"jessica\") is True\n\ndef test_ask_case_insensitive():\n    # Testing Prompt.ask shortcut\n    # Since Prompt.ask calls __call__, and __call__ calls process_response, \n    # and process_response calls check_choice.\n    \n    # We can't easily run the full loop without mocking input, \n    # but we can test the logic.\n    \n    # Let's test process_response directly\n    console = MagicMock()\n    prompt = Prompt(choices=[\"Paul\", \"Jessica\", \"Duncan\"], console=console, case_sensitive=False)\n    \n    # process_response for Prompt (which is PromptBase[str])\n    # should return the value if it's in choices (case-insensitively)\n    assert prompt.process_response(\"paul\") == \"paul\"\n    assert prompt.process_response(\"Paul\") == \"Paul\"\n    \n    try:\n        prompt.process_response(\"Unknown\")\n    except Exception as e:\n        # Should raise InvalidResponse\n        assert \"InvalidResponse\" in str(type(e))\n\nif __name__ == \"__main__\":\n    test_case_sensitive_default()\n    test_case_insensitive()\n    test_ask_case_insensitive()\n    print(\"All tests passed!\")\nPY\n"
}
{"status": "ok", "stdout": "All tests passed!\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "All tests passed!\n", "stderr": "", "exit_code": 0}
10run_command
{
 "command": "python3 -m pytest tests/test_prompt.py -q"
}
{"status": "ok", "stdout": "...... [100%]\n6 passed in 0.09s\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "......                                                                   [100%]\n6 passed in 0.09s\n", "stderr": "", "exit_code": 0}
11run_command
{
 "command": "git diff"
}
{"status": "ok", "stdout": "diff --git a/rich/prompt.py b/rich/prompt.py\nindex 064c959b..46bbe0e0 100644\n--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -36,6
full
{"status": "ok", "stdout": "diff --git a/rich/prompt.py b/rich/prompt.py\nindex 064c959b..46bbe0e0 100644\n--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -36,6 +36,7 @@ class PromptBase(Generic[PromptType]):\n         console (Console, optional): A Console instance or None to use global console. Defaults to None.\n         password (bool, optional): Enable password input. Defaults to False.\n         choices (List[str], optional): A list of valid choices. Defaults to None.\n+        case_sensitive (bool, optional): Whether choices are case sensitive. Defaults to True.\n         show_default (bool, optional): Show default in prompt. Defaults to True.\n         show_choices (bool, optional): Show choices in prompt. Defaults to True.\n     \"\"\"\n@@ -57,6 +58,7 @@ class PromptBase(Generic[PromptType]):\n         console: Optional[Console] = None,\n         password: bool = False,\n         choices: Optional[List[str]] = None,\n+        case_sensitive: bool = True,\n         show_default: bool = True,\n         show_choices: bool = True,\n     ) -> None:\n@@ -69,6 +71,7 @@ class PromptBase(Generic[PromptType]):\n         self.password = password\n         if choices is not None:\n             self.choices = choices\n+        self.case_sensitive = case_sensitive\n         self.show_default = show_default\n         self.show_choices = show_choices\n \n@@ -81,6 +84,7 @@ class PromptBase(Generic[PromptType]):\n         console: Optional[Console] = None,\n         password: bool = False,\n         choices: Optional[List[str]] = None,\n+        case_sensitive: bool = True,\n         show_default: bool = True,\n         show_choices: bool = True,\n         default: DefaultType,\n@@ -97,6 +101,7 @@ class PromptBase(Generic[PromptType]):\n         console: Optional[Console] = None,\n         password: bool = False,\n         choices: Optional[List[str]] = None,\n+        case_sensitive: bool = True,\n         show_default: bool = True,\n         show_choices: bool = True,\n    
12run_command
{
 "command": "submit_patch"
}
{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/bas
full
{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/bash: submit_patch: command not found\n", "exit_code": 127}}
13submit_patch
{}
{"status": "ok", "patch_size": 3803, "files_changed": 1}
full
{"status": "ok", "patch_size": 3803, "files_changed": 1}

Patch

diff --git a/rich/prompt.py b/rich/prompt.py
index 064c959b..46bbe0e0 100644
--- 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): Whether choices are 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 @@ class PromptBase(Generic[PromptType]):
         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 @@ class PromptBase(Generic[PromptType]):
         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 @@ class PromptBase(Generic[PromptType]):
         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 @@ class PromptBase(Generic[PromptType]):
         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 @@ class PromptBase(Generic[PromptType]):
         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 @@ 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): Whether choices are 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 @@ class PromptBase(Generic[PromptType]):
             console=console,
             password=password,
             choices=choices,
+            case_sensitive=case_sensitive,
             show_default=show_default,
             show_choices=show_choices,
         )
@@ -212,7 +220,9 @@ class PromptBase(Generic[PromptType]):
             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.

Test output

show
.F
=================================== FAILURES ===================================
_______________________ test_prompt_str_case_insensitive _______________________

    def test_prompt_str_case_insensitive():
        INPUT = "egg\nFoO"
        console = Console(file=io.StringIO())
        name = Prompt.ask(
            "what is your name",
            console=console,
            choices=["foo", "bar"],
            default="baz",
            case_sensitive=False,
            stream=io.StringIO(INPUT),
        )
>       assert name == "foo"
E       AssertionError: assert 'FoO' == 'foo'
E         
E         - foo
E         + FoO

tests/test_prompt.py:35: AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 passed in 0.10s