resolved RESOLVED PASS · 13 tool calls · 86 s · Textualize/rich
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 | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"PromptBase.on_validate_error\" ."
} | {"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 2 | run_command | {
"command": "grep -rn \"on_validate_error\" ."
} | {"status": "ok", "stdout": "./rich/prompt.py:258: def on_validate_error(self, value: str, error: InvalidResponse) -> None:\n./rich/prompt.py:298: self.on_validafull{"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} |
| 3 | run_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() forfull{"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} |
| 4 | run_command | {
"command": "python3 - <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import Prompt, InvalidResponse\n\nclass MyPrompt(Prompt):\n def validate(self, value: str) -> str:\n if value != \"yes\":\n raise InvalidResponse(\"[red]Invalid response![/red]\")\n return value\n\nconsole = Console(markup=False)\nprompt = MyPrompt(console=console)\n\n# We want to see if [red]Invalid response![/red] is printed as raw text or as red text.\n# Since we can't easily capture stdout and check colors in this environment, \n# we can mock the console.print method.\n\nimport unittest.mock as mock\nwith mock.patch.object(console, 'print') as mocked_print:\n try:\n # This will trigger on_validate_error because \"no\" is invalid\n # But Prompt.ask() loops until valid. We need to simulate the flow.\n # Let's just call on_validate_error directly.\n prompt.on_validate_error(\"no\", InvalidResponse(\"[red]Invalid response![/red]\"))\n except Exception:\n pass\n\nargs, kwargs = mocked_print.call_args\nprint(f\"Print called with: {args}\")\nprint(f\"Print kwargs: {kwargs}\")\nPY\n"
} | {"status": "ok", "stdout": "Print called with: (InvalidResponse('[red]Invalid response![/red]'),)\nPrint kwargs: {}\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Print called with: (InvalidResponse('[red]Invalid response![/red]'),)\nPrint kwargs: {}\n", "stderr": "", "exit_code": 0} |
| 5 | run_command | {
"command": "python3 - <<'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) -> str:\n if value != \"yes\":\n raise InvalidResponse(\"[red]Invalid response![/red]\")\n return value\n\n# Case 1: Markup enabled (default)\nconsole_markup_true = Console(markup=True)\nprompt_markup_true = MyPrompt(console=console_markup_true)\n\nwith mock.patch.object(console_markup_true, 'print') as mocked_print:\n prompt_markup_true.on_validate_error(\"no\", InvalidResponse(\"[red]Invalid response![/red]\"))\n args, kwargs = mocked_print.call_args\n print(f\"Markup True - Args: {args}, Kwargs: {kwargs}\")\n\n# Case 2: Markup disabled\nconsole_markup_false = Console(markup=False)\nprompt_markup_false = MyPrompt(console=console_markup_false)\n\nwith mock.patch.object(console_markup_false, 'print') as mocked_print:\n prompt_markup_false.on_validate_error(\"no\", InvalidResponse(\"[red]Invalid response![/red]\"))\n args, kwargs = mocked_print.call_args\n print(f\"Markup False - Args: {args}, Kwargs: {kwargs}\")\nPY\n"
} | {"status": "ok", "stdout": "Markup True - Args: (InvalidResponse('[red]Invalid response![/red]'),), Kwargs: {}\nMarkup False - Args: (InvalidResponse('[red]Invafull{"status": "ok", "stdout": "Markup True - Args: (InvalidResponse('[red]Invalid response![/red]'),), Kwargs: {}\nMarkup False - Args: (InvalidResponse('[red]Invalid response![/red]'),), Kwargs: {}\n", "stderr": "", "exit_code": 0} |
| 6 | run_command | {
"command": "python3 - <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import Prompt, InvalidResponse\nimport unittest.mock as mock\n\n# We want to see what happens when console.print is called with an InvalidResponse object\n# and markup=False.\n\nclass MockConsole:\n def __init__(self, markup):\n self.markup = markup\n def print(self, *args, **kwargs):\n # Simulate rich.console.Console.print behavior\n # If markup=False, it should print the string representation without processing markup\n # unless markup=True is passed to print()\n print(f\"Console.print called with markup={self.markup}, args={args}, kwargs={kwargs}\")\n\nconsole = MockConsole(markup=False)\nprompt = Prompt(console=console)\nprompt.on_validate_error(\"no\", InvalidResponse(\"[red]Error[/red]\"))\nPY\n"
} | {"status": "ok", "stdout": "Console.print called with markup=False, args=(InvalidResponse('[red]Error[/red]'),), kwargs={}\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Console.print called with markup=False, args=(InvalidResponse('[red]Error[/red]'),), kwargs={}\n", "stderr": "", "exit_code": 0} |
| 7 | run_command | {
"command": "python3 - <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import InvalidResponse\nimport io\nfrom contextlib import redirect_stdout\n\n# Test with markup=False globally\nconsole = Console(markup=False, force_terminal=False)\nf = io.StringIO()\nwith redirect_stdout(f):\n console.print(InvalidResponse(\"[red]Error[/red]\"))\nprint(f\"Output: {f.getvalue()}\")\nPY\n"
} | {"status": "ok", "stdout": "Output: [red]Error[/red]\n\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Output: [red]Error[/red]\n\n", "stderr": "", "exit_code": 0} |
| 8 | run_command | {
"command": "python3 - <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import InvalidResponse\nimport io\nfrom contextlib import redirect_stdout\n\n# Test with markup=False globally, but markup=True passed to print\nconsole = Console(markup=False, force_terminal=False)\nf = io.StringIO()\nwith redirect_stdout(f):\n console.print(InvalidResponse(\"[red]Error[/red]\"), markup=True)\nprint(f\"Output: {f.getvalue()}\")\nPY\n"
} | {"status": "ok", "stdout": "Output: Error\n\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Output: Error\n\n", "stderr": "", "exit_code": 0} |
| 9 | run_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} |
| 10 | run_command | {
"command": "python3 -m py_compile rich/prompt.py"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 11 | run_command | {
"command": "python3 - <<'PY'\nfrom rich.console import Console\nfrom rich.prompt import Prompt, InvalidResponse\nimport io\nfrom contextlib import redirect_stdout\n\nclass MyPrompt(Prompt):\n def validate(self, value: str) -> str:\n if value != \"yes\":\n raise InvalidResponse(\"[red]Invalid response![/red]\")\n return value\n\nconsole = Console(markup=False, force_terminal=False)\nprompt = MyPrompt(console=console)\n\nf = io.StringIO()\nwith redirect_stdout(f):\n prompt.on_validate_error(\"no\", InvalidResponse(\"[red]Invalid response![/red]\"))\n\noutput = f.getvalue()\nprint(f\"Output: {output}\")\n# With markup=True in on_validate_error, the [red] tags should be processed (removed in non-terminal)\n# instead of printed literally.\nif \"[red]\" in output:\n print(\"FAILURE: Markup was printed literally\")\nelse:\n print(\"SUCCESS: Markup was processed\")\nPY\n"
} | {"status": "ok", "stdout": "Output: Invalid response!\n\nSUCCESS: Markup was processed\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Output: Invalid response!\n\nSUCCESS: Markup was processed\n", "stderr": "", "exit_code": 0} |
| 12 | run_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,7full{"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} |
| 13 | run_command | {
"command": "git status"
} | {"status": "ok", "stdout": "On branch main\nChanges not staged for commit:\n (use \"git add <file>...\" to update what will be committed)\n (use \"git restore <full{"status": "ok", "stdout": "On branch main\nChanges not staged for commit:\n (use \"git add <file>...\" to update what will be committed)\n (use \"git restore <file>...\" to discard changes in working directory)\n\tmodified: rich/prompt.py\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n", "stderr": "", "exit_code": 0} |
| 14 | submit_patch | {} | {"status": "ok", "patch_size": 503, "files_changed": 1}full{"status": "ok", "patch_size": 503, "files_changed": 1} |
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."""
........ [100%] 8 passed in 0.10s