mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 07:48:57 +00:00
Use custom SeedPassError instead of sys.exit
This commit is contained in:
@@ -9,6 +9,7 @@ from typing import Optional
|
|||||||
import typer
|
import typer
|
||||||
|
|
||||||
from .common import _get_services
|
from .common import _get_services
|
||||||
|
from seedpass.core.errors import SeedPassError
|
||||||
|
|
||||||
app = typer.Typer(
|
app = typer.Typer(
|
||||||
help="SeedPass command line interface",
|
help="SeedPass command line interface",
|
||||||
@@ -49,6 +50,15 @@ app.add_typer(util.app, name="util")
|
|||||||
app.add_typer(api.app, name="api")
|
app.add_typer(api.app, name="api")
|
||||||
|
|
||||||
|
|
||||||
|
def run() -> None:
|
||||||
|
"""Invoke the CLI, handling SeedPass errors gracefully."""
|
||||||
|
try:
|
||||||
|
app()
|
||||||
|
except SeedPassError as exc:
|
||||||
|
typer.echo(str(exc), err=True)
|
||||||
|
raise typer.Exit(1) from exc
|
||||||
|
|
||||||
|
|
||||||
def _gui_backend_available() -> bool:
|
def _gui_backend_available() -> bool:
|
||||||
"""Return True if a platform-specific BeeWare backend is installed."""
|
"""Return True if a platform-specific BeeWare backend is installed."""
|
||||||
for pkg in ("toga_gtk", "toga_winforms", "toga_cocoa"):
|
for pkg in ("toga_gtk", "toga_winforms", "toga_cocoa"):
|
||||||
@@ -173,4 +183,4 @@ def gui(
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": # pragma: no cover
|
if __name__ == "__main__": # pragma: no cover
|
||||||
app()
|
run()
|
||||||
|
@@ -25,7 +25,6 @@ except Exception: # pragma: no cover - fallback when orjson is missing
|
|||||||
USE_ORJSON = False
|
USE_ORJSON = False
|
||||||
import logging
|
import logging
|
||||||
import hashlib
|
import hashlib
|
||||||
import sys
|
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Tuple, Dict, Any, List
|
from typing import Optional, Tuple, Dict, Any, List
|
||||||
@@ -48,6 +47,7 @@ from utils.key_validation import (
|
|||||||
|
|
||||||
from .vault import Vault
|
from .vault import Vault
|
||||||
from .backup import BackupManager
|
from .backup import BackupManager
|
||||||
|
from .errors import SeedPassError
|
||||||
|
|
||||||
|
|
||||||
# Instantiate the logger
|
# Instantiate the logger
|
||||||
@@ -148,7 +148,7 @@ class EntryManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error determining next index: {e}", exc_info=True)
|
logger.error(f"Error determining next index: {e}", exc_info=True)
|
||||||
print(colored(f"Error determining next index: {e}", "red"))
|
print(colored(f"Error determining next index: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Error determining next index: {e}") from e
|
||||||
|
|
||||||
def add_entry(
|
def add_entry(
|
||||||
self,
|
self,
|
||||||
@@ -238,7 +238,7 @@ class EntryManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to add entry: {e}", exc_info=True)
|
logger.error(f"Failed to add entry: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to add entry: {e}", "red"))
|
print(colored(f"Error: Failed to add entry: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to add entry: {e}") from e
|
||||||
|
|
||||||
def get_next_totp_index(self) -> int:
|
def get_next_totp_index(self) -> int:
|
||||||
"""Return the next available derivation index for TOTP secrets."""
|
"""Return the next available derivation index for TOTP secrets."""
|
||||||
|
21
src/seedpass/core/errors.py
Normal file
21
src/seedpass/core/errors.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
"""Custom exceptions for SeedPass core modules.
|
||||||
|
|
||||||
|
This module defines :class:`SeedPassError`, a base exception used across the
|
||||||
|
core modules. Library code should raise this error instead of terminating the
|
||||||
|
process with ``sys.exit`` so that callers can handle failures gracefully.
|
||||||
|
|
||||||
|
When raised inside the CLI, :class:`SeedPassError` behaves like a Click
|
||||||
|
exception, displaying a friendly message and exiting with code ``1``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from click import ClickException
|
||||||
|
|
||||||
|
|
||||||
|
class SeedPassError(ClickException):
|
||||||
|
"""Base exception for SeedPass-related errors."""
|
||||||
|
|
||||||
|
def __init__(self, message: str):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["SeedPassError"]
|
@@ -38,6 +38,7 @@ from .backup import BackupManager
|
|||||||
from .vault import Vault
|
from .vault import Vault
|
||||||
from .portable_backup import export_backup, import_backup, PortableMode
|
from .portable_backup import export_backup, import_backup, PortableMode
|
||||||
from cryptography.fernet import InvalidToken
|
from cryptography.fernet import InvalidToken
|
||||||
|
from .errors import SeedPassError
|
||||||
from .totp import TotpManager
|
from .totp import TotpManager
|
||||||
from .entry_types import EntryType
|
from .entry_types import EntryType
|
||||||
from .pubsub import bus
|
from .pubsub import bus
|
||||||
@@ -559,7 +560,7 @@ class PasswordManager:
|
|||||||
print(
|
print(
|
||||||
colored(f"Error: Failed to initialize FingerprintManager: {e}", "red")
|
colored(f"Error: Failed to initialize FingerprintManager: {e}", "red")
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to initialize FingerprintManager: {e}") from e
|
||||||
|
|
||||||
def setup_parent_seed(self) -> None:
|
def setup_parent_seed(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -601,7 +602,7 @@ class PasswordManager:
|
|||||||
choice = input("Select a seed profile by number: ").strip()
|
choice = input("Select a seed profile by number: ").strip()
|
||||||
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints) + 1):
|
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints) + 1):
|
||||||
print(colored("Invalid selection. Exiting.", "red"))
|
print(colored("Invalid selection. Exiting.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Invalid selection.")
|
||||||
|
|
||||||
choice = int(choice)
|
choice = int(choice)
|
||||||
if choice == len(fingerprints) + 1:
|
if choice == len(fingerprints) + 1:
|
||||||
@@ -615,7 +616,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error during seed profile selection: {e}", exc_info=True)
|
logger.error(f"Error during seed profile selection: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to select seed profile: {e}", "red"))
|
print(colored(f"Error: Failed to select seed profile: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to select seed profile: {e}") from e
|
||||||
|
|
||||||
def add_new_fingerprint(self):
|
def add_new_fingerprint(self):
|
||||||
"""
|
"""
|
||||||
@@ -638,7 +639,7 @@ class PasswordManager:
|
|||||||
fingerprint = self.generate_new_seed()
|
fingerprint = self.generate_new_seed()
|
||||||
else:
|
else:
|
||||||
print(colored("Invalid choice. Exiting.", "red"))
|
print(colored("Invalid choice. Exiting.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Invalid choice.")
|
||||||
|
|
||||||
if not fingerprint:
|
if not fingerprint:
|
||||||
return None
|
return None
|
||||||
@@ -661,7 +662,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error adding new seed profile: {e}", exc_info=True)
|
logger.error(f"Error adding new seed profile: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to add new seed profile: {e}", "red"))
|
print(colored(f"Error: Failed to add new seed profile: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to add new seed profile: {e}") from e
|
||||||
|
|
||||||
def select_fingerprint(
|
def select_fingerprint(
|
||||||
self, fingerprint: str, *, password: Optional[str] = None
|
self, fingerprint: str, *, password: Optional[str] = None
|
||||||
@@ -678,7 +679,9 @@ class PasswordManager:
|
|||||||
"red",
|
"red",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise SeedPassError(
|
||||||
|
f"Seed profile directory for {fingerprint} not found."
|
||||||
|
)
|
||||||
# Setup the encryption manager and load parent seed
|
# Setup the encryption manager and load parent seed
|
||||||
self.setup_encryption_manager(self.fingerprint_dir, password)
|
self.setup_encryption_manager(self.fingerprint_dir, password)
|
||||||
# Initialize BIP85 and other managers
|
# Initialize BIP85 and other managers
|
||||||
@@ -692,7 +695,7 @@ class PasswordManager:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
print(colored(f"Error: Seed profile {fingerprint} not found.", "red"))
|
print(colored(f"Error: Seed profile {fingerprint} not found.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Seed profile {fingerprint} not found.")
|
||||||
|
|
||||||
def setup_encryption_manager(
|
def setup_encryption_manager(
|
||||||
self,
|
self,
|
||||||
@@ -784,10 +787,10 @@ class PasswordManager:
|
|||||||
logger.error(f"Failed to set up EncryptionManager: {e}", exc_info=True)
|
logger.error(f"Failed to set up EncryptionManager: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to set up encryption: {e}", "red"))
|
print(colored(f"Error: Failed to set up encryption: {e}", "red"))
|
||||||
if exit_on_fail:
|
if exit_on_fail:
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to set up encryption: {e}") from e
|
||||||
return False
|
return False
|
||||||
if exit_on_fail:
|
if exit_on_fail:
|
||||||
sys.exit(1)
|
raise SeedPassError("Failed to set up encryption")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def load_parent_seed(
|
def load_parent_seed(
|
||||||
@@ -829,7 +832,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to load parent seed: {e}", exc_info=True)
|
logger.error(f"Failed to load parent seed: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to load parent seed: {e}", "red"))
|
print(colored(f"Error: Failed to load parent seed: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to load parent seed: {e}") from e
|
||||||
|
|
||||||
@requires_unlocked
|
@requires_unlocked
|
||||||
def handle_switch_fingerprint(self, *, password: Optional[str] = None) -> bool:
|
def handle_switch_fingerprint(self, *, password: Optional[str] = None) -> bool:
|
||||||
@@ -913,7 +916,9 @@ class PasswordManager:
|
|||||||
"red",
|
"red",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise SeedPassError(
|
||||||
|
"No seed profiles available. Please add a seed profile first."
|
||||||
|
)
|
||||||
|
|
||||||
print(colored("Available Seed Profiles:", "cyan"))
|
print(colored("Available Seed Profiles:", "cyan"))
|
||||||
for idx, fp in enumerate(fingerprints, start=1):
|
for idx, fp in enumerate(fingerprints, start=1):
|
||||||
@@ -927,7 +932,7 @@ class PasswordManager:
|
|||||||
choice = input("Select a seed profile by number: ").strip()
|
choice = input("Select a seed profile by number: ").strip()
|
||||||
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints)):
|
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints)):
|
||||||
print(colored("Invalid selection. Exiting.", "red"))
|
print(colored("Invalid selection. Exiting.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Invalid selection.")
|
||||||
|
|
||||||
selected_fingerprint = fingerprints[int(choice) - 1]
|
selected_fingerprint = fingerprints[int(choice) - 1]
|
||||||
self.current_fingerprint = selected_fingerprint
|
self.current_fingerprint = selected_fingerprint
|
||||||
@@ -936,7 +941,7 @@ class PasswordManager:
|
|||||||
)
|
)
|
||||||
if not fingerprint_dir:
|
if not fingerprint_dir:
|
||||||
print(colored("Error: Seed profile directory not found.", "red"))
|
print(colored("Error: Seed profile directory not found.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Seed profile directory not found.")
|
||||||
|
|
||||||
# Derive encryption key from password using selected fingerprint
|
# Derive encryption key from password using selected fingerprint
|
||||||
iterations = (
|
iterations = (
|
||||||
@@ -966,14 +971,14 @@ class PasswordManager:
|
|||||||
if not self.validate_bip85_seed(self.parent_seed):
|
if not self.validate_bip85_seed(self.parent_seed):
|
||||||
logging.error("Decrypted seed is invalid. Exiting.")
|
logging.error("Decrypted seed is invalid. Exiting.")
|
||||||
print(colored("Error: Decrypted seed is invalid.", "red"))
|
print(colored("Error: Decrypted seed is invalid.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Decrypted seed is invalid.")
|
||||||
|
|
||||||
self.initialize_bip85()
|
self.initialize_bip85()
|
||||||
logging.debug("Parent seed decrypted and validated successfully.")
|
logging.debug("Parent seed decrypted and validated successfully.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to decrypt parent seed: {e}", exc_info=True)
|
logging.error(f"Failed to decrypt parent seed: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to decrypt parent seed: {e}", "red"))
|
print(colored(f"Error: Failed to decrypt parent seed: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to decrypt parent seed: {e}") from e
|
||||||
|
|
||||||
def handle_new_seed_setup(self) -> None:
|
def handle_new_seed_setup(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -1013,7 +1018,7 @@ class PasswordManager:
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
print(colored("Invalid choice. Exiting.", "red"))
|
print(colored("Invalid choice. Exiting.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Invalid choice.")
|
||||||
|
|
||||||
# Some seed loading paths may not initialize managers; ensure they exist
|
# Some seed loading paths may not initialize managers; ensure they exist
|
||||||
if getattr(self, "config_manager", None) is None:
|
if getattr(self, "config_manager", None) is None:
|
||||||
@@ -1050,13 +1055,13 @@ class PasswordManager:
|
|||||||
if not self.validate_bip85_seed(parent_seed):
|
if not self.validate_bip85_seed(parent_seed):
|
||||||
logging.error("Invalid BIP-85 seed phrase. Exiting.")
|
logging.error("Invalid BIP-85 seed phrase. Exiting.")
|
||||||
print(colored("Error: Invalid BIP-85 seed phrase.", "red"))
|
print(colored("Error: Invalid BIP-85 seed phrase.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Invalid BIP-85 seed phrase.")
|
||||||
fingerprint = self._finalize_existing_seed(parent_seed, password=password)
|
fingerprint = self._finalize_existing_seed(parent_seed, password=password)
|
||||||
return fingerprint
|
return fingerprint
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logging.info("Operation cancelled by user.")
|
logging.info("Operation cancelled by user.")
|
||||||
self.notify("Operation cancelled by user.", level="WARNING")
|
self.notify("Operation cancelled by user.", level="WARNING")
|
||||||
sys.exit(0)
|
raise SeedPassError("Operation cancelled by user.")
|
||||||
|
|
||||||
def setup_existing_seed_word_by_word(
|
def setup_existing_seed_word_by_word(
|
||||||
self, *, seed: Optional[str] = None, password: Optional[str] = None
|
self, *, seed: Optional[str] = None, password: Optional[str] = None
|
||||||
@@ -1089,7 +1094,9 @@ class PasswordManager:
|
|||||||
"red",
|
"red",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise SeedPassError(
|
||||||
|
"Failed to generate seed profile for the provided seed."
|
||||||
|
)
|
||||||
|
|
||||||
fingerprint_dir = self.fingerprint_manager.get_fingerprint_directory(
|
fingerprint_dir = self.fingerprint_manager.get_fingerprint_directory(
|
||||||
fingerprint
|
fingerprint
|
||||||
@@ -1098,7 +1105,7 @@ class PasswordManager:
|
|||||||
print(
|
print(
|
||||||
colored("Error: Failed to retrieve seed profile directory.", "red")
|
colored("Error: Failed to retrieve seed profile directory.", "red")
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise SeedPassError("Failed to retrieve seed profile directory.")
|
||||||
|
|
||||||
self.current_fingerprint = fingerprint
|
self.current_fingerprint = fingerprint
|
||||||
self.fingerprint_manager.current_fingerprint = fingerprint
|
self.fingerprint_manager.current_fingerprint = fingerprint
|
||||||
@@ -1152,7 +1159,7 @@ class PasswordManager:
|
|||||||
else:
|
else:
|
||||||
logging.error("Invalid BIP-85 seed phrase. Exiting.")
|
logging.error("Invalid BIP-85 seed phrase. Exiting.")
|
||||||
print(colored("Error: Invalid BIP-85 seed phrase.", "red"))
|
print(colored("Error: Invalid BIP-85 seed phrase.", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError("Invalid BIP-85 seed phrase.")
|
||||||
|
|
||||||
@requires_unlocked
|
@requires_unlocked
|
||||||
def generate_new_seed(self) -> Optional[str]:
|
def generate_new_seed(self) -> Optional[str]:
|
||||||
@@ -1197,7 +1204,7 @@ class PasswordManager:
|
|||||||
"red",
|
"red",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise SeedPassError("Failed to generate seed profile for the new seed.")
|
||||||
|
|
||||||
fingerprint_dir = self.fingerprint_manager.get_fingerprint_directory(
|
fingerprint_dir = self.fingerprint_manager.get_fingerprint_directory(
|
||||||
fingerprint
|
fingerprint
|
||||||
@@ -1206,7 +1213,7 @@ class PasswordManager:
|
|||||||
print(
|
print(
|
||||||
colored("Error: Failed to retrieve seed profile directory.", "red")
|
colored("Error: Failed to retrieve seed profile directory.", "red")
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise SeedPassError("Failed to retrieve seed profile directory.")
|
||||||
|
|
||||||
# Persist the assigned account index for the new profile
|
# Persist the assigned account index for the new profile
|
||||||
try:
|
try:
|
||||||
@@ -1234,7 +1241,7 @@ class PasswordManager:
|
|||||||
return fingerprint # Return the generated fingerprint
|
return fingerprint # Return the generated fingerprint
|
||||||
else:
|
else:
|
||||||
self.notify("Seed generation cancelled. Exiting.", level="WARNING")
|
self.notify("Seed generation cancelled. Exiting.", level="WARNING")
|
||||||
sys.exit(0)
|
raise SeedPassError("Seed generation cancelled.")
|
||||||
|
|
||||||
def validate_bip85_seed(self, seed: str) -> bool:
|
def validate_bip85_seed(self, seed: str) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -1271,11 +1278,11 @@ class PasswordManager:
|
|||||||
except Bip85Error as e:
|
except Bip85Error as e:
|
||||||
logging.error(f"Failed to generate BIP-85 seed: {e}", exc_info=True)
|
logging.error(f"Failed to generate BIP-85 seed: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to generate BIP-85 seed: {e}", "red"))
|
print(colored(f"Error: Failed to generate BIP-85 seed: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to generate BIP-85 seed: {e}") from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to generate BIP-85 seed: {e}", exc_info=True)
|
logging.error(f"Failed to generate BIP-85 seed: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to generate BIP-85 seed: {e}", "red"))
|
print(colored(f"Error: Failed to generate BIP-85 seed: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to generate BIP-85 seed: {e}") from e
|
||||||
|
|
||||||
@requires_unlocked
|
@requires_unlocked
|
||||||
def save_and_encrypt_seed(
|
def save_and_encrypt_seed(
|
||||||
@@ -1348,7 +1355,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to encrypt and save parent seed: {e}", exc_info=True)
|
logging.error(f"Failed to encrypt and save parent seed: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to encrypt and save parent seed: {e}", "red"))
|
print(colored(f"Error: Failed to encrypt and save parent seed: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to encrypt and save parent seed: {e}") from e
|
||||||
|
|
||||||
def initialize_bip85(self):
|
def initialize_bip85(self):
|
||||||
"""
|
"""
|
||||||
@@ -1377,7 +1384,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to initialize BIP-85: {e}", exc_info=True)
|
logging.error(f"Failed to initialize BIP-85: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to initialize BIP-85: {e}", "red"))
|
print(colored(f"Error: Failed to initialize BIP-85: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to initialize BIP-85: {e}") from e
|
||||||
|
|
||||||
def initialize_managers(self) -> None:
|
def initialize_managers(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -1417,7 +1424,7 @@ class PasswordManager:
|
|||||||
)
|
)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
print(colored(str(exc), "red"))
|
print(colored(str(exc), "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(str(exc))
|
||||||
|
|
||||||
self.entry_manager = EntryManager(
|
self.entry_manager = EntryManager(
|
||||||
vault=self.vault,
|
vault=self.vault,
|
||||||
@@ -1519,7 +1526,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to initialize managers: {e}", exc_info=True)
|
logger.error(f"Failed to initialize managers: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to initialize managers: {e}", "red"))
|
print(colored(f"Error: Failed to initialize managers: {e}", "red"))
|
||||||
sys.exit(1)
|
raise SeedPassError(f"Failed to initialize managers: {e}") from e
|
||||||
|
|
||||||
async def sync_index_from_nostr_async(self) -> None:
|
async def sync_index_from_nostr_async(self) -> None:
|
||||||
"""Always fetch the latest vault data from Nostr and update the local index."""
|
"""Always fetch the latest vault data from Nostr and update the local index."""
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
class VaultLockedError(Exception):
|
"""Compatibility layer for historic exception types."""
|
||||||
|
|
||||||
|
from .core.errors import SeedPassError
|
||||||
|
|
||||||
|
|
||||||
|
class VaultLockedError(SeedPassError):
|
||||||
"""Raised when an operation requires an unlocked vault."""
|
"""Raised when an operation requires an unlocked vault."""
|
||||||
|
|
||||||
pass
|
def __init__(self, message: str = "Vault is locked") -> None:
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["VaultLockedError", "SeedPassError"]
|
||||||
|
@@ -14,6 +14,7 @@ import gzip
|
|||||||
|
|
||||||
from seedpass.core.manager import PasswordManager, EncryptionMode
|
from seedpass.core.manager import PasswordManager, EncryptionMode
|
||||||
from seedpass.core.vault import Vault
|
from seedpass.core.vault import Vault
|
||||||
|
from seedpass.core.errors import SeedPassError
|
||||||
|
|
||||||
|
|
||||||
def test_legacy_index_migrates(monkeypatch, tmp_path: Path):
|
def test_legacy_index_migrates(monkeypatch, tmp_path: Path):
|
||||||
@@ -386,7 +387,7 @@ def test_declined_migration_no_sync_prompt(monkeypatch, tmp_path: Path):
|
|||||||
|
|
||||||
monkeypatch.setattr("seedpass.core.manager.confirm_action", fake_confirm)
|
monkeypatch.setattr("seedpass.core.manager.confirm_action", fake_confirm)
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SeedPassError):
|
||||||
pm.initialize_managers()
|
pm.initialize_managers()
|
||||||
|
|
||||||
assert calls["confirm"] == 0
|
assert calls["confirm"] == 0
|
||||||
@@ -425,7 +426,7 @@ def test_failed_migration_no_sync_prompt(monkeypatch, tmp_path: Path):
|
|||||||
|
|
||||||
monkeypatch.setattr("seedpass.core.manager.confirm_action", fake_confirm)
|
monkeypatch.setattr("seedpass.core.manager.confirm_action", fake_confirm)
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SeedPassError):
|
||||||
pm.initialize_managers()
|
pm.initialize_managers()
|
||||||
|
|
||||||
assert calls["confirm"] == 0
|
assert calls["confirm"] == 0
|
||||||
|
@@ -4,6 +4,7 @@ from types import SimpleNamespace
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import seedpass.core.manager as manager_module
|
import seedpass.core.manager as manager_module
|
||||||
|
from seedpass.core.errors import SeedPassError
|
||||||
from helpers import TEST_SEED
|
from helpers import TEST_SEED
|
||||||
from utils import seed_prompt
|
from utils import seed_prompt
|
||||||
|
|
||||||
@@ -86,7 +87,7 @@ def test_add_new_fingerprint_words_flow_invalid_phrase(monkeypatch):
|
|||||||
monkeypatch.setattr(seed_prompt, "clear_screen", lambda *_a, **_k: None)
|
monkeypatch.setattr(seed_prompt, "clear_screen", lambda *_a, **_k: None)
|
||||||
monkeypatch.setattr(builtins, "input", lambda *_: next(inputs))
|
monkeypatch.setattr(builtins, "input", lambda *_: next(inputs))
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SeedPassError):
|
||||||
pm.add_new_fingerprint()
|
pm.add_new_fingerprint()
|
||||||
|
|
||||||
assert pm.fingerprint_manager.current_fingerprint is None
|
assert pm.fingerprint_manager.current_fingerprint is None
|
||||||
|
Reference in New Issue
Block a user