Merge pull request #342 from nicoddemus/3.8-warnings
Fix warnings transfer between workers and master node with pytest >= 3.8
This commit is contained in:
@@ -22,6 +22,7 @@ env:
|
||||
- TOXENV=py-pytest32
|
||||
- TOXENV=py-pytest33
|
||||
- TOXENV=py-pytest36
|
||||
- TOXENV=py-pytest38
|
||||
|
||||
install: pip install tox setuptools_scm
|
||||
script: tox
|
||||
|
||||
@@ -6,6 +6,7 @@ environment:
|
||||
- TOXENV: "py35-pytest33"
|
||||
- TOXENV: "py36-pytest33"
|
||||
- TOXENV: "py36-pytest36"
|
||||
- TOXENV: "py36-pytest38"
|
||||
- TOXENV: "py27-pytest33-pexpect"
|
||||
- TOXENV: "py36-pytest33-pexpect"
|
||||
|
||||
|
||||
1
changelog/341.bugfix.rst
Normal file
1
changelog/341.bugfix.rst
Normal file
@@ -0,0 +1 @@
|
||||
Fix warnings transfer between workers and master node with pytest >= 3.8.
|
||||
@@ -402,7 +402,7 @@ class TestTerminalReporting:
|
||||
|
||||
@pytest.mark.parametrize("n", ["-n0", "-n1"])
|
||||
@pytest.mark.parametrize("warn_type", ["pytest", "builtin"])
|
||||
def test_logwarning(self, testdir, n, warn_type):
|
||||
def test_warnings(self, testdir, n, warn_type):
|
||||
from pkg_resources import parse_version
|
||||
|
||||
if parse_version(pytest.__version__) < parse_version("3.1"):
|
||||
@@ -417,7 +417,9 @@ class TestTerminalReporting:
|
||||
assert False
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import warnings, py
|
||||
import warnings, py, pytest
|
||||
|
||||
@pytest.mark.filterwarnings('ignore:config.warn has been deprecated')
|
||||
def test_func(request):
|
||||
{warn_code}
|
||||
""".format(
|
||||
|
||||
3
tox.ini
3
tox.ini
@@ -2,7 +2,7 @@
|
||||
# if you change the envlist, please update .travis.yml file as well
|
||||
envlist=
|
||||
linting
|
||||
py{27,34,35,36}-pytest{30,31,32,33,36}
|
||||
py{27,34,35,36}-pytest{30,31,32,33,36,38}
|
||||
py{27,36}-pytest36-pexpect
|
||||
py{27,36}-pytest{master,features}
|
||||
|
||||
@@ -19,6 +19,7 @@ deps =
|
||||
pytest32: pytest~=3.2.0
|
||||
pytest33: pytest~=3.3.0
|
||||
pytest36: pytest~=3.6.0
|
||||
pytest38: pytest~=3.8.0
|
||||
pytestmaster: git+https://github.com/pytest-dev/pytest.git@master
|
||||
pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features
|
||||
pexpect: pexpect
|
||||
|
||||
@@ -270,6 +270,11 @@ class DSession(object):
|
||||
kwargs = dict(message=message, code=code, nodeid=nodeid, fslocation=fslocation)
|
||||
self.config.hook.pytest_logwarning.call_historic(kwargs=kwargs)
|
||||
|
||||
def worker_warning_captured(self, warning_message, when, item):
|
||||
"""Emitted when a node calls the pytest_logwarning hook."""
|
||||
kwargs = dict(warning_message=warning_message, when=when, item=item)
|
||||
self.config.hook.pytest_warning_captured.call_historic(kwargs=kwargs)
|
||||
|
||||
def _clone_node(self, node):
|
||||
"""Return new node based on an existing one.
|
||||
|
||||
|
||||
@@ -123,6 +123,18 @@ class WorkerInteractor(object):
|
||||
fslocation=str(fslocation),
|
||||
)
|
||||
|
||||
# the pytest_warning_captured hook was introduced in pytest 3.8
|
||||
if hasattr(_pytest.hookspec, "pytest_warning_captured"):
|
||||
|
||||
def pytest_warning_captured(self, warning_message, when, item):
|
||||
self.sendevent(
|
||||
"warning_captured",
|
||||
warning_message_data=serialize_warning_message(warning_message),
|
||||
when=when,
|
||||
# item cannot be serialized and will always be None when used with xdist
|
||||
item=None,
|
||||
)
|
||||
|
||||
|
||||
def serialize_report(rep):
|
||||
def disassembled_report(rep):
|
||||
@@ -165,6 +177,40 @@ def serialize_report(rep):
|
||||
return d
|
||||
|
||||
|
||||
def serialize_warning_message(warning_message):
|
||||
if isinstance(warning_message.message, Warning):
|
||||
message_module = type(warning_message.message).__module__
|
||||
message_class_name = type(warning_message.message).__name__
|
||||
message_args = warning_message.message.args
|
||||
message_str = None
|
||||
else:
|
||||
message_str = warning_message.message
|
||||
message_module = None
|
||||
message_class_name = None
|
||||
message_args = None
|
||||
if warning_message.category:
|
||||
category_module = warning_message.category.__module__
|
||||
category_class_name = warning_message.category.__name__
|
||||
else:
|
||||
category_module = None
|
||||
category_class_name = None
|
||||
|
||||
result = {
|
||||
"message_str": message_str,
|
||||
"message_module": message_module,
|
||||
"message_class_name": message_class_name,
|
||||
"message_args": message_args,
|
||||
"category_module": category_module,
|
||||
"category_class_name": category_class_name,
|
||||
}
|
||||
# access private _WARNING_DETAILS because the attributes vary between Python versions
|
||||
for attr_name in warning_message._WARNING_DETAILS:
|
||||
if attr_name in ("message", "category"):
|
||||
continue
|
||||
result[attr_name] = getattr(warning_message, attr_name)
|
||||
return result
|
||||
|
||||
|
||||
def getinfodict():
|
||||
import platform
|
||||
|
||||
|
||||
@@ -327,6 +327,16 @@ class WorkerController(object):
|
||||
nodeid=kwargs["nodeid"],
|
||||
fslocation=kwargs["nodeid"],
|
||||
)
|
||||
elif eventname == "warning_captured":
|
||||
warning_message = unserialize_warning_message(
|
||||
kwargs["warning_message_data"]
|
||||
)
|
||||
self.notify_inproc(
|
||||
eventname,
|
||||
warning_message=warning_message,
|
||||
when=kwargs["when"],
|
||||
item=kwargs["item"],
|
||||
)
|
||||
else:
|
||||
raise ValueError("unknown event: %s" % (eventname,))
|
||||
except KeyboardInterrupt:
|
||||
@@ -409,6 +419,33 @@ def unserialize_report(name, reportdict):
|
||||
return runner.CollectReport(**assembled_report(reportdict))
|
||||
|
||||
|
||||
def unserialize_warning_message(data):
|
||||
import warnings
|
||||
import importlib
|
||||
|
||||
if data["message_module"]:
|
||||
mod = importlib.import_module(data["message_module"])
|
||||
cls = getattr(mod, data["message_class_name"])
|
||||
message = cls(*data["message_args"])
|
||||
else:
|
||||
message = data["message_str"]
|
||||
|
||||
if data["category_module"]:
|
||||
mod = importlib.import_module(data["category_module"])
|
||||
category = getattr(mod, data["category_class_name"])
|
||||
else:
|
||||
category = None
|
||||
|
||||
kwargs = {"message": message, "category": category}
|
||||
# access private _WARNING_DETAILS because the attributes vary between Python versions
|
||||
for attr_name in warnings.WarningMessage._WARNING_DETAILS:
|
||||
if attr_name in ("message", "category"):
|
||||
continue
|
||||
kwargs[attr_name] = data[attr_name]
|
||||
|
||||
return warnings.WarningMessage(**kwargs)
|
||||
|
||||
|
||||
def report_unserialization_failure(type_name, report_name, reportdict):
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
Reference in New Issue
Block a user