Initial public release

This commit is contained in:
Harvey
2026-04-03 15:53:00 +01:00
commit a99c738164
679 changed files with 108326 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
import asyncio
import logging
import threading
from pathlib import Path
from typing import Any
import aiofiles
import aiofiles.os
import msgspec
logger = logging.getLogger(__name__)
_file_locks: dict[str, threading.Lock] = {}
_locks_lock = threading.Lock()
_async_locks: dict[str, asyncio.Lock] = {}
def _get_file_lock(file_path: Path) -> threading.Lock:
path_str = str(file_path.resolve())
with _locks_lock:
if path_str not in _file_locks:
_file_locks[path_str] = threading.Lock()
return _file_locks[path_str]
def _get_async_lock(file_path: Path) -> asyncio.Lock:
path_str = str(file_path.resolve())
if path_str not in _async_locks:
_async_locks[path_str] = asyncio.Lock()
return _async_locks[path_str]
def atomic_write_json(file_path: Path, data: Any, indent: int = 2) -> None:
lock = _get_file_lock(file_path)
with lock:
file_path.parent.mkdir(parents=True, exist_ok=True)
tmp_path = file_path.with_suffix(file_path.suffix + '.tmp')
try:
_ = indent
with open(tmp_path, 'wb') as f:
f.write(msgspec.json.encode(data))
f.flush()
tmp_path.replace(file_path)
logger.debug(f"Atomically wrote JSON to {file_path}")
except Exception as e:
if tmp_path.exists():
try:
tmp_path.unlink()
except OSError as cleanup_error:
logger.warning("Couldn't remove temp file %s: %s", tmp_path, cleanup_error)
logger.error(f"Failed to write JSON to {file_path}: {e}")
raise
async def atomic_write_json_async(file_path: Path, data: Any, indent: int = 2) -> None:
lock = _get_async_lock(file_path)
async with lock:
await aiofiles.os.makedirs(file_path.parent, exist_ok=True)
tmp_path = file_path.with_suffix(file_path.suffix + '.tmp')
try:
_ = indent
content = msgspec.json.encode(data)
async with aiofiles.open(tmp_path, 'wb') as f:
await f.write(content)
await asyncio.to_thread(tmp_path.replace, file_path)
logger.debug(f"Atomically wrote JSON to {file_path}")
except Exception as e:
try:
if tmp_path.exists():
await aiofiles.os.remove(tmp_path)
except OSError as cleanup_error:
logger.warning("Couldn't remove temp file %s: %s", tmp_path, cleanup_error)
logger.error(f"Failed to write JSON to {file_path}: {e}")
raise
def read_json(file_path: Path, default: Any = None) -> Any:
lock = _get_file_lock(file_path)
with lock:
if not file_path.exists():
return default
with open(file_path, 'rb') as f:
return msgspec.json.decode(f.read())
async def read_json_async(file_path: Path, default: Any = None) -> Any:
lock = _get_async_lock(file_path)
async with lock:
if not file_path.exists():
return default
async with aiofiles.open(file_path, 'rb') as f:
content = await f.read()
return msgspec.json.decode(content)