Skip to content

Commit

Permalink
debug: improvements to debugging information
Browse files Browse the repository at this point in the history
- Include non-method callable attributes in auto-reprs.

- Remove uninteresting initial stack frames from --debug=callers.
  • Loading branch information
nedbat committed Oct 5, 2023
1 parent 40c58d0 commit d521cfa
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 15 deletions.
30 changes: 22 additions & 8 deletions coverage/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,33 @@ def short_stack(limit: Optional[int] = None, skip: int = 0) -> str:
the function name, the file name, and the line number:
...
start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95
import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81
import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159
start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py:95
import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py:81
import_local_file : /Users/ned/coverage/trunk/coverage/backward.py:159
...
`limit` is the number of frames to include, defaulting to all of them.
`skip` is the number of frames to skip, so that debugging functions can
call this and not be included in the result.
Initial frames deemed uninteresting are automatically skipped.
"""
stack = inspect.stack()[limit:skip:-1]
return "\n".join("%30s : %s:%d" % (t[3], t[1], t[2]) for t in stack)
# Substrings in initial frames that we don't care about.
BORING_PRELUDE = [
"<string>", # pytest-xdist has string execution.
f"{os.sep}igor.py", # Our test runner.
f"{os.sep}site-packages{os.sep}", # pytest etc getting to our tests.
]

stack: Iterable[inspect.FrameInfo] = inspect.stack()[limit:skip:-1]
for snip in BORING_PRELUDE:
stack = itertools.dropwhile(
(lambda fi, snip=snip: snip in fi.filename), # type: ignore[misc]
stack
)
return "\n".join(f"{fi.function:>30s} : {fi.filename}:{fi.lineno}" for fi in stack)


def dump_stack_frames(
Expand Down Expand Up @@ -242,13 +256,13 @@ def auto_repr(self: Any) -> str:
show_attrs = (
(k, v) for k, v in self.__dict__.items()
if getattr(v, "show_repr_attr", True)
and not callable(v)
and not inspect.ismethod(v)
and k not in AUTO_REPR_IGNORE
)
return "<{klass} @0x{id:x} {attrs}>".format(
return "<{klass} @0x{id:x}{attrs}>".format(
klass=self.__class__.__name__,
id=id(self),
attrs=" ".join(f"{k}={v!r}" for k, v in show_attrs),
attrs="".join(f" {k}={v!r}" for k, v in show_attrs),
)


Expand Down
20 changes: 13 additions & 7 deletions tests/test_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,18 +288,24 @@ class ShortStackTest(CoverageTest):

def test_short_stack(self) -> None:
stack = f_one().splitlines()
assert len(stack) > 10
assert "f_three" in stack[-1]
assert "f_two" in stack[-2]
assert "f_one" in stack[-3]
assert len(stack) == 4
assert "test_short_stack" in stack[0]
assert "f_one" in stack[1]
assert "f_two" in stack[2]
assert "f_three" in stack[3]

def test_short_stack_limit(self) -> None:
stack = f_one(limit=5).splitlines()
assert len(stack) == 5
stack = f_one(limit=2).splitlines()
assert len(stack) == 2
assert "f_two" in stack[0]
assert "f_three" in stack[1]

def test_short_stack_skip(self) -> None:
stack = f_one(skip=1).splitlines()
assert "f_two" in stack[-1]
assert len(stack) == 3
assert "test_short_stack" in stack[0]
assert "f_one" in stack[1]
assert "f_two" in stack[2]


def test_relevant_environment_display() -> None:
Expand Down

0 comments on commit d521cfa

Please sign in to comment.