Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Investigating/fixing capsysbin #277

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 0 additions & 5 deletions .pre-commit-config.yaml
@@ -1,9 +1,4 @@
repos:
- repo: https://github.com/psf/black
rev: 19.10b0
hooks:
- id: black
args: [--safe, --quiet]
- repo: https://github.com/asottile/blacken-docs
rev: v1.0.0
hooks:
Expand Down
16 changes: 13 additions & 3 deletions src/_pytest/capture.py
Expand Up @@ -2,7 +2,6 @@
per-test stdout/stderr capturing mechanism.

"""
import collections
import contextlib
import io
import os
Expand All @@ -12,7 +11,9 @@
from typing import BinaryIO
from typing import Generator
from typing import Iterable
from typing import NamedTuple
from typing import Optional
from typing import Union

import pytest
from _pytest.compat import CaptureAndPassthroughIO
Expand Down Expand Up @@ -439,7 +440,11 @@ def __getattr__(self, name):
return getattr(object.__getattribute__(self, "buffer"), name)


CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"])
# XXX: Generic[AnyStr], and AnyStr as type directly does not work?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like mypy doesn't support generic namedtuples yet: python/mypy#685

# Would be good to ensure out and err are of the same type.
class CaptureResult(NamedTuple):
out: None # type: Union[str, bytes]
err: None # type: Union[str, bytes]


class MultiCapture:
Expand Down Expand Up @@ -682,7 +687,8 @@ def resume(self):
setattr(sys, self.name, self.tmpfile)
self._state = "resumed"

def writeorg(self, data):
def writeorg(self, data: str) -> None:
data = data.decode(self._old.encoding)
self._old.write(data)
self._old.flush()

Expand All @@ -696,6 +702,10 @@ def snap(self):
self.tmpfile.truncate()
return res

def writeorg(self, data: str) -> None:
self._old.write(data)
self._old.flush()


class TeeSysCapture(SysCapture):
def __init__(self, fd, tmpfile=None):
Expand Down
40 changes: 40 additions & 0 deletions testing/test_capture.py
Expand Up @@ -16,8 +16,10 @@
from _pytest import capture
from _pytest.capture import _get_multicapture
from _pytest.capture import CaptureManager
from _pytest.capture import CaptureResult
from _pytest.capture import MultiCapture
from _pytest.config import ExitCode
from _pytest.pytester import Testdir

# note: py.io capture tests where copied from
# pylib 1.4.20.dev2 (rev 13d9af95547e)
Expand All @@ -40,6 +42,44 @@ def TeeStdCapture(out=True, err=True, in_=True):
return capture.MultiCapture(out, err, in_, Capture=capture.TeeSysCapture)


def test_CaptureResult() -> None:
with pytest.raises(TypeError):
CaptureResult() # type: ignore

cr = CaptureResult(out="out", err="err")
repr_ = repr(cr)
assert repr_ == "CaptureResult(out='out', err='err')"
assert str(cr) == repr_

cr = CaptureResult(out=b"out\nout2", err=b"err\nerr2")
repr_ = repr(cr)
assert repr_ == r"CaptureResult(out=b'out\nout2', err=b'err\nerr2')"
assert str(cr) == repr_

# XXX: should cause mypy type error?!
cr = CaptureResult(out="out", err=b"err")
repr_ = repr(cr)
assert repr_ == r"CaptureResult(out='out', err=b'err')"
assert str(cr) == repr_


def test_capsysbinary(testdir: Testdir) -> None:
p1 = testdir.makepyfile(
"""
def test(capsysbinary):
print("hello")
assert False, "trigger_failure"
"""
)
result = testdir.runpytest(str(p1))
result.stdout.fnmatch_lines([
'E assert False',
'*- Captured stdout call -*',
'hello',
])
assert result.ret == 1


class TestCaptureManager:
def test_getmethod_default_no_fd(self, monkeypatch):
from _pytest.capture import pytest_addoption
Expand Down