73 lines
2.2 KiB
Python
73 lines
2.2 KiB
Python
"""Run Typst watchers with readable, document-specific output."""
|
|
|
|
from pathlib import Path
|
|
from queue import Empty, Queue
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
from threading import Thread
|
|
|
|
|
|
def relay(process, label, messages):
|
|
with process.stdout:
|
|
for line in process.stdout:
|
|
messages.put((label, line.rstrip()))
|
|
messages.put((label, None))
|
|
|
|
|
|
def interrupted(signum, frame):
|
|
raise KeyboardInterrupt
|
|
|
|
|
|
def main():
|
|
separator = sys.argv.index("--")
|
|
executable, *flags = sys.argv[1:separator]
|
|
sources = sys.argv[separator + 1:]
|
|
messages = Queue()
|
|
processes = []
|
|
signal.signal(signal.SIGTERM, interrupted)
|
|
try:
|
|
for source in sources:
|
|
process = subprocess.Popen(
|
|
[executable, "watch", *flags, source, str(Path(source).with_suffix(".pdf"))],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
encoding="utf-8",
|
|
errors="replace",
|
|
start_new_session=True,
|
|
)
|
|
processes.append(process)
|
|
Thread(target=relay, args=(process, source, messages), daemon=True).start()
|
|
while True:
|
|
try:
|
|
label, line = messages.get(timeout=0.2)
|
|
except Empty:
|
|
continue
|
|
if line is None:
|
|
print(f"[{label}] Watch-Prozess unerwartet beendet.", file=sys.stderr)
|
|
return 1
|
|
if line:
|
|
print(f"[{label}] {line}", flush=True)
|
|
except KeyboardInterrupt:
|
|
print("\nÜberwachung beendet.", flush=True)
|
|
return 0
|
|
except OSError as error:
|
|
print(f"Watch konnte nicht gestartet werden: {error}", file=sys.stderr)
|
|
return 1
|
|
finally:
|
|
# Stop all watchers, including when startup fails partway through.
|
|
for process in processes:
|
|
if process.poll() is None:
|
|
process.terminate()
|
|
for process in processes:
|
|
try:
|
|
process.wait(timeout=3)
|
|
except subprocess.TimeoutExpired:
|
|
process.kill()
|
|
process.wait()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|