Capture and display warnings during collection

Fix #3251
This commit is contained in:
Bruno Oliveira 2018-08-26 11:09:54 -03:00
parent 51e32cf7cc
commit 1a9d913ee1
4 changed files with 51 additions and 13 deletions

View File

@ -0,0 +1 @@
Warnings are now captured and displayed during test collection.

View File

@ -339,8 +339,9 @@ class TerminalReporter(object):
fslocation = get_fslocation_from_item(item) fslocation = get_fslocation_from_item(item)
message = warning_record_to_str(warning_message) message = warning_record_to_str(warning_message)
nodeid = item.nodeid if item is not None else ""
warning_report = WarningReport( warning_report = WarningReport(
fslocation=fslocation, message=message, nodeid=item.nodeid fslocation=fslocation, message=message, nodeid=nodeid
) )
warnings.append(warning_report) warnings.append(warning_report)
@ -707,7 +708,8 @@ class TerminalReporter(object):
self.write_sep("=", "warnings summary", yellow=True, bold=False) self.write_sep("=", "warnings summary", yellow=True, bold=False)
for location, warning_records in grouped: for location, warning_records in grouped:
self._tw.line(str(location) if location else "<undetermined location>") if location:
self._tw.line(str(location))
for w in warning_records: for w in warning_records:
lines = w.message.splitlines() lines = w.message.splitlines()
indented = "\n".join(" " + x for x in lines) indented = "\n".join(" " + x for x in lines)

View File

@ -58,14 +58,16 @@ def pytest_configure(config):
@contextmanager @contextmanager
def catch_warnings_for_item(item): def catch_warnings_for_item(config, ihook, item):
""" """
catches the warnings generated during setup/call/teardown execution Context manager that catches warnings generated in the contained execution block.
of the given item and after it is done posts them as warnings to this
item. ``item`` can be None if we are not in the context of an item execution.
Each warning captured triggers the ``pytest_warning_captured`` hook.
""" """
args = item.config.getoption("pythonwarnings") or [] args = config.getoption("pythonwarnings") or []
inifilters = item.config.getini("filterwarnings") inifilters = config.getini("filterwarnings")
with warnings.catch_warnings(record=True) as log: with warnings.catch_warnings(record=True) as log:
for arg in args: for arg in args:
warnings._setoption(arg) warnings._setoption(arg)
@ -73,14 +75,15 @@ def catch_warnings_for_item(item):
for arg in inifilters: for arg in inifilters:
_setoption(warnings, arg) _setoption(warnings, arg)
for mark in item.iter_markers(name="filterwarnings"): if item is not None:
for arg in mark.args: for mark in item.iter_markers(name="filterwarnings"):
warnings._setoption(arg) for arg in mark.args:
warnings._setoption(arg)
yield yield
for warning_message in log: for warning_message in log:
item.ihook.pytest_warning_captured.call_historic( ihook.pytest_warning_captured.call_historic(
kwargs=dict(warning_message=warning_message, when="runtest", item=item) kwargs=dict(warning_message=warning_message, when="runtest", item=item)
) )
@ -119,5 +122,12 @@ def warning_record_to_str(warning_message):
@pytest.hookimpl(hookwrapper=True) @pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item): def pytest_runtest_protocol(item):
with catch_warnings_for_item(item): with catch_warnings_for_item(config=item.config, ihook=item.ihook, item=item):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_collection(session):
config = session.config
with catch_warnings_for_item(config=config, ihook=config.hook, item=None):
yield yield

View File

@ -321,3 +321,28 @@ def test_warning_captured_hook(testdir, pyfile_with_warnings):
(RuntimeWarning, "runtest", "test_func"), (RuntimeWarning, "runtest", "test_func"),
] ]
assert collected == expected assert collected == expected
@pytest.mark.filterwarnings("always")
def test_collection_warnings(testdir):
"""
"""
testdir.makepyfile(
"""
import warnings
warnings.warn(UserWarning("collection warning"))
def test_foo():
pass
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*== %s ==*" % WARNINGS_SUMMARY_HEADER,
"*collection_warnings.py:3: UserWarning: collection warning",
' warnings.warn(UserWarning("collection warning"))',
"* 1 passed, 1 warnings*",
]
)