Revisit _pytest.capture: repr, doc fixes, minor

This commit is contained in:
Daniel Hahler 2019-03-18 01:06:50 +01:00
parent a624b84097
commit 920bffbfbb
2 changed files with 40 additions and 27 deletions

View File

@ -91,6 +91,13 @@ class CaptureManager(object):
self._global_capturing = None self._global_capturing = None
self._current_item = None self._current_item = None
def __repr__(self):
return "<CaptureManager _method=%r _global_capturing=%r _current_item=%r>" % (
self._method,
self._global_capturing,
self._current_item,
)
def _getcapture(self, method): def _getcapture(self, method):
if method == "fd": if method == "fd":
return MultiCapture(out=True, err=True, Capture=FDCapture) return MultiCapture(out=True, err=True, Capture=FDCapture)
@ -98,8 +105,7 @@ class CaptureManager(object):
return MultiCapture(out=True, err=True, Capture=SysCapture) return MultiCapture(out=True, err=True, Capture=SysCapture)
elif method == "no": elif method == "no":
return MultiCapture(out=False, err=False, in_=False) return MultiCapture(out=False, err=False, in_=False)
else: raise ValueError("unknown capturing method: %r" % method) # pragma: no cover
raise ValueError("unknown capturing method: %r" % method)
# Global capturing control # Global capturing control
@ -161,8 +167,8 @@ class CaptureManager(object):
@contextlib.contextmanager @contextlib.contextmanager
def global_and_fixture_disabled(self): def global_and_fixture_disabled(self):
"""Context manager to temporarily disables global and current fixture capturing.""" """Context manager to temporarily disable global and current fixture capturing."""
# Need to undo local capsys-et-al if exists before disabling global capture # Need to undo local capsys-et-al if it exists before disabling global capture.
self.suspend_fixture(self._current_item) self.suspend_fixture(self._current_item)
self.suspend_global_capture(in_=False) self.suspend_global_capture(in_=False)
try: try:
@ -247,10 +253,11 @@ def _ensure_only_one_capture_fixture(request, name):
@pytest.fixture @pytest.fixture
def capsys(request): def capsys(request):
"""Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make """Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text`` The captured output is made available via ``capsys.readouterr()`` method
objects. calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
""" """
_ensure_only_one_capture_fixture(request, "capsys") _ensure_only_one_capture_fixture(request, "capsys")
with _install_capture_fixture_on_item(request, SysCapture) as fixture: with _install_capture_fixture_on_item(request, SysCapture) as fixture:
@ -259,26 +266,28 @@ def capsys(request):
@pytest.fixture @pytest.fixture
def capsysbinary(request): def capsysbinary(request):
"""Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make """Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes`` The captured output is made available via ``capsysbinary.readouterr()``
objects. method calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``bytes`` objects.
""" """
_ensure_only_one_capture_fixture(request, "capsysbinary") _ensure_only_one_capture_fixture(request, "capsysbinary")
# Currently, the implementation uses the python3 specific `.buffer` # Currently, the implementation uses the python3 specific `.buffer`
# property of CaptureIO. # property of CaptureIO.
if sys.version_info < (3,): if sys.version_info < (3,):
raise request.raiseerror("capsysbinary is only supported on python 3") raise request.raiseerror("capsysbinary is only supported on Python 3")
with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture: with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture:
yield fixture yield fixture
@pytest.fixture @pytest.fixture
def capfd(request): def capfd(request):
"""Enable capturing of writes to file descriptors ``1`` and ``2`` and make """Enable text capturing of writes to file descriptors ``1`` and ``2``.
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text`` The captured output is made available via ``capfd.readouterr()`` method
objects. calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
""" """
_ensure_only_one_capture_fixture(request, "capfd") _ensure_only_one_capture_fixture(request, "capfd")
if not hasattr(os, "dup"): if not hasattr(os, "dup"):
@ -291,10 +300,11 @@ def capfd(request):
@pytest.fixture @pytest.fixture
def capfdbinary(request): def capfdbinary(request):
"""Enable capturing of write to file descriptors 1 and 2 and make """Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
captured output available via ``capfdbinary.readouterr`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be The captured output is made available via ``capfd.readouterr()`` method
``bytes`` objects. calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``byte`` objects.
""" """
_ensure_only_one_capture_fixture(request, "capfdbinary") _ensure_only_one_capture_fixture(request, "capfdbinary")
if not hasattr(os, "dup"): if not hasattr(os, "dup"):
@ -316,9 +326,9 @@ def _install_capture_fixture_on_item(request, capture_class):
""" """
request.node._capture_fixture = fixture = CaptureFixture(capture_class, request) request.node._capture_fixture = fixture = CaptureFixture(capture_class, request)
capmanager = request.config.pluginmanager.getplugin("capturemanager") capmanager = request.config.pluginmanager.getplugin("capturemanager")
# need to active this fixture right away in case it is being used by another fixture (setup phase) # Need to active this fixture right away in case it is being used by another fixture (setup phase).
# if this fixture is being used only by a test function (call phase), then we wouldn't need this # If this fixture is being used only by a test function (call phase), then we wouldn't need this
# activation, but it doesn't hurt # activation, but it doesn't hurt.
capmanager.activate_fixture(request.node) capmanager.activate_fixture(request.node)
yield fixture yield fixture
fixture.close() fixture.close()
@ -357,7 +367,7 @@ class CaptureFixture(object):
def readouterr(self): def readouterr(self):
"""Read and return the captured output so far, resetting the internal buffer. """Read and return the captured output so far, resetting the internal buffer.
:return: captured content as a namedtuple with ``out`` and ``err`` string attributes :return: captured content as a namedtuple with ``out`` and ``err`` string attributes
""" """
captured_out, captured_err = self._captured_out, self._captured_err captured_out, captured_err = self._captured_out, self._captured_err
if self._capture is not None: if self._capture is not None:
@ -446,6 +456,9 @@ class MultiCapture(object):
if err: if err:
self.err = Capture(2) self.err = Capture(2)
def __repr__(self):
return "<MultiCapture out=%r err=%r in_=%r>" % (self.out, self.err, self.in_)
def start_capturing(self): def start_capturing(self):
if self.in_: if self.in_:
self.in_.start() self.in_.start()
@ -590,7 +603,7 @@ class FDCapture(FDCaptureBinary):
EMPTY_BUFFER = str() EMPTY_BUFFER = str()
def snap(self): def snap(self):
res = FDCaptureBinary.snap(self) res = super(FDCapture, self).snap()
enc = getattr(self.tmpfile, "encoding", None) enc = getattr(self.tmpfile, "encoding", None)
if enc and isinstance(res, bytes): if enc and isinstance(res, bytes):
res = six.text_type(res, enc, "replace") res = six.text_type(res, enc, "replace")

View File

@ -566,7 +566,7 @@ class TestCaptureFixture(object):
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
"*test_hello*", "*test_hello*",
"*capsysbinary is only supported on python 3*", "*capsysbinary is only supported on Python 3*",
"*1 error in*", "*1 error in*",
] ]
) )