failed WRONG_FIX wrong_fix · 7 tool calls · 34 s · Textualize/rich
fix(panel): fix title missing panel background
Fix `Panel` title missing the panel background style.
This really just reverts the change in 7a38204. There's a history of issues related to the panel title styles, so I was careful to run all the examples in those issues to ensure there wasn't any regression.
Fixes #3569
## 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.
- [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.
| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"title\" rich/panel.py"
} | {"status": "ok", "stdout": "26: title (Optional[TextType], optional): Optional title displayed in panel header. Defaults to None.\n27: title_align (AlignMethod,full{"status": "ok", "stdout": "26: title (Optional[TextType], optional): Optional title displayed in panel header. Defaults to None.\n27: title_align (AlignMethod, optional): Alignment of title. Defaults to \"center\".\n28: subtitle (Optional[TextType], optional): Optional subtitle displayed in panel footer. Defaults to None.\n29: subtitle_align (AlignMethod, optional): Alignment of subtitle. Defaults to \"center\".\n37: highlight (bool, optional): Enable automatic highlighting of panel title (if str). Defaults to False.\n45: title: Optional[TextType] = None,\n46: title_align: AlignMethod = \"center\",\n47: subtitle: Optional[TextType] = None,\n48: subtitle_align: AlignMethod = \"center\",\n60: self.title = title\n61: self.title_align: AlignMethod = title_align\n62: self.subtitle = subtitle\n63: self.subtitle_align = subtitle_align\n79: title: Optional[TextType] = None,\n80: title_align: AlignMethod = \"center\",\n81: subtitle: Optional[TextType] = None,\n82: subtitle_align: AlignMethod = \"center\",\n95: title=title,\n96: title_align=title_align,\n97: subtitle=subtitle,\n98: subtitle_align=subtitle_align,\n110: def _title(self) -> Optional[Text]:\n111: if self.title:\n112: title_text = (\n113: Text.from_markup(self.title)\n114: if isinstance(self.title, str)\n115: else self.title.copy()\n117: title_text.end = \"\"\n118: title_text.plain = title_text.plain.replace(\"\\n\", \" \")\n119: title_text.no_wrap = True\n120: title_text.expand_tabs()\n121: title_text.pad(1)\n122: return title_text\n126: def _subtitle(self) -> Optional[Text]:\n127: if self.subtitle:\n128: subtitle_text = (\n129: Text.from_markup(self.subtitle)\n130: if isi |
| 2 | read_file | {
"end_line": 250,
"filepath": "rich/panel.py",
"start_line": 110
} | {"status": "ok", "filepath": "rich/panel.py", "content": " def _title(self) -> Optional[Text]:\n if self.title:\n title_text = (\n Text.from_markup(self.title)\full{"status": "ok", "filepath": "rich/panel.py", "content": " def _title(self) -> Optional[Text]:\n if self.title:\n title_text = (\n Text.from_markup(self.title)\n if isinstance(self.title, str)\n else self.title.copy()\n )\n title_text.end = \"\"\n title_text.plain = title_text.plain.replace(\"\\n\", \" \")\n title_text.no_wrap = True\n title_text.expand_tabs()\n title_text.pad(1)\n return title_text\n return None\n\n @property\n def _subtitle(self) -> Optional[Text]:\n if self.subtitle:\n subtitle_text = (\n Text.from_markup(self.subtitle)\n if isinstance(self.subtitle, str)\n else self.subtitle.copy()\n )\n subtitle_text.end = \"\"\n subtitle_text.plain = subtitle_text.plain.replace(\"\\n\", \" \")\n subtitle_text.no_wrap = True\n subtitle_text.expand_tabs()\n subtitle_text.pad(1)\n return subtitle_text\n return None\n\n def __rich_console__(\n self, console: \"Console\", options: \"ConsoleOptions\"\n ) -> \"RenderResult\":\n _padding = Padding.unpack(self.padding)\n renderable = (\n Padding(self.renderable, _padding) if any(_padding) else self.renderable\n )\n style = console.get_style(self.style)\n partial_border_style = console.get_style(self.border_style)\n border_style = style + partial_border_style\n width = (\n options.max_width\n if self.width is None\n else min(options.max_width, self.width)\n )\n\n safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box\n box = self.box.substitute(options, safe=safe_box)\n\n def align_text(\n text: Text, width: int, align: str, character: str, style: Style\n |
| 3 | read_file | {
"filepath": "rich/panel.py",
"start_line": 180,
"end_line": 230
} | {"status": "ok", "filepath": "rich/panel.py", "content": "\n if excess_space:\n if align == \"left\":\n return Text.assemble(\n text,\n (character * excess_spacfull{"status": "ok", "filepath": "rich/panel.py", "content": "\n if excess_space:\n if align == \"left\":\n return Text.assemble(\n text,\n (character * excess_space, style),\n no_wrap=True,\n end=\"\",\n )\n elif align == \"center\":\n left = excess_space // 2\n return Text.assemble(\n (character * left, style),\n text,\n (character * (excess_space - left), style),\n no_wrap=True,\n end=\"\",\n )\n else:\n return Text.assemble(\n (character * excess_space, style),\n text,\n no_wrap=True,\n end=\"\",\n )\n return text\n\n title_text = self._title\n if title_text is not None:\n title_text.stylize_before(partial_border_style)\n\n child_width = (\n width - 2\n if self.expand\n else console.measure(\n renderable, options=options.update_width(width - 2)\n ).maximum\n )\n child_height = self.height or options.height or None\n if child_height:\n child_height -= 2\n if title_text is not None:\n child_width = min(\n options.max_width - 2, max(child_width, title_text.cell_len + 2)\n )\n\n width = child_width + 2\n child_options = options.update(\n width=child_width, height=child_height, highlight=self.highlight\n )\n lines = console.render_lines(renderable, child_options, style=style)\n", "start_line": 180, "end_line": 230, "total_lines": 318, "is_truncated": false} |
| 4 | read_file | {
"end_line": 210,
"filepath": "rich/panel.py",
"start_line": 190
} | {"status": "ok", "filepath": "rich/panel.py", "content": " left = excess_space // 2\n return Text.assemble(\n (character * left, style),\n text,\n (character * full{"status": "ok", "filepath": "rich/panel.py", "content": " left = excess_space // 2\n return Text.assemble(\n (character * left, style),\n text,\n (character * (excess_space - left), style),\n no_wrap=True,\n end=\"\",\n )\n else:\n return Text.assemble(\n (character * excess_space, style),\n text,\n no_wrap=True,\n end=\"\",\n )\n return text\n\n title_text = self._title\n if title_text is not None:\n title_text.stylize_before(partial_border_style)\n\n", "start_line": 190, "end_line": 210, "total_lines": 318, "is_truncated": false} |
| 5 | read_file | {
"end_line": 210,
"filepath": "rich/panel.py",
"start_line": 170
} | {"status": "ok", "filepath": "rich/panel.py", "content": " style (Style): Border style\n\n Returns:\n Text: New text instance\n \"\"\"\n text = text.copy()\n tefull{"status": "ok", "filepath": "rich/panel.py", "content": " style (Style): Border style\n\n Returns:\n Text: New text instance\n \"\"\"\n text = text.copy()\n text.truncate(width)\n excess_space = width - cell_len(text.plain)\n if text.style:\n text.stylize(console.get_style(text.style))\n\n if excess_space:\n if align == \"left\":\n return Text.assemble(\n text,\n (character * excess_space, style),\n no_wrap=True,\n end=\"\",\n )\n elif align == \"center\":\n left = excess_space // 2\n return Text.assemble(\n (character * left, style),\n text,\n (character * (excess_space - left), style),\n no_wrap=True,\n end=\"\",\n )\n else:\n return Text.assemble(\n (character * excess_space, style),\n text,\n no_wrap=True,\n end=\"\",\n )\n return text\n\n title_text = self._title\n if title_text is not None:\n title_text.stylize_before(partial_border_style)\n\n", "start_line": 170, "end_line": 210, "total_lines": 318, "is_truncated": false} |
| 6 | read_file | {
"end_line": 210,
"filepath": "rich/panel.py",
"start_line": 150
} | {"status": "ok", "filepath": "rich/panel.py", "content": " border_style = style + partial_border_style\n width = (\n options.max_width\n if self.width is None\nfull{"status": "ok", "filepath": "rich/panel.py", "content": " border_style = style + partial_border_style\n width = (\n options.max_width\n if self.width is None\n else min(options.max_width, self.width)\n )\n\n safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box\n box = self.box.substitute(options, safe=safe_box)\n\n def align_text(\n text: Text, width: int, align: str, character: str, style: Style\n ) -> Text:\n \"\"\"Gets new aligned text.\n\n Args:\n text (Text): Title or subtitle text.\n width (int): Desired width.\n align (str): Alignment.\n character (str): Character for alignment.\n style (Style): Border style\n\n Returns:\n Text: New text instance\n \"\"\"\n text = text.copy()\n text.truncate(width)\n excess_space = width - cell_len(text.plain)\n if text.style:\n text.stylize(console.get_style(text.style))\n\n if excess_space:\n if align == \"left\":\n return Text.assemble(\n text,\n (character * excess_space, style),\n no_wrap=True,\n end=\"\",\n )\n elif align == \"center\":\n left = excess_space // 2\n return Text.assemble(\n (character * left, style),\n text,\n (character * (excess_space - left), style),\n no_wrap=True,\n end=\"\",\n )\n else:\n return Text.assemble(\n (character * excess_space, style),\n text,\n |
| 7 | edit_file | {
"filepath": "rich/panel.py",
"new_string": " if title_text is not None:\n title_text.stylize_before(border_style)",
"old_string": " if title_text is not None:\n title_text.stylize_before(partial_border_style)"
} | {"status": "ok", "filepath": "rich/panel.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/panel.py\n+++ b/rich/panel.py\n@@ -206,7 +206,7 @@\n \nfull{"status": "ok", "filepath": "rich/panel.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/panel.py\n+++ b/rich/panel.py\n@@ -206,7 +206,7 @@\n \n title_text = self._title\n if title_text is not None:\n- title_text.stylize_before(partial_border_style)\n+ title_text.stylize_before(border_style)\n \n child_width = (\n width - 2\n", "is_truncated": false} |
| 8 | submit_patch | [] | {"status": "ok", "patch_size": 398, "files_changed": 1}full{"status": "ok", "patch_size": 398, "files_changed": 1} |
diff --git a/rich/panel.py b/rich/panel.py
index d411e291..2ff113e7 100644
--- a/rich/panel.py
+++ b/rich/panel.py
@@ -206,7 +206,7 @@ class Panel(JupyterMixin):
title_text = self._title
if title_text is not None:
- title_text.stylize_before(partial_border_style)
+ title_text.stylize_before(border_style)
child_width = (
width - 2
............F
=================================== FAILURES ===================================
____________________ test_title_text_with_panel_background _____________________
def test_title_text_with_panel_background() -> None:
"""Regression test for https://github.com/Textualize/rich/issues/3569"""
panel = Panel(
"Hello, World",
style="on blue",
title=Text("title", style="red"),
subtitle=Text("subtitle", style="magenta bold"),
)
console = Console(
file=io.StringIO(),
width=50,
height=20,
legacy_windows=False,
force_terminal=True,
color_system="truecolor",
)
console.print(panel)
result = console.file.getvalue()
print(repr(result))
expected = "\x1b[44m╭─\x1b[0m\x1b[44m───────────────────\x1b[0m\x1b[31;44m title \x1b[0m\x1b[44m────────────────────\x1b[0m\x1b[44m─╮\x1b[0m\n\x1b[44m│\x1b[0m\x1b[44m \x1b[0m\x1b[44mHello, World\x1b[0m\x1b[44m \x1b[0m\x1b[44m \x1b[0m\x1b[44m│\x1b[0m\n\x1b[44m╰─\x1b[0m\x1b[44m──────────────────\x1b[0m\x1b[1;35;44m subtitle \x1b[0m\x1b[44m──────────────────\x1b[0m\x1b[44m─╯\x1b[0m\n"
> assert result == expected
E AssertionError: assert '\x1b[44m╭─\x...4m─╯\x1b[0m\n' == '\x1b[44m╭─\x...4m─╯\x1b[0m\n'
E
E Skipping 238 identical leading characters in diff, use -v to show
E - [0m[1;35;44m subtitle [0m[44m──────────────────[0m[44m─╯[0m
E ? ---
E + [0m[1;35m subtitle [0m[44m──────────────────[0m[44m─╯[0m
tests/test_panel.py:172: AssertionError
----------------------------- Captured stdout call -----------------------------
'\x1b[44m╭─\x1b[0m\x1b[44m───────────────────\x1b[0m\x1b[31;44m title \x1b[0m\x1b[44m────────────────────\x1b[0m\x1b[44m─╮\x1b[0m\n\x1b[44m│\x1b[0m\x1b[44m \x1b[0m\x1b[44mHello, World\x1b[0m\x1b[44m \x1b[0m\x1b[44m \x1b[0m\x1b[44m│\x1b[0m\n\x1b[44m╰─\x1b[0m\x1b[44m──────────────────\x1b[0m\x1b[1;35m subtitle \x1b[0m\x1b[44m──────────────────\x1b[0m\x1b[44m─╯\x1b[0m\n'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 12 passed in 0.18s