Coverage for pyWebLayout/core/persistence.py: 90%
27 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-08 20:34 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-08 20:34 +0000
1"""
2Small JSON-file helpers shared by the per-document stores.
4BookmarkManager and HighlightManager both keep a JSON file per document under a
5directory, and both had their own copy of "make the directory, try to read it,
6swallow and print on failure". The duplication is the point of this module; the
7file formats themselves stay owned by each store.
8"""
10from __future__ import annotations
12import json
13import logging
14from pathlib import Path
15from typing import Any
17logger = logging.getLogger(__name__)
20def ensure_dir(path: str | Path) -> Path:
21 """Return `path` as a Path, creating it and any missing parents."""
22 directory = Path(path)
23 directory.mkdir(parents=True, exist_ok=True)
24 return directory
27def read_json(path: Path, default: Any) -> Any:
28 """
29 Read JSON from `path`, returning `default` if it is missing or unreadable.
31 A corrupt store must not stop a book from opening, so failures are logged
32 and swallowed. `default` is returned as given, so pass a fresh mutable if
33 the caller intends to mutate it.
34 """
35 if not path.exists():
36 return default
38 try:
39 with open(path, 'r', encoding='utf-8') as handle:
40 return json.load(handle)
41 except (OSError, ValueError):
42 logger.warning("Could not read %s; ignoring its contents", path, exc_info=True)
43 return default
46def write_json(path: Path, data: Any) -> bool:
47 """
48 Write `data` to `path` as JSON.
50 Returns True on success. Failures are logged rather than raised: losing a
51 bookmark is not a reason to take down the reader.
52 """
53 try:
54 with open(path, 'w', encoding='utf-8') as handle:
55 json.dump(data, handle, indent=2)
56 return True
57 except (OSError, TypeError, ValueError):
58 logger.error("Could not write %s", path, exc_info=True)
59 return False