From 7b0344739f62a2b9b66364c2afe3464ab4d5338e Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 22 Aug 2025 22:24:04 -0400 Subject: [PATCH] refactor: restrict console logging and pause during UI --- src/main.py | 18 +++++++++--------- src/seedpass/core/manager.py | 25 +++++++++++++++++++++++++ src/seedpass/core/menu_handler.py | 3 +++ src/utils/logging_utils.py | 23 +++++++++++++++++++++++ 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/main.py b/src/main.py index d6c16ce..858b4ec 100644 --- a/src/main.py +++ b/src/main.py @@ -39,7 +39,7 @@ from utils import ( ) from utils.clipboard import ClipboardUnavailableError from utils.atomic_write import atomic_write -from utils.logging_utils import ConsolePauseFilter +from utils.logging_utils import ConsolePauseFilter, pause_logging_for_ui import queue from local_bip85.bip85 import Bip85Error @@ -86,12 +86,6 @@ def configure_logging(): """Configure application-wide logging with queue-based handlers.""" global _queue_listener - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) - - for handler in logger.handlers[:]: - logger.removeHandler(handler) - log_directory = Path("logs") log_directory.mkdir(parents=True, exist_ok=True) @@ -99,7 +93,7 @@ def configure_logging(): queue_handler = QueueHandler(log_queue) console_handler = logging.StreamHandler(sys.stderr) - console_handler.setLevel(logging.ERROR) + console_handler.setLevel(logging.WARNING) console_handler.addFilter(ConsolePauseFilter()) file_handler = logging.FileHandler(log_directory / "main.log") @@ -114,12 +108,17 @@ def configure_logging(): _queue_listener = QueueListener(log_queue, console_handler, file_handler) _queue_listener.start() - logger.addHandler(queue_handler) + logging.basicConfig( + level=logging.DEBUG, + handlers=[queue_handler], + force=True, + ) logging.getLogger("monstr").setLevel(logging.WARNING) logging.getLogger("nostr").setLevel(logging.WARNING) +@pause_logging_for_ui def confirm_action(prompt: str) -> bool: """ Prompts the user for confirmation. @@ -168,6 +167,7 @@ def get_notification_text(pm: PasswordManager) -> str: return color_text(getattr(note, "message", ""), category) +@pause_logging_for_ui def handle_switch_fingerprint(password_manager: PasswordManager): """ Handles switching the active fingerprint. diff --git a/src/seedpass/core/manager.py b/src/seedpass/core/manager.py index 66fe9aa..690aef0 100644 --- a/src/seedpass/core/manager.py +++ b/src/seedpass/core/manager.py @@ -103,6 +103,7 @@ from mnemonic import Mnemonic from datetime import datetime from utils.fingerprint_manager import FingerprintManager +from utils.logging_utils import pause_logging_for_ui # Import NostrClient from nostr.client import NostrClient @@ -834,6 +835,7 @@ class PasswordManager: print(colored(f"Error: Failed to load parent seed: {e}", "red")) raise SeedPassError(f"Failed to load parent seed: {e}") from e + @pause_logging_for_ui @requires_unlocked def handle_switch_fingerprint(self, *, password: Optional[str] = None) -> bool: return self.profile_service.handle_switch_fingerprint(password=password) @@ -894,6 +896,7 @@ class PasswordManager: self.update_activity() self.start_background_sync() + @pause_logging_for_ui def handle_existing_seed(self, *, password: Optional[str] = None) -> None: """ Handles the scenario where an existing parent seed file is found. @@ -980,6 +983,7 @@ class PasswordManager: print(colored(f"Error: Failed to decrypt parent seed: {e}", "red")) raise SeedPassError(f"Failed to decrypt parent seed: {e}") from e + @pause_logging_for_ui def handle_new_seed_setup(self) -> None: """ Handles the setup process when no existing parent seed is found. @@ -1881,9 +1885,11 @@ class PasswordManager: self.notify("Starting with a new, empty vault.", level="INFO") return + @pause_logging_for_ui def handle_add_password(self) -> None: self.entry_service.handle_add_password() + @pause_logging_for_ui def handle_add_totp(self) -> None: """Add a TOTP entry either derived from the seed or imported.""" try: @@ -2025,6 +2031,7 @@ class PasswordManager: print(colored(f"Error: Failed to add TOTP: {e}", "red")) pause() + @pause_logging_for_ui def handle_add_ssh_key(self) -> None: """Add an SSH key pair entry and display the derived keys.""" try: @@ -2082,6 +2089,7 @@ class PasswordManager: print(colored(f"Error: Failed to add SSH key: {e}", "red")) pause() + @pause_logging_for_ui def handle_add_seed(self) -> None: """Add a derived BIP-39 seed phrase entry.""" try: @@ -2151,6 +2159,7 @@ class PasswordManager: print(colored(f"Error: Failed to add seed phrase: {e}", "red")) pause() + @pause_logging_for_ui def handle_add_pgp(self) -> None: """Add a PGP key entry and display the generated key.""" try: @@ -2218,6 +2227,7 @@ class PasswordManager: print(colored(f"Error: Failed to add PGP key: {e}", "red")) pause() + @pause_logging_for_ui def handle_add_nostr_key(self) -> None: """Add a Nostr key entry and display the derived keys.""" try: @@ -2277,6 +2287,7 @@ class PasswordManager: print(colored(f"Error: Failed to add Nostr key: {e}", "red")) pause() + @pause_logging_for_ui def handle_add_key_value(self) -> None: """Add a generic key/value entry.""" try: @@ -2358,6 +2369,7 @@ class PasswordManager: print(colored(f"Error: Failed to add key/value entry: {e}", "red")) pause() + @pause_logging_for_ui def handle_add_managed_account(self) -> None: """Add a managed account seed entry.""" try: @@ -3147,6 +3159,7 @@ class PasswordManager: print(colored("Error: Failed to retrieve the password.", "red")) return + @pause_logging_for_ui def handle_retrieve_entry(self) -> None: """Prompt for an index and display the corresponding entry.""" try: @@ -3181,6 +3194,7 @@ class PasswordManager: print(colored(f"Error: Failed to retrieve password: {e}", "red")) pause() + @pause_logging_for_ui def handle_modify_entry(self) -> None: """ Handles modifying an existing password entry by prompting the user for the index number @@ -3621,6 +3635,7 @@ class PasswordManager: logging.error(f"Error during modifying entry: {e}", exc_info=True) print(colored(f"Error: Failed to modify entry: {e}", "red")) + @pause_logging_for_ui def handle_search_entries(self) -> None: """Prompt for a query, list matches and optionally show details.""" try: @@ -3830,6 +3845,7 @@ class PasswordManager: ) print("-" * 40) + @pause_logging_for_ui def handle_list_entries(self) -> None: self.menu_handler.handle_list_entries() @@ -3870,6 +3886,7 @@ class PasswordManager: logging.error(f"Error during entry deletion: {e}", exc_info=True) print(colored(f"Error: Failed to delete entry: {e}", "red")) + @pause_logging_for_ui def handle_archive_entry(self) -> None: """Archive an entry without deleting it.""" try: @@ -3888,6 +3905,7 @@ class PasswordManager: logging.error(f"Error archiving entry: {e}", exc_info=True) print(colored(f"Error: Failed to archive entry: {e}", "red")) + @pause_logging_for_ui def handle_view_archived_entries(self) -> None: """Display archived entries and optionally view or restore them.""" try: @@ -3955,9 +3973,11 @@ class PasswordManager: logging.error(f"Error viewing archived entries: {e}", exc_info=True) print(colored(f"Error: Failed to view archived entries: {e}", "red")) + @pause_logging_for_ui def handle_display_totp_codes(self) -> None: self.menu_handler.handle_display_totp_codes() + @pause_logging_for_ui def handle_verify_checksum(self) -> None: """ Handles verifying the script's checksum against the stored checksum to ensure integrity. @@ -3997,6 +4017,7 @@ class PasswordManager: logging.error(f"Error during checksum verification: {e}", exc_info=True) print(colored(f"Error: Failed to verify checksum: {e}", "red")) + @pause_logging_for_ui def handle_update_script_checksum(self) -> None: """Generate a new checksum for the manager script.""" if not confirm_action("Generate new script checksum? (Y/N): "): @@ -4142,6 +4163,7 @@ class PasswordManager: logging.error(f"Failed to restore backup: {e}", exc_info=True) print(colored(f"Error: Failed to restore backup: {e}", "red")) + @pause_logging_for_ui def handle_export_database( self, dest: Path | None = None, @@ -4184,6 +4206,7 @@ class PasswordManager: print(colored(f"Error: Failed to export database: {e}", "red")) return None + @pause_logging_for_ui def handle_import_database(self, src: Path) -> None: """Import a portable database file, replacing the current index.""" @@ -4266,6 +4289,7 @@ class PasswordManager: print(colored("Database imported successfully.", "green")) self.sync_vault() + @pause_logging_for_ui def handle_export_totp_codes(self) -> Path | None: """Export all 2FA codes to a JSON file for other authenticator apps.""" try: @@ -4336,6 +4360,7 @@ class PasswordManager: print(colored(f"Error: Failed to export 2FA codes: {e}", "red")) return None + @pause_logging_for_ui def handle_backup_reveal_parent_seed( self, file: Path | None = None, *, password: Optional[str] = None ) -> None: diff --git a/src/seedpass/core/menu_handler.py b/src/seedpass/core/menu_handler.py index 1bea30d..6cbbfef 100644 --- a/src/seedpass/core/menu_handler.py +++ b/src/seedpass/core/menu_handler.py @@ -10,6 +10,7 @@ from .entry_types import EntryType, ALL_ENTRY_TYPES import seedpass.core.manager as manager_module from utils.color_scheme import color_text from utils.terminal_utils import clear_header_with_notification +from utils.logging_utils import pause_logging_for_ui if TYPE_CHECKING: # pragma: no cover - typing only from .manager import PasswordManager @@ -21,6 +22,7 @@ class MenuHandler: def __init__(self, manager: PasswordManager) -> None: self.manager = manager + @pause_logging_for_ui def handle_list_entries(self) -> None: """List entries and optionally show details.""" pm = self.manager @@ -86,6 +88,7 @@ class MenuHandler: logging.error(f"Failed to list entries: {e}", exc_info=True) print(colored(f"Error: Failed to list entries: {e}", "red")) + @pause_logging_for_ui def handle_display_totp_codes(self) -> None: """Display all stored TOTP codes with a countdown progress bar.""" pm = self.manager diff --git a/src/utils/logging_utils.py b/src/utils/logging_utils.py index f11c07f..b3f573b 100644 --- a/src/utils/logging_utils.py +++ b/src/utils/logging_utils.py @@ -1,4 +1,6 @@ import logging +from contextlib import contextmanager +from functools import wraps _console_paused = False @@ -22,3 +24,24 @@ def resume_console_logging() -> None: """Resume logging to console handlers.""" global _console_paused _console_paused = False + + +@contextmanager +def console_logging_paused() -> None: + """Context manager to pause console logging within a block.""" + pause_console_logging() + try: + yield + finally: + resume_console_logging() + + +def pause_logging_for_ui(func): + """Decorator to pause console logging while ``func`` executes.""" + + @wraps(func) + def wrapper(*args, **kwargs): + with console_logging_paused(): + return func(*args, **kwargs) + + return wrapper