refactor: replace `.display()` with `View.run_screen`
What changed, and why it matters
This commit is a straightforward internal code cleanup: it replaces direct calls to a screen's `.display()` method with a new helper called `self.run_screen()`. The user-visible behavior appears unchanged; it is a refactoring, not a security fix.
No security action required; treat as routine refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff refactors view classes in psbt_views.py, seed_views.py, and tools_views.py to call self.run_screen(ScreenClass, ...) instead of instantiating a screen and invoking .display(). A related test file removes is_redirect=True annotations for the zoomed-in SeedQR flow steps, suggesting the new helper normalizes how screens are executed in tests. No security-sensitive logic, input handling, cryptography, or access control is modified.
Changed components
src/seedsigner/views/psbt_views.pysrc/seedsigner/views/seed_views.pysrc/seedsigner/views/tools_views.pytests/test_flows_seed.pyInspect captured patch +60 / −42
diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py
index b39c936..cac4c68 100644
--- a/src/seedsigner/views/psbt_views.py
+++ b/src/seedsigner/views/psbt_views.py
@@ -166,11 +166,12 @@ class PSBTOverviewView(View):
class PSBTUnsupportedScriptTypeWarningView(View):
def run(self):
- selected_menu_num = WarningScreen(
+ selected_menu_num = self.run_screen(
+ WarningScreen,
status_headline=_("Unsupported Script Type!"),
text=_("PSBT has unsupported input script type, please verify your change addresses."),
button_data=[ButtonOption("Continue")],
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -186,12 +187,13 @@ class PSBTUnsupportedScriptTypeWarningView(View):
class PSBTNoChangeWarningView(View):
def run(self):
- selected_menu_num = WarningScreen(
+ selected_menu_num = self.run_screen(
+ WarningScreen,
# TRANSLATOR_NOTE: User will receive no change back; the inputs to this transaction are fully spent
status_headline=_("Full Spend!"),
text=_("This PSBT spends its entire input value. No change is coming back to your wallet."),
button_data=[ButtonOption("Continue")],
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -466,13 +468,14 @@ class PSBTAddressVerificationFailedView(View):
# TRANSLATOR_NOTE: Variable is either "change" or "self-transfer".
text = _("PSBT's {} address could not be generated from your seed.").format(_("change") if self.is_change else _("self-transfer"))
- DireWarningScreen(
+ self.run_screen(
+ DireWarningScreen,
title=_("Suspicious PSBT"),
status_headline=_("Address Verification Failed"),
text=text,
button_data=[ButtonOption("Discard PSBT")],
show_back_button=False,
- ).display()
+ )
# We're done with this PSBT. Route back to MainMenuView which always
# clears all ephemeral data (except in-memory seeds).
diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py
index 6b73052..6fb94b2 100644
--- a/src/seedsigner/views/seed_views.py
+++ b/src/seedsigner/views/seed_views.py
@@ -1086,13 +1086,14 @@ class SeedWordsView(View):
else:
button_data.append(self.DONE)
- selected_menu_num = seed_screens.SeedWordsScreen(
+ selected_menu_num = self.run_screen(
+ seed_screens.SeedWordsScreen,
title=f"{title}: {self.page_index+1}/{num_pages}",
words=words,
page_index=self.page_index,
num_pages=num_pages,
button_data=button_data,
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -1143,10 +1144,11 @@ class SeedBIP85ApplicationModeView(View):
def run(self):
button_data = [self.WORDS_12, self.WORDS_24]
- selected_menu_num = ButtonListScreen(
+ selected_menu_num = self.run_screen(
+ ButtonListScreen,
title=_("BIP-85 Num Words"),
button_data=button_data
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -1173,7 +1175,7 @@ class SeedBIP85SelectChildIndexView(View):
def run(self):
# TODO: Change this later to use the generic Screen input keyboard
- ret = seed_screens.SeedBIP85SelectChildIndexScreen().display()
+ ret = self.run_screen(seed_screens.SeedBIP85SelectChildIndexScreen)
if ret == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -1206,14 +1208,15 @@ class SeedBIP85InvalidChildIndexView(View):
def run(self):
- DireWarningScreen(
+ self.run_screen(
+ DireWarningScreen,
title=_("BIP-85 Index Error"),
show_back_button=False,
status_icon_name=SeedSignerIconConstants.ERROR,
status_headline=_("Invalid Child Index"),
text=_("BIP-85 Child Index must be between 0 and 2^31-1."),
button_data=[ButtonOption("Try Again")]
- ).display()
+ )
return Destination(
SeedBIP85SelectChildIndexView,
@@ -1241,9 +1244,10 @@ class SeedWordsBackupTestPromptView(View):
def run(self):
button_data = [self.VERIFY, self.SKIP]
- selected_menu_num = seed_screens.SeedWordsBackupTestPromptScreen(
+ selected_menu_num = self.run_screen(
+ seed_screens.SeedWordsBackupTestPromptScreen,
button_data=button_data,
- ).display()
+ )
if button_data[selected_menu_num] == self.VERIFY:
return Destination(
@@ -1307,13 +1311,14 @@ class SeedWordsBackupTestView(View):
# TRANSLATOR_NOTE: Inserts the word number (e.g. "Verify Word #1")
title = _("Verify Word #{}").format(self.cur_index + 1)
- selected_menu_num = ButtonListScreen(
+ selected_menu_num = self.run_screen(
+ ButtonListScreen,
title=title,
show_back_button=False,
button_data=button_data,
is_bottom_list=True,
is_button_text_centered=True,
- ).display()
+ )
if button_data[selected_menu_num] == real_word:
self.confirmed_list.append(self.cur_index)
@@ -1367,14 +1372,15 @@ class SeedWordsBackupTestMistakeView(View):
# TRANSLATOR_NOTE: User selected the wrong word during the mnemonic backup test (e.g. incorrectly said the 5th word was "zoo")
status_headline = _("Wrong Word!")
- selected_menu_num = DireWarningScreen(
+ selected_menu_num = self.run_screen(
+ DireWarningScreen,
title=_("Verification Error"),
show_back_button=False,
status_icon_name=SeedSignerIconConstants.ERROR,
status_headline=status_headline,
button_data=button_data,
text=text,
- ).display()
+ )
if button_data[selected_menu_num] == self.REVIEW:
return Destination(
@@ -1402,13 +1408,14 @@ class SeedWordsBackupTestSuccessView(View):
def run(self):
from seedsigner.gui.screens.screen import LargeIconStatusScreen
- LargeIconStatusScreen(
+ self.run_screen(
+ LargeIconStatusScreen,
title=_("Backup Verified"),
show_back_button=False,
status_headline=_("Success!"),
text=_("All mnemonic backup words were successfully verified!"),
button_data=[ButtonOption("OK")]
- ).display()
+ )
if self.seed_num is not None:
return Destination(SeedOptionsView, view_args=dict(seed_num=self.seed_num), clear_history=True)
@@ -1593,12 +1600,13 @@ class SeedTranscribeSeedQRZoomedInView(View):
else:
num_modules = 25
- seed_screens.SeedTranscribeSeedQRZoomedInScreen(
+ self.run_screen(
+ seed_screens.SeedTranscribeSeedQRZoomedInScreen,
qr_data=data,
num_modules=num_modules,
initial_zone_x=self.initial_zone_x,
initial_zone_y=self.initial_zone_y,
- ).display()
+ )
return Destination(SeedTranscribeSeedQRConfirmQRPromptView, view_args={"seed_num": self.seed_num})
diff --git a/src/seedsigner/views/tools_views.py b/src/seedsigner/views/tools_views.py
index 2b9d6c2..27ce1ca 100644
--- a/src/seedsigner/views/tools_views.py
+++ b/src/seedsigner/views/tools_views.py
@@ -64,7 +64,7 @@ class ToolsImageEntropyLivePreviewView(View):
def run(self):
from seedsigner.gui.screens.tools_screens import ToolsImageEntropyLivePreviewScreen
self.controller.image_entropy_preview_frames = None
- ret = ToolsImageEntropyLivePreviewScreen().display()
+ ret = self.run_screen(ToolsImageEntropyLivePreviewScreen)
if ret == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -104,9 +104,10 @@ class ToolsImageEntropyFinalImageView(View):
sampling_method=Image.Resampling.BICUBIC,
)
- ret = ToolsImageEntropyFinalImageScreen(
+ ret = self.run_screen(
+ ToolsImageEntropyFinalImageScreen,
final_image=display_version
- ).display()
+ )
if ret == RET_CODE__BACK_BUTTON:
# Go back to live preview and reshoot
@@ -124,10 +125,11 @@ class ToolsImageEntropyMnemonicLengthView(View):
def run(self):
button_data = [self.TWELVE_WORDS, self.TWENTYFOUR_WORDS]
- selected_menu_num = ButtonListScreen(
+ selected_menu_num = self.run_screen(
+ ButtonListScreen,
title=_("Mnemonic Length?"),
button_data=button_data,
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -212,12 +214,13 @@ class ToolsDiceEntropyMnemonicLengthView(View):
TWENTY_FOUR = ButtonOption(twenty_four, return_data=mnemonic_generation.DICE__NUM_ROLLS__24WORD)
button_data = [TWELVE, TWENTY_FOUR]
- selected_menu_num = ButtonListScreen(
+ selected_menu_num = self.run_screen(
+ ButtonListScreen,
title=_("Mnemonic Length"),
is_bottom_list=True,
is_button_text_centered=True,
button_data=button_data,
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -238,9 +241,10 @@ class ToolsDiceEntropyEntryView(View):
def run(self):
from seedsigner.gui.screens.tools_screens import ToolsDiceEntropyEntryScreen
- ret = ToolsDiceEntropyEntryScreen(
+ ret = self.run_screen(
+ ToolsDiceEntropyEntryScreen,
return_after_n_chars=self.total_rolls,
- ).display()
+ )
if ret == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -303,11 +307,12 @@ class ToolsCalcFinalWordFinalizePromptView(View):
num_entropy_bits = 3
button_data = [self.COIN_FLIPS, self.SELECT_WORD, self.ZEROS]
- selected_menu_num = ToolsCalcFinalWordFinalizePromptScreen(
+ selected_menu_num = self.run_screen(
+ ToolsCalcFinalWordFinalizePromptScreen,
mnemonic_length=mnemonic_length,
num_entropy_bits=num_entropy_bits,
button_data=button_data,
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -339,9 +344,10 @@ class ToolsCalcFinalWordCoinFlipsView(View):
else:
total_flips = 3
- ret_val = ToolsCoinFlipEntryScreen(
+ ret_val = self.run_screen(
+ ToolsCoinFlipEntryScreen,
return_after_n_chars=total_flips,
- ).display()
+ )
if ret_val == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
@@ -442,12 +448,13 @@ class ToolsCalcFinalWordDoneView(View):
button_data = [self.LOAD, self.DISCARD]
- selected_menu_num = ToolsCalcFinalWordDoneScreen(
+ selected_menu_num = self.run_screen(
+ ToolsCalcFinalWordDoneScreen,
final_word=final_word,
mnemonic_word_length=mnemonic_word_length,
fingerprint=self.controller.storage.get_pending_mnemonic_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK)),
button_data=button_data,
- ).display()
+ )
if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py
index b905636..2923156 100644
--- a/tests/test_flows_seed.py
+++ b/tests/test_flows_seed.py
@@ -449,25 +449,25 @@ class TestSeedFlows(FlowTest):
FlowStep(seed_views.SeedTranscribeSeedQRFormatView, button_data_selection=seed_views.SeedTranscribeSeedQRFormatView.STANDARD_12),
FlowStep(seed_views.SeedTranscribeSeedQRWarningView),
FlowStep(seed_views.SeedTranscribeSeedQRWholeQRView),
- FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView, is_redirect=True), # Live interactive screens are a bit weird; not sure why `is_redirect` is necessary here
+ FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView),
FlowStep(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, button_data_selection=seed_views.SeedTranscribeSeedQRConfirmQRPromptView.SCAN),
# Intentionally "scan" the wrong SeedQR
FlowStep(seed_views.SeedTranscribeSeedQRConfirmScanView, before_run=load_wrong_seed_into_decoder),
FlowStep(seed_views.SeedTranscribeSeedQRConfirmWrongSeedView),
- FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView, is_redirect=True), # Live interactive screens are still weird
+ FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView),
# Intentionally scan QR data that makes no sense for this flow
FlowStep(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, button_data_selection=seed_views.SeedTranscribeSeedQRConfirmQRPromptView.SCAN),
FlowStep(seed_views.SeedTranscribeSeedQRConfirmScanView, before_run=load_completely_wrong_qr_type_into_decoder),
FlowStep(seed_views.SeedTranscribeSeedQRConfirmInvalidQRView),
- FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView, is_redirect=True), # Live interactive screens are still weird
+ FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView),
# Intentionally scan QR data that makes no sense for this flow because is another QR recognized but is not a SeedQR (e.g., bitcoin address, psbt)
FlowStep(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, button_data_selection=seed_views.SeedTranscribeSeedQRConfirmQRPromptView.SCAN),
FlowStep(seed_views.SeedTranscribeSeedQRConfirmScanView, before_run=load_recognized_qr_type_isnot_seed_into_decoder),
FlowStep(seed_views.SeedTranscribeSeedQRConfirmInvalidQRView),
- FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView, is_redirect=True), # Live interactive screens are still weird
+ FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView),
# Now scan the correct SeedQR
FlowStep(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, button_data_selection=seed_views.SeedTranscribeSeedQRConfirmQRPromptView.SCAN),
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.