Skip to content

Commit

Permalink
Use tb_lineno to point to correct line in traceback (#17)
Browse files Browse the repository at this point in the history
* Use tb_lineno to point to correct line in traceback

* Keep the original co_firstlineno

* Adjust co_firstlineno so that the pytest includes the correct context.

* Loosen testing result.outlines
  • Loading branch information
alexmojaki authored Sep 27, 2023
1 parent 9ca93a0 commit 3bef5d6
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 18 deletions.
26 changes: 13 additions & 13 deletions pytest_examples/traceback.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations as _annotations

import sys
import traceback
from types import CodeType, FrameType, TracebackType
from typing import TYPE_CHECKING

Expand All @@ -21,27 +20,28 @@ def create_example_traceback(exc: Exception, module_path: str, example: CodeExam
# f_code.co_posonlyargcount was added in 3.8
return None
frames = []
for frame, _ in traceback.walk_tb(exc.__traceback__):
tb = exc.__traceback__
while tb is not None:
frame = tb.tb_frame
if frame.f_code.co_filename == module_path:
frames.append(create_custom_frame(frame, example))
frames.append((create_custom_frame(frame, example), tb.tb_lasti, tb.tb_lineno + example.start_line))
tb = tb.tb_next

frames.reverse()
new_tb = None
for altered_frame in frames:
new_tb = TracebackType(
tb_next=new_tb, tb_frame=altered_frame, tb_lasti=altered_frame.f_lasti, tb_lineno=altered_frame.f_lineno
)
for altered_frame, lasti, lineno in frames:
new_tb = TracebackType(tb_next=new_tb, tb_frame=altered_frame, tb_lasti=lasti, tb_lineno=lineno)
return new_tb


def create_custom_frame(frame: FrameType, example: CodeExample) -> FrameType:
"""
Create a new frame that mostly matches `frame` but with a filename from `example` and line number
altered to match the example.
Create a new frame that mostly matches `frame` but with a code object that has
a filename from `example` and adjusted an adjusted first line number
so that pytest shows the correct code context in the traceback.
Taken mostly from https://naleraphael.github.io/blog/posts/devlog_create_a_builtin_frame_object/
With the CodeType creation inspired by https://stackoverflow.com/a/16123158/949890. However, we use
`frame.f_lineno` for the line number instead of `f_code.co_firstlineno` as that seems to work.
With the CodeType creation inspired by https://stackoverflow.com/a/16123158/949890.
"""
import ctypes

Expand Down Expand Up @@ -77,7 +77,7 @@ def create_custom_frame(frame: FrameType, example: CodeExample) -> FrameType:
str(example.path),
f_code.co_name,
f_code.co_qualname,
frame.f_lineno + example.start_line,
f_code.co_firstlineno + example.start_line,
f_code.co_lnotab,
f_code.co_exceptiontable,
)
Expand All @@ -95,7 +95,7 @@ def create_custom_frame(frame: FrameType, example: CodeExample) -> FrameType:
f_code.co_varnames,
str(example.path),
f_code.co_name,
frame.f_lineno + example.start_line,
f_code.co_firstlineno + example.start_line,
f_code.co_lnotab,
)

Expand Down
15 changes: 10 additions & 5 deletions tests/test_run_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ def test_find_run_examples(example: CodeExample, eval_example: EvalExample):
result = pytester.runpytest('-p', 'no:pretty', '-v')
result.assert_outcomes(passed=1, failed=1)

# assert 'my_file_9_13.py:12: AssertionError' in '\n'.join(result.outlines)
assert result.outlines[-8:-3] == [
assert result.outlines[-11].startswith('_ _ _ _ ')
assert result.outlines[-10:-3] == [
'',
' a = 1',
' b = 2',
'> assert a + b == 4',
'E AssertionError',
'',
Expand Down Expand Up @@ -224,7 +226,10 @@ def test_run_directly(tmp_path, eval_example):
x = 4
def div(y):
return x / y
try:
return x / y
finally:
str(y)
div(2)
div(0)"""
Expand All @@ -244,10 +249,10 @@ def div(y):

# debug(exc_info.traceback)
assert exc_info.traceback[-1].frame.code.path == md_file
assert exc_info.traceback[-1].lineno == 6
assert exc_info.traceback[-1].lineno == 7

assert exc_info.traceback[-2].frame.code.path == md_file
assert exc_info.traceback[-2].lineno == 9
assert exc_info.traceback[-2].lineno == 12


def test_print_sub(pytester: pytest.Pytester):
Expand Down

0 comments on commit 3bef5d6

Please sign in to comment.