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:
parent
ffd47ceefc
commit
3fcc4cdbd5
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*"])
|
||||
|
|
Loading…
Reference in New Issue