diff --git a/mapillary_tools/history.py b/mapillary_tools/history.py index 3cdb426c..92c7121f 100644 --- a/mapillary_tools/history.py +++ b/mapillary_tools/history.py @@ -118,12 +118,21 @@ def get(self, key: str) -> str | None: s = time.perf_counter() - with store.KeyValueStore(self._file, flag="r") as db: + try: + db_ctx = store.KeyValueStore(self._file, flag="r") + except sqlite3.DatabaseError as ex: + LOG.warning(f"Failed to open cache database: {ex}") + return None + + with db_ctx as db: try: raw_payload: bytes | None = db.get(key) # data retrieved from db[key] except Exception as ex: if self._table_not_found(ex): return None + if isinstance(ex, sqlite3.DatabaseError): + LOG.warning(f"Cache read error for {key}: {ex}") + return None raise ex if raw_payload is None: diff --git a/tests/unit/test_persistent_cache.py b/tests/unit/test_persistent_cache.py index e69fe0be..bc7067d4 100644 --- a/tests/unit/test_persistent_cache.py +++ b/tests/unit/test_persistent_cache.py @@ -372,6 +372,20 @@ def test_keys_read_mode_corrupted_database(tmpdir): assert all(isinstance(key, str) for key in keys) +def test_get_returns_none_on_corrupt_database_file(tmpdir): + """Test that get() returns None instead of raising when the database is corrupt.""" + cache_file = os.path.join(tmpdir, "cache_corrupt") + cache = PersistentCache(cache_file) + + cache.set("key1", "value1") + assert cache.get("key1") == "value1" + + with open(cache_file, "wb") as f: + f.write(b"this is not a valid sqlite database file") + + assert cache.get("key1") is None + + def test_multithread_shared_cache_comprehensive(tmpdir): """Test shared cache instance across multiple threads using get->set pattern.