Skip to content

Commit 7b46496

Browse files
committed
testresult: correctly apply verbose word markup and avoid crash
The following snippet would have resulted in crash on multiple places since `_get_verbose_word` expects only string, not a tuple. ```python @pytest.hookimpl(tryfirst=True) def pytest_report_teststatus(report: pytest.CollectReport | pytest.TestReport, config: pytest.Config): if report.when == "call": return ("error", "A", ("AVC", {"bold": True, "red": True})) return None ``` ``` Traceback (most recent call last): File "/home/pbrezina/workspace/sssd/.venv/bin/pytest", line 8, in <module> sys.exit(console_main()) ^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/config/__init__.py", line 207, in console_main code = main() ^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/config/__init__.py", line 179, in main ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall raise exception.with_traceback(exception.__traceback__) File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 103, in _multicall res = hook_impl.function(*args) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/main.py", line 333, in pytest_cmdline_main return wrap_session(config, _main) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/main.py", line 321, in wrap_session config.hook.pytest_sessionfinish( File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall raise exception.with_traceback(exception.__traceback__) File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 122, in _multicall teardown.throw(exception) # type: ignore[union-attr] ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/logging.py", line 872, in pytest_sessionfinish return (yield) ^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 124, in _multicall teardown.send(result) # type: ignore[union-attr] ^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 899, in pytest_sessionfinish self.config.hook.pytest_terminal_summary( File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall raise exception.with_traceback(exception.__traceback__) File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 124, in _multicall teardown.send(result) # type: ignore[union-attr] ^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 923, in pytest_terminal_summary self.short_test_summary() File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1272, in short_test_summary action(lines) File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1205, in show_simple line = _get_line_with_reprcrash_message( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1429, in _get_line_with_reprcrash_message word = tw.markup(verbose_word, **word_markup) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pbrezina/workspace/pytest/src/_pytest/_io/terminalwriter.py", line 114, in markup text = "".join(f"\x1b[{cod}m" for cod in esc) + text + "\x1b[0m" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~ TypeError: can only concatenate str (not "tuple") to str ``` Signed-off-by: Pavel Březina <[email protected]>
1 parent 49374ec commit 7b46496

File tree

2 files changed

+19
-13
lines changed

2 files changed

+19
-13
lines changed

src/_pytest/reports.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,17 @@ def head_line(self) -> Optional[str]:
196196
return domain
197197
return None
198198

199-
def _get_verbose_word(self, config: Config):
199+
def _get_verbose_word(
200+
self, config: Config, default_markup: Mapping[str, bool]
201+
) -> Tuple[str, Mapping[str, bool]]:
200202
_category, _short, verbose = config.hook.pytest_report_teststatus(
201203
report=self, config=config
202204
)
203-
return verbose
205+
206+
if not isinstance(verbose, tuple):
207+
return verbose, default_markup
208+
209+
return verbose[0], verbose[1]
204210

205211
def _to_json(self) -> Dict[str, Any]:
206212
"""Return the contents of this report as a dict of builtin entries,

src/_pytest/terminal.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -1210,10 +1210,10 @@ def show_simple(lines: List[str], *, stat: str) -> None:
12101210
def show_xfailed(lines: List[str]) -> None:
12111211
xfailed = self.stats.get("xfailed", [])
12121212
for rep in xfailed:
1213-
verbose_word = rep._get_verbose_word(self.config)
1214-
markup_word = self._tw.markup(
1215-
verbose_word, **{_color_for_type["warnings"]: True}
1213+
verbose_word, verbose_markup = rep._get_verbose_word(
1214+
self.config, {_color_for_type["warnings"]: True}
12161215
)
1216+
markup_word = self._tw.markup(verbose_word, **verbose_markup)
12171217
nodeid = _get_node_id_with_markup(self._tw, self.config, rep)
12181218
line = f"{markup_word} {nodeid}"
12191219
reason = rep.wasxfail
@@ -1225,10 +1225,10 @@ def show_xfailed(lines: List[str]) -> None:
12251225
def show_xpassed(lines: List[str]) -> None:
12261226
xpassed = self.stats.get("xpassed", [])
12271227
for rep in xpassed:
1228-
verbose_word = rep._get_verbose_word(self.config)
1229-
markup_word = self._tw.markup(
1230-
verbose_word, **{_color_for_type["warnings"]: True}
1228+
verbose_word, verbose_markup = rep._get_verbose_word(
1229+
self.config, {_color_for_type["warnings"]: True}
12311230
)
1231+
markup_word = self._tw.markup(verbose_word, **verbose_markup)
12321232
nodeid = _get_node_id_with_markup(self._tw, self.config, rep)
12331233
line = f"{markup_word} {nodeid}"
12341234
reason = rep.wasxfail
@@ -1241,10 +1241,10 @@ def show_skipped(lines: List[str]) -> None:
12411241
fskips = _folded_skips(self.startpath, skipped) if skipped else []
12421242
if not fskips:
12431243
return
1244-
verbose_word = skipped[0]._get_verbose_word(self.config)
1245-
markup_word = self._tw.markup(
1246-
verbose_word, **{_color_for_type["warnings"]: True}
1244+
verbose_word, verbose_markup = skipped[0]._get_verbose_word(
1245+
self.config, {_color_for_type["warnings"]: True}
12471246
)
1247+
markup_word = self._tw.markup(verbose_word, **verbose_markup)
12481248
prefix = "Skipped: "
12491249
for num, fspath, lineno, reason in fskips:
12501250
if reason.startswith(prefix):
@@ -1425,8 +1425,8 @@ def _get_line_with_reprcrash_message(
14251425
config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: Dict[str, bool]
14261426
) -> str:
14271427
"""Get summary line for a report, trying to add reprcrash message."""
1428-
verbose_word = rep._get_verbose_word(config)
1429-
word = tw.markup(verbose_word, **word_markup)
1428+
verbose_word, verbose_markup = rep._get_verbose_word(config, word_markup)
1429+
word = tw.markup(verbose_word, **verbose_markup)
14301430
node = _get_node_id_with_markup(tw, config, rep)
14311431

14321432
line = f"{word} {node}"

0 commit comments

Comments
 (0)