From e3a3c90d944a3340a10b4a013d2b58caab8bff84 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 2 Apr 2020 14:31:32 +0300 Subject: [PATCH] [5.4.x] Fix crash when printing while capsysbinary is active (#7002) Backport of 1fda86119055f7a5db8a30d867b26e802f6067ab from master. --- changelog/6871.bugfix.rst | 1 + src/_pytest/capture.py | 14 +++++++++++--- testing/test_capture.py | 36 +++++++++++++++++++++++++++++------- 3 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 changelog/6871.bugfix.rst diff --git a/changelog/6871.bugfix.rst b/changelog/6871.bugfix.rst new file mode 100644 index 000000000..fe69c7509 --- /dev/null +++ b/changelog/6871.bugfix.rst @@ -0,0 +1 @@ +Fix crash with captured output when using the :fixture:`capsysbinary fixture `. diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 5f29c5ca2..673bb07a9 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -610,8 +610,6 @@ class FDCaptureBinary: def writeorg(self, data): """ write to original file descriptor. """ - if isinstance(data, str): - data = data.encode("utf8") # XXX use encoding of original stream os.write(self.targetfd_save, data) @@ -631,6 +629,11 @@ class FDCapture(FDCaptureBinary): res = str(res, enc, "replace") return res + def writeorg(self, data): + """ write to original file descriptor. """ + data = data.encode("utf-8") # XXX use encoding of original stream + os.write(self.targetfd_save, data) + class SysCaptureBinary: @@ -682,8 +685,9 @@ class SysCaptureBinary: self._state = "resumed" def writeorg(self, data): - self._old.write(data) self._old.flush() + self._old.buffer.write(data) + self._old.buffer.flush() class SysCapture(SysCaptureBinary): @@ -695,6 +699,10 @@ class SysCapture(SysCaptureBinary): self.tmpfile.truncate() return res + def writeorg(self, data): + self._old.write(data) + self._old.flush() + class TeeSysCapture(SysCapture): def __init__(self, fd, tmpfile=None): diff --git a/testing/test_capture.py b/testing/test_capture.py index a3e558560..c5d50ae95 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -542,18 +542,40 @@ class TestCaptureFixture: reprec.assertoutcome(passed=1) def test_capsysbinary(self, testdir): - reprec = testdir.inline_runsource( - """\ + p1 = testdir.makepyfile( + r""" def test_hello(capsysbinary): import sys - # some likely un-decodable bytes - sys.stdout.buffer.write(b'\\xfe\\x98\\x20') + + sys.stdout.buffer.write(b'hello') + + # Some likely un-decodable bytes. + sys.stdout.buffer.write(b'\xfe\x98\x20') + + sys.stdout.buffer.flush() + + # Ensure writing in text mode still works and is captured. + # https://github.com/pytest-dev/pytest/issues/6871 + print("world", flush=True) + out, err = capsysbinary.readouterr() - assert out == b'\\xfe\\x98\\x20' + assert out == b'hello\xfe\x98\x20world\n' assert err == b'' + + print("stdout after") + print("stderr after", file=sys.stderr) """ ) - reprec.assertoutcome(passed=1) + result = testdir.runpytest(str(p1), "-rA") + result.stdout.fnmatch_lines( + [ + "*- Captured stdout call -*", + "stdout after", + "*- Captured stderr call -*", + "stderr after", + "*= 1 passed in *", + ] + ) def test_partial_setup_failure(self, testdir): p = testdir.makepyfile( @@ -977,7 +999,7 @@ class TestFDCapture: cap.start() tmpfile.write(data1) tmpfile.flush() - cap.writeorg(data2) + cap.writeorg(data2.decode("ascii")) scap = cap.snap() cap.done() assert scap == data1.decode("ascii")