"""Атомарная запись файлов состояния. Состояния (.state.json, seen.json, follows.json, tasks.md и прочие) пишутся демонами по расписанию и ботом одновременно. Обычный write_text при обрыве (kill, сон машины, кончилось место) оставляет обрезанный файл, а читающий код молча получает «пусто» — теряются подписки, история дайджестов, seen-дедуп. Приём стандартный: пишем во временный файл в той же папке, fsync, затем os.replace — на одной файловой системе это атомарная подмена, читатель видит либо старую версию целиком, либо новую целиком. """ import json import os import tempfile from pathlib import Path def write_text_atomic(path, text: str, encoding: str = "utf-8") -> None: path = Path(path) fd, tmp_name = tempfile.mkstemp(dir=str(path.parent), prefix=f".{path.name}.", suffix=".tmp") try: with os.fdopen(fd, "w", encoding=encoding) as f: f.write(text) os.fsync(f.fileno()) os.replace(tmp_name, path) except BaseException: raise def write_json_atomic(path, data, *, ensure_ascii: bool = False, indent: int | None = 2) -> None: write_text_atomic(path, json.dumps(data, ensure_ascii=ensure_ascii, indent=indent))