Skip to content

Commit b151e56

Browse files
committed
Handle unserializable warning arguments
Fix #349
1 parent 70688b7 commit b151e56

File tree

4 files changed

+37
-4
lines changed

4 files changed

+37
-4
lines changed

changelog/349.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correctly handle warnings created with arguments that can't be serialized during the transfer from workers to master node.

testing/acceptance_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,23 @@ def test_func(request):
766766
result = testdir.runpytest(n)
767767
result.stdout.fnmatch_lines(["*MyWarning*", "*1 passed, 1 warnings*"])
768768

769+
@pytest.mark.parametrize("n", ["-n0", "-n1"])
770+
def test_unserializable_arguments(self, testdir, n):
771+
"""Check that warnings with unserializable arguments are handled correctly (#349)."""
772+
testdir.makepyfile(
773+
"""
774+
import warnings, pytest
775+
776+
def test_func(tmpdir):
777+
fn = (tmpdir / 'foo.txt').ensure(file=1)
778+
with fn.open('r') as f:
779+
warnings.warn(UserWarning("foo", f))
780+
"""
781+
)
782+
testdir.syspathinsert()
783+
result = testdir.runpytest(n)
784+
result.stdout.fnmatch_lines(["*UserWarning*foo.txt*", "*1 passed, 1 warnings*"])
785+
769786

770787
class TestNodeFailure:
771788
def test_load_single(self, testdir):

xdist/remote.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import _pytest.hookspec
1414
import pytest
15+
from execnet.gateway_base import dumps, DumpError
1516

1617

1718
class WorkerInteractor(object):
@@ -181,8 +182,15 @@ def serialize_warning_message(warning_message):
181182
if isinstance(warning_message.message, Warning):
182183
message_module = type(warning_message.message).__module__
183184
message_class_name = type(warning_message.message).__name__
184-
message_args = warning_message.message.args
185185
message_str = str(warning_message.message)
186+
# check now if we can serialize the warning arguments (#349)
187+
# if not, we will just use the exception message on the master node
188+
try:
189+
dumps(warning_message.message.args)
190+
except DumpError:
191+
message_args = None
192+
else:
193+
message_args = warning_message.message.args
186194
else:
187195
message_str = warning_message.message
188196
message_module = None

xdist/workermanage.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,16 @@ def unserialize_warning_message(data):
426426
if data["message_module"]:
427427
mod = importlib.import_module(data["message_module"])
428428
cls = getattr(mod, data["message_class_name"])
429-
try:
430-
message = cls(*data["message_args"])
431-
except TypeError:
429+
message = None
430+
if data["message_args"] is not None:
431+
try:
432+
message = cls(*data["message_args"])
433+
except TypeError:
434+
pass
435+
if message is None:
436+
# could not recreate the original warning instance;
437+
# create a generic Warning instance with the original
438+
# message at least
432439
message_text = "{mod}.{cls}: {msg}".format(
433440
mod=data["message_module"],
434441
cls=data["message_class_name"],

0 commit comments

Comments
 (0)