Skip to content

Commit a076bad

Browse files
authored
[py] Reduce traceback output on test failures (SeleniumHQ#16854)
1 parent d58b3a4 commit a076bad

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

py/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ TEST_DEPS = [
7777
requirement("pytest-instafail"),
7878
requirement("pytest-trio"),
7979
requirement("pytest-mock"),
80+
requirement("rich"),
8081
requirement("zipp"),
8182
"@rules_python//python/runfiles",
8283
]

py/conftest.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
import socketserver
2121
import sys
2222
import threading
23+
import types
2324
from dataclasses import dataclass
2425
from pathlib import Path
2526

2627
import pytest
28+
import rich.console
29+
import rich.traceback
2730

2831
from selenium import webdriver
2932
from selenium.common.exceptions import WebDriverException
@@ -44,6 +47,64 @@
4447
)
4548

4649

50+
TRACEBACK_WIDTH = 130
51+
# don't force colors on RBE since errors get redirected to a log file
52+
force_terminal = "REMOTE_BUILD" not in os.environ
53+
console = rich.console.Console(force_terminal=force_terminal, width=TRACEBACK_WIDTH)
54+
55+
56+
def extract_traceback_frames(tb):
57+
"""Extract frames from a traceback object."""
58+
frames = []
59+
while tb:
60+
if hasattr(tb, "tb_frame") and hasattr(tb, "tb_lineno"):
61+
# Skip frames without source files
62+
if Path(tb.tb_frame.f_code.co_filename).exists():
63+
frames.append((tb.tb_frame, tb.tb_lineno, getattr(tb, "tb_lasti", 0)))
64+
tb = getattr(tb, "tb_next", None)
65+
return frames
66+
67+
68+
def filter_frames(frames):
69+
"""Filter out frames from pytest internals."""
70+
skip_modules = ["pytest", "_pytest", "pluggy"]
71+
filtered = []
72+
for frame, lineno, lasti in reversed(frames):
73+
mod_name = frame.f_globals.get("__name__", "")
74+
if not any(skip in mod_name for skip in skip_modules):
75+
filtered.append((frame, lineno, lasti))
76+
return filtered
77+
78+
79+
def rebuild_traceback(frames):
80+
"""Rebuild a traceback object from frames list."""
81+
new_tb = None
82+
for frame, lineno, lasti in frames:
83+
new_tb = types.TracebackType(new_tb, frame, lasti, lineno)
84+
return new_tb
85+
86+
87+
def pytest_runtest_makereport(item, call):
88+
"""Hook to print Rich traceback for test failures."""
89+
if call.excinfo is None:
90+
return
91+
exc_type = call.excinfo.type
92+
exc_value = call.excinfo.value
93+
exc_tb = call.excinfo.tb
94+
frames = extract_traceback_frames(exc_tb)
95+
filtered_frames = filter_frames(frames)
96+
new_tb = rebuild_traceback(filtered_frames)
97+
tb = rich.traceback.Traceback.from_exception(
98+
exc_type,
99+
exc_value,
100+
new_tb,
101+
show_locals=False,
102+
max_frames=5,
103+
width=TRACEBACK_WIDTH,
104+
)
105+
console.print("\n", tb)
106+
107+
47108
def pytest_addoption(parser):
48109
parser.addoption(
49110
"--driver",

py/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ console_output_style = "progress"
9292
faulthandler_timeout = "60"
9393
log_cli = true
9494
trio_mode = true
95+
addopts = ["-s", "--tb=no"]
96+
filterwarnings = [
97+
"ignore::DeprecationWarning",
98+
]
9599
markers = [
96100
"xfail_chrome: Tests expected to fail in Chrome",
97101
"xfail_edge: Tests expected to fail in Edge",

0 commit comments

Comments
 (0)