|
1 | 1 | """Per-test stdout/stderr capturing mechanism."""
|
| 2 | +import collections |
2 | 3 | import contextlib
|
3 |
| -import functools |
4 | 4 | import io
|
5 | 5 | import os
|
6 | 6 | import sys
|
|
10 | 10 | from typing import AnyStr
|
11 | 11 | from typing import Generator
|
12 | 12 | from typing import Generic
|
13 |
| -from typing import Iterator |
| 13 | +from typing import NamedTuple |
14 | 14 | from typing import Optional
|
15 | 15 | from typing import TextIO
|
16 | 16 | from typing import Tuple
|
@@ -492,59 +492,24 @@ def writeorg(self, data):
|
492 | 492 | # MultiCapture
|
493 | 493 |
|
494 | 494 |
|
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: |
504 | 497 |
|
505 |
| - __slots__ = ("out", "err") |
| 498 | + @final |
| 499 | + class CaptureResult(NamedTuple, Generic[AnyStr]): |
| 500 | + """The result of :method:`CaptureFixture.readouterr`.""" |
506 | 501 |
|
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 |
510 | 504 |
|
511 |
| - def __len__(self) -> int: |
512 |
| - return 2 |
| 505 | +else: |
513 | 506 |
|
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`.""" |
516 | 511 |
|
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__ = () |
548 | 513 |
|
549 | 514 |
|
550 | 515 | class MultiCapture(Generic[AnyStr]):
|
|
0 commit comments