Skip to content

Support Pytest 7.4+ #97

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Support Pytest 7.4+
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
  • Loading branch information
edgarrmondragon and henryiii committed Jan 17, 2025
commit 0ed069599d3d2d338e0f9dc303f849a3049b86b9
6 changes: 6 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ jobs:
PYTEST_MAJOR_VERSION: 7
PYTEST_PLUGINS: pytest_github_actions_annotate_failures

- name: Run tests with PyTest 8
run: tox
env:
PYTEST_MAJOR_VERSION: 8
PYTEST_PLUGINS: pytest_github_actions_annotate_failures

post-test:
name: All tests passed
if: always()
Expand Down
2 changes: 1 addition & 1 deletion plugin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def test_fail():
result = testdir.runpytest_subprocess("--rootdir=foo")
result.stderr.fnmatch_lines(
[
"::error file=test_annotation_fail_cwd.py,line=5::test_fail*assert 0*",
"::error file=test_annotation_fail_cwd0/test_annotation_fail_cwd.py,line=5::test_fail*assert 0*",
]
)

Expand Down
74 changes: 40 additions & 34 deletions pytest_github_actions_annotate_failures/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import TYPE_CHECKING

import pytest
from _pytest._code.code import ExceptionRepr
from _pytest._code.code import ExceptionRepr, ReprEntry
from packaging import version

if TYPE_CHECKING:
Expand Down Expand Up @@ -38,59 +38,65 @@ def pytest_runtest_makereport(item: Item, call): # noqa: ARG001
return

if report.when == "call" and report.failed:
# collect information to be annotated
filesystempath, lineno, _ = report.location

runpath = os.environ.get("PYTEST_RUN_PATH")
if runpath:
filesystempath = os.path.join(runpath, filesystempath)

# try to convert to absolute path in GitHub Actions
workspace = os.environ.get("GITHUB_WORKSPACE")
if workspace:
full_path = os.path.abspath(filesystempath)
try:
rel_path = os.path.relpath(full_path, workspace)
except ValueError:
# os.path.relpath() will raise ValueError on Windows
# when full_path and workspace have different mount points.
# https://github.com/utgwkk/pytest-github-actions-annotate-failures/issues/20
rel_path = filesystempath
if not rel_path.startswith(".."):
filesystempath = rel_path

if lineno is not None:
# 0-index to 1-index
lineno += 1

# get the name of the current failed test, with parametrize info
longrepr = report.head_line or item.name

# get the error message and line number from the actual error
if isinstance(report.longrepr, ExceptionRepr):
if report.longrepr.reprcrash is not None:
longrepr += "\n\n" + report.longrepr.reprcrash.message
tb_entries = report.longrepr.reprtraceback.reprentries
if len(tb_entries) > 1 and tb_entries[0].reprfileloc is not None:
if tb_entries:
entry = tb_entries[0]
# Handle third-party exceptions
lineno = tb_entries[0].reprfileloc.lineno
if isinstance(entry, ReprEntry) and entry.reprfileloc is not None:
lineno = entry.reprfileloc.lineno
filesystempath = entry.reprfileloc.path

elif report.longrepr.reprcrash is not None:
lineno = report.longrepr.reprcrash.lineno
elif isinstance(report.longrepr, tuple):
_, lineno, message = report.longrepr
filesystempath, lineno, message = report.longrepr
longrepr += "\n\n" + message
elif isinstance(report.longrepr, str):
longrepr += "\n\n" + report.longrepr

workflow_command = _build_workflow_command(
"error",
filesystempath,
compute_path(filesystempath),
lineno,
message=longrepr,
)
print(workflow_command, file=sys.stderr)


def compute_path(filesystempath: str) -> str:
"""Extract and process location information from the report."""
runpath = os.environ.get("PYTEST_RUN_PATH")
if runpath:
filesystempath = os.path.join(runpath, filesystempath)

# try to convert to absolute path in GitHub Actions
workspace = os.environ.get("GITHUB_WORKSPACE")
if workspace:
full_path = os.path.abspath(filesystempath)
try:
rel_path = os.path.relpath(full_path, workspace)
except ValueError:
# os.path.relpath() will raise ValueError on Windows
# when full_path and workspace have different mount points.
rel_path = filesystempath
if not rel_path.startswith(".."):
filesystempath = rel_path

return filesystempath


class _AnnotateWarnings:
def pytest_warning_recorded(self, warning_message, when, nodeid, location): # noqa: ARG002
# enable only in a workflow of GitHub Actions
Expand Down Expand Up @@ -139,14 +145,14 @@ def pytest_configure(config):


def _build_workflow_command(
command_name,
file,
line,
end_line=None,
column=None,
end_column=None,
title=None,
message=None,
command_name: str,
file: str,
line: int,
end_line: int | None = None,
column: int | None = None,
end_column: int | None = None,
title: str | None = None,
message: str | None = None,
):
"""Build a command to annotate a workflow."""
result = f"::{command_name} "
Expand All @@ -168,5 +174,5 @@ def _build_workflow_command(
return result


def _escape(s):
def _escape(s: str) -> str:
return s.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ PYTEST_MAJOR_VERSION =
extras = test
deps =
pytest6: pytest>=6.0.0,<7.0.0
pytest7: pytest>=7.0.0,<7.4.0
pytest7: pytest>=7.0.0,<8.0.0
pytest8: pytest>=8.0.0,<9.0.0

commands = {envpython} -m pytest {posargs}
Loading