From 2eb10027812e47eae1d2abe773afd9e4ff98d0a5 Mon Sep 17 00:00:00 2001 From: thou Date: Mon, 20 Nov 2017 11:42:30 -0600 Subject: [PATCH] Issue #241 Make copy during serialization of reprtraceback and reprcrash to avoid mutating. --- changelog/241.bugfix | 1 + testing/test_remote.py | 27 ++++++++++++++++++++------- xdist/remote.py | 8 ++++---- 3 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 changelog/241.bugfix diff --git a/changelog/241.bugfix b/changelog/241.bugfix new file mode 100644 index 0000000..109fac6 --- /dev/null +++ b/changelog/241.bugfix @@ -0,0 +1 @@ +Fix accidental mutation of test report during serialization causing longrepr string-ification to break. \ No newline at end of file diff --git a/testing/test_remote.py b/testing/test_remote.py index 553b61c..b647a9f 100644 --- a/testing/test_remote.py +++ b/testing/test_remote.py @@ -79,6 +79,21 @@ def test_remoteinitconfig(testdir): class TestReportSerialization: + def test_xdist_longrepr_to_str_issue_241(self, testdir): + testdir.makepyfile(""" + import os + def test_a(): assert False + def test_b(): pass + """) + testdir.makeconftest(""" + def pytest_runtest_logreport(report): + print(report.longrepr) + """) + res = testdir.runpytest('-n1', '-s') + res.stdout.fnmatch_lines([ + '*1 failed, 1 passed *' + ]) + def test_xdist_report_longrepr_reprcrash_130(self, testdir): reprec = testdir.inline_runsource(""" import py @@ -107,8 +122,6 @@ class TestReportSerialization: assert rep.longrepr.reprtraceback.style \ == a.longrepr.reprtraceback.style assert rep.longrepr.sections == a.longrepr.sections - assert rep.longrepr.reprtraceback.reprentries \ - == a.longrepr.reprtraceback.reprentries # Missing section attribute PR171 assert added_section in a.longrepr.sections @@ -127,14 +140,15 @@ class TestReportSerialization: rep_entries = rep.longrepr.reprtraceback.reprentries a_entries = a.longrepr.reprtraceback.reprentries - assert rep_entries == a_entries for i in range(len(a_entries)): assert isinstance(rep_entries[i], ReprEntry) assert rep_entries[i].lines == a_entries[i].lines assert rep_entries[i].localssep == a_entries[i].localssep - assert rep_entries[i].reprfileloc == a_entries[i].reprfileloc - assert rep_entries[i].reprfuncargs == a_entries[i].reprfuncargs - assert rep_entries[i].reprlocals == a_entries[i].reprlocals + assert rep_entries[i].reprfileloc.lineno == a_entries[i].reprfileloc.lineno + assert rep_entries[i].reprfileloc.message == a_entries[i].reprfileloc.message + assert rep_entries[i].reprfileloc.path == a_entries[i].reprfileloc.path + assert rep_entries[i].reprfuncargs.args == a_entries[i].reprfuncargs.args + assert rep_entries[i].reprlocals.lines == a_entries[i].reprlocals.lines assert rep_entries[i].style == a_entries[i].style def test_reprentries_serialization_196(self, testdir): @@ -152,7 +166,6 @@ class TestReportSerialization: rep_entries = rep.longrepr.reprtraceback.reprentries a_entries = a.longrepr.reprtraceback.reprentries - assert rep_entries == a_entries for i in range(len(a_entries)): assert isinstance(rep_entries[i], ReprEntryNative) assert rep_entries[i].lines == a_entries[i].lines diff --git a/xdist/remote.py b/xdist/remote.py index fb55a8e..b35a9a7 100644 --- a/xdist/remote.py +++ b/xdist/remote.py @@ -111,18 +111,18 @@ class SlaveInteractor: def serialize_report(rep): def disassembled_report(rep): - reprtraceback = rep.longrepr.reprtraceback.__dict__ - reprcrash = rep.longrepr.reprcrash.__dict__ + reprtraceback = rep.longrepr.reprtraceback.__dict__.copy() + reprcrash = rep.longrepr.reprcrash.__dict__.copy() new_entries = [] for entry in reprtraceback['reprentries']: entry_data = { 'type': type(entry).__name__, - 'data': entry.__dict__, + 'data': entry.__dict__.copy(), } for key, value in entry_data['data'].items(): if hasattr(value, '__dict__'): - entry_data['data'][key] = value.__dict__ + entry_data['data'][key] = value.__dict__.copy() new_entries.append(entry_data) reprtraceback['reprentries'] = new_entries