Make terminal capture pytest_warning_capture

pytest_logwarning is no longer emitted by the warnings plugin,
only ever emitted from .warn() functions in config and item
This commit is contained in:
Bruno Oliveira 2018-08-25 22:15:22 -03:00
parent ffd47ceefc
commit 3fcc4cdbd5
5 changed files with 59 additions and 35 deletions

View File

@ -536,13 +536,13 @@ def pytest_logwarning(message, code, nodeid, fslocation):
@hookspec(historic=True)
def pytest_warning_captured(warning, when, item):
def pytest_warning_captured(warning_message, when, item):
"""
Process a warning captured by the internal pytest plugin.
:param warnings.WarningMessage warning:
:param warnings.WarningMessage warning_message:
The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains
the same attributes as :py:func:`warnings.showwarning`.
the same attributes as the parameters of :py:func:`warnings.showwarning`.
:param str when:
Indicates when the warning was captured. Possible values:

View File

@ -138,9 +138,7 @@ class Node(object):
""" generate a warning with the given code and message for this
item. """
assert isinstance(code, str)
fslocation = getattr(self, "location", None)
if fslocation is None:
fslocation = getattr(self, "fspath", None)
fslocation = get_fslocation_from_item(self)
self.ihook.pytest_logwarning.call_historic(
kwargs=dict(
code=code, message=message, nodeid=self.nodeid, fslocation=fslocation
@ -310,6 +308,18 @@ class Node(object):
repr_failure = _repr_failure_py
def get_fslocation_from_item(item):
"""Tries to extract the actual location from an item, depending on available attributes:
* "fslocation": a pair (path, lineno)
* "fspath": just a path
"""
fslocation = getattr(item, "location", None)
if fslocation is None:
fslocation = getattr(item, "fspath", None)
return fslocation
class Collector(Node):
""" Collector instances create children through collect()
and thus iteratively build a tree.

View File

@ -9,6 +9,7 @@ import platform
import sys
import time
import attr
import pluggy
import py
import six
@ -184,23 +185,20 @@ def pytest_report_teststatus(report):
return report.outcome, letter, report.outcome.upper()
@attr.s
class WarningReport(object):
"""
Simple structure to hold warnings information captured by ``pytest_logwarning``.
"""
def __init__(self, code, message, nodeid=None, fslocation=None):
"""
:param code: unused
:param str message: user friendly message about the warning
:param str|None nodeid: node id that generated the warning (see ``get_location``).
:param tuple|py.path.local fslocation:
:ivar str message: user friendly message about the warning
:ivar str|None nodeid: node id that generated the warning (see ``get_location``).
:ivar tuple|py.path.local fslocation:
file system location of the source of the warning (see ``get_location``).
"""
self.code = code
self.message = message
self.nodeid = nodeid
self.fslocation = fslocation
message = attr.ib()
nodeid = attr.ib(default=None)
fslocation = attr.ib(default=None)
def get_location(self, config):
"""
@ -327,13 +325,25 @@ class TerminalReporter(object):
self.write_line("INTERNALERROR> " + line)
return 1
def pytest_logwarning(self, code, fslocation, message, nodeid):
def pytest_logwarning(self, fslocation, message, nodeid):
warnings = self.stats.setdefault("warnings", [])
warning = WarningReport(
code=code, fslocation=fslocation, message=message, nodeid=nodeid
)
warning = WarningReport(fslocation=fslocation, message=message, nodeid=nodeid)
warnings.append(warning)
def pytest_warning_captured(self, warning_message, item):
from _pytest.nodes import get_fslocation_from_item
from _pytest.warnings import warning_record_to_str
warnings = self.stats.setdefault("warnings", [])
fslocation = get_fslocation_from_item(item)
message = warning_record_to_str(warning_message)
warning_report = WarningReport(
fslocation=fslocation, message=message, nodeid=item.nodeid
)
warnings.append(warning_report)
def pytest_plugin_registered(self, plugin):
if self.config.option.traceconfig:
msg = "PLUGIN registered: %s" % (plugin,)

View File

@ -58,7 +58,7 @@ def pytest_configure(config):
@contextmanager
def deprecated_catch_warnings_for_item(item):
def catch_warnings_for_item(item):
"""
catches the warnings generated during setup/call/teardown execution
of the given item and after it is done posts them as warnings to this
@ -79,18 +79,18 @@ def deprecated_catch_warnings_for_item(item):
yield
for warning in log:
for warning_message in log:
item.ihook.pytest_warning_captured.call_historic(
kwargs=dict(warning=warning, when="runtest", item=item)
kwargs=dict(warning_message=warning_message, when="runtest", item=item)
)
deprecated_emit_warning(item, warning)
def deprecated_emit_warning(item, warning):
def warning_record_to_str(warning_message):
"""Convert a warnings.WarningMessage to a string, taking in account a lot of unicode shenaningans in Python 2.
When Python 2 support is tropped this function can be greatly simplified.
"""
Emits the deprecated ``pytest_logwarning`` for the given warning and item.
"""
warn_msg = warning.message
warn_msg = warning_message.message
unicode_warning = False
if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args):
new_args = []
@ -102,18 +102,22 @@ def deprecated_emit_warning(item, warning):
warn_msg.args = new_args
msg = warnings.formatwarning(
warn_msg, warning.category, warning.filename, warning.lineno, warning.line
warn_msg,
warning_message.category,
warning_message.filename,
warning_message.lineno,
warning_message.line,
)
item.warn("unused", msg)
if unicode_warning:
warnings.warn(
"Warning is using unicode non convertible to ascii, "
"converting to a safe representation:\n %s" % msg,
UnicodeWarning,
)
return msg
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item):
with deprecated_catch_warnings_for_item(item):
with catch_warnings_for_item(item):
yield

View File

@ -310,8 +310,8 @@ def test_warning_captured_hook(testdir, pyfile_with_warnings):
collected = []
class WarningCollector:
def pytest_warning_captured(self, warning, when, item):
collected.append((warning.category, when, item.name))
def pytest_warning_captured(self, warning_message, when, item):
collected.append((warning_message.category, when, item.name))
result = testdir.runpytest(plugins=[WarningCollector()])
result.stdout.fnmatch_lines(["*1 passed*"])