diff --git a/src/biocommons/eutils/_internal/queryservice.py b/src/biocommons/eutils/_internal/queryservice.py index 350cb2a..ff24ee2 100644 --- a/src/biocommons/eutils/_internal/queryservice.py +++ b/src/biocommons/eutils/_internal/queryservice.py @@ -144,6 +144,15 @@ def __init__( cache_path = False self._cache = SQLiteCache(cache_path) if cache_path else None + def close(self): + """Close the cache connection.""" + if self._cache is not None: + self._cache.close() + + def __del__(self): + """Ensure cache connection is closed on object destruction.""" + self.close() + def efetch(self, args): """ execute a cached, throttled efetch query diff --git a/src/biocommons/eutils/_internal/sqlitecache.py b/src/biocommons/eutils/_internal/sqlitecache.py index 80bca30..b813152 100644 --- a/src/biocommons/eutils/_internal/sqlitecache.py +++ b/src/biocommons/eutils/_internal/sqlitecache.py @@ -1,7 +1,4 @@ -"""simple key-value cache with transparent payload compression and expiration - -Taken from http://bitbucket.org/reece/rcore/ -""" +"""simple key-value cache with transparent payload compression and expiration""" import logging import pickle @@ -35,6 +32,26 @@ def __init__(self, db_path, compress_values=True): self._logger = logging.getLogger(__name__) self._connect(db_path) + def close(self): + """Close the database connection.""" + if self._con is not None: + self._con.close() + self._logger.info("closed %s", self._db_path) + self._con = None + + def __enter__(self): + """Context manager entry.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit.""" + self.close() + return False + + def __del__(self): + """Ensure connection is closed on object destruction.""" + self.close() + def expire(self, age): cur = self._execute("DELETE FROM cache WHERE strftime('%s','now') - created > ?", [age]) return cur.rowcount diff --git a/tests/test_rcore_sqllitecache.py b/tests/test_rcore_sqllitecache.py index c867470..61eb950 100644 --- a/tests/test_rcore_sqllitecache.py +++ b/tests/test_rcore_sqllitecache.py @@ -15,6 +15,9 @@ def setUp(self): self.cache = SQLiteCache(self._fn) + def tearDown(self): + self.cache.close() + class Test_SQLiteCache_AttrLookup(Test_SQLiteCacheBase): def test_str_str(self):