mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-07 06:48:52 +00:00
Merge pull request #705 from PR0M3TH3AN/codex/revise-start_background_sync-implementation
Implement async sync worker cleanup
This commit is contained in:
@@ -1056,6 +1056,7 @@ def display_menu(
|
||||
continue
|
||||
logging.info("Exiting the program.")
|
||||
print(colored("Exiting the program.", "green"))
|
||||
getattr(password_manager, "cleanup", lambda: None)()
|
||||
password_manager.nostr_client.close_client_pool()
|
||||
sys.exit(0)
|
||||
if choice == "1":
|
||||
@@ -1259,6 +1260,7 @@ def main(argv: list[str] | None = None, *, fingerprint: str | None = None) -> in
|
||||
print(colored("\nReceived shutdown signal. Exiting gracefully...", "yellow"))
|
||||
logging.info(f"Received shutdown signal: {sig}. Initiating graceful shutdown.")
|
||||
try:
|
||||
getattr(password_manager, "cleanup", lambda: None)()
|
||||
password_manager.nostr_client.close_client_pool()
|
||||
logging.info("NostrClient closed successfully.")
|
||||
except Exception as exc:
|
||||
@@ -1277,6 +1279,7 @@ def main(argv: list[str] | None = None, *, fingerprint: str | None = None) -> in
|
||||
logger.info("Program terminated by user via KeyboardInterrupt.")
|
||||
print(colored("\nProgram terminated by user.", "yellow"))
|
||||
try:
|
||||
getattr(password_manager, "cleanup", lambda: None)()
|
||||
password_manager.nostr_client.close_client_pool()
|
||||
logging.info("NostrClient closed successfully.")
|
||||
except Exception as exc:
|
||||
@@ -1287,6 +1290,7 @@ def main(argv: list[str] | None = None, *, fingerprint: str | None = None) -> in
|
||||
logger.error(f"A user-related error occurred: {e}", exc_info=True)
|
||||
print(colored(f"Error: {e}", "red"))
|
||||
try:
|
||||
getattr(password_manager, "cleanup", lambda: None)()
|
||||
password_manager.nostr_client.close_client_pool()
|
||||
logging.info("NostrClient closed successfully.")
|
||||
except Exception as exc:
|
||||
@@ -1297,6 +1301,7 @@ def main(argv: list[str] | None = None, *, fingerprint: str | None = None) -> in
|
||||
logger.error(f"An unexpected error occurred: {e}", exc_info=True)
|
||||
print(colored(f"Error: An unexpected error occurred: {e}", "red"))
|
||||
try:
|
||||
getattr(password_manager, "cleanup", lambda: None)()
|
||||
password_manager.nostr_client.close_client_pool()
|
||||
logging.info("NostrClient closed successfully.")
|
||||
except Exception as exc:
|
||||
|
@@ -1281,20 +1281,30 @@ class PasswordManager:
|
||||
|
||||
async def _worker() -> None:
|
||||
try:
|
||||
if hasattr(self, "nostr_client") and hasattr(self, "vault"):
|
||||
self.attempt_initial_sync()
|
||||
if hasattr(self, "sync_index_from_nostr"):
|
||||
self.sync_index_from_nostr()
|
||||
await self.attempt_initial_sync_async()
|
||||
await self.sync_index_from_nostr_async()
|
||||
except Exception as exc:
|
||||
logger.warning(f"Background sync failed: {exc}")
|
||||
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
threading.Thread(target=lambda: asyncio.run(_worker()), daemon=True).start()
|
||||
thread = threading.Thread(
|
||||
target=lambda: asyncio.run(_worker()), daemon=True
|
||||
)
|
||||
thread.start()
|
||||
self._sync_task = thread
|
||||
else:
|
||||
self._sync_task = asyncio.create_task(_worker())
|
||||
|
||||
def cleanup(self) -> None:
|
||||
"""Cancel any pending background sync task."""
|
||||
task = getattr(self, "_sync_task", None)
|
||||
if isinstance(task, asyncio.Task) and not task.done():
|
||||
task.cancel()
|
||||
elif isinstance(task, threading.Thread) and task.is_alive():
|
||||
task.join(timeout=0.1)
|
||||
|
||||
def start_background_relay_check(self) -> None:
|
||||
"""Check relay health in a background thread."""
|
||||
if (
|
||||
|
@@ -195,6 +195,9 @@ class MainWindow(toga.Window):
|
||||
bus.unsubscribe("sync_started", self.sync_started)
|
||||
bus.unsubscribe("sync_finished", self.sync_finished)
|
||||
bus.unsubscribe("vault_locked", self.vault_locked)
|
||||
manager = getattr(self.nostr, "_manager", None)
|
||||
if manager is not None:
|
||||
manager.cleanup()
|
||||
|
||||
|
||||
class EntryDialog(toga.Window):
|
||||
|
@@ -1,4 +1,6 @@
|
||||
import time
|
||||
import asyncio
|
||||
import warnings
|
||||
from types import SimpleNamespace
|
||||
from pathlib import Path
|
||||
import sys
|
||||
@@ -17,14 +19,15 @@ def test_unlock_triggers_sync(monkeypatch, tmp_path):
|
||||
pm.initialize_managers = lambda: None
|
||||
called = {"sync": False}
|
||||
|
||||
def fake_sync(self):
|
||||
async def fake_sync(self):
|
||||
called["sync"] = True
|
||||
|
||||
monkeypatch.setattr(PasswordManager, "sync_index_from_nostr", fake_sync)
|
||||
monkeypatch.setattr(PasswordManager, "sync_index_from_nostr_async", fake_sync)
|
||||
|
||||
pm.unlock_vault("pw")
|
||||
pm.start_background_sync()
|
||||
time.sleep(0.05)
|
||||
pm.cleanup()
|
||||
|
||||
assert called["sync"]
|
||||
|
||||
@@ -54,3 +57,29 @@ def test_quick_unlock_background_sync(monkeypatch, tmp_path):
|
||||
pm.exit_managed_account()
|
||||
|
||||
assert called["bg"]
|
||||
|
||||
|
||||
def test_start_background_sync_running_loop(monkeypatch):
|
||||
pm = PasswordManager.__new__(PasswordManager)
|
||||
pm.offline_mode = False
|
||||
called = {"init": False, "sync": False}
|
||||
|
||||
async def fake_attempt(self):
|
||||
called["init"] = True
|
||||
|
||||
async def fake_sync(self):
|
||||
called["sync"] = True
|
||||
|
||||
monkeypatch.setattr(PasswordManager, "attempt_initial_sync_async", fake_attempt)
|
||||
monkeypatch.setattr(PasswordManager, "sync_index_from_nostr_async", fake_sync)
|
||||
|
||||
async def runner():
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
pm.start_background_sync()
|
||||
await asyncio.sleep(0.01)
|
||||
assert not any(issubclass(wi.category, RuntimeWarning) for wi in w)
|
||||
|
||||
asyncio.run(runner())
|
||||
pm.cleanup()
|
||||
assert called["init"] and called["sync"]
|
||||
|
Reference in New Issue
Block a user