feat: add ServiceError and detached-safe session factory
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,10 @@ def get_engine(settings: Settings) -> Engine:
|
|||||||
|
|
||||||
|
|
||||||
def get_session_factory(engine: Engine) -> sessionmaker:
|
def get_session_factory(engine: Engine) -> sessionmaker:
|
||||||
return sessionmaker(bind=engine)
|
# expire_on_commit=False: services open a session per operation and return
|
||||||
|
# ORM rows to the TUI after the session closes; loaded attributes must
|
||||||
|
# stay readable on those detached instances.
|
||||||
|
return sessionmaker(bind=engine, expire_on_commit=False)
|
||||||
|
|
||||||
|
|
||||||
def ensure_db(settings: Settings) -> None:
|
def ensure_db(settings: Settings) -> None:
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
class ServiceError(Exception):
|
||||||
|
"""Expected, user-facing failure in a service operation.
|
||||||
|
|
||||||
|
Raised for recoverable conditions: missing DB rows (project/session
|
||||||
|
deleted underneath an action), harnesses no longer installed, duplicate
|
||||||
|
project paths. The TUI catches this and shows a notification instead of
|
||||||
|
letting the worker crash.
|
||||||
|
"""
|
||||||
@@ -123,3 +123,16 @@ def test_pending_migrations_applied_in_order_once(tmp_path, monkeypatch):
|
|||||||
assert _user_version(engine) == 3
|
assert _user_version(engine) == 3
|
||||||
migrate(engine) # re-run is a no-op
|
migrate(engine) # re-run is a no-op
|
||||||
assert applied == [2, 3]
|
assert applied == [2, 3]
|
||||||
|
|
||||||
|
|
||||||
|
def test_rows_stay_readable_after_session_closes(tmp_path):
|
||||||
|
# Services use per-operation sessions; rows they return must remain
|
||||||
|
# readable after the originating session closes (expire_on_commit=False).
|
||||||
|
settings = _tmp_settings(tmp_path)
|
||||||
|
ensure_db(settings)
|
||||||
|
factory = get_session_factory(get_engine(settings))
|
||||||
|
with factory() as session:
|
||||||
|
p = Project(name="x", path="/x")
|
||||||
|
session.add(p)
|
||||||
|
session.commit()
|
||||||
|
assert p.name == "x" # would raise DetachedInstanceError without the flag
|
||||||
|
|||||||
Reference in New Issue
Block a user