| 339 | |
| 340 | |
| 341 | def _call_with_output(cmd): |
| 342 | if QUIET: |
| 343 | return _call_quiet(cmd) |
| 344 | |
| 345 | print(f"# {cmd}") |
| 346 | # The following trickery is required so that the 'cmd' thinks it's running |
| 347 | # in a real terminal, while this script gets to intercept its output. |
| 348 | parent, child = pty.openpty() |
| 349 | p = subprocess.Popen(cmd, shell=True, stdin=child, stdout=child, stderr=child) |
| 350 | os.close(child) |
| 351 | output = [] |
| 352 | try: |
| 353 | while True: |
| 354 | # Use select with a timeout to read from the pty. This avoids a potential |
| 355 | # hang if the subprocess dies unexpectedly (e.g. OOM killer) without |
| 356 | # closing the pty, which would cause a blocking os.read to wait forever. |
| 357 | ready, _, _ = select.select([parent], [], [], 0.1) |
| 358 | if ready: |
| 359 | try: |
| 360 | data = os.read(parent, 512).decode('utf-8') |
| 361 | except OSError as e: |
| 362 | if e.errno != errno.EIO: |
| 363 | raise |
| 364 | break # EIO means EOF on some systems |
| 365 | else: |
| 366 | if not data: # EOF |
| 367 | break |
| 368 | print(data, end="") |
| 369 | sys.stdout.flush() |
| 370 | output.append(data) |
| 371 | elif p.poll() is not None: |
| 372 | # If the process has exited and no data is available, stop waiting. |
| 373 | break |
| 374 | finally: |
| 375 | os.close(parent) |
| 376 | while p.poll() is None: |
| 377 | print(".", end="") |
| 378 | time.sleep(0.1) |
| 379 | return p.returncode, "".join(output) |
| 380 | |
| 381 | |
| 382 | def _write(filename, content, log=True): |