From 2a005692eee62bfe8425ebf800e758728775bbfe Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Fri, 23 Jan 2026 21:31:54 +0000 Subject: [PATCH] Fixes to JSONL management, starting from empty state rather than default DB. --- paskia/db/jsonl.py | 43 ++++++++++++++++++++++------------------- paskia/db/operations.py | 19 +++++++++++++----- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/paskia/db/jsonl.py b/paskia/db/jsonl.py index a009321..2dff383 100644 --- a/paskia/db/jsonl.py +++ b/paskia/db/jsonl.py @@ -32,7 +32,7 @@ class _ChangeRecord(msgspec.Struct): _change_encoder = msgspec.json.Encoder() -async def load_jsonl(db_path: Path, empty_data: dict) -> dict: +async def load_jsonl(db_path: Path) -> dict: """Load data from disk by applying change log. Replays all changes from JSONL file using plain dicts (to handle @@ -40,29 +40,32 @@ async def load_jsonl(db_path: Path, empty_data: dict) -> dict: Args: db_path: Path to the JSONL database file - empty_data: Empty data structure to start with (as dict) Returns: The final state after applying all changes + + Raises: + ValueError: If file doesn't exist or cannot be loaded """ - data_dict = empty_data.copy() - if db_path.exists(): - try: - # Read entire file at once and split into lines - async with aiofiles.open(db_path, "rb") as f: - content = await f.read() - for line_num, line in enumerate(content.split(b"\n"), 1): - line = line.strip() - if not line: - continue - try: - change = msgspec.json.decode(line) - # Apply the diff to current state (marshal=True for $-prefixed keys) - data_dict = jsondiff.patch(data_dict, change["diff"], marshal=True) - except Exception as e: - raise ValueError(f"Error parsing line {line_num}: {e}") - except (OSError, ValueError, msgspec.DecodeError) as e: - raise ValueError(f"Failed to load database: {e}") + if not db_path.exists(): + raise ValueError(f"Database file not found: {db_path}") + data_dict: dict = {} + try: + # Read entire file at once and split into lines + async with aiofiles.open(db_path, "rb") as f: + content = await f.read() + for line_num, line in enumerate(content.split(b"\n"), 1): + line = line.strip() + if not line: + continue + try: + change = msgspec.json.decode(line) + # Apply the diff to current state (marshal=True for $-prefixed keys) + data_dict = jsondiff.patch(data_dict, change["diff"], marshal=True) + except Exception as e: + raise ValueError(f"Error parsing line {line_num}: {e}") + except (OSError, ValueError, msgspec.DecodeError) as e: + raise ValueError(f"Failed to load database: {e}") return data_dict diff --git a/paskia/db/operations.py b/paskia/db/operations.py index fff966a..db0fbc5 100644 --- a/paskia/db/operations.py +++ b/paskia/db/operations.py @@ -73,13 +73,22 @@ class DB: self._current_actor: str = "system" async def load(self, db_path: str | None = None) -> None: - """Load data from JSONL change log.""" + """Load data from JSONL change log. + + If file doesn't exist, keeps the initialized empty structure and + sets _previous_builtins to {} for creating a new database. + """ if db_path is not None: self.db_path = Path(db_path) - empty = msgspec.to_builtins(self._data) - data_dict = await load_jsonl(self.db_path, empty) - self._data = _json_decoder.decode(_json_encoder.encode(data_dict)) - self._previous_builtins = msgspec.to_builtins(self._data) + try: + data_dict = await load_jsonl(self.db_path) + self._data = _json_decoder.decode(_json_encoder.encode(data_dict)) + # Track the JSONL file state directly - this is what we diff against + self._previous_builtins = data_dict + except ValueError: + if self.db_path.exists(): + raise # File exists but failed to load - re-raise + # File doesn't exist: keep initialized _data, _previous_builtins stays {} def _queue_change(self) -> None: current = msgspec.to_builtins(self._data)