|
5 | 5 | import subprocess |
6 | 6 | from subprocess import CompletedProcess |
7 | 7 | from dataclasses import dataclass |
8 | | -from io import StringIO |
| 8 | +from io import StringIO, TextIOBase |
9 | 9 | import shutil |
10 | 10 | import tempfile |
11 | 11 | import contextlib |
|
33 | 33 | ANSI_DECODER = AnsiDecoder() |
34 | 34 |
|
35 | 35 |
|
| 36 | +class _LogTee(TextIOBase): |
| 37 | + """Output stream that writes both to a buffer and to the real |
| 38 | + std/err stream. Used by the 'with_log' operation.""" |
| 39 | + |
| 40 | + def __init__(self, log_buffer: StringIO, live_stream: TextIOBase): |
| 41 | + self._buf = log_buffer |
| 42 | + self._live = live_stream |
| 43 | + |
| 44 | + # @overrides |
| 45 | + def write(self, s: str) -> int: |
| 46 | + # -- Write to the log buffer |
| 47 | + self._buf.write(s) |
| 48 | + # -- Write to live output. |
| 49 | + self._live.write(s) |
| 50 | + self._live.flush() |
| 51 | + # -- All done. |
| 52 | + return len(s) |
| 53 | + |
| 54 | + # @overrides |
| 55 | + def flush(self) -> None: |
| 56 | + self._buf.flush() |
| 57 | + self._live.flush() |
| 58 | + |
| 59 | + |
36 | 60 | class CapturedLog: |
37 | 61 | """Holds the captured stdout + stderr.""" |
38 | 62 |
|
39 | 63 | def __init__(self): |
40 | 64 | self._buf = StringIO() |
| 65 | + self._output_stream = _LogTee(self._buf, sys.__stdout__) |
| 66 | + |
| 67 | + @property |
| 68 | + def output_stream(self) -> TextIOBase: |
| 69 | + """Getter to the output stream that sends the output to the logger |
| 70 | + buffer and to live output.""" |
| 71 | + return self._output_stream |
41 | 72 |
|
42 | 73 | @property |
43 | 74 | def buf(self) -> StringIO: |
@@ -480,12 +511,14 @@ def _get_local_config_url() -> str: |
480 | 511 | @contextlib.contextmanager |
481 | 512 | def with_logger(self): |
482 | 513 | """Capture stdout + stderr and yield a CapturedLog object.""" |
| 514 | + print("----- Begin log") |
483 | 515 | log = CapturedLog() |
484 | 516 | with ( |
485 | | - contextlib.redirect_stdout(log.buf), |
486 | | - contextlib.redirect_stderr(log.buf), |
| 517 | + contextlib.redirect_stdout(log.output_stream), |
| 518 | + contextlib.redirect_stderr(log.output_stream), |
487 | 519 | ): |
488 | 520 | yield log |
| 521 | + print("----- End log") |
489 | 522 |
|
490 | 523 | @property |
491 | 524 | def sandbox(self) -> Optional[ApioSandbox]: |
|
0 commit comments