fix: repair DashboardState.__init__ (my prior commit orphaned _backtest_lock)
The self-heal commit (a00f967) inserted the _research method INSIDE __init__ at
method-body indent, truncating it so _change_signal/_backtest_lock/_backtest_
cache never got set -> AttributeError on backtest. Moved _research to a sibling
method; __init__ now sets all attrs. Two chart/sparkline tests updated: a None
research_con now REOPENS lazily, so 'no research db attached' is reached only
when the reopen also fails (they monkeypatch open to fail). 22 api tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a00f967bdf
commit
e7bd8fa279
3 files changed
+17
-9
No files matched your search
+7
-7
@@ -257,6 +257,13 @@ class DashboardState:
|
||||
self.watch_inject = watch_inject
|
||||
self._lock = threading.Lock()
|
||||
self._research_lock = threading.Lock()
|
||||
# lazily-built, process-wide SSE change signal (shared by all clients)
|
||||
self._change_signal: _ChangeSignal | None = None
|
||||
# backtest replay cache (plan 015) — a DEDICATED lock, not self._lock,
|
||||
# so a slow replay (10-30s) never blocks the live snapshot/SSE path.
|
||||
# key = (since, until, repr(settings)) -> (result, monotonic timestamp).
|
||||
self._backtest_lock = threading.Lock()
|
||||
self._backtest_cache: dict = {}
|
||||
|
||||
def _research(self):
|
||||
"""The research DB connection, opened LAZILY and self-healing. It used
|
||||
@@ -276,13 +283,6 @@ class DashboardState:
|
||||
logger.warning("research DB open failed (charts empty): %s", exc)
|
||||
self.research_con = None
|
||||
return self.research_con
|
||||
# lazily-built, process-wide SSE change signal (shared by all clients)
|
||||
self._change_signal: _ChangeSignal | None = None
|
||||
# backtest replay cache (plan 015) — a DEDICATED lock, not self._lock,
|
||||
# so a slow replay (10-30s) never blocks the live snapshot/SSE path.
|
||||
# key = (since, until, repr(settings)) -> (result, monotonic timestamp).
|
||||
self._backtest_lock = threading.Lock()
|
||||
self._backtest_cache: dict = {}
|
||||
|
||||
def change_stamp(self):
|
||||
"""SSE change token from the shared once-per-second signal (see _ChangeSignal)."""
|
||||
|
||||
@@ -93,7 +93,11 @@ def _get(state, path):
|
||||
# Step 3: DashboardState.chart() — bars, markers, levels
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def test_chart_no_research_con_reports_missing_db():
|
||||
def test_chart_no_research_con_reports_missing_db(monkeypatch):
|
||||
# self-heal (2026-07-20): None con now REOPENS lazily; the "missing db"
|
||||
# path is reached only when the reopen ALSO fails.
|
||||
import api.main as m
|
||||
monkeypatch.setattr(m, "open_research_db", lambda name: (_ for _ in ()).throw(RuntimeError("db down")))
|
||||
out = DashboardState("momentum", research_con=None).chart("AAPL")
|
||||
assert out["bars"] == []
|
||||
assert out.get("reason") == "no research db attached"
|
||||
|
||||
@@ -20,7 +20,11 @@ class _FakeResearchCon:
|
||||
return _FakeResult()
|
||||
|
||||
|
||||
def test_bars_without_reader_reports_missing_db():
|
||||
def test_bars_without_reader_reports_missing_db(monkeypatch):
|
||||
# self-heal (2026-07-20): None con reopens lazily; "missing db" only when
|
||||
# the reopen also fails.
|
||||
import api.main as m
|
||||
monkeypatch.setattr(m, "open_research_db", lambda name: (_ for _ in ()).throw(RuntimeError("db down")))
|
||||
out = DashboardState("momentum", research_con=None).bars("AAPL")
|
||||
assert out["bars"] == []
|
||||
assert out.get("reason") == "no research db attached"
|
||||
|
||||
Reference in new issue
Block a user