Wraps `subprocess.Popen.communicate()` and logs the command being executed, sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout with leading and trailing whitespace removed.
(cmds: "Sequence[str]", cwd: "Union[str, None]" = None, can_fail: bool = False)
| 473 | |
| 474 | |
| 475 | def run(cmds: "Sequence[str]", cwd: "Union[str, None]" = None, can_fail: bool = False) -> str: |
| 476 | """ |
| 477 | Wraps `subprocess.Popen.communicate()` and logs the command being executed, |
| 478 | sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout |
| 479 | with leading and trailing whitespace removed. |
| 480 | """ |
| 481 | |
| 482 | def timestamp() -> str: |
| 483 | return datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] # same format as logging |
| 484 | |
| 485 | def stream_reader(pipe, collector: "list[str]", log_file) -> None: |
| 486 | for line in iter(pipe.readline, ""): |
| 487 | log_file.write(f"{timestamp()} {line}") |
| 488 | log_file.flush() |
| 489 | collector.append(line) |
| 490 | pipe.close() |
| 491 | |
| 492 | logger.debug(f"running command `{' '.join(cmds)}` in directory '{cwd}'") |
| 493 | stdout: list[str] = [] |
| 494 | stderr: list[str] = [] |
| 495 | |
| 496 | # Ensure both live logs available in the log file |
| 497 | # and the putput. |
| 498 | with open(LOG_FILE, "a", encoding="utf-8") as log_file_handle: |
| 499 | proc = sp.Popen(cmds, cwd=cwd, stdout=sp.PIPE, stderr=sp.PIPE, encoding="utf-8") |
| 500 | assert proc.stdout and proc.stderr |
| 501 | |
| 502 | t_out = threading.Thread(target=stream_reader, args=(proc.stdout, stdout, log_file_handle)) |
| 503 | t_err = threading.Thread(target=stream_reader, args=(proc.stderr, stderr, log_file_handle)) |
| 504 | t_out.start() |
| 505 | t_err.start() |
| 506 | t_out.join() |
| 507 | t_err.join() |
| 508 | proc.wait() |
| 509 | |
| 510 | logger.debug(f"command returned {proc.returncode}") |
| 511 | |
| 512 | if proc.returncode != 0 and not can_fail: |
| 513 | print("-" * 70) |
| 514 | print("".join(stderr)) |
| 515 | print("-" * 70) |
| 516 | raise RuntimeError(f"Command `{' '.join(cmds)}` returned exit code {proc.returncode}") |
| 517 | |
| 518 | return "".join(stdout).strip() |
| 519 | |
| 520 | |
| 521 | if platform.system() == "Darwin": |
no test coverage detected