@@ -93,7 +93,7 @@ def _getcapture(self, method):
93
93
if method == "fd" :
94
94
return MultiCapture (out = True , err = True , Capture = FDCapture )
95
95
elif method == "sys" :
96
- return MultiCapture (out = True , err = True , Capture = SysCapture )
96
+ return MultiCapture (out = True , err = True , Capture = OrderedCapture )
97
97
elif method == "no" :
98
98
return MultiCapture (out = False , err = False , in_ = False )
99
99
elif method == "tee-sys" :
@@ -351,11 +351,13 @@ def close(self):
351
351
self ._capture .stop_capturing ()
352
352
self ._capture = None
353
353
354
- def readouterr (self ):
354
+ def readouterr (self , combined = False ):
355
355
"""Read and return the captured output so far, resetting the internal buffer.
356
356
357
357
:return: captured content as a namedtuple with ``out`` and ``err`` string attributes
358
358
"""
359
+ if combined :
360
+ return CaptureResult (self ._get_combined (), None )
359
361
captured_out , captured_err = self ._captured_out , self ._captured_err
360
362
if self ._capture is not None :
361
363
out , err = self ._capture .readouterr ()
@@ -365,6 +367,13 @@ def readouterr(self):
365
367
self ._captured_err = self .captureclass .EMPTY_BUFFER
366
368
return CaptureResult (captured_out , captured_err )
367
369
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
+
368
377
def _suspend (self ):
369
378
"""Suspends this fixture's own capturing temporarily."""
370
379
if self ._capture is not None :
@@ -630,12 +639,16 @@ def __init__(self, fd, tmpfile=None):
630
639
name = patchsysdict [fd ]
631
640
self ._old = getattr (sys , name )
632
641
self .name = name
642
+ self .fd = fd
633
643
if tmpfile is None :
634
644
if name == "stdin" :
635
645
tmpfile = DontReadFromInput ()
636
646
else :
637
- tmpfile = CaptureIO ()
647
+ tmpfile = self . _get_writer ()
638
648
self .tmpfile = tmpfile
649
+
650
+ def _get_writer (self ):
651
+ return CaptureIO ()
639
652
640
653
def __repr__ (self ):
641
654
return "<{} {} _old={} _state={!r} tmpfile={!r}>" .format (
@@ -698,14 +711,80 @@ def __init__(self, fd, tmpfile=None):
698
711
self .tmpfile = tmpfile
699
712
700
713
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
+
701
729
map_fixname_class = {
702
730
"capfd" : FDCapture ,
703
731
"capfdbinary" : FDCaptureBinary ,
704
- "capsys" : SysCapture ,
732
+ "capsys" : OrderedCapture ,
705
733
"capsysbinary" : SysCaptureBinary ,
706
734
}
707
735
708
736
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
+
709
788
class DontReadFromInput :
710
789
encoding = None
711
790
0 commit comments