Skip to content

Commit e3a3c90

Browse files
authored
[5.4.x] Fix crash when printing while capsysbinary is active (#7002)
Backport of 1fda861 from master.
1 parent f732775 commit e3a3c90

File tree

3 files changed

+41
-10
lines changed

3 files changed

+41
-10
lines changed

changelog/6871.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash with captured output when using the :fixture:`capsysbinary fixture <capsysbinary>`.

src/_pytest/capture.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,6 @@ def resume(self):
610610

611611
def writeorg(self, data):
612612
""" write to original file descriptor. """
613-
if isinstance(data, str):
614-
data = data.encode("utf8") # XXX use encoding of original stream
615613
os.write(self.targetfd_save, data)
616614

617615

@@ -631,6 +629,11 @@ def snap(self):
631629
res = str(res, enc, "replace")
632630
return res
633631

632+
def writeorg(self, data):
633+
""" write to original file descriptor. """
634+
data = data.encode("utf-8") # XXX use encoding of original stream
635+
os.write(self.targetfd_save, data)
636+
634637

635638
class SysCaptureBinary:
636639

@@ -682,8 +685,9 @@ def resume(self):
682685
self._state = "resumed"
683686

684687
def writeorg(self, data):
685-
self._old.write(data)
686688
self._old.flush()
689+
self._old.buffer.write(data)
690+
self._old.buffer.flush()
687691

688692

689693
class SysCapture(SysCaptureBinary):
@@ -695,6 +699,10 @@ def snap(self):
695699
self.tmpfile.truncate()
696700
return res
697701

702+
def writeorg(self, data):
703+
self._old.write(data)
704+
self._old.flush()
705+
698706

699707
class TeeSysCapture(SysCapture):
700708
def __init__(self, fd, tmpfile=None):

testing/test_capture.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -542,18 +542,40 @@ def test_hello(capfdbinary):
542542
reprec.assertoutcome(passed=1)
543543

544544
def test_capsysbinary(self, testdir):
545-
reprec = testdir.inline_runsource(
546-
"""\
545+
p1 = testdir.makepyfile(
546+
r"""
547547
def test_hello(capsysbinary):
548548
import sys
549-
# some likely un-decodable bytes
550-
sys.stdout.buffer.write(b'\\xfe\\x98\\x20')
549+
550+
sys.stdout.buffer.write(b'hello')
551+
552+
# Some likely un-decodable bytes.
553+
sys.stdout.buffer.write(b'\xfe\x98\x20')
554+
555+
sys.stdout.buffer.flush()
556+
557+
# Ensure writing in text mode still works and is captured.
558+
# https://github.com/pytest-dev/pytest/issues/6871
559+
print("world", flush=True)
560+
551561
out, err = capsysbinary.readouterr()
552-
assert out == b'\\xfe\\x98\\x20'
562+
assert out == b'hello\xfe\x98\x20world\n'
553563
assert err == b''
564+
565+
print("stdout after")
566+
print("stderr after", file=sys.stderr)
554567
"""
555568
)
556-
reprec.assertoutcome(passed=1)
569+
result = testdir.runpytest(str(p1), "-rA")
570+
result.stdout.fnmatch_lines(
571+
[
572+
"*- Captured stdout call -*",
573+
"stdout after",
574+
"*- Captured stderr call -*",
575+
"stderr after",
576+
"*= 1 passed in *",
577+
]
578+
)
557579

558580
def test_partial_setup_failure(self, testdir):
559581
p = testdir.makepyfile(
@@ -977,7 +999,7 @@ def test_writeorg(self, tmpfile):
977999
cap.start()
9781000
tmpfile.write(data1)
9791001
tmpfile.flush()
980-
cap.writeorg(data2)
1002+
cap.writeorg(data2.decode("ascii"))
9811003
scap = cap.snap()
9821004
cap.done()
9831005
assert scap == data1.decode("ascii")

0 commit comments

Comments
 (0)