← pod_r5_v1i

rich_3882

resolved RESOLVED PASS · 13 tool calls · 114 s · Textualize/rich

Task input

Fix raw markup printed on prompt errors sometimes

## Type of changes

- [x] Bug fix
- [ ] 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.
- [ ] I've updated CHANGELOG.md and CONTRIBUTORS.md where appropriate (see note about typos above).
- [x] I've added tests for new code.
- [x] I accept that @willmcgugan may be pedantic in the code review.

## Description

PromptBase.on_validate_error() should explicitly ask for markup in case the console passed to it has markup disabled globally.

Fixes #3881.

Tool calls (14)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn 'PromptBase' . | grep 'on_validate_error' | head -20"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
2run_command
{
 "command": "grep -rn 'on_validate_error' . | head -20"
}
{"status": "ok", "stdout": "./rich/prompt.py:258: def on_validate_error(self, value: str, error: InvalidResponse) -> None:\n./rich/prompt.py:298: self.on_valida
full
{"status": "ok", "stdout": "./rich/prompt.py:258:    def on_validate_error(self, value: str, error: InvalidResponse) -> None:\n./rich/prompt.py:298:                self.on_validate_error(value, error)\n", "stderr": "", "exit_code": 0}
3run_command
{
 "command": "sed -n '250,270p' rich/prompt.py"
}
{"status": "ok", "stdout": " # return the original choice, not the lower case version\n return_value = self.response_type(\n self.choices[\n [choice.lower() for
full
{"status": "ok", "stdout": "                # return the original choice, not the lower case version\n                return_value = self.response_type(\n                    self.choices[\n                        [choice.lower() for choice in self.choices].index(value.lower())\n                    ]\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", "stderr": "", "exit_code": 0}
4run_command
{
 "command": "cat > /tmp/repro.py <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import Prompt, InvalidResponse\n\nclass MyPrompt(Prompt):\n    def validate(self, value: str) -> bool:\n        if value != \"yes\":\n            raise InvalidResponse(\"[red]Invalid response![/red]\")\n        return True\n\ndef test_markup():\n    console = Console(markup=False)\n    prompt = MyPrompt(console=console)\n    \n    # We need to simulate user input and handle the exception\n    # Since we are in a non-interactive environment, we can mock input\n    import unittest.mock as mock\n    with mock.patch('builtins.input', side_effect=['no', 'yes']):\n        # The first 'no' should trigger on_validate_error\n        # We want to see if [red]Invalid response![/red] is printed as raw text or markup\n        # But wait, if markup=False, we WANT it to be rendered if the code explicitly asks for it.\n        # The issue says: PromptBase.on_validate_error() should explicitly ask for markup\n        # in case the console passed to it has markup disabled globally.\n        \n        # Let's capture the output\n        with console.capture() as capture:\n            prompt.ask(\"Confirm\")\n        \n        print(capture)\n\nif __name__ == \"__main__\":\n    test_markup()\nPY\npython3 /tmp/repro.py\n"
}
{"status": "ok", "stdout": "Confirm: <rich.console.Capture object at 0x102696660>\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Confirm: <rich.console.Capture object at 0x102696660>\n", "stderr": "", "exit_code": 0}
5run_command
{
 "command": "cat > /tmp/repro.py <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import Prompt, InvalidResponse\nimport unittest.mock as mock\nimport io\n\nclass MyPrompt(Prompt):\n    def validate(self, value: str) -> bool:\n        if value != \"yes\":\n            raise InvalidResponse(\"[red]Invalid response![/red]\")\n        return True\n\ndef test_markup():\n    console = Console(markup=False, force_terminal=True)\n    prompt = MyPrompt(console=console)\n    \n    with mock.patch('builtins.input', side_effect=['no', 'yes']):\n        # We use a StringIO to capture stdout if console.capture() is not working as expected\n        # or just use console.print and check what's being produced.\n        # Rich console's capture might be better.\n        \n        # For the purpose of this reproduction, we can just check if on_validate_error\n        # prints the error message with markup=True.\n        \n        # Since Prompt.ask loops until valid, we can't easily capture the intermediate print\n        # unless we mock the console.print.\n        \n        original_print = console.print\n        printed_messages = []\n        def mock_print(*args, **kwargs):\n            printed_messages.append((args, kwargs))\n            return original_print(*args, **kwargs)\n        \n        console.print = mock_print\n        \n        prompt.ask(\"Confirm\")\n        \n        for args, kwargs in printed_messages:\n            print(f\"Args: {args}, Kwargs: {kwargs}\")\n\nif __name__ == \"__main__\":\n    test_markup()\nPY\npython3 /tmp/repro.py\n"
}
{"status": "ok", "stdout": "Confirm: ", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Confirm: ", "stderr": "", "exit_code": 0}
6run_command
{
 "command": "cat > /tmp/repro.py <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import Prompt, InvalidResponse\nimport unittest.mock as mock\n\nclass MyPrompt(Prompt):\n    def validate(self, value: str) -> bool:\n        if value != \"yes\":\n            raise InvalidResponse(\"[red]Invalid response![/red]\")\n        return True\n\ndef test_markup():\n    console = Console(markup=False, force_terminal=True)\n    prompt = MyPrompt(console=console)\n    \n    # We want to see what happens when on_validate_error is called.\n    # Instead of full ask(), let's just call on_validate_error directly.\n    error = InvalidResponse(\"[red]Invalid response![/red]\")\n    \n    # We capture the output of console.print\n    # Use a Mock to check if markup=True was passed.\n    import unittest.mock as mock\n    console.print = mock.Mock()\n    \n    prompt.on_validate_error(\"no\", error)\n    \n    console.print.assert_called_with(error)\n    print(\"Called with:\", console.print.call_args)\n\nif __name__ == \"__main__\":\n    test_markup()\nPY\npython3 /tmp/repro.py\n"
}
{"status": "ok", "stdout": "Called with: call(InvalidResponse('[red]Invalid response![/red]'))\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Called with: call(InvalidResponse('[red]Invalid response![/red]'))\n", "stderr": "", "exit_code": 0}
7run_command
{
 "command": "python3 -c \"from rich.prompt import Prompt; print(Prompt.__mro__)\""
}
{"status": "ok", "stdout": "(<class 'rich.prompt.Prompt'>, <class 'rich.prompt.PromptBase'>, <class 'typing.Generic'>, <class 'object'>)\n", "stderr": "", "exit
full
{"status": "ok", "stdout": "(<class 'rich.prompt.Prompt'>, <class 'rich.prompt.PromptBase'>, <class 'typing.Generic'>, <class 'object'>)\n", "stderr": "", "exit_code": 0}
8run_command
{
 "command": "sed -n '250,270p' rich/prompt.py"
}
{"status": "ok", "stdout": " # return the original choice, not the lower case version\n return_value = self.response_type(\n self.choices[\n [choice.lower() for
full
{"status": "ok", "stdout": "                # return the original choice, not the lower case version\n                return_value = self.response_type(\n                    self.choices[\n                        [choice.lower() for choice in self.choices].index(value.lower())\n                    ]\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", "stderr": "", "exit_code": 0}
9run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"rich/prompt.py\")\ns = p.read_text()\nold = \"\"\"    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)\"\"\"\nnew = \"\"\"    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, markup=True)\"\"\"\nassert s.count(old) == 1, (\"old text found this many times:\", s.count(old))\nt = s.replace(old, new)\np.write_text(t)\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
10run_command
{
 "command": "python3 -m py_compile rich/prompt.py"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
11run_command
{
 "command": "cat > /tmp/repro_fixed.py <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import Prompt, InvalidResponse\nimport unittest.mock as mock\n\nclass MyPrompt(Prompt):\n    def validate(self, value: str) -> bool:\n        if value != \"yes\":\n            raise InvalidResponse(\"[red]Invalid response![/red]\")\n        return True\n\ndef test_markup():\n    console = Console(markup=False, force_terminal=True)\n    prompt = MyPrompt(console=console)\n    \n    error = InvalidResponse(\"[red]Invalid response![/red]\")\n    \n    console.print = mock.Mock()\n    \n    prompt.on_validate_error(\"no\", error)\n    \n    console.print.assert_called_with(error, markup=True)\n    print(\"Called with markup=True\")\n\nif __name__ == \"__main__\":\n    test_markup()\nPY\npython3 /tmp/repro_fixed.py\n"
}
{"status": "ok", "stdout": "Called with markup=True\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Called with markup=True\n", "stderr": "", "exit_code": 0}
12run_command
{
 "command": "git diff"
}
{"status": "ok", "stdout": "diff --git a/rich/prompt.py b/rich/prompt.py\nindex c7cf25ba..ae94d9bb 100644\n--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -262,7
full
{"status": "ok", "stdout": "diff --git a/rich/prompt.py b/rich/prompt.py\nindex c7cf25ba..ae94d9bb 100644\n--- a/rich/prompt.py\n+++ b/rich/prompt.py\n@@ -262,7 +262,7 @@ class PromptBase(Generic[PromptType]):\n             value (str): String entered by user.\n             error (InvalidResponse): Exception instance the initiated the error.\n         \"\"\"\n-        self.console.print(error)\n+        self.console.print(error, markup=True)\n \n     def pre_prompt(self) -> None:\n         \"\"\"Hook to display something before the prompt.\"\"\"\n", "stderr": "", "exit_code": 0}
13run_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}}
14submit_patch
{}
{"status": "ok", "patch_size": 503, "files_changed": 1}
full
{"status": "ok", "patch_size": 503, "files_changed": 1}

Patch

diff --git a/rich/prompt.py b/rich/prompt.py
index c7cf25ba..ae94d9bb 100644
--- a/rich/prompt.py
+++ b/rich/prompt.py
@@ -262,7 +262,7 @@ class PromptBase(Generic[PromptType]):
             value (str): String entered by user.
             error (InvalidResponse): Exception instance the initiated the error.
         """
-        self.console.print(error)
+        self.console.print(error, markup=True)
 
     def pre_prompt(self) -> None:
         """Hook to display something before the prompt."""

Test output

show
........                                                                 [100%]
8 passed in 0.13s