diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ee3eae09700..771436febf4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,9 @@ New Features * ``pytest.raises`` now asserts that the error message matches a text or regex with the ``match`` keyword argument. Thanks `@Kriechi`_ for the PR. +* ``capsys`` and ``capfd`` fixtures now expose a ``readouterr_bytes()`` method + which returns captured output as bytes. Thanks `@asottile`_ for the PR. + Changes ------- diff --git a/_pytest/capture.py b/_pytest/capture.py index 6a1cae41d49..8ceaa0e17f5 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -204,6 +204,12 @@ def readouterr(self): except AttributeError: return self._outerr + def readouterr_bytes(self): + try: + return self._capture.readouterr_bytes() + except AttributeError: + return self._outerr + @contextlib.contextmanager def disabled(self): capmanager = self.request.config.pluginmanager.getplugin('capturemanager') @@ -315,6 +321,12 @@ def readouterr(self): return (self.out.snap() if self.out is not None else "", self.err.snap() if self.err is not None else "") + def readouterr_bytes(self): + """ return snapshot bytes value of stdout/stderr capturings. """ + return (self.out.snap_bytes() if self.out is not None else b'', + self.err.snap_bytes() if self.err is not None else b'') + + class NoCapture(object): __init__ = start = done = suspend = resume = lambda *args: None @@ -370,6 +382,14 @@ def snap(self): return res return '' + def snap_bytes(self): + f = self.tmpfile + f.seek(0) + res = f.read() + f.truncate(0) + f.seek(0) + return res + def done(self): """ stop capturing, restore streams, return original capture file, seeked to position zero. """ @@ -416,6 +436,13 @@ def snap(self): f.seek(0) return res + def snap_bytes(self): + f = getattr(self.tmpfile, 'buffer', self.tmpfile) + res = f.getvalue() + f.truncate(0) + f.seek(0) + return res + def done(self): setattr(sys, self.name, self._old) del self._old diff --git a/testing/test_capture.py b/testing/test_capture.py index 364281fe672..5d6f4324c68 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -898,6 +898,16 @@ def test_capturing_readouterr_decode_error_handling(self): out, err = cap.readouterr() assert out == py.builtin._totext('\ufffd\n', 'unicode-escape') + def test_capturing_bytes(self): + some_bytes = b'\xf1\xe7\x12\r\n' + with self.getcapture() as cap: + # Write bytes to output in a py2+py3 + getattr(sys.stdout, 'buffer', sys.stdout).write(some_bytes) + getattr(sys.stderr, 'buffer', sys.stderr).write(some_bytes) + out, err = cap.readouterr_bytes() + assert out == some_bytes + assert err == some_bytes + def test_reset_twice_error(self): with self.getcapture() as cap: print ("hello")