PingPongRealWorldRL / scripts /bounded_tee.py
KurisiTOMA's picture
chore: publish H2 real-world RL system
d8290eb verified
Raw History Blame Contribute Delete
2.78 kB
#!/usr/bin/env python3
"""Mirror a pane stream to stdout and a small fixed set of rotated files."""
from __future__ import annotations
import argparse
import os
from pathlib import Path
import sys
def _rotate(path: Path, backups: int) -> None:
oldest = path.with_name(f"{path.name}.{backups}")
try:
oldest.unlink()
except FileNotFoundError:
pass
for index in range(backups - 1, 0, -1):
source = path.with_name(f"{path.name}.{index}")
destination = path.with_name(f"{path.name}.{index + 1}")
try:
source.replace(destination)
except FileNotFoundError:
pass
try:
path.replace(path.with_name(f"{path.name}.1"))
except FileNotFoundError:
pass
def _warn(message: str) -> None:
try:
os.write(2, f"[bounded_tee warning] {message}\n".encode("utf-8", errors="replace"))
except OSError:
pass
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--path", type=Path, required=True)
parser.add_argument("--max-mib", type=float, default=8.0)
parser.add_argument("--backups", type=int, default=3)
args = parser.parse_args()
if not 1.0 <= args.max_mib <= 1024.0 or not 1 <= args.backups <= 20:
parser.error("--max-mib must be in [1,1024] and --backups in [1,20]")
path = args.path.expanduser().resolve()
max_bytes = int(args.max_mib * 1024 * 1024)
sink = None
try:
path.parent.mkdir(parents=True, exist_ok=True)
sink = path.open("ab", buffering=0)
except OSError as error:
_warn(f"file logging disabled for {path}: {error}")
try:
while True:
chunk = sys.stdin.buffer.read1(65536)
if not chunk:
break
try:
sys.stdout.buffer.write(chunk)
sys.stdout.buffer.flush()
except BrokenPipeError:
pass
if sink is None:
continue
try:
sink.write(chunk)
if sink.tell() >= max_bytes:
sink.close()
sink = None
_rotate(path, args.backups)
sink = path.open("ab", buffering=0)
except OSError as error:
_warn(f"file logging disabled for {path}: {error}")
if sink is not None:
try:
sink.close()
except OSError:
pass
sink = None
finally:
if sink is not None:
try:
sink.close()
except OSError:
pass
return 0
if __name__ == "__main__":
raise SystemExit(main())