← pod_r4_v1

rich_3052

failed WRONG_FIX wrong_fix · 17 tool calls · 197 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 (21)

#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}
2read_file
{
 "filepath": "rich/prompt.py"
}
{"status": "ok", "filepath": "rich/prompt.py", "content": "from typing import Any, Generic, List, Optional, TextIO, TypeVar, Union, overload\n\nfrom . import ge
full
{"status": "ok", "filepath": "rich/prompt.py", "content": "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
3read_file
{
 "filepath": "rich/prompt.py",
 "start_line": 151
}
{"status": "ok", "filepath": "rich/prompt.py", "content": " \"\"\"\n return Text(f\"({default})\", \"prompt.default\")\n\n def make_prompt(self, default: Defaul
full
{"status": "ok", "filepath": "rich/prompt.py", "content": "        \"\"\"\n        return Text(f\"({default})\", \"prompt.default\")\n\n    def make_prompt(self, default: DefaultType) -> Text:\n        \"\"\"Make prompt text.\n\n        Args:\n            default (DefaultType): Default value.\n\n        Returns:\n            Text: Text to display in prompt.\n        \"\"\"\n        prompt = self.prompt.copy()\n        prompt.end = \"\"\n\n        if self.show_choices and self.choices:\n            _choices = \"/\".join(self.choices)\n            choices = f\"[{_choices}]\"\n            prompt.append(\" \")\n            prompt.append(choices, \"prompt.choices\")\n\n        if (\n            default != ...\n            and self.show_default\n            and isinstance(default, (str, self.response_type))\n        ):\n            prompt.append(\" \")\n            _default = self.render_default(default)\n            prompt.append(_default)\n\n        prompt.append(self.prompt_suffix)\n\n        return prompt\n\n    @classmethod\n    def get_input(\n        cls,\n        console: Console,\n        prompt: TextType,\n        password: bool,\n        stream: Optional[TextIO] = None,\n    ) -> str:\n        \"\"\"Get input from user.\n\n        Args:\n            console (Console): Console instance.\n            prompt (TextType): Prompt text.\n            password (bool): Enable password entry.\n\n        Returns:\n            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 
4read_file
{
 "filepath": "rich/prompt.py",
 "start_line": 301
}
{"status": "ok", "filepath": "rich/prompt.py", "content": " Example:\n >>> burrito_count = IntPrompt.ask(\"How many burritos do you want to order\")\n\n \"\"\"\
full
{"status": "ok", "filepath": "rich/prompt.py", "content": "    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.a
5write_file
{
 "content": "from rich.prompt import Prompt\n\ndef test_prompt_case_sensitive():\n    # This is a mock test, we can't easily run it because it requires user input.\n    # But we can test the logic by subclassing or mocking.\n    pass\n\ndef test_prompt_case_insensitive():\n    pass\n",
 "filepath": "/tmp/test_prompt_case.py"
}
{"status": "error", "error_type": "FileWriteError", "error_message": "Path traversal detected: '/tmp/test_prompt_case.py' escapes workspace root."}
full
{"status": "error", "error_type": "FileWriteError", "error_message": "Path traversal detected: '/tmp/test_prompt_case.py' escapes workspace root."}
6run_command
{
 "command": "grep -rn \"test_prompt\" tests/"
}
{"status": "ok", "stdout": "tests/test_prompt.py:7:def test_prompt_str():\ntests/test_prompt.py:24:def test_prompt_str_default():\ntests/test_prompt.py:40:def t
full
{"status": "ok", "stdout": "tests/test_prompt.py:7:def test_prompt_str():\ntests/test_prompt.py:24:def test_prompt_str_default():\ntests/test_prompt.py:40:def test_prompt_int():\ntests/test_prompt.py:55:def test_prompt_confirm_no():\ntests/test_prompt.py:70:def test_prompt_confirm_yes():\ntests/test_prompt.py:85:def test_prompt_confirm_default():\n", "stderr": "", "exit_code": 0}
7read_file
{
 "filepath": "tests/test_prompt.py"
}
{"status": "ok", "filepath": "tests/test_prompt.py", "content": "import io\n\nfrom rich.console import Console\nfrom rich.prompt import Prompt, IntPrompt, Confi
full
{"status": "ok", "filepath": "tests/test_prompt.py", "content": "import io\n\nfrom rich.console import Console\nfrom rich.prompt import Prompt, IntPrompt, Confirm\n\n\ndef test_prompt_str():\n    INPUT = \"egg\\nfoo\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        stream=io.StringIO(INPUT),\n    )\n    assert name == \"foo\"\n    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    print(repr(output))\n    assert output == expected\n\n\ndef test_prompt_str_default():\n    INPUT = \"\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        default=\"Will\",\n        stream=io.StringIO(INPUT),\n    )\n    assert name == \"Will\"\n    expected = \"what is your name (Will): \"\n    output = console.file.getvalue()\n    print(repr(output))\n    assert output == expected\n\n\ndef test_prompt_int():\n    INPUT = \"foo\\n100\"\n    console = Console(file=io.StringIO())\n    number = IntPrompt.ask(\n        \"Enter a number\",\n        console=console,\n        stream=io.StringIO(INPUT),\n    )\n    assert number == 100\n    expected = \"Enter a number: Please enter a valid integer number\\nEnter a number: \"\n    output = console.file.getvalue()\n    print(repr(output))\n    assert output == expected\n\n\ndef test_prompt_confirm_no():\n    INPUT = \"foo\\nNO\\nn\"\n    console = Console(file=io.StringIO())\n    answer = Confirm.ask(\n        \"continue\",\n        console=console,\n        stream=io.StringIO(INPUT),\n    )\n    assert answer is False\n    expected = \"continue [y/n]: Please enter Y or N\\ncontinue [y/n]: Please enter Y or N\\ncontinue [y/n]: \"\n    output = console.file.getvalue()\n    print(repr(output))\n    as
8edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "    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        case_sensitive (bool, optional): Whether choices are case-sensitive. Defaults to True.\n    \"\"\",old_string:",
 "Args": "prompt (TextType",
 "optional)": "Show choices in prompt. Defaults to True.\n    \"\"\"",
 "], optional)": "A list of valid choices. Defaults to None.\n        show_default (bool"
}
{'error': 'Invoking `edit_file()` failed as the following mandatory input parameters are not present:\nold_string\nYou could retry calling this tool, but it is
full
{'error': 'Invoking `edit_file()` failed as the following mandatory input parameters are not present:\nold_string\nYou could retry calling this tool, but it is IMPORTANT for you to provide all the mandatory parameters.'}
9-10 ×2edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "    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        case_sensitive (bool, optional): Whether choices are case-sensitive. Defaults to True.\n    \"\"\",old_string:",
 "Args": "prompt (TextType",
 "optional)": "Show choices in prompt. Defaults to True.\n    \"\"\"<|\"|>",
 "], optional)": "A list of valid choices. Defaults to None.\n        show_default (bool"
}
{'error': 'Invoking `edit_file()` failed as the following mandatory input parameters are not present:\nold_string\nYou could retry calling this tool, but it is
full
{'error': 'Invoking `edit_file()` failed as the following mandatory input parameters are not present:\nold_string\nYou could retry calling this tool, but it is IMPORTANT for you to provide all the mandatory parameters.'}
11edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "        show_choices (bool, optional): Show choices in prompt. Defaults to True.\n        case_sensitive (bool, optional): Whether choices are case-sensitive. Defaults to True.\n    \"\"\"",
 "old_string": "        show_choices (bool, optional): Show choices in prompt. Defaults to True.\n    \"\"\""
}
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -38,6 +38,7 @@\n c
full
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -38,6 +38,7 @@\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+        case_sensitive (bool, optional): Whether choices are case-sensitive. Defaults to True.\n     \"\"\"\n \n     response_type: type = str\n", "is_truncated": false}
12edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "    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        case_sensitive: bool = True,\n    ) -> None:\n        self.console = console or get_console()\n        self.prompt = (\n            Text.from_markup(prompt, style=\"prompt\")\n            if isinstance(prompt, str)\n            else prompt\n        )\n        self.password = password\n        if choices is not None:\n            self.choices = choices\n        self.show_default = show_default\n        self.show_choices = show_choices\n        self.case_sensitive = case_sensitive",
 "old_string": "    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:\n        self.console = console or get_console()\n        self.prompt = (\n            Text.from_markup(prompt, style=\"prompt\")\n            if isinstance(prompt, str)\n            else prompt\n        )\n        self.password = password\n        if choices is not None:\n            self.choices = choices\n        self.show_default = show_default\n        self.show_choices = show_choices"
}
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -60,6 +60,7 @@\n c
full
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -60,6 +60,7 @@\n         choices: Optional[List[str]] = None,\n         show_default: bool = True,\n         show_choices: bool = True,\n+        case_sensitive: bool = True,\n     ) -> None:\n         self.console = console or get_console()\n         self.prompt = (\n@@ -72,6 +73,7 @@\n             self.choices = choices\n         self.show_default = show_default\n         self.show_choices = show_choices\n+        self.case_sensitive = case_sensitive\n \n     @classmethod\n     @overload\n", "is_truncated": false}
13edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "    @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        case_sensitive: bool = True,\n        default: DefaultType,\n        stream: Optional[TextIO] = None,\n    ) -> Union[DefaultType, PromptType]:\n        ...",
 "old_string": "    @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]:\n        ..."
}
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -86,6 +86,7 @@\n c
full
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -86,6 +86,7 @@\n         choices: Optional[List[str]] = None,\n         show_default: bool = True,\n         show_choices: bool = True,\n+        case_sensitive: bool = True,\n         default: DefaultType,\n         stream: Optional[TextIO] = None,\n     ) -> Union[DefaultType, PromptType]:\n", "is_truncated": false}
14edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "    @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        case_sensitive: bool = True,\n        stream: Optional[TextIO] = None,\n    ) -> PromptType:\n        ...",
 "old_string": "    @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:\n        ..."
}
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -103,6 +103,7 @@\n
full
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -103,6 +103,7 @@\n         choices: Optional[List[str]] = None,\n         show_default: bool = True,\n         show_choices: bool = True,\n+        case_sensitive: bool = True,\n         stream: Optional[TextIO] = None,\n     ) -> PromptType:\n         ...\n", "is_truncated": false}
15edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "    @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        case_sensitive: 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            case_sensitive (bool, optional): Whether choices are case-sensitive. 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            case_sensitive=case_sensitive,\n        )\n        return _prompt(default=default, stream=stream)",
 "old_string": "    @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)"
}
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -118,6 +118,7 @@\n
full
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -118,6 +118,7 @@\n         choices: Optional[List[str]] = None,\n         show_default: bool = True,\n         show_choices: bool = True,\n+        case_sensitive: bool = True,\n         default: Any = ...,\n         stream: Optional[TextIO] = None,\n     ) -> Any:\n@@ -133,6 +134,7 @@\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+            case_sensitive (bool, optional): Whether choices are case-sensitive. Defaults to True.\n             stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None.\n         \"\"\"\n         _prompt = cls(\n@@ -142,6 +144,7 @@\n             choices=choices,\n             show_default=show_default,\n             show_choices=show_choices,\n+            case_sensitive=case_sensitive,\n         )\n         return _prompt(default=default, stream=stream)\n \n", "is_truncated": false}
16edit_file
{
 "filepath": "rich/prompt.py",
 "new_string": "    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]",
 "old_string": "    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"
}
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -220,7 +220,9 @@\n
full
{"status": "ok", "filepath": "rich/prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -220,7 +220,9 @@\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+        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 \n     def process_response(self, value: str) -> PromptType:\n         \"\"\"Process response from user, convert to prompt type.\n", "is_truncated": false}
17edit_file
{
 "filepath": "tests/test_prompt.py",
 "new_string": "def test_prompt_str_case_insensitive():\n    INPUT = \"EGG\\nfoo\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        case_sensitive=False,\n        stream=io.StringIO(INPUT),\n    )\n    assert name == \"foo\"\n    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    assert output == expected\n\ndef test_prompt_str_case_sensitive():\n    INPUT = \"EGG\\nfoo\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        case_sensitive=True,\n        stream=io.StringIO(INPUT),\n    )\n    # This should fail and ask again, then we provide 'foo'\n    # Wait, the INPUT is \"EGG\\nfoo\". \n    # If case_sensitive=True, \"EGG\" is invalid, \"foo\" is valid.\n    assert name == \"foo\"\n    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    assert output == expected\n",
 "old_string": "def test_prompt_str():\n    INPUT = \"egg\\nfoo\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        stream=io.StringIO(INPUT),\n    )\n    assert name == \"foo\"\n    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    print(repr(output))\n    assert output == expected\n"
}
{"status": "ok", "filepath": "tests/test_prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/tests/test_prompt.py\n+++ b/tests/test_prompt.py\n@@
full
{"status": "ok", "filepath": "tests/test_prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/tests/test_prompt.py\n+++ b/tests/test_prompt.py\n@@ -4,20 +4,39 @@\n from rich.prompt import Prompt, IntPrompt, Confirm\n \n \n-def test_prompt_str():\n-    INPUT = \"egg\\nfoo\"\n+def test_prompt_str_case_insensitive():\n+    INPUT = \"EGG\\nfoo\"\n     console = Console(file=io.StringIO())\n     name = Prompt.ask(\n         \"what is your name\",\n         console=console,\n         choices=[\"foo\", \"bar\"],\n         default=\"baz\",\n+        case_sensitive=False,\n         stream=io.StringIO(INPUT),\n     )\n     assert name == \"foo\"\n     expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n     output = console.file.getvalue()\n-    print(repr(output))\n+    assert output == expected\n+\n+def test_prompt_str_case_sensitive():\n+    INPUT = \"EGG\\nfoo\"\n+    console = Console(file=io.StringIO())\n+    name = Prompt.ask(\n+        \"what is your name\",\n+        console=console,\n+        choices=[\"foo\", \"bar\"],\n+        default=\"baz\",\n+        case_sensitive=True,\n+        stream=io.StringIO(INPUT),\n+    )\n+    # This should fail and ask again, then we provide 'foo'\n+    # Wait, the INPUT is \"EGG\\nfoo\". \n+    # If case_sensitive=True, \"EGG\" is invalid, \"foo\" is valid.\n+    assert name == \"foo\"\n+    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n+    output = console.file.getvalue()\n     assert output == expected\n \n \n", "is_truncated": false}
18run_command
{
 "command": "python -m pytest tests/test_prompt.py"
}
{"status": "ok", "stdout": "============================= test session starts ==============================\nplatform darwin -- Python 3.13.15, pytest-8.3.4, p
full
{"status": "ok", "stdout": "============================= test session starts ==============================\nplatform darwin -- Python 3.13.15, pytest-8.3.4, pluggy-1.6.0\nrootdir: /private/workspace/tests\nconfigfile: pytest.ini\ncollected 7 items\n\ntests/test_prompt.py .......                                             [100%]\n\n============================== 7 passed in 0.11s ===============================\n", "stderr": "", "exit_code": 0}
19edit_file
{
 "filepath": "tests/test_prompt.py",
 "new_string": "def test_prompt_str_case_insensitive():\n    INPUT = \"FOO\\n\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        case_sensitive=False,\n        stream=io.StringIO(INPUT),\n    )\n    assert name == \"FOO\"\n    expected = \"what is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    assert output == expected\n\ndef test_prompt_str_case_sensitive():\n    INPUT = \"FOO\\nfoo\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        case_sensitive=True,\n        stream=io.StringIO(INPUT),\n    )\n    assert name == \"foo\"\n    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    assert output == expected\n",
 "old_string": "def test_prompt_str_case_insensitive():\n    INPUT = \"EGG\\nfoo\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        case_sensitive=False,\n        stream=io.StringIO(INPUT),\n    )\n    assert name == \"foo\"\n    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    assert output == expected\n\ndef test_prompt_str_case_sensitive():\n    INPUT = \"EGG\\nfoo\"\n    console = Console(file=io.StringIO())\n    name = Prompt.ask(\n        \"what is your name\",\n        console=console,\n        choices=[\"foo\", \"bar\"],\n        default=\"baz\",\n        case_sensitive=True,\n        stream=io.StringIO(INPUT),\n    )\n    # This should fail and ask again, then we provide 'foo'\n    # Wait, the INPUT is \"EGG\\nfoo\". \n    # If case_sensitive=True, \"EGG\" is invalid, \"foo\" is valid.\n    assert name == \"foo\"\n    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n    output = console.file.getvalue()\n    assert output == expected\n"
}
{"status": "ok", "filepath": "tests/test_prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/tests/test_prompt.py\n+++ b/tests/test_prompt.py\n@@
full
{"status": "ok", "filepath": "tests/test_prompt.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/tests/test_prompt.py\n+++ b/tests/test_prompt.py\n@@ -5,7 +5,7 @@\n \n \n def test_prompt_str_case_insensitive():\n-    INPUT = \"EGG\\nfoo\"\n+    INPUT = \"FOO\\n\"\n     console = Console(file=io.StringIO())\n     name = Prompt.ask(\n         \"what is your name\",\n@@ -15,13 +15,13 @@\n         case_sensitive=False,\n         stream=io.StringIO(INPUT),\n     )\n-    assert name == \"foo\"\n-    expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n+    assert name == \"FOO\"\n+    expected = \"what is your name [foo/bar] (baz): \"\n     output = console.file.getvalue()\n     assert output == expected\n \n def test_prompt_str_case_sensitive():\n-    INPUT = \"EGG\\nfoo\"\n+    INPUT = \"FOO\\nfoo\"\n     console = Console(file=io.StringIO())\n     name = Prompt.ask(\n         \"what is your name\",\n@@ -31,9 +31,6 @@\n         case_sensitive=True,\n         stream=io.StringIO(INPUT),\n     )\n-    # This should fail and ask again, then we provide 'foo'\n-    # Wait, the INPUT is \"EGG\\nfoo\". \n-    # If case_sensitive=True, \"EGG\" is invalid, \"foo\" is valid.\n     assert name == \"foo\"\n     expected = \"what is your name [foo/bar] (baz): Please select one of the available options\\nwhat is your name [foo/bar] (baz): \"\n     output = console.file.getvalue()\n", "is_truncated": false}
20run_command
{
 "command": "python -m pytest tests/test_prompt.py"
}
{"status": "ok", "stdout": "============================= test session starts ==============================\nplatform darwin -- Python 3.13.15, pytest-8.3.4, p
full
{"status": "ok", "stdout": "============================= test session starts ==============================\nplatform darwin -- Python 3.13.15, pytest-8.3.4, pluggy-1.6.0\nrootdir: /private/workspace/tests\nconfigfile: pytest.ini\ncollected 7 items\n\ntests/test_prompt.py .......                                             [100%]\n\n============================== 7 passed in 0.14s ===============================\n", "stderr": "", "exit_code": 0}
21submit_patch
{}
{"status": "ok", "patch_size": 4739, "files_changed": 2}
full
{"status": "ok", "patch_size": 4739, "files_changed": 2}

Patch

diff --git a/rich/prompt.py b/rich/prompt.py
index 064c959b..ef524e1b 100644
--- a/rich/prompt.py
+++ b/rich/prompt.py
@@ -38,6 +38,7 @@ class PromptBase(Generic[PromptType]):
         choices (List[str], optional): A list of valid choices. Defaults to None.
         show_default (bool, optional): Show default in prompt. Defaults to True.
         show_choices (bool, optional): Show choices in prompt. Defaults to True.
+        case_sensitive (bool, optional): Whether choices are case-sensitive. Defaults to True.
     """
 
     response_type: type = str
@@ -59,6 +60,7 @@ class PromptBase(Generic[PromptType]):
         choices: Optional[List[str]] = None,
         show_default: bool = True,
         show_choices: bool = True,
+        case_sensitive: bool = True,
     ) -> None:
         self.console = console or get_console()
         self.prompt = (
@@ -71,6 +73,7 @@ class PromptBase(Generic[PromptType]):
             self.choices = choices
         self.show_default = show_default
         self.show_choices = show_choices
+        self.case_sensitive = case_sensitive
 
     @classmethod
     @overload
@@ -83,6 +86,7 @@ class PromptBase(Generic[PromptType]):
         choices: Optional[List[str]] = None,
         show_default: bool = True,
         show_choices: bool = True,
+        case_sensitive: bool = True,
         default: DefaultType,
         stream: Optional[TextIO] = None,
     ) -> Union[DefaultType, PromptType]:
@@ -99,6 +103,7 @@ class PromptBase(Generic[PromptType]):
         choices: Optional[List[str]] = None,
         show_default: bool = True,
         show_choices: bool = True,
+        case_sensitive: bool = True,
         stream: Optional[TextIO] = None,
     ) -> PromptType:
         ...
@@ -113,6 +118,7 @@ class PromptBase(Generic[PromptType]):
         choices: Optional[List[str]] = None,
         show_default: bool = True,
         show_choices: bool = True,
+        case_sensitive: bool = True,
         default: Any = ...,
         stream: Optional[TextIO] = None,
     ) -> Any:
@@ -128,6 +134,7 @@ class PromptBase(Generic[PromptType]):
             choices (List[str], optional): A list of valid choices. Defaults to None.
             show_default (bool, optional): Show default in prompt. Defaults to True.
             show_choices (bool, optional): Show choices in prompt. Defaults to True.
+            case_sensitive (bool, optional): Whether choices are case-sensitive. Defaults to True.
             stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None.
         """
         _prompt = cls(
@@ -137,6 +144,7 @@ class PromptBase(Generic[PromptType]):
             choices=choices,
             show_default=show_default,
             show_choices=show_choices,
+            case_sensitive=case_sensitive,
         )
         return _prompt(default=default, stream=stream)
 
@@ -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.
diff --git a/tests/test_prompt.py b/tests/test_prompt.py
index 9a41cc39..076b7855 100644
--- a/tests/test_prompt.py
+++ b/tests/test_prompt.py
@@ -4,20 +4,36 @@ from rich.console import Console
 from rich.prompt import Prompt, IntPrompt, Confirm
 
 
-def test_prompt_str():
-    INPUT = "egg\nfoo"
+def test_prompt_str_case_insensitive():
+    INPUT = "FOO\n"
     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"
+    expected = "what is your name [foo/bar] (baz): "
+    output = console.file.getvalue()
+    assert output == expected
+
+def test_prompt_str_case_sensitive():
+    INPUT = "FOO\nfoo"
+    console = Console(file=io.StringIO())
+    name = Prompt.ask(
+        "what is your name",
+        console=console,
+        choices=["foo", "bar"],
+        default="baz",
+        case_sensitive=True,
         stream=io.StringIO(INPUT),
     )
     assert name == "foo"
     expected = "what is your name [foo/bar] (baz): Please select one of the available options\nwhat is your name [foo/bar] (baz): "
     output = console.file.getvalue()
-    print(repr(output))
     assert output == expected
 
 

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.13s