Redirect a file descriptor to a temporary file for capture. Saves the current target of *targetfd* via :func:`os.dup`, then redirects it to a temporary file via :func:`os.dup2`. On :meth:`stop`, restores the original ``fd`` and returns the captured bytes. Inspired by Pytest's ``FDCa
| 78 | |
| 79 | |
| 80 | class _FDCapture: |
| 81 | """Redirect a file descriptor to a temporary file for capture. |
| 82 | |
| 83 | Saves the current target of *targetfd* via :func:`os.dup`, then |
| 84 | redirects it to a temporary file via :func:`os.dup2`. On |
| 85 | :meth:`stop`, restores the original ``fd`` and returns the captured |
| 86 | bytes. Inspired by Pytest's ``FDCapture``. |
| 87 | |
| 88 | .. versionadded:: 8.4.0 |
| 89 | """ |
| 90 | |
| 91 | _targetfd: int |
| 92 | saved_fd: int |
| 93 | _tmpfile: t.BinaryIO | None |
| 94 | |
| 95 | def __init__(self, targetfd: int) -> None: |
| 96 | self._targetfd = targetfd |
| 97 | self.saved_fd = -1 |
| 98 | self._tmpfile = None |
| 99 | |
| 100 | def start(self) -> None: |
| 101 | self.saved_fd = os.dup(self._targetfd) |
| 102 | self._tmpfile = tempfile.TemporaryFile(buffering=0) |
| 103 | os.dup2(self._tmpfile.fileno(), self._targetfd) |
| 104 | |
| 105 | def stop(self) -> bytes: |
| 106 | assert self._tmpfile is not None, "_FDCapture.start() was not called" |
| 107 | os.dup2(self.saved_fd, self._targetfd) |
| 108 | os.close(self.saved_fd) |
| 109 | self.saved_fd = -1 |
| 110 | self._tmpfile.seek(0) |
| 111 | data = self._tmpfile.read() |
| 112 | self._tmpfile.close() |
| 113 | self._tmpfile = None |
| 114 | return data |
| 115 | |
| 116 | |
| 117 | class BytesIOCopy(io.BytesIO): |