Skip to content

Commit 46aa29e

Browse files
committed
[4.6] capsys: ensure fd is unbuffered
Fixes #5134.
1 parent 3edf417 commit 46aa29e

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

changelog/5134.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix buffering with capsys fixture on Python 2.

src/_pytest/capture.py

+11
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,17 @@ def done(self):
686686

687687
def suspend(self):
688688
setattr(sys, self.name, self._old)
689+
if six.PY2:
690+
if self.name in ("stdout", "stderr"):
691+
if getattr(sys, "__{}__".format(self.name)) is self._old:
692+
if not hasattr(self, "_fdopen_done"):
693+
self._fdopen_done = True
694+
try:
695+
fd = self._old.fileno()
696+
# Ensure fd is unbuffered (#5134).
697+
setattr(sys, self.name, os.fdopen(fd, "w", 0))
698+
except UnsupportedOperation:
699+
pass
689700
self._state = "suspended"
690701

691702
def resume(self):

testing/test_capture.py

+51
Original file line numberDiff line numberDiff line change
@@ -1577,3 +1577,54 @@ def test_fails():
15771577
)
15781578
else:
15791579
assert result_with_capture.ret == 0
1580+
1581+
1582+
def test_syscapture_is_unbuffered_when_suspended(testdir, LineMatcher):
1583+
import time
1584+
1585+
stampfile = testdir.tmpdir.join("stampfile")
1586+
testdir.makepyfile(
1587+
**{
1588+
"conftest.py": """
1589+
import ctypes
1590+
1591+
libc = ctypes.CDLL(None)
1592+
libc.puts(b'this comes from C via conftest')
1593+
""",
1594+
"test_pass.py": """
1595+
import os
1596+
import time
1597+
1598+
def test_capfd(capfd):
1599+
print("test_capfd_start")
1600+
with capfd.disabled():
1601+
for i in range(0, 50):
1602+
print("test_capfd_loop: %d" % i)
1603+
time.sleep(0.1)
1604+
if os.path.exists({stampfile!r}):
1605+
break
1606+
""".format(
1607+
stampfile=str(stampfile)
1608+
),
1609+
}
1610+
)
1611+
1612+
child = testdir.spawn_pytest("-s --color=no -vv test_pass.py")
1613+
start = time.time()
1614+
child.expect_exact("test_capfd_loop: 0\r\n")
1615+
duration = time.time() - start
1616+
stampfile.ensure()
1617+
out = child.before + child.buffer + child.after
1618+
1619+
out += child.read()
1620+
lm = LineMatcher(out.decode().splitlines())
1621+
lm.fnmatch_lines(
1622+
[
1623+
"this comes from C via conftest",
1624+
"test_pass.py::test_capfd test_capfd_loop: 0",
1625+
"*= 1 passed in *",
1626+
]
1627+
)
1628+
assert duration < 5
1629+
child.wait()
1630+
assert child.exitstatus == 0

0 commit comments

Comments
 (0)