@@ -93,7 +93,7 @@ def _getcapture(self, method):
9393 if method == "fd" :
9494 return MultiCapture (out = True , err = True , Capture = FDCapture )
9595 elif method == "sys" :
96- return MultiCapture (out = True , err = True , Capture = SysCapture )
96+ return MultiCapture (out = True , err = True , Capture = OrderedCapture )
9797 elif method == "no" :
9898 return MultiCapture (out = False , err = False , in_ = False )
9999 elif method == "tee-sys" :
@@ -351,11 +351,13 @@ def close(self):
351351 self ._capture .stop_capturing ()
352352 self ._capture = None
353353
354- def readouterr (self ):
354+ def readouterr (self , combined = False ):
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 """
359+ if combined :
360+ return CaptureResult (self ._get_combined (), None )
359361 captured_out , captured_err = self ._captured_out , self ._captured_err
360362 if self ._capture is not None :
361363 out , err = self ._capture .readouterr ()
@@ -365,6 +367,13 @@ def readouterr(self):
365367 self ._captured_err = self .captureclass .EMPTY_BUFFER
366368 return CaptureResult (captured_out , captured_err )
367369
370+ def _get_combined (self ):
371+ if self .captureclass is not OrderedCapture :
372+ raise AttributeError ("Only ordered capture is able to combine streams." )
373+ result = "" .join (line [0 ] for line in OrderedCapture .streams )
374+ OrderedCapture .streams .clear ()
375+ return result
376+
368377 def _suspend (self ):
369378 """Suspends this fixture's own capturing temporarily."""
370379 if self ._capture is not None :
@@ -630,12 +639,16 @@ def __init__(self, fd, tmpfile=None):
630639 name = patchsysdict [fd ]
631640 self ._old = getattr (sys , name )
632641 self .name = name
642+ self .fd = fd
633643 if tmpfile is None :
634644 if name == "stdin" :
635645 tmpfile = DontReadFromInput ()
636646 else :
637- tmpfile = CaptureIO ()
647+ tmpfile = self . _get_writer ()
638648 self .tmpfile = tmpfile
649+
650+ def _get_writer (self ):
651+ return CaptureIO ()
639652
640653 def __repr__ (self ):
641654 return "<{} {} _old={} _state={!r} tmpfile={!r}>" .format (
@@ -698,14 +711,80 @@ def __init__(self, fd, tmpfile=None):
698711 self .tmpfile = tmpfile
699712
700713
714+
715+ class OrderedCapture (SysCapture ):
716+ """This class uses deque to keep streams in order."""
717+ streams = collections .deque ()
718+
719+ def _get_writer (self ):
720+ return OrderedWriter (self .fd )
721+
722+ def snap (self ):
723+ res = self .tmpfile .getvalue ()
724+ if self .name == "stderr" :
725+ # both streams are being read one after another, while stderr is last - it will clear the queue
726+ self .streams .clear ()
727+ return res
728+
701729map_fixname_class = {
702730 "capfd" : FDCapture ,
703731 "capfdbinary" : FDCaptureBinary ,
704- "capsys" : SysCapture ,
732+ "capsys" : OrderedCapture ,
705733 "capsysbinary" : SysCaptureBinary ,
706734}
707735
708736
737+ class OrderedWriter :
738+ encoding = sys .getdefaultencoding ()
739+
740+ def __init__ (self , fd : int ) -> None :
741+ super ().__init__ ()
742+ self ._fd = fd # type: int
743+
744+ def write (self , text , ** kwargs ):
745+ OrderedCapture .streams .append ((text , self ._fd ))
746+ return len (text )
747+
748+ def getvalue (self ):
749+ return "" .join ((line [0 ] for line in OrderedCapture .streams if line [1 ] == self ._fd ))
750+
751+ def close (self ):
752+ OrderedCapture .streams .clear ()
753+
754+
755+ class OrderedCapture (SysCapture ):
756+ """This class uses deque to keep streams in order."""
757+ streams = collections .deque ()
758+
759+ def _get_writer (self ):
760+ return OrderedWriter (self .fd )
761+
762+ def snap (self ):
763+ res = self .tmpfile .getvalue ()
764+ if self .name == "stderr" :
765+ # both streams are being read one after another, while stderr is last - it will clear the queue
766+ self .streams .clear ()
767+ return res
768+
769+
770+ class OrderedWriter :
771+ encoding = sys .getdefaultencoding ()
772+
773+ def __init__ (self , fd : int ) -> None :
774+ super ().__init__ ()
775+ self ._fd = fd # type: int
776+
777+ def write (self , text , ** kwargs ):
778+ OrderedCapture .streams .append ((text , self ._fd ))
779+ return len (text )
780+
781+ def getvalue (self ):
782+ return "" .join ((line [0 ] for line in OrderedCapture .streams if line [1 ] == self ._fd ))
783+
784+ def close (self ):
785+ OrderedCapture .streams .clear ()
786+
787+
709788class DontReadFromInput :
710789 encoding = None
711790
0 commit comments