Skip to content

Commit 6a714d7

Browse files
authored
capture: CaptureResult can be a namedtuple again (pytest-dev#10678)
mypy now supports generic NamedTuple.
1 parent 5a23eef commit 6a714d7

File tree

1 file changed

+15
-50
lines changed

1 file changed

+15
-50
lines changed

src/_pytest/capture.py

Lines changed: 15 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Per-test stdout/stderr capturing mechanism."""
2+
import collections
23
import contextlib
3-
import functools
44
import io
55
import os
66
import sys
@@ -10,7 +10,7 @@
1010
from typing import AnyStr
1111
from typing import Generator
1212
from typing import Generic
13-
from typing import Iterator
13+
from typing import NamedTuple
1414
from typing import Optional
1515
from typing import TextIO
1616
from typing import Tuple
@@ -492,59 +492,24 @@ def writeorg(self, data):
492492
# MultiCapture
493493

494494

495-
# This class was a namedtuple, but due to mypy limitation[0] it could not be
496-
# made generic, so was replaced by a regular class which tries to emulate the
497-
# pertinent parts of a namedtuple. If the mypy limitation is ever lifted, can
498-
# make it a namedtuple again.
499-
# [0]: https://github.com/python/mypy/issues/685
500-
@final
501-
@functools.total_ordering
502-
class CaptureResult(Generic[AnyStr]):
503-
"""The result of :method:`CaptureFixture.readouterr`."""
495+
# Generic NamedTuple only supported since Python 3.11.
496+
if sys.version_info >= (3, 11) or TYPE_CHECKING:
504497

505-
__slots__ = ("out", "err")
498+
@final
499+
class CaptureResult(NamedTuple, Generic[AnyStr]):
500+
"""The result of :method:`CaptureFixture.readouterr`."""
506501

507-
def __init__(self, out: AnyStr, err: AnyStr) -> None:
508-
self.out: AnyStr = out
509-
self.err: AnyStr = err
502+
out: AnyStr
503+
err: AnyStr
510504

511-
def __len__(self) -> int:
512-
return 2
505+
else:
513506

514-
def __iter__(self) -> Iterator[AnyStr]:
515-
return iter((self.out, self.err))
507+
class CaptureResult(
508+
collections.namedtuple("CaptureResult", ["out", "err"]), Generic[AnyStr]
509+
):
510+
"""The result of :method:`CaptureFixture.readouterr`."""
516511

517-
def __getitem__(self, item: int) -> AnyStr:
518-
return tuple(self)[item]
519-
520-
def _replace(
521-
self, *, out: Optional[AnyStr] = None, err: Optional[AnyStr] = None
522-
) -> "CaptureResult[AnyStr]":
523-
return CaptureResult(
524-
out=self.out if out is None else out, err=self.err if err is None else err
525-
)
526-
527-
def count(self, value: AnyStr) -> int:
528-
return tuple(self).count(value)
529-
530-
def index(self, value) -> int:
531-
return tuple(self).index(value)
532-
533-
def __eq__(self, other: object) -> bool:
534-
if not isinstance(other, (CaptureResult, tuple)):
535-
return NotImplemented
536-
return tuple(self) == tuple(other)
537-
538-
def __hash__(self) -> int:
539-
return hash(tuple(self))
540-
541-
def __lt__(self, other: object) -> bool:
542-
if not isinstance(other, (CaptureResult, tuple)):
543-
return NotImplemented
544-
return tuple(self) < tuple(other)
545-
546-
def __repr__(self) -> str:
547-
return f"CaptureResult(out={self.out!r}, err={self.err!r})"
512+
__slots__ = ()
548513

549514

550515
class MultiCapture(Generic[AnyStr]):

0 commit comments

Comments
 (0)