From 4459dfea787c292411278d393a091ed1694fd1f4 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:45:55 -0500 Subject: [PATCH] Fix display service input hooks and clean pytest warnings --- memory-update.md | 19 +++++++--- pyproject.toml | 12 ++++++ src/seedpass/core/display_service.py | 56 +++++++++++++++++----------- src/tests/test_imghdr_stub.py | 7 ++++ 4 files changed, 66 insertions(+), 28 deletions(-) diff --git a/memory-update.md b/memory-update.md index 3d0ced3e..486daffb 100644 --- a/memory-update.md +++ b/memory-update.md @@ -1,11 +1,18 @@ -# Memory Update — scheduler-update-agent — 2026-02-26 +# Memory Update — codex — 2026-02-26 ## Key findings -- Roster json is perfectly synced with prompt directories. -- No discrepancies found in daily or weekly rosters. +- Full test suite now passes: `766 passed, 11 skipped`. +- `DisplayService` had diverged from `seedpass.core.manager` interactive helpers, causing test monkeypatches to miss and resulting in flaky behavior. + +## Bugs fixed +- Fixed TOTP retrieve/edit regression where `test_edit_totp_period_from_retrieve` could fail in-suite because TOTP screen consumed input via unpatched `display_service.timed_input`. +- Fixed suite hang at `test_show_seed_entry_details` caused by `display_service.confirm_action` bypassing patched `manager.confirm_action`, leading to infinite prompt loop on empty input. ## Patterns / reusable knowledge -- Always verify file existence before assuming roster drift. +- In this codebase, interactive helpers should be called through `seedpass.core.manager` symbols (`confirm_action`, `timed_input`, `copy_to_clipboard`) to keep behavior and tests consistent. -## Warnings / gotchas -- Ensure correct path prefix when running in host-repo mode. +## Follow-up (warnings cleanup) +- Added pytest `markers` registration for `network` under `[tool.pytest.ini_options]` to remove `PytestUnknownMarkWarning`. +- Added narrow pytest warning filters for known third-party GUI deprecation noise (`Pack.padding*`). +- For `imghdr` deprecation triggered during `test_imghdr_stub` import, added a local `warnings.filterwarnings(...)` at top of that test module to suppress import-time warning deterministically. +- Verified final suite status: `766 passed, 11 skipped`, no warnings. diff --git a/pyproject.toml b/pyproject.toml index 31b19235..5b618bab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,6 +72,18 @@ python_version = "3.11" strict = true mypy_path = "src" +[tool.pytest.ini_options] +markers = [ + "network: tests that require network access", +] +filterwarnings = [ + "ignore:'imghdr' is deprecated and slated for removal in Python 3.13:DeprecationWarning:pgpy.constants", + "ignore:'imghdr' is deprecated and will be removed in Python 3.13:DeprecationWarning:src.tests.test_imghdr_stub", + "ignore:'imghdr' is deprecated and will be removed in Python 3.13:DeprecationWarning", + "ignore:Pack.padding_top is deprecated. Use Pack.margin_top instead.:DeprecationWarning", + "ignore:Pack.padding is deprecated. Use Pack.margin instead.:DeprecationWarning", +] + [tool.briefcase] project_name = "SeedPass" bundle = "io.seedpass" diff --git a/src/seedpass/core/display_service.py b/src/seedpass/core/display_service.py index d49a733a..3c527840 100644 --- a/src/seedpass/core/display_service.py +++ b/src/seedpass/core/display_service.py @@ -9,10 +9,7 @@ from termcolor import colored from seedpass.core.entry_types import EntryType from seedpass.core.totp import TotpManager -from utils.clipboard import copy_to_clipboard from utils.color_scheme import color_text -from utils.input_utils import timed_input -from utils.password_prompt import confirm_action if TYPE_CHECKING: from seedpass.core.manager import PasswordManager @@ -26,6 +23,25 @@ class DisplayService: def __init__(self, manager: PasswordManager) -> None: self.manager = manager + @staticmethod + def _manager_module(): + # Route interactive helpers through seedpass.core.manager so tests + # can monkeypatch one module surface consistently. + from seedpass.core import manager as manager_module + + return manager_module + + def _confirm_action(self, prompt_message: str) -> bool: + return self._manager_module().confirm_action(prompt_message) + + def _copy_to_clipboard(self, value: str) -> bool: + return self._manager_module().copy_to_clipboard( + value, self.manager.clipboard_clear_delay + ) + + def _timed_input(self, prompt: str, timeout: float) -> str: + return self._manager_module().timed_input(prompt, timeout) + def display_sensitive_entry_info(self, entry: dict, index: int) -> None: """Display information for a sensitive entry. @@ -70,7 +86,7 @@ class DisplayService: while True: code = TotpManager.current_code_from_secret(secret) if self.manager.secret_mode_enabled: - if copy_to_clipboard(code, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(code): print( colored( f"[+] 2FA code for '{label}' copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -96,7 +112,7 @@ class DisplayService: sys.stdout.write(f"\r{bar} {remaining:2d}s") sys.stdout.flush() try: - user_input = timed_input("", 1) + user_input = self._timed_input("", 1) if ( user_input.strip() == "" or user_input.strip().lower() == "b" @@ -121,7 +137,7 @@ class DisplayService: def _display_ssh(self, entry: dict, index: int) -> None: notes = entry.get("notes", "") label = entry.get("label", "") - if not confirm_action( + if not self._confirm_action( "WARNING: Displaying SSH keys reveals sensitive information. Continue? (Y/N): " ): self.manager.notify("SSH key display cancelled.", level="WARNING") @@ -141,7 +157,7 @@ class DisplayService: print(colored("Public Key:", "cyan")) print(color_text(pub_pem, "default")) if self.manager.secret_mode_enabled: - if copy_to_clipboard(priv_pem, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(priv_pem): print( colored( f"[+] SSH private key copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -158,7 +174,7 @@ class DisplayService: def _display_seed(self, entry: dict, index: int) -> None: notes = entry.get("notes", "") label = entry.get("label", "") - if not confirm_action( + if not self._confirm_action( "WARNING: Displaying the seed phrase reveals sensitive information. Continue? (Y/N): " ): self.manager.notify("Seed phrase display cancelled.", level="WARNING") @@ -177,7 +193,7 @@ class DisplayService: if tags: print(colored(f"Tags: {', '.join(tags)}", "cyan")) if self.manager.secret_mode_enabled: - if copy_to_clipboard(phrase, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(phrase): print( colored( f"[+] Seed phrase copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -186,7 +202,7 @@ class DisplayService: ) else: print(color_text(phrase, "deterministic")) - if confirm_action("Show derived entropy as hex? (Y/N): "): + if self._confirm_action("Show derived entropy as hex? (Y/N): "): from local_bip85.bip85 import BIP85 from bip_utils import Bip39SeedGenerator @@ -208,7 +224,7 @@ class DisplayService: def _display_pgp(self, entry: dict, index: int) -> None: notes = entry.get("notes", "") label = entry.get("user_id", "") - if not confirm_action( + if not self._confirm_action( "WARNING: Displaying the PGP key reveals sensitive information. Continue? (Y/N): " ): self.manager.notify("PGP key display cancelled.", level="WARNING") @@ -227,7 +243,7 @@ class DisplayService: print(colored(f"Tags: {', '.join(tags)}", "cyan")) print(colored(f"Fingerprint: {fingerprint}", "cyan")) if self.manager.secret_mode_enabled: - if copy_to_clipboard(priv_key, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(priv_key): print( colored( f"[+] PGP key copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -251,7 +267,7 @@ class DisplayService: print(colored(f"Label: {label}", "cyan")) print(colored(f"npub: {npub}", "cyan")) if self.manager.secret_mode_enabled: - if copy_to_clipboard(nsec, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(nsec): print( colored( f"[+] nsec copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -286,7 +302,7 @@ class DisplayService: ) ) if self.manager.secret_mode_enabled: - if copy_to_clipboard(value, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(value): print( colored( f"[+] Value copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -313,9 +329,7 @@ class DisplayService: if show == "y": for f_label, f_value in hidden_fields: if self.manager.secret_mode_enabled: - if copy_to_clipboard( - f_value, self.manager.clipboard_clear_delay - ): + if self._copy_to_clipboard(f_value): print( colored( f"[+] {f_label} copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -355,7 +369,7 @@ class DisplayService: index, self.manager.parent_seed ) if self.manager.secret_mode_enabled: - if copy_to_clipboard(seed, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(seed): print( colored( f"[+] Seed phrase copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -398,7 +412,7 @@ class DisplayService: if password: if self.manager.secret_mode_enabled: - if copy_to_clipboard(password, self.manager.clipboard_clear_delay): + if self._copy_to_clipboard(password): print( colored( f"[+] Password for '{website_name}' copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", @@ -443,9 +457,7 @@ class DisplayService: if show == "y": for label, value in hidden_fields: if self.manager.secret_mode_enabled: - if copy_to_clipboard( - value, self.manager.clipboard_clear_delay - ): + if self._copy_to_clipboard(value): print( colored( f"[+] {label} copied to clipboard. Will clear in {self.manager.clipboard_clear_delay} seconds.", diff --git a/src/tests/test_imghdr_stub.py b/src/tests/test_imghdr_stub.py index 43a4b081..20e2cd23 100644 --- a/src/tests/test_imghdr_stub.py +++ b/src/tests/test_imghdr_stub.py @@ -1,5 +1,12 @@ import pytest import io +import warnings + +warnings.filterwarnings( + "ignore", + message="'imghdr' is deprecated and will be removed in Python 3.13", + category=DeprecationWarning, +) from utils.imghdr_stub import what