This is an automated email from the ASF dual-hosted git repository.
skrawcz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/burr.git
The following commit(s) were added to refs/heads/main by this push:
new 692789e2 fix(persistence): improve error message for uninitialized
SQLitePersister (#613)
692789e2 is described below
commit 692789e21e2c950bd97c201d42356f8748affce3
Author: lif <[email protected]>
AuthorDate: Thu Jan 1 04:47:48 2026 +0800
fix(persistence): improve error message for uninitialized SQLitePersister
(#613)
* fix(persistence): improve error message for uninitialized SQLitePersister
When load(), save(), or list_app_ids() are called before initialize(),
raise a clear RuntimeError with actionable guidance instead of cryptic
sqlite3.OperationalError about missing table.
Closes #417
Signed-off-by: majiayu000 <[email protected]>
* refactor: extract error message to constant
Address review feedback: extract the uninitialized persister
error message to a module-level constant _UNINITIALIZED_PERSISTER_ERROR
and use it in all three places (list_app_ids, load, save).
---------
Signed-off-by: majiayu000 <[email protected]>
---
burr/core/persistence.py | 92 +++++++++++++++++++++++++++---------------
tests/core/test_persistence.py | 32 +++++++++++++++
2 files changed, 92 insertions(+), 32 deletions(-)
diff --git a/burr/core/persistence.py b/burr/core/persistence.py
index f217f739..c32bf8e9 100644
--- a/burr/core/persistence.py
+++ b/burr/core/persistence.py
@@ -33,6 +33,13 @@ try:
except ImportError:
Self = None
+# Error message template for uninitialized SQLitePersister
+_UNINITIALIZED_PERSISTER_ERROR = (
+ "Uninitialized persister: table '{table_name}' does not exist. "
+ "Make sure to call .initialize() on the persister before passing it "
+ "to the ApplicationBuilder."
+)
+
class PersistedStateData(TypedDict):
partition_key: str
@@ -444,12 +451,19 @@ class SQLitePersister(BaseStatePersister, BaseCopyable):
)
cursor = self.connection.cursor()
- cursor.execute(
- f"SELECT DISTINCT app_id FROM {self.table_name} "
- f"WHERE partition_key = ? "
- f"ORDER BY created_at DESC",
- (partition_key,),
- )
+ try:
+ cursor.execute(
+ f"SELECT DISTINCT app_id FROM {self.table_name} "
+ f"WHERE partition_key = ? "
+ f"ORDER BY created_at DESC",
+ (partition_key,),
+ )
+ except sqlite3.OperationalError as e:
+ if "no such table" in str(e):
+ raise RuntimeError(
+
_UNINITIALIZED_PERSISTER_ERROR.format(table_name=self.table_name)
+ ) from e
+ raise
app_ids = [row[0] for row in cursor.fetchall()]
return app_ids
@@ -475,27 +489,34 @@ class SQLitePersister(BaseStatePersister, BaseCopyable):
)
logger.debug("Loading %s, %s, %s", partition_key, app_id, sequence_id)
cursor = self.connection.cursor()
- if app_id is None:
- # get latest for all app_ids
- cursor.execute(
- f"SELECT position, state, sequence_id, app_id, created_at,
status FROM {self.table_name} "
- f"WHERE partition_key = ? "
- f"ORDER BY CREATED_AT DESC LIMIT 1",
- (partition_key,),
- )
- elif sequence_id is None:
- cursor.execute(
- f"SELECT position, state, sequence_id, app_id, created_at,
status FROM {self.table_name} "
- f"WHERE partition_key = ? AND app_id = ? "
- f"ORDER BY sequence_id DESC LIMIT 1",
- (partition_key, app_id),
- )
- else:
- cursor.execute(
- f"SELECT position, state, sequence_id, app_id, created_at,
status FROM {self.table_name} "
- f"WHERE partition_key = ? AND app_id = ? AND sequence_id = ?",
- (partition_key, app_id, sequence_id),
- )
+ try:
+ if app_id is None:
+ # get latest for all app_ids
+ cursor.execute(
+ f"SELECT position, state, sequence_id, app_id, created_at,
status FROM {self.table_name} "
+ f"WHERE partition_key = ? "
+ f"ORDER BY CREATED_AT DESC LIMIT 1",
+ (partition_key,),
+ )
+ elif sequence_id is None:
+ cursor.execute(
+ f"SELECT position, state, sequence_id, app_id, created_at,
status FROM {self.table_name} "
+ f"WHERE partition_key = ? AND app_id = ? "
+ f"ORDER BY sequence_id DESC LIMIT 1",
+ (partition_key, app_id),
+ )
+ else:
+ cursor.execute(
+ f"SELECT position, state, sequence_id, app_id, created_at,
status FROM {self.table_name} "
+ f"WHERE partition_key = ? AND app_id = ? AND sequence_id =
?",
+ (partition_key, app_id, sequence_id),
+ )
+ except sqlite3.OperationalError as e:
+ if "no such table" in str(e):
+ raise RuntimeError(
+
_UNINITIALIZED_PERSISTER_ERROR.format(table_name=self.table_name)
+ ) from e
+ raise
row = cursor.fetchone()
if row is None:
return None
@@ -551,11 +572,18 @@ class SQLitePersister(BaseStatePersister, BaseCopyable):
)
cursor = self.connection.cursor()
json_state = json.dumps(state.serialize(**self.serde_kwargs))
- cursor.execute(
- f"INSERT INTO {self.table_name} (partition_key, app_id,
sequence_id, position, state, status) "
- f"VALUES (?, ?, ?, ?, ?, ?)",
- (partition_key, app_id, sequence_id, position, json_state, status),
- )
+ try:
+ cursor.execute(
+ f"INSERT INTO {self.table_name} (partition_key, app_id,
sequence_id, position, state, status) "
+ f"VALUES (?, ?, ?, ?, ?, ?)",
+ (partition_key, app_id, sequence_id, position, json_state,
status),
+ )
+ except sqlite3.OperationalError as e:
+ if "no such table" in str(e):
+ raise RuntimeError(
+
_UNINITIALIZED_PERSISTER_ERROR.format(table_name=self.table_name)
+ ) from e
+ raise
self.connection.commit()
def cleanup(self):
diff --git a/tests/core/test_persistence.py b/tests/core/test_persistence.py
index b990b7d6..b362cd96 100644
--- a/tests/core/test_persistence.py
+++ b/tests/core/test_persistence.py
@@ -94,6 +94,38 @@ def
test_sqlite_persistence_is_initialized_true_new_connection(tmp_path):
p2.cleanup()
+def test_sqlite_persister_load_without_initialize_raises_runtime_error():
+ """Test that calling load() without initialize() raises a clear
RuntimeError."""
+ persister = SQLLitePersister(db_path=":memory:", table_name="test_table")
+ try:
+ with pytest.raises(RuntimeError, match="Uninitialized persister"):
+ persister.load("partition_key", "app_id")
+ finally:
+ persister.cleanup()
+
+
+def test_sqlite_persister_save_without_initialize_raises_runtime_error():
+ """Test that calling save() without initialize() raises a clear
RuntimeError."""
+ persister = SQLLitePersister(db_path=":memory:", table_name="test_table")
+ try:
+ with pytest.raises(RuntimeError, match="Uninitialized persister"):
+ persister.save(
+ "partition_key", "app_id", 1, "position", State({"key":
"value"}), "completed"
+ )
+ finally:
+ persister.cleanup()
+
+
+def
test_sqlite_persister_list_app_ids_without_initialize_raises_runtime_error():
+ """Test that calling list_app_ids() without initialize() raises a clear
RuntimeError."""
+ persister = SQLLitePersister(db_path=":memory:", table_name="test_table")
+ try:
+ with pytest.raises(RuntimeError, match="Uninitialized persister"):
+ persister.list_app_ids("partition_key")
+ finally:
+ persister.cleanup()
+
+
@pytest.mark.parametrize(
"method_name,kwargs",
[