Make capsys more like stdio streams in python3. Resolves #1407.

This commit is contained in:
Anthony Sottile 2017-02-19 09:16:32 -08:00
parent 44ad369c17
commit 6ba3475448
5 changed files with 46 additions and 8 deletions

View File

@ -13,6 +13,7 @@ Andrzej Ostrowski
Andy Freeland Andy Freeland
Anthon van der Neut Anthon van der Neut
Antony Lee Antony Lee
Anthony Sottile
Armin Rigo Armin Rigo
Aron Curzon Aron Curzon
Aviv Palivoda Aviv Palivoda

View File

@ -55,6 +55,14 @@ Changes
Thanks `@The-Compiler`_ for the PR. Thanks `@The-Compiler`_ for the PR.
Bug Fixes
---------
* Fix ``AttributeError`` on ``sys.stdout.buffer`` / ``sys.stderr.buffer``
while using ``capsys`` fixture in python 3. (`#1407`_).
Thanks to `@asottile`_.
.. _@davidszotten: https://github.com/davidszotten .. _@davidszotten: https://github.com/davidszotten
.. _@fushi: https://github.com/fushi .. _@fushi: https://github.com/fushi
.. _@mattduck: https://github.com/mattduck .. _@mattduck: https://github.com/mattduck
@ -65,6 +73,7 @@ Changes
.. _@unsignedint: https://github.com/unsignedint .. _@unsignedint: https://github.com/unsignedint
.. _@Kriechi: https://github.com/Kriechi .. _@Kriechi: https://github.com/Kriechi
.. _#1407: https://github.com/pytest-dev/pytest/issues/1407
.. _#1512: https://github.com/pytest-dev/pytest/issues/1512 .. _#1512: https://github.com/pytest-dev/pytest/issues/1512
.. _#1874: https://github.com/pytest-dev/pytest/pull/1874 .. _#1874: https://github.com/pytest-dev/pytest/pull/1874
.. _#1952: https://github.com/pytest-dev/pytest/pull/1952 .. _#1952: https://github.com/pytest-dev/pytest/pull/1952

View File

@ -12,8 +12,8 @@ from tempfile import TemporaryFile
import py import py
import pytest import pytest
from _pytest.compat import CaptureIO
from py.io import TextIO
unicode = py.builtin.text unicode = py.builtin.text
patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
@ -403,7 +403,7 @@ class SysCapture(object):
if name == "stdin": if name == "stdin":
tmpfile = DontReadFromInput() tmpfile = DontReadFromInput()
else: else:
tmpfile = TextIO() tmpfile = CaptureIO()
self.tmpfile = tmpfile self.tmpfile = tmpfile
def start(self): def start(self):

View File

@ -251,3 +251,19 @@ else:
except UnicodeError: except UnicodeError:
errors = 'replace' errors = 'replace'
return v.encode('ascii', errors) return v.encode('ascii', errors)
if _PY2:
from py.io import TextIO as CaptureIO
else:
import io
class CaptureIO(io.TextIOWrapper):
def __init__(self):
super(CaptureIO, self).__init__(
io.BytesIO(),
encoding='UTF-8', newline='', write_through=True,
)
def getvalue(self):
return self.buffer.getvalue().decode('UTF-8')

View File

@ -281,7 +281,7 @@ class TestLoggingInteraction(object):
def test_logging(): def test_logging():
import logging import logging
import pytest import pytest
stream = capture.TextIO() stream = capture.CaptureIO()
logging.basicConfig(stream=stream) logging.basicConfig(stream=stream)
stream.close() # to free memory/release resources stream.close() # to free memory/release resources
""") """)
@ -622,16 +622,16 @@ def test_error_during_readouterr(testdir):
]) ])
class TestTextIO(object): class TestCaptureIO(object):
def test_text(self): def test_text(self):
f = capture.TextIO() f = capture.CaptureIO()
f.write("hello") f.write("hello")
s = f.getvalue() s = f.getvalue()
assert s == "hello" assert s == "hello"
f.close() f.close()
def test_unicode_and_str_mixture(self): def test_unicode_and_str_mixture(self):
f = capture.TextIO() f = capture.CaptureIO()
if sys.version_info >= (3, 0): if sys.version_info >= (3, 0):
f.write("\u00f6") f.write("\u00f6")
pytest.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))") pytest.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))")
@ -642,6 +642,18 @@ class TestTextIO(object):
f.close() f.close()
assert isinstance(s, unicode) assert isinstance(s, unicode)
@pytest.mark.skipif(
sys.version_info[0] == 2,
reason='python 3 only behaviour',
)
def test_write_bytes_to_buffer(self):
"""In python3, stdout / stderr are text io wrappers (exposing a buffer
property of the underlying bytestream). See issue #1407
"""
f = capture.CaptureIO()
f.buffer.write(b'foo\r\n')
assert f.getvalue() == 'foo\r\n'
def test_bytes_io(): def test_bytes_io():
f = py.io.BytesIO() f = py.io.BytesIO()
@ -900,8 +912,8 @@ class TestStdCapture(object):
with self.getcapture() as cap: with self.getcapture() as cap:
sys.stdout.write("hello") sys.stdout.write("hello")
sys.stderr.write("world") sys.stderr.write("world")
sys.stdout = capture.TextIO() sys.stdout = capture.CaptureIO()
sys.stderr = capture.TextIO() sys.stderr = capture.CaptureIO()
print ("not seen") print ("not seen")
sys.stderr.write("not seen\n") sys.stderr.write("not seen\n")
out, err = cap.readouterr() out, err = cap.readouterr()