Skip to content

Commit 9ee6550

Browse files
committed
Add in a new hook pytest_warning_recorded for warning capture
communication
1 parent c594bbb commit 9ee6550

File tree

5 files changed

+50
-23
lines changed

5 files changed

+50
-23
lines changed

doc/en/reference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ Session related reporting hooks:
711711
.. autofunction:: pytest_fixture_setup
712712
.. autofunction:: pytest_fixture_post_finalizer
713713
.. autofunction:: pytest_warning_captured
714+
.. autofunction:: pytest_warning_record
714715

715716
Central hook for reporting about test execution:
716717

src/_pytest/hookspec.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,10 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config):
622622

623623
@hookspec(historic=True)
624624
def pytest_warning_captured(warning_message, when, item, location):
625-
"""
626-
Process a warning captured by the internal pytest warnings plugin.
625+
"""(**Deprecated**) Process a warning captured by the internal pytest warnings plugin.
626+
627+
This hook is considered deprecated and will be removed in a future pytest version.
628+
Use :func:`pytest_warning_record` instead.
627629
628630
:param warnings.WarningMessage warning_message:
629631
The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains
@@ -637,9 +639,6 @@ def pytest_warning_captured(warning_message, when, item, location):
637639
* ``"runtest"``: during test execution.
638640
639641
:param pytest.Item|None item:
640-
**DEPRECATED**: This parameter is incompatible with ``pytest-xdist``, and will always receive ``None``
641-
in a future release.
642-
643642
The item being executed if ``when`` is ``"runtest"``, otherwise ``None``.
644643
645644
:param tuple location:
@@ -648,6 +647,30 @@ def pytest_warning_captured(warning_message, when, item, location):
648647
"""
649648

650649

650+
@hookspec(historic=True)
651+
def pytest_warning_record(warning_message, when, nodeid, location):
652+
"""
653+
Process a warning captured by the internal pytest warnings plugin.
654+
655+
:param warnings.WarningMessage warning_message:
656+
The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains
657+
the same attributes as the parameters of :py:func:`warnings.showwarning`.
658+
659+
:param str when:
660+
Indicates when the warning was captured. Possible values:
661+
662+
* ``"config"``: during pytest configuration/initialization stage.
663+
* ``"collect"``: during test collection.
664+
* ``"runtest"``: during test execution.
665+
666+
:param str nodeid: full id of the item
667+
668+
:param tuple location:
669+
Holds information about the execution context of the captured warning (filename, linenumber, function).
670+
``function`` evaluates to <module> when the execution context is at the module level.
671+
"""
672+
673+
651674
# -------------------------------------------------------------------------
652675
# doctest hooks
653676
# -------------------------------------------------------------------------

src/_pytest/terminal.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def pytest_report_teststatus(report: TestReport) -> Tuple[str, str, str]:
227227
@attr.s
228228
class WarningReport:
229229
"""
230-
Simple structure to hold warnings information captured by ``pytest_warning_captured``.
230+
Simple structure to hold warnings information captured by ``pytest_warning_record``.
231231
232232
:ivar str message: user friendly message about the warning
233233
:ivar str|None nodeid: node id that generated the warning (see ``get_location``).
@@ -412,13 +412,14 @@ def pytest_internalerror(self, excrepr):
412412
return 1
413413

414414
def pytest_warning_captured(self, warning_message, item):
415-
# from _pytest.nodes import get_fslocation_from_item
415+
pass
416+
417+
def pytest_warning_record(self, warning_message, nodeid):
416418
from _pytest.warnings import warning_record_to_str
417419

418420
fslocation = warning_message.filename, warning_message.lineno
419421
message = warning_record_to_str(warning_message)
420422

421-
nodeid = item.nodeid if item is not None else ""
422423
warning_report = WarningReport(
423424
fslocation=fslocation, message=message, nodeid=nodeid
424425
)

src/_pytest/warnings.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def catch_warnings_for_item(config, ihook, when, item):
8181
8282
``item`` can be None if we are not in the context of an item execution.
8383
84-
Each warning captured triggers the ``pytest_warning_captured`` hook.
84+
Each warning captured triggers the ``pytest_warning_record`` hook.
8585
"""
8686
cmdline_filters = config.getoption("pythonwarnings") or []
8787
inifilters = config.getini("filterwarnings")
@@ -102,6 +102,7 @@ def catch_warnings_for_item(config, ihook, when, item):
102102
for arg in cmdline_filters:
103103
warnings.filterwarnings(*_parse_filter(arg, escape=True))
104104

105+
nodeid = "" if item is None else item.nodeid
105106
if item is not None:
106107
for mark in item.iter_markers(name="filterwarnings"):
107108
for arg in mark.args:
@@ -110,8 +111,9 @@ def catch_warnings_for_item(config, ihook, when, item):
110111
yield
111112

112113
for warning_message in log:
113-
ihook.pytest_warning_captured.call_historic(
114-
kwargs=dict(warning_message=warning_message, when=when, item=item)
114+
# raise ValueError(ihook.pytest_warning_record)
115+
ihook.pytest_warning_record.call_historic(
116+
kwargs=dict(warning_message=warning_message, nodeid=nodeid, when=when)
115117
)
116118

117119

@@ -166,8 +168,9 @@ def pytest_sessionfinish(session):
166168
def _issue_warning_captured(warning, hook, stacklevel):
167169
"""
168170
This function should be used instead of calling ``warnings.warn`` directly when we are in the "configure" stage:
169-
at this point the actual options might not have been set, so we manually trigger the pytest_warning_captured
170-
hook so we can display these warnings in the terminal. This is a hack until we can sort out #2891.
171+
at this point the actual options might not have been set, so we manually trigger the pytest_warning_record hooks
172+
so we can display these warnings in the terminal.
173+
This is a hack until we can sort out #2891.
171174
172175
:param warning: the warning instance.
173176
:param hook: the hook caller
@@ -180,8 +183,8 @@ def _issue_warning_captured(warning, hook, stacklevel):
180183
assert records is not None
181184
frame = sys._getframe(stacklevel - 1)
182185
location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name
183-
hook.pytest_warning_captured.call_historic(
186+
hook.pytest_warning_record.call_historic(
184187
kwargs=dict(
185-
warning_message=records[0], when="config", item=None, location=location
188+
warning_message=records[0], when="config", nodeid="", location=location
186189
)
187190
)

testing/test_warnings.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -268,21 +268,20 @@ def test_func(fix):
268268
collected = []
269269

270270
class WarningCollector:
271-
def pytest_warning_captured(self, warning_message, when, item):
272-
imge_name = item.name if item is not None else ""
273-
collected.append((str(warning_message.message), when, imge_name))
271+
def pytest_warning_record(self, warning_message, when, nodeid):
272+
collected.append((str(warning_message.message), when, nodeid))
274273

275274
result = testdir.runpytest(plugins=[WarningCollector()])
276275
result.stdout.fnmatch_lines(["*1 passed*"])
277276

278277
expected = [
279278
("config warning", "config", ""),
280279
("collect warning", "collect", ""),
281-
("setup warning", "runtest", "test_func"),
282-
("call warning", "runtest", "test_func"),
283-
("teardown warning", "runtest", "test_func"),
280+
("setup warning", "runtest", "test_warning_captured_hook.py::test_func"),
281+
("call warning", "runtest", "test_warning_captured_hook.py::test_func"),
282+
("teardown warning", "runtest", "test_warning_captured_hook.py::test_func"),
284283
]
285-
assert collected == expected
284+
assert collected == expected, str(collected)
286285

287286

288287
@pytest.mark.filterwarnings("always")
@@ -649,7 +648,7 @@ class CapturedWarnings:
649648
captured = []
650649

651650
@classmethod
652-
def pytest_warning_captured(cls, warning_message, when, item, location):
651+
def pytest_warning_record(cls, warning_message, when, nodeid, location):
653652
cls.captured.append((warning_message, location))
654653

655654
testdir.plugins = [CapturedWarnings()]

0 commit comments

Comments
 (0)