# Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca """I/O helpers: read-only SQLite access, parquet layers, manifests.""" from __future__ import annotations import hashlib import json import sqlite3 from datetime import datetime, timezone from pathlib import Path import polars as pl def sha256_file(path: Path, chunk: int = 1 << 24) -> str: h = hashlib.sha256() with open(path, "rb") as f: while True: b = f.read(chunk) if not b: break h.update(b) return h.hexdigest() def sqlite_ro(path: Path) -> sqlite3.Connection: """Open a SQLite file strictly read-only (URI mode).""" return sqlite3.connect(f"file:{path}?mode=ro", uri=True) def query(con: sqlite3.Connection, sql: str) -> pl.DataFrame: return pl.read_database(sql, con, infer_schema_length=None) def write_manifest(path: Path, payload: dict) -> None: payload = {"written_utc": datetime.now(timezone.utc).isoformat(timespec="seconds"), **payload} path.write_text(json.dumps(payload, indent=2, ensure_ascii=False, default=str)) def read_manifest(path: Path) -> dict: return json.loads(path.read_text())