Skip to content

Commit 0bf8cc7

Browse files
fix: handle cases where pytest itself fails (#70)
* Check if traceback location is not `None` * Check if exception repr object is not null before retrieving message and lineno * Remove `__future__.print_function` import * Add test * Match line using regex
1 parent b94d206 commit 0bf8cc7

File tree

2 files changed

+41
-9
lines changed

2 files changed

+41
-9
lines changed

plugin_test.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,28 @@ def test_success():
3434
no_fnmatch_line(result, "::error file=test_annotation_succeed_no_output.py")
3535

3636

37+
def test_annotation_pytest_error(testdir):
38+
testdir.makepyfile(
39+
"""
40+
import pytest
41+
pytest_plugins = 'pytest_github_actions_annotate_failures'
42+
43+
@pytest.fixture
44+
def fixture():
45+
return 1
46+
47+
def test_error():
48+
assert fixture() == 1
49+
"""
50+
)
51+
testdir.monkeypatch.setenv("GITHUB_ACTIONS", "true")
52+
result = testdir.runpytest_subprocess()
53+
54+
result.stderr.re_match_lines(
55+
[r"::error file=test_annotation_pytest_error\.py,line=8::test_error.*",]
56+
)
57+
58+
3759
def test_annotation_fail(testdir):
3860
testdir.makepyfile(
3961
"""

pytest_github_actions_annotate_failures/plugin.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
# -*- coding: utf-8 -*-
2-
from __future__ import print_function
2+
3+
from __future__ import annotations
34

45
import os
56
import sys
67
from collections import OrderedDict
8+
from typing import TYPE_CHECKING
9+
10+
from _pytest._code.code import ExceptionRepr
711

812
import pytest
913

14+
if TYPE_CHECKING:
15+
from _pytest.nodes import Item
16+
from _pytest.reports import CollectReport
17+
18+
1019
# Reference:
1120
# https://docs.pytest.org/en/latest/writing_plugins.html#hookwrapper-executing-around-other-hooks
1221
# https://docs.pytest.org/en/latest/writing_plugins.html#hook-function-ordering-call-example
@@ -17,10 +26,10 @@
1726

1827

1928
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
20-
def pytest_runtest_makereport(item, call):
29+
def pytest_runtest_makereport(item: Item, call):
2130
# execute all other hooks to obtain the report object
2231
outcome = yield
23-
report = outcome.get_result()
32+
report: CollectReport = outcome.get_result()
2433

2534
# enable only in a workflow of GitHub Actions
2635
# ref: https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
@@ -57,13 +66,14 @@ def pytest_runtest_makereport(item, call):
5766
longrepr = report.head_line or item.name
5867

5968
# get the error message and line number from the actual error
60-
if hasattr(report.longrepr, "reprcrash"):
61-
longrepr += "\n\n" + report.longrepr.reprcrash.message
62-
traceback_entries = report.longrepr.reprtraceback.reprentries
63-
if len(traceback_entries) > 1:
69+
if isinstance(report.longrepr, ExceptionRepr):
70+
if report.longrepr.reprcrash is not None:
71+
longrepr += "\n\n" + report.longrepr.reprcrash.message
72+
tb_entries = report.longrepr.reprtraceback.reprentries
73+
if len(tb_entries) > 1 and tb_entries[0].reprfileloc is not None:
6474
# Handle third-party exceptions
65-
lineno = traceback_entries[0].reprfileloc.lineno
66-
else:
75+
lineno = tb_entries[0].reprfileloc.lineno
76+
elif report.longrepr.reprcrash is not None:
6777
lineno = report.longrepr.reprcrash.lineno
6878
elif isinstance(report.longrepr, tuple):
6979
_, lineno, message = report.longrepr

0 commit comments

Comments
 (0)