From c3463a7a2659288b7c35178750a61dae745d3182 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 12 Nov 2023 07:23:20 -0500 Subject: [PATCH] debug: short_stack(full=False) Now we have the option to show the full stack or an abbreviated stack. But debug=premain should still show the complete stack. --- coverage/cmdline.py | 2 +- coverage/debug.py | 13 +++++++------ tests/test_cmdline.py | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/coverage/cmdline.py b/coverage/cmdline.py index 67243b230..d24242140 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -892,7 +892,7 @@ def do_debug(self, args: List[str]) -> int: write_formatted_info(print, "config", self.coverage.config.debug_info()) elif args[0] == "premain": print(info_header("premain")) - print(short_stack()) + print(short_stack(full=True)) elif args[0] == "pybehave": write_formatted_info(print, "pybehave", env.debug_info()) else: diff --git a/coverage/debug.py b/coverage/debug.py index ba1f0fde4..0e000eb52 100644 --- a/coverage/debug.py +++ b/coverage/debug.py @@ -181,7 +181,7 @@ def exc_one_line(exc: Exception) -> str: return "|".join(l.rstrip() for l in lines) -def short_stack(limit: Optional[int] = None, skip: int = 0) -> str: +def short_stack(limit: Optional[int] = None, skip: int = 0, full: bool = False) -> str: """Return a string summarizing the call stack. The string is multi-line, with one line per stack frame. Each line shows @@ -209,11 +209,12 @@ def short_stack(limit: Optional[int] = None, skip: int = 0) -> str: ] stack: Iterable[inspect.FrameInfo] = inspect.stack()[limit:skip:-1] - for pat in BORING_PRELUDE: - stack = itertools.dropwhile( - (lambda fi, pat=pat: re.search(pat, fi.filename)), # type: ignore[misc] - stack - ) + if not full: + for pat in BORING_PRELUDE: + stack = itertools.dropwhile( + (lambda fi, pat=pat: re.search(pat, fi.filename)), # type: ignore[misc] + stack + ) return "\n".join(f"{fi.function:>30s} : {fi.filename}:{fi.lineno}" for fi in stack) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index a310371f1..f5eb3d1e8 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -6,6 +6,7 @@ from __future__ import annotations import ast +import os import pprint import re import sys @@ -333,16 +334,25 @@ def test_debug_pybehave(self) -> None: def test_debug_premain(self) -> None: self.command_line("debug premain") out = self.stdout() + # -- premain --------------------------------------------------- # ... many lines ... + # _multicall : /Users/ned/cov/trunk/.tox/py39/site-packages/pluggy/_callers.py:77 # pytest_pyfunc_call : /Users/ned/cov/trunk/.tox/py39/site-packages/_pytest/python.py:183 # test_debug_premain : /Users/ned/cov/trunk/tests/test_cmdline.py:284 # command_line : /Users/ned/cov/trunk/tests/coveragetest.py:309 # command_line : /Users/ned/cov/trunk/tests/coveragetest.py:472 # command_line : /Users/ned/cov/trunk/coverage/cmdline.py:592 # do_debug : /Users/ned/cov/trunk/coverage/cmdline.py:804 - assert re.search(r"(?m)^\s+test_debug_premain : .*[/\\]tests[/\\]test_cmdline.py:\d+$", out) - assert re.search(r"(?m)^\s+command_line : .*[/\\]coverage[/\\]cmdline.py:\d+$", out) - assert re.search(r"(?m)^\s+do_debug : .*[/\\]coverage[/\\]cmdline.py:\d+$", out) + lines = out.splitlines() + s = re.escape(os.sep) + assert lines[0].startswith("-- premain ----") + assert len(lines) > 25 + assert re.search(fr"{s}site-packages{s}_pytest{s}", out) + assert re.search(fr"{s}site-packages{s}pluggy{s}", out) + assert re.search(fr"(?m)^\s+test_debug_premain : .*{s}tests{s}test_cmdline.py:\d+$", out) + assert re.search(fr"(?m)^\s+command_line : .*{s}coverage{s}cmdline.py:\d+$", out) + assert re.search(fr"(?m)^\s+do_debug : .*{s}coverage{s}cmdline.py:\d+$", out) + assert "do_debug : " in lines[-1] def test_erase(self) -> None: # coverage erase