From 4632df37a6e00b7b1b059e8f3f5cc9b4147b20ac Mon Sep 17 00:00:00 2001 From: Alex Adamson Date: Wed, 21 Nov 2018 14:29:08 -0500 Subject: [PATCH 1/7] Check that we can serialize the warning detail attr --- xdist/remote.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xdist/remote.py b/xdist/remote.py index b0092e0..1faf3b2 100644 --- a/xdist/remote.py +++ b/xdist/remote.py @@ -218,7 +218,15 @@ def serialize_warning_message(warning_message): for attr_name in warning_message._WARNING_DETAILS: if attr_name in ("message", "category"): continue - result[attr_name] = getattr(warning_message, attr_name) + attr = getattr(warning_message, attr_name) + # Check if we can serialize the warning detail, marking `None` otherwise + # Note that we need to define the attr (even as `None`) to allow deserializing + try: + dumps(attr) + except DumpError: + result[attr_name] = None + else: + result[attr_name] = attr return result From d55fab3aa7d585dde952afc6fb246bc4929ee156 Mon Sep 17 00:00:00 2001 From: Alex Adamson Date: Wed, 21 Nov 2018 14:35:01 -0500 Subject: [PATCH 2/7] Add changelog entry --- changelog/379.bugfix.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/379.bugfix.rst diff --git a/changelog/379.bugfix.rst b/changelog/379.bugfix.rst new file mode 100644 index 0000000..f77ad12 --- /dev/null +++ b/changelog/379.bugfix.rst @@ -0,0 +1,2 @@ +Attributes of _WARNING_DETAILS are checked to make sure they can be dumped +prior to serializing the warning for submission to the master node. From fa85371c73b998743410a5bdb4a2f4842bed7ca8 Mon Sep 17 00:00:00 2001 From: Alex Adamson Date: Wed, 21 Nov 2018 15:26:35 -0500 Subject: [PATCH 3/7] Add test for #379 --- testing/acceptance_test.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 44b2efd..c0cefed 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -803,6 +803,37 @@ class TestWarnings: result = testdir.runpytest(n) result.stdout.fnmatch_lines(["*UserWarning*foo.txt*", "*1 passed, 1 warnings*"]) + @pytest.mark.parametrize("n", ["-n0", "-n1"]) + def test_unserializable_warning_details(self, testdir, n): + """Check that warnings with unserializable _WARNING_DETAILS are + handled correctly (#379). + """ + testdir.makepyfile( + """ + import warnings, pytest + import socket + import gc + def abuse_socket(): + s = socket.socket() + del s + + # Deliberately provoke a ResourceWarning for an unclosed socket. + # The socket itself will end up attached as a value in + # _WARNING_DETAIL. We need to test that it is not serialized + # (it can't be, so the test will fail if we try to). + def test_func(tmpdir): + warnings.resetwarnings() + warnings.simplefilter('always', ResourceWarning) + abuse_socket() + gc.collect() + """ + ) + testdir.syspathinsert() + result = testdir.runpytest(n) + result.stdout.fnmatch_lines( + ["*ResourceWarning*unclosed*", "*1 passed, 1 warnings*"] + ) + class TestNodeFailure: def test_load_single(self, testdir): From ac2bf10fe2c2d22cad4430f5f27b76ae6535ed71 Mon Sep 17 00:00:00 2001 From: Alex Adamson Date: Mon, 7 Jan 2019 18:43:59 -0500 Subject: [PATCH 4/7] Disable unserialized arguments check for py2 --- testing/acceptance_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index c0cefed..84c3bbe 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1,5 +1,6 @@ import os import re +import sys import textwrap import py @@ -808,6 +809,9 @@ class TestWarnings: """Check that warnings with unserializable _WARNING_DETAILS are handled correctly (#379). """ + if sys.version_info[0] < 3: + # The issue is only present in Python 3 warnings + return testdir.makepyfile( """ import warnings, pytest From 0ee219dc485fb7fd2d8aa10779577b975a8590cc Mon Sep 17 00:00:00 2001 From: Alex Adamson Date: Thu, 10 Jan 2019 15:45:48 -0500 Subject: [PATCH 5/7] Address comments --- changelog/379.bugfix.rst | 4 ++-- testing/acceptance_test.py | 3 +-- xdist/remote.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/changelog/379.bugfix.rst b/changelog/379.bugfix.rst index f77ad12..d569ce1 100644 --- a/changelog/379.bugfix.rst +++ b/changelog/379.bugfix.rst @@ -1,2 +1,2 @@ -Attributes of _WARNING_DETAILS are checked to make sure they can be dumped -prior to serializing the warning for submission to the master node. +Warning attributes are checked to make sure they can be dumped prior to +serializing the warning for submission to the master node. diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 84c3bbe..f462ecd 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -825,9 +825,8 @@ class TestWarnings: # The socket itself will end up attached as a value in # _WARNING_DETAIL. We need to test that it is not serialized # (it can't be, so the test will fail if we try to). + @pytest.mark.filterwarnings('always') def test_func(tmpdir): - warnings.resetwarnings() - warnings.simplefilter('always', ResourceWarning) abuse_socket() gc.collect() """ diff --git a/xdist/remote.py b/xdist/remote.py index 1faf3b2..2e97675 100644 --- a/xdist/remote.py +++ b/xdist/remote.py @@ -224,7 +224,7 @@ def serialize_warning_message(warning_message): try: dumps(attr) except DumpError: - result[attr_name] = None + result[attr_name] = repr(attr) else: result[attr_name] = attr return result From 242c00986629180057497c33637d8cf013364bf4 Mon Sep 17 00:00:00 2001 From: Alex Adamson Date: Thu, 10 Jan 2019 15:50:25 -0500 Subject: [PATCH 6/7] Remove trailing whitespace --- testing/acceptance_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index f462ecd..27f2c65 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -825,7 +825,7 @@ class TestWarnings: # The socket itself will end up attached as a value in # _WARNING_DETAIL. We need to test that it is not serialized # (it can't be, so the test will fail if we try to). - @pytest.mark.filterwarnings('always') + @pytest.mark.filterwarnings('always') def test_func(tmpdir): abuse_socket() gc.collect() From b9423d894c24937d280b688ab8d366a9c4e4cd80 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 10 Jan 2019 19:19:34 -0200 Subject: [PATCH 7/7] Fix linting --- testing/acceptance_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 27f2c65..e0d4770 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -825,7 +825,7 @@ class TestWarnings: # The socket itself will end up attached as a value in # _WARNING_DETAIL. We need to test that it is not serialized # (it can't be, so the test will fail if we try to). - @pytest.mark.filterwarnings('always') + @pytest.mark.filterwarnings('always') def test_func(tmpdir): abuse_socket() gc.collect()