_load() masked permission/IO errors as an empty store via os.path.exists();
now raises FileNotFoundError only, propagating real failures. Non-str keys
silently never round-tripped through get() since JSON object keys are always
strings; set()/get() now raise ValueError on a non-str key. _save() clobbered
a symlinked store file with os.replace(); now realpaths the target first.
json.dumps() allows NaN/Infinity by default, producing invalid JSON for
strict readers; now allow_nan=False. A whitespace-only file raised while a
zero-byte file returned {}; both now return {}. Orphaned .<pid>.tmp files
from a hard crash of a dead process are swept on save. JSON (de)serialization
now runs via asyncio.to_thread instead of on the event loop.
Signed-off-by: disqualifier <dev@disqualifier.me>
clear() handles FileNotFoundError as success (the goal state — no file — is reached)
instead of returning False. read/write open with explicit encoding='utf-8'. atomic-write
prose scoped to process-crash safety (NOT power-loss durability — no fsync), in module,
README, and CLAUDE.
Signed-off-by: disqualifier <dev@disqualifier.me>
README + CLAUDE.md error contract now note that _load raises ValueError on valid-but-
non-object JSON (bare list/number/string/null) in addition to JSONDecodeError on a
corrupt file, matching the module docstring (L1).
Signed-off-by: disqualifier <dev@disqualifier.me>
a store file holding valid JSON that is not an object (null/number/string/array) returned a non-dict, breaking get/set/delete/get_all. now raises ValueError, matching the documented corrupt-file contract.
Signed-off-by: disqualifier <dev@disqualifier.me>
AioKV: async file-backed key-value store for single-process local state
(persist-forever, not a cache — no TTL/eviction). atomic writes via
temp-file + os.replace, every read and write under one asyncio.Lock,
blocking fs calls off the loop via asyncio.to_thread. set(key) defaults
to int(time.time()) for seen/did-X-at-T. aiocache alias kept for
back-compat. src/ layout, hatchling, aiofiles dep.
Signed-off-by: disqualifier <dev@disqualifier.me>