Skip to content

Commit 450d18c

Browse files
committed
Allow to read streams without flushing
1 parent b4b1c89 commit 450d18c

File tree

2 files changed

+36
-25
lines changed

2 files changed

+36
-25
lines changed

src/_pytest/capture.py

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -351,13 +351,17 @@ def close(self):
351351
self._capture.stop_capturing()
352352
self._capture = None
353353

354-
def readouterr(self, combined=False):
354+
def readouterr(self, combined=False, flush=True):
355355
"""Read and return the captured output so far, resetting the internal buffer.
356356
357357
:return: captured content as a namedtuple with ``out`` and ``err`` string attributes
358358
"""
359359
if combined:
360360
return CaptureResult(self._get_combined(), None)
361+
if flush is False:
362+
if self.captureclass is not OrderedCapture:
363+
raise AttributeError("Only capsys can read streams without flushing.")
364+
OrderedCapture.set_flush(False)
361365
captured_out, captured_err = self._captured_out, self._captured_err
362366
if self._capture is not None:
363367
out, err = self._capture.readouterr()
@@ -369,9 +373,9 @@ def readouterr(self, combined=False):
369373

370374
def _get_combined(self):
371375
if self.captureclass is not OrderedCapture:
372-
raise AttributeError("Only ordered capture is able to combine streams.")
376+
raise AttributeError("Only capsys is able to combine streams.")
373377
result = "".join(line[0] for line in OrderedCapture.streams)
374-
OrderedCapture.streams.clear()
378+
OrderedCapture.flush()
375379
return result
376380

377381
def _suspend(self):
@@ -713,8 +717,9 @@ def __init__(self, fd, tmpfile=None):
713717

714718

715719
class OrderedCapture(SysCapture):
716-
"""This class uses deque to keep streams in order."""
720+
"""Capture class that keeps streams in order."""
717721
streams = collections.deque()
722+
_flush = True
718723

719724
def _get_writer(self):
720725
return OrderedWriter(self.fd)
@@ -723,9 +728,26 @@ def snap(self):
723728
res = self.tmpfile.getvalue()
724729
if self.name == "stderr":
725730
# both streams are being read one after another, while stderr is last - it will clear the queue
726-
self.streams.clear()
731+
self.flush()
727732
return res
728-
733+
734+
@classmethod
735+
def set_flush(cls, flush: bool) -> None:
736+
cls._flush = flush
737+
738+
@classmethod
739+
def flush(cls) -> None:
740+
"""Clear streams. """
741+
if cls._flush is False:
742+
cls.set_flush(True)
743+
else:
744+
cls.streams.clear()
745+
746+
@classmethod
747+
def close(cls) -> None:
748+
cls.set_flush(True)
749+
cls.flush()
750+
729751
map_fixname_class = {
730752
"capfd": FDCapture,
731753
"capfdbinary": FDCaptureBinary,
@@ -749,25 +771,7 @@ def getvalue(self):
749771
return "".join((line[0] for line in OrderedCapture.streams if line[1] == self._fd))
750772

751773
def close(self):
752-
OrderedCapture.streams.clear()
753-
754-
755-
class OrderedWriter:
756-
encoding = sys.getdefaultencoding()
757-
758-
def __init__(self, fd: int) -> None:
759-
super().__init__()
760-
self._fd = fd # type: int
761-
762-
def write(self, text, **kwargs):
763-
OrderedCapture.streams.append((text, self._fd))
764-
return len(text)
765-
766-
def getvalue(self):
767-
return "".join((line[0] for line in OrderedCapture.streams if line[1] == self._fd))
768-
769-
def close(self):
770-
OrderedCapture.streams.clear()
774+
OrderedCapture.close()
771775

772776

773777
class DontReadFromInput:

testing/test_capture.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,13 @@ def test_combined_streams(capsys):
15751575
print("stdout4")
15761576
print("stdout5")
15771577

1578+
# this won't clear streams
1579+
out, err = capsys.readouterr(flush=False)
1580+
assert "stdout1" in out
1581+
assert "stdout1" not in err
1582+
assert "stderr1" in err
1583+
assert "stderr1" not in out
1584+
15781585
out, err = capsys.readouterr(combined=True)
15791586
assert (
15801587
out == "stdout1\n"

0 commit comments

Comments
 (0)