Merge pull request #2266 from asottile/capture_v2

Make capsys more like stdio streams in python3. Resolves #1407.
This commit is contained in:
Bruno Oliveira 2017-02-24 20:18:55 -03:00 committed by GitHub
commit 0f3d7acdc4
6 changed files with 51 additions and 10 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

@ -14,6 +14,7 @@ from weakref import WeakKeyDictionary
from py.builtin import print_ from py.builtin import print_
from _pytest.capture import MultiCapture, SysCapture
from _pytest._code import Source from _pytest._code import Source
import py import py
import pytest import pytest
@ -737,7 +738,8 @@ class Testdir(object):
if kwargs.get("syspathinsert"): if kwargs.get("syspathinsert"):
self.syspathinsert() self.syspathinsert()
now = time.time() now = time.time()
capture = py.io.StdCapture() capture = MultiCapture(Capture=SysCapture)
capture.start_capturing()
try: try:
try: try:
reprec = self.inline_run(*args, **kwargs) reprec = self.inline_run(*args, **kwargs)
@ -752,7 +754,8 @@ class Testdir(object):
class reprec(object): class reprec(object):
ret = 3 ret = 3
finally: finally:
out, err = capture.reset() out, err = capture.readouterr()
capture.stop_capturing()
sys.stdout.write(out) sys.stdout.write(out)
sys.stderr.write(err) sys.stderr.write(err)

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()