mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 15:58:48 +00:00
Merge pull request #779 from PR0M3TH3AN/codex/wrap-worker-functions-with-error-handling
Queue background errors
This commit is contained in:
@@ -1064,6 +1064,7 @@ def display_menu(
|
|||||||
getattr(password_manager, "start_background_relay_check", lambda: None)()
|
getattr(password_manager, "start_background_relay_check", lambda: None)()
|
||||||
_display_live_stats(password_manager)
|
_display_live_stats(password_manager)
|
||||||
while True:
|
while True:
|
||||||
|
getattr(password_manager, "poll_background_errors", lambda: None)()
|
||||||
fp, parent_fp, child_fp = getattr(
|
fp, parent_fp, child_fp = getattr(
|
||||||
password_manager,
|
password_manager,
|
||||||
"header_fingerprint_args",
|
"header_fingerprint_args",
|
||||||
|
@@ -177,6 +177,7 @@ class PasswordManager:
|
|||||||
self.state_manager: Optional[StateManager] = None
|
self.state_manager: Optional[StateManager] = None
|
||||||
self.stats_manager: StatsManager = StatsManager()
|
self.stats_manager: StatsManager = StatsManager()
|
||||||
self.notifications: queue.Queue[Notification] = queue.Queue()
|
self.notifications: queue.Queue[Notification] = queue.Queue()
|
||||||
|
self.error_queue: queue.Queue[Exception] = queue.Queue()
|
||||||
self._current_notification: Optional[Notification] = None
|
self._current_notification: Optional[Notification] = None
|
||||||
self._notification_expiry: float = 0.0
|
self._notification_expiry: float = 0.0
|
||||||
|
|
||||||
@@ -307,6 +308,18 @@ class PasswordManager:
|
|||||||
return self._current_notification
|
return self._current_notification
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def poll_background_errors(self) -> None:
|
||||||
|
"""Process exceptions raised by background threads."""
|
||||||
|
if not hasattr(self, "error_queue"):
|
||||||
|
return
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
exc = self.error_queue.get_nowait()
|
||||||
|
except queue.Empty:
|
||||||
|
break
|
||||||
|
logger.warning("Background task failed: %s", exc)
|
||||||
|
self.notify(f"Background task failed: {exc}", level="WARNING")
|
||||||
|
|
||||||
def lock_vault(self) -> None:
|
def lock_vault(self) -> None:
|
||||||
"""Clear sensitive information from memory."""
|
"""Clear sensitive information from memory."""
|
||||||
if self.entry_manager is not None:
|
if self.entry_manager is not None:
|
||||||
@@ -1430,6 +1443,8 @@ class PasswordManager:
|
|||||||
await self.sync_index_from_nostr_async()
|
await self.sync_index_from_nostr_async()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(f"Background sync failed: {exc}")
|
logger.warning(f"Background sync failed: {exc}")
|
||||||
|
if hasattr(self, "error_queue"):
|
||||||
|
self.error_queue.put(exc)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
@@ -1473,6 +1488,8 @@ class PasswordManager:
|
|||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(f"Relay health check failed: {exc}")
|
logger.warning(f"Relay health check failed: {exc}")
|
||||||
|
if hasattr(self, "error_queue"):
|
||||||
|
self.error_queue.put(exc)
|
||||||
|
|
||||||
self._relay_thread = threading.Thread(target=_worker, daemon=True)
|
self._relay_thread = threading.Thread(target=_worker, daemon=True)
|
||||||
self._relay_thread.start()
|
self._relay_thread.start()
|
||||||
@@ -1489,6 +1506,8 @@ class PasswordManager:
|
|||||||
bus.publish("sync_finished", result)
|
bus.publish("sync_finished", result)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.error(f"Background vault sync failed: {exc}", exc_info=True)
|
logging.error(f"Background vault sync failed: {exc}", exc_info=True)
|
||||||
|
if hasattr(self, "error_queue"):
|
||||||
|
self.error_queue.put(exc)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
|
55
src/tests/test_background_error_reporting.py
Normal file
55
src/tests/test_background_error_reporting.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import logging
|
||||||
|
import queue
|
||||||
|
|
||||||
|
import seedpass.core.manager as manager_module
|
||||||
|
|
||||||
|
|
||||||
|
def _make_pm():
|
||||||
|
pm = manager_module.PasswordManager.__new__(manager_module.PasswordManager)
|
||||||
|
pm.offline_mode = False
|
||||||
|
pm.notifications = queue.Queue()
|
||||||
|
pm.error_queue = queue.Queue()
|
||||||
|
pm.notify = lambda msg, level="INFO": pm.notifications.put(
|
||||||
|
manager_module.Notification(msg, level)
|
||||||
|
)
|
||||||
|
return pm
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_background_sync_error(monkeypatch, caplog):
|
||||||
|
pm = _make_pm()
|
||||||
|
|
||||||
|
async def failing_sync(*_args, **_kwargs):
|
||||||
|
raise RuntimeError("boom")
|
||||||
|
|
||||||
|
monkeypatch.setattr(pm, "attempt_initial_sync_async", failing_sync)
|
||||||
|
monkeypatch.setattr(pm, "sync_index_from_nostr_async", failing_sync)
|
||||||
|
|
||||||
|
pm.start_background_sync()
|
||||||
|
pm._sync_task.join(timeout=1)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
pm.poll_background_errors()
|
||||||
|
|
||||||
|
note = pm.notifications.get_nowait()
|
||||||
|
assert "boom" in note.message
|
||||||
|
assert "boom" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_background_relay_check_error(monkeypatch, caplog):
|
||||||
|
pm = _make_pm()
|
||||||
|
|
||||||
|
class DummyClient:
|
||||||
|
def check_relay_health(self, *_args, **_kwargs):
|
||||||
|
raise RuntimeError("relay boom")
|
||||||
|
|
||||||
|
pm.nostr_client = DummyClient()
|
||||||
|
|
||||||
|
pm.start_background_relay_check()
|
||||||
|
pm._relay_thread.join(timeout=1)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
pm.poll_background_errors()
|
||||||
|
|
||||||
|
note = pm.notifications.get_nowait()
|
||||||
|
assert "relay boom" in note.message
|
||||||
|
assert "relay boom" in caplog.text
|
Reference in New Issue
Block a user