File
Blob: src/pyodide/tool_utils.py
| 1 | import subprocess |
| 2 | import sys |
| 3 | from base64 import b64encode |
| 4 | from contextlib import contextmanager |
| 5 | from hashlib import file_digest |
| 6 | from pathlib import Path |
| 7 | from time import time |
| 8 | from typing import Any |
| 9 | |
| 10 | |
| 11 | def run( |
| 12 | cmd: list[str | Path], |
| 13 | cwd: Path | str | None = None, |
| 14 | env: dict[str, str] | None = None, |
| 15 | ) -> subprocess.CompletedProcess[Any]: |
| 16 | """Execute a command and exit on failure with error output.""" |
| 17 | res = subprocess.run( |
| 18 | cmd, capture_output=True, text=True, check=False, cwd=cwd, env=env |
| 19 | ) |
| 20 | if res.returncode: |
| 21 | print("Invocation failed:") |
| 22 | print(res.stdout) |
| 23 | print(res.stderr) |
| 24 | sys.exit(res.returncode) |
| 25 | return res |
| 26 | |
| 27 | |
| 28 | def bytesdigest(p: Path | str) -> bytes: |
| 29 | """Calculate SHA256 digest of a file as bytes.""" |
| 30 | return file_digest(Path(p).open("rb"), "sha256").digest() |
| 31 | |
| 32 | |
| 33 | def hexdigest(p: Path | str) -> str: |
| 34 | """Calculate SHA256 digest of a file as hex string.""" |
| 35 | digest = bytesdigest(p) |
| 36 | return "".join(hex(e)[2:].zfill(2) for e in digest) |
| 37 | |
| 38 | |
| 39 | def b64digest(p: Path | str): |
| 40 | return "sha256-" + b64encode(bytesdigest(p)).decode() |
| 41 | |
| 42 | |
| 43 | @contextmanager |
| 44 | def timing(name: str): |
| 45 | """Context manager to time operations and print elapsed time.""" |
| 46 | print(f"Making {name}...") |
| 47 | start = time() |
| 48 | yield |
| 49 | elapsed = time() - start |
| 50 | print(f" {elapsed:.2f}s") |