From 865084133304ff8b2022dab982cfcaff002cabaf Mon Sep 17 00:00:00 2001 From: kdmukai Date: Sun, 23 Jul 2023 11:29:11 -0500 Subject: [PATCH 1/5] Interim commit --- src/main.py | 60 ++++++++++++- src/seedsigner/controller.py | 89 ++++++++++++------- src/seedsigner/gui/components.py | 12 ++- src/seedsigner/gui/renderer.py | 6 +- src/seedsigner/gui/screens/scan_screens.py | 2 +- src/seedsigner/gui/screens/screen.py | 8 +- src/seedsigner/gui/screens/seed_screens.py | 9 +- src/seedsigner/hardware/buttons.py | 2 +- src/seedsigner/hardware/camera.py | 5 +- src/seedsigner/helpers/embit_utils.py | 1 + src/seedsigner/helpers/mnemonic_generation.py | 2 +- src/seedsigner/models/__init__.py | 10 --- src/seedsigner/models/decode_qr.py | 5 +- src/seedsigner/models/encode_qr.py | 7 +- src/seedsigner/models/psbt_parser.py | 6 +- src/seedsigner/models/seed.py | 4 +- src/seedsigner/models/seed_storage.py | 5 +- src/seedsigner/models/settings.py | 3 +- src/seedsigner/views/psbt_views.py | 3 +- src/seedsigner/views/scan_views.py | 3 +- src/seedsigner/views/seed_views.py | 2 + src/seedsigner/views/tools_views.py | 2 + src/seedsigner/views/view.py | 5 +- tests/base.py | 2 +- tests/test_bip85.py | 2 +- tests/test_decodepsbtqr.py | 8 +- tests/test_encodepsbtqr.py | 5 +- tests/test_flows_settings.py | 3 +- tests/test_psbt_parser.py | 5 +- tests/test_seed.py | 5 +- 30 files changed, 178 insertions(+), 103 deletions(-) diff --git a/src/main.py b/src/main.py index 740faf30..100c3154 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,60 @@ -from seedsigner.controller import Controller +from time import time +master_start = time() + +start = time() +from seedsigner.controller import Controller +print("main.py time elapsed to import Controller:", time() - start) # Get the one and only Controller instance and start our main loop -Controller.get_instance().start() +controller = Controller.get_instance() +print("main.py time elapsed to import and initialize Controller:", time() - start) + + +start = time() +from seedsigner.models.threads import BaseThread +class BackgroundImportThread(BaseThread): + def run(self): + start = time() + from importlib import import_module + + # import seedsigner.hardware.buttons # slowly imports GPIO along the way + + def time_import(module_name): + last = time() + import_module(module_name) + print(time() - last, module_name) + + time_import('embit') + time_import('seedsigner.helpers.embit_utils') + + # Do costly initializations + time_import('seedsigner.models.seed_storage') + from seedsigner.models.seed_storage import SeedStorage + Controller.get_instance().storage = SeedStorage() + + # Get MainMenuView ready to respond quickly + time_import('seedsigner.views.scan_views') + + time_import('seedsigner.views.seed_views') + + time_import('seedsigner.views.tools_views') + + time_import('seedsigner.views.settings_views') + + # Lowest priority costly initializations + # time_import('picamera') + # time_import('picamera.array') + # time_import('seedsigner.hardware.pivideostream') + + print("Total BackgroundImportThread import time:", time() - start) + + exit() +background_import_thread = BackgroundImportThread() +background_import_thread.start() +print("main.py time elapsed through BackgroundImportThread.start():", time() - start) + + +print("*" * 80) +print("\nmain.py OVERALL time elapsed until Controller.start():", time() - master_start) +print("\n" + "*" * 80) +controller.start() diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index 25be36f7..a9a07b98 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -1,26 +1,29 @@ +print("controller.py") + +from time import time + +start = time() +print("Starting Controller import...") + import logging import traceback -import os -from embit.descriptor import Descriptor -from embit.psbt import PSBT +# from embit.descriptor import Descriptor +# from embit.psbt import PSBT from PIL.Image import Image -from typing import List -from seedsigner.gui.renderer import Renderer -from seedsigner.hardware.buttons import HardwareButtons -from seedsigner.hardware.microsd import MicroSD -from seedsigner.views.screensaver import ScreensaverScreen -from seedsigner.views.view import Destination, NotYetImplementedView, UnhandledExceptionView +from seedsigner.views.view import Destination -from .models import Seed, SeedStorage, Settings, Singleton, PSBTParser +from .models.settings import Settings +from .models.singleton import Singleton logger = logging.getLogger(__name__) +print("Total Controller import time:", time() - start) -class BackStack(List[Destination]): +class BackStack(list[Destination]): def __repr__(self): if len(self) == 0: return "[]" @@ -40,6 +43,7 @@ class StopFlowBasedTest(Exception): pass + class FlowBasedTestException(Exception): """ This is a special exception that is only raised by the test suite. @@ -71,22 +75,20 @@ class Controller(Singleton): # Declare class member vars with type hints to enable richer IDE support throughout # the code. - buttons: HardwareButtons = None - storage: SeedStorage = None + storage: 'SeedStorage' = None # TODO: Rename "storage" to something more indicative of its temp, in-memory state settings: Settings = None - renderer: Renderer = None # TODO: Refactor these flow-related attrs that survive across multiple Screens. # TODO: Should all in-memory flow-related attrs get wiped on MainMenuView? - psbt: PSBT = None - psbt_seed: Seed = None - psbt_parser: PSBTParser = None + psbt: 'embit.psbt.PSBT' = None + psbt_seed: 'Seed' = None + psbt_parser: 'PSBTParser' = None unverified_address = None - multisig_wallet_descriptor: Descriptor = None + multisig_wallet_descriptor: 'embit.descriptor.Descriptor' = None - image_entropy_preview_frames: List[Image] = None + image_entropy_preview_frames: list[Image] = None image_entropy_final_image: Image = None address_explorer_data: dict = None @@ -102,7 +104,7 @@ class Controller(Singleton): resume_main_flow: str = None back_stack: BackStack = None - screensaver: ScreensaverScreen = None + screensaver: 'ScreensaverScreen' = None @classmethod @@ -127,6 +129,10 @@ def configure_instance(cls, disable_hardware=False): each time you try to re-initialize a Controller. """ + start = time() + from seedsigner.gui.renderer import Renderer + from seedsigner.hardware.microsd import MicroSD + # Must be called before the first get_instance() call if cls._instance: raise Exception("Instance already configured") @@ -135,15 +141,7 @@ def configure_instance(cls, disable_hardware=False): controller = cls.__new__(cls) cls._instance = controller - # Input Buttons - if disable_hardware: - controller.buttons = None - else: - controller.buttons = HardwareButtons.get_instance() - # models - # TODO: Rename "storage" to something more indicative of its temp, in-memory state - controller.storage = SeedStorage() controller.settings = Settings.get_instance() controller.microsd = MicroSD.get_instance() @@ -156,13 +154,19 @@ def configure_instance(cls, disable_hardware=False): # Configure the Renderer Renderer.configure_instance() - controller.screensaver = ScreensaverScreen(controller.buttons) - controller.back_stack = BackStack() # Other behavior constants controller.screensaver_activation_ms = 120 * 1000 + + print(f"Controller configured in {time() - start:.3f}s") + start = time() + background_import_thread = BackgroundImportThread() + background_import_thread.start() + print("Time elapsed through BackgroundImportThread.start():", time() - start) + + return cls._instance @@ -172,7 +176,7 @@ def camera(self): return Camera.get_instance() - def get_seed(self, seed_num: int) -> Seed: + def get_seed(self, seed_num: int) -> 'Seed': if seed_num < len(self.storage.seeds): return self.storage.seeds[seed_num] else: @@ -187,7 +191,6 @@ def discard_seed(self, seed_num: int): def pop_prev_from_back_stack(self): - from .views import Destination if len(self.back_stack) > 0: # Pop the top View (which is the current View_cls) self.back_stack.pop() @@ -212,9 +215,16 @@ def start(self, initial_destination: Destination = None) -> None: from .views import MainMenuView, BackStackView from .views.screensaver import OpeningSplashScreen + start = time() + opening_splash = OpeningSplashScreen() + + print(f"Instantiating opening splash screen took {time() - start:.3f}s") + start = time() opening_splash.start() + print(f"Opening splash screen took {time() - start:.3f}s") + """ Class references can be stored as variables in python! This loop receives a View class to execute and stores it in the `View_cls` @@ -288,6 +298,7 @@ def run(self): if not next_destination: # Should only happen during dev when you hit an unimplemented option + from seedsigner.views.view import NotYetImplementedView next_destination = Destination(NotYetImplementedView) if next_destination.skip_current_view: @@ -320,7 +331,8 @@ def run(self): print("-" * 30) finally: - if self.screensaver.is_running: + from seedsigner.gui.renderer import Renderer + if self.is_screensaver_running: self.screensaver.stop() # Clear the screen when exiting @@ -328,7 +340,17 @@ def run(self): Renderer.get_instance().display_blank_screen() + @property + def is_screensaver_running(self): + return self.screensaver is not None and self.screensaver.is_running + + def start_screensaver(self): + if not self.screensaver: + # Do a lazy/late import and instantiation to reduce Controller initial startup time + from seedsigner.views.screensaver import ScreensaverScreen + from seedsigner.hardware.buttons import HardwareButtons + self.screensaver = ScreensaverScreen(HardwareButtons.get_instance()) self.screensaver.start() @@ -342,6 +364,7 @@ def handle_exception(self, e) -> Destination: * python file, line num, method name * Exception message """ + from seedsigner.views.view import UnhandledExceptionView logger.exception(e) # The final exception output line is: diff --git a/src/seedsigner/gui/components.py b/src/seedsigner/gui/components.py index 429890f9..c04a15ac 100644 --- a/src/seedsigner/gui/components.py +++ b/src/seedsigner/gui/components.py @@ -7,9 +7,9 @@ from PIL import Image, ImageDraw, ImageFont, ImageFilter from typing import List, Tuple -from seedsigner.models import Singleton from seedsigner.models.settings import Settings from seedsigner.models.settings_definition import SettingsConstants +from seedsigner.models.singleton import Singleton # TODO: Remove all pixel hard coding @@ -610,13 +610,15 @@ def __post_init__(self): def render(self): import time from seedsigner.controller import Controller + from seedsigner.hardware.buttons import HardwareButtons self.controller: Controller = Controller.get_instance() self.current_screen = self.renderer.canvas.copy() + buttons = HardwareButtons.get_instance() # Special case when screensaver is running - if self.controller.screensaver._is_running: - self.controller.buttons.override_ind = True + if self.controller.is_screensaver_running: + buttons.override_ind = True self.image_draw.rounded_rectangle( ( GUIConstants.EDGE_PADDING + 2, self.canvas_height - 60, self.canvas_width - GUIConstants.EDGE_PADDING - 2, self.canvas_width - GUIConstants.EDGE_PADDING - 2), @@ -633,11 +635,13 @@ def render(self): t_end = time.time() + 3 while time.time() < t_end: - if self.controller.buttons.has_any_input(): + if buttons.has_any_input(): break self.renderer.show_image(self.current_screen) + + @dataclass class FormattedAddress(BaseComponent): """ diff --git a/src/seedsigner/gui/renderer.py b/src/seedsigner/gui/renderer.py index cc733bfa..8aaaef4a 100644 --- a/src/seedsigner/gui/renderer.py +++ b/src/seedsigner/gui/renderer.py @@ -1,9 +1,9 @@ -from PIL import Image, ImageDraw, ImageFont +from PIL import Image, ImageDraw from threading import Lock from seedsigner.gui.components import Fonts, GUIConstants from seedsigner.hardware.ST7789 import ST7789 -from seedsigner.models import ConfigurableSingleton +from seedsigner.models.singleton import ConfigurableSingleton @@ -19,8 +19,6 @@ class Renderer(ConfigurableSingleton): @classmethod def configure_instance(cls): - from seedsigner.models.settings import Settings - # Instantiate the one and only Renderer instance renderer = cls.__new__(cls) cls._instance = renderer diff --git a/src/seedsigner/gui/screens/scan_screens.py b/src/seedsigner/gui/screens/scan_screens.py index 87062786..746b0216 100644 --- a/src/seedsigner/gui/screens/scan_screens.py +++ b/src/seedsigner/gui/screens/scan_screens.py @@ -6,7 +6,7 @@ from seedsigner.gui import renderer from seedsigner.hardware.buttons import HardwareButtonsConstants from seedsigner.hardware.camera import Camera -from seedsigner.models import DecodeQR, DecodeQRStatus +from seedsigner.models.decode_qr import DecodeQR, DecodeQRStatus from seedsigner.models.threads import BaseThread from .screen import BaseScreen, ButtonListScreen diff --git a/src/seedsigner/gui/screens/screen.py b/src/seedsigner/gui/screens/screen.py index ea08a4f5..e3eb0e44 100644 --- a/src/seedsigner/gui/screens/screen.py +++ b/src/seedsigner/gui/screens/screen.py @@ -7,12 +7,10 @@ from seedsigner.gui.renderer import Renderer from seedsigner.models.threads import BaseThread, ThreadsafeCounter -from seedsigner.models.encode_qr import EncodeQR from seedsigner.models.settings import SettingsConstants from ..components import (FontAwesomeIconConstants, GUIConstants, BaseComponent, Button, Icon, IconButton, - LargeIconButton, SeedSignerCustomIconConstants, TopNav, TextArea, load_image, ToastOverlay, - Fonts) + LargeIconButton, SeedSignerCustomIconConstants, TopNav, TextArea, load_image, ToastOverlay) from seedsigner.hardware.buttons import HardwareButtonsConstants, HardwareButtons @@ -660,10 +658,10 @@ def swap_selected_button(new_selected_button: int): @dataclass class QRDisplayScreen(BaseScreen): - qr_encoder: EncodeQR = None + qr_encoder: 'EncodeQR' = None class QRDisplayThread(BaseThread): - def __init__(self, qr_encoder: EncodeQR, qr_brightness: ThreadsafeCounter, renderer: Renderer, + def __init__(self, qr_encoder: 'EncodeQR', qr_brightness: ThreadsafeCounter, renderer: Renderer, tips_start_time: ThreadsafeCounter): super().__init__() self.qr_encoder = qr_encoder diff --git a/src/seedsigner/gui/screens/seed_screens.py b/src/seedsigner/gui/screens/seed_screens.py index 74f0cacf..484f368c 100644 --- a/src/seedsigner/gui/screens/seed_screens.py +++ b/src/seedsigner/gui/screens/seed_screens.py @@ -2,21 +2,16 @@ import time from dataclasses import dataclass -from typing import List, Tuple +from typing import List from PIL import Image, ImageDraw, ImageFilter from seedsigner.gui.renderer import Renderer from seedsigner.helpers.qr import QR -from seedsigner.models.qr_type import QRType from seedsigner.models.threads import BaseThread, ThreadsafeCounter -from seedsigner.models.seed import Seed -from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition - from .screen import RET_CODE__BACK_BUTTON, BaseScreen, BaseTopNavScreen, ButtonListScreen, KeyboardScreen, WarningEdgesMixin from ..components import (Button, FontAwesomeIconConstants, Fonts, FormattedAddress, IconButton, - IconTextLine, SeedSignerCustomIconConstants, TextArea, GUIConstants, - calc_text_centering) + IconTextLine, SeedSignerCustomIconConstants, TextArea, GUIConstants) from seedsigner.gui.keyboard import Keyboard, TextEntryDisplay from seedsigner.hardware.buttons import HardwareButtons, HardwareButtonsConstants diff --git a/src/seedsigner/hardware/buttons.py b/src/seedsigner/hardware/buttons.py index ca2011aa..8d42105d 100644 --- a/src/seedsigner/hardware/buttons.py +++ b/src/seedsigner/hardware/buttons.py @@ -72,7 +72,7 @@ def wait_for(self, keys=[], check_release=True, release_keys=[]) -> int: while True: cur_time = int(time.time() * 1000) - if cur_time - self.last_input_time > controller.screensaver_activation_ms and not controller.screensaver.is_running: + if cur_time - self.last_input_time > controller.screensaver_activation_ms and not controller.is_screensaver_running: # Start the screensaver. Will block execution until input detected. controller.start_screensaver() diff --git a/src/seedsigner/hardware/camera.py b/src/seedsigner/hardware/camera.py index b07ed3aa..32dc182d 100644 --- a/src/seedsigner/hardware/camera.py +++ b/src/seedsigner/hardware/camera.py @@ -2,9 +2,9 @@ from picamera import PiCamera from PIL import Image -from seedsigner.models import Singleton from seedsigner.hardware.pivideostream import PiVideoStream -from seedsigner.models.settings import SettingsConstants +from seedsigner.models.settings import Settings, SettingsConstants +from seedsigner.models.singleton import Singleton @@ -16,7 +16,6 @@ class Camera(Singleton): @classmethod def get_instance(cls): # This is the only way to access the one and only Controller - from seedsigner.models import Settings if cls._instance is None: cls._instance = cls.__new__(cls) cls._instance._camera_rotation = int(Settings.get_instance().get_value(SettingsConstants.SETTING__CAMERA_ROTATION)) diff --git a/src/seedsigner/helpers/embit_utils.py b/src/seedsigner/helpers/embit_utils.py index bdd95b7f..a1655f20 100644 --- a/src/seedsigner/helpers/embit_utils.py +++ b/src/seedsigner/helpers/embit_utils.py @@ -1,3 +1,4 @@ +print("embit_utils.py") import embit from embit import bip32 from embit.bip32 import HDKey diff --git a/src/seedsigner/helpers/mnemonic_generation.py b/src/seedsigner/helpers/mnemonic_generation.py index dab63a48..a021a834 100644 --- a/src/seedsigner/helpers/mnemonic_generation.py +++ b/src/seedsigner/helpers/mnemonic_generation.py @@ -5,7 +5,6 @@ from embit.bip39 import mnemonic_to_bytes, mnemonic_from_bytes from typing import List -from seedsigner.models.seed import Seed def calculate_checksum(mnemonic: list, wordlist_language_code: str) -> List[str]: @@ -15,6 +14,7 @@ def calculate_checksum(mnemonic: list, wordlist_language_code: str) -> List[str] If 11- or 23-words are provided, append word `0000` to end of list as temp final word. """ + from seedsigner.models.seed import Seed if len(mnemonic) in [11, 23]: mnemonic.append(Seed.get_wordlist(wordlist_language_code)[0]) diff --git a/src/seedsigner/models/__init__.py b/src/seedsigner/models/__init__.py index c21cf9cb..e69de29b 100644 --- a/src/seedsigner/models/__init__.py +++ b/src/seedsigner/models/__init__.py @@ -1,10 +0,0 @@ -# Must import crucial base class first! -from .singleton import Singleton, ConfigurableSingleton - -from .seed import * -from .qr_type import * -from .decode_qr import * -from .encode_qr import * -from .psbt_parser import * -from .seed_storage import * -from .settings import * diff --git a/src/seedsigner/models/decode_qr.py b/src/seedsigner/models/decode_qr.py index 9c1623b2..3c8d3ad7 100644 --- a/src/seedsigner/models/decode_qr.py +++ b/src/seedsigner/models/decode_qr.py @@ -1,3 +1,5 @@ +print("decode_qr.py") + import base64 import json import logging @@ -13,7 +15,8 @@ from urtypes.bytes import Bytes from seedsigner.helpers.ur2.ur_decoder import URDecoder -from seedsigner.models import QRType, Seed +from seedsigner.models.qr_type import QRType +from seedsigner.models.seed import Seed from seedsigner.models.settings import SettingsConstants diff --git a/src/seedsigner/models/encode_qr.py b/src/seedsigner/models/encode_qr.py index ece3cb42..caef52f5 100644 --- a/src/seedsigner/models/encode_qr.py +++ b/src/seedsigner/models/encode_qr.py @@ -1,3 +1,5 @@ +print("encode_qr.py") + import math from embit import bip32 @@ -11,12 +13,13 @@ from seedsigner.helpers.ur2.ur_encoder import UREncoder from seedsigner.helpers.ur2.ur import UR from seedsigner.helpers.qr import QR -from seedsigner.models import Seed, QRType +from seedsigner.models.qr_type import QRType +from seedsigner.models.seed import Seed +from seedsigner.models.settings import SettingsConstants from urtypes.crypto import PSBT as UR_PSBT from urtypes.crypto import Account, HDKey, Output, Keypath, PathComponent, SCRIPT_EXPRESSION_TAG_MAP -from seedsigner.models.settings import SettingsConstants diff --git a/src/seedsigner/models/psbt_parser.py b/src/seedsigner/models/psbt_parser.py index 3a1374be..84979ba8 100644 --- a/src/seedsigner/models/psbt_parser.py +++ b/src/seedsigner/models/psbt_parser.py @@ -1,12 +1,14 @@ +print("psbt_parser.py") + from binascii import hexlify -from embit import psbt, script, ec, bip32, bip39 +from embit import psbt, script, ec, bip32 from embit.descriptor import Descriptor from embit.networks import NETWORKS from embit.psbt import PSBT from io import BytesIO from typing import List -from seedsigner.models import Seed +from seedsigner.models.seed import Seed from seedsigner.models.settings import SettingsConstants diff --git a/src/seedsigner/models/seed.py b/src/seedsigner/models/seed.py index 7a848ecd..108755ad 100644 --- a/src/seedsigner/models/seed.py +++ b/src/seedsigner/models/seed.py @@ -1,3 +1,4 @@ +print("seed.py") import unicodedata from binascii import hexlify @@ -6,7 +7,6 @@ from typing import List from seedsigner.models.settings import SettingsConstants -from seedsigner.helpers import embit_utils class InvalidSeedException(Exception): @@ -108,6 +108,8 @@ def get_fingerprint(self, network: str = SettingsConstants.MAINNET) -> str: def get_xpub(self, wallet_path: str = '/', network: str = SettingsConstants.MAINNET): + # Import here to avoid slow startup times; takes 1.35s to import the first time + from seedsigner.helpers import embit_utils return embit_utils.get_xpub(seed_bytes=self.seed_bytes, derivation_path=wallet_path, embit_network=SettingsConstants.map_network_to_embit(network)) diff --git a/src/seedsigner/models/seed_storage.py b/src/seedsigner/models/seed_storage.py index d2881e32..6d2379f1 100644 --- a/src/seedsigner/models/seed_storage.py +++ b/src/seedsigner/models/seed_storage.py @@ -1,6 +1,7 @@ +print("seed_storage.py") + from typing import List -from seedsigner.models import Seed -from seedsigner.models.seed import InvalidSeedException +from seedsigner.models.seed import Seed, InvalidSeedException from seedsigner.models.settings_definition import SettingsConstants diff --git a/src/seedsigner/models/settings.py b/src/seedsigner/models/settings.py index 6898474b..eaf9c247 100644 --- a/src/seedsigner/models/settings.py +++ b/src/seedsigner/models/settings.py @@ -5,8 +5,7 @@ from typing import List from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition -from .singleton import Singleton - +from seedsigner.models.singleton import Singleton class InvalidSettingsQRData(Exception): diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py index 80ecc52b..7d43d3b4 100644 --- a/src/seedsigner/views/psbt_views.py +++ b/src/seedsigner/views/psbt_views.py @@ -1,5 +1,6 @@ +print("psbt_views.py") + import logging -from typing import List from embit.psbt import PSBT from embit import script diff --git a/src/seedsigner/views/scan_views.py b/src/seedsigner/views/scan_views.py index 50251267..0da350e6 100644 --- a/src/seedsigner/views/scan_views.py +++ b/src/seedsigner/views/scan_views.py @@ -3,7 +3,8 @@ from embit.descriptor import Descriptor from seedsigner.gui.screens import scan_screens -from seedsigner.models import DecodeQR, Seed +from seedsigner.models.decode_qr import DecodeQR +from seedsigner.models.seed import Seed from seedsigner.models.settings import SettingsConstants from seedsigner.views.settings_views import SettingsIngestSettingsQRView from seedsigner.views.view import MainMenuView, NotYetImplementedView, View, Destination diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 41491d47..46949669 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -1,3 +1,5 @@ +print("seed_views.py") + import embit import random import time diff --git a/src/seedsigner/views/tools_views.py b/src/seedsigner/views/tools_views.py index 6cd0c0bd..88b3ee88 100644 --- a/src/seedsigner/views/tools_views.py +++ b/src/seedsigner/views/tools_views.py @@ -1,3 +1,5 @@ +print("tools_views.py") + import hashlib import os diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index de4bac71..53315411 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -1,11 +1,13 @@ +print("view.py") + from dataclasses import dataclass from typing import Type from seedsigner.gui.components import FontAwesomeIconConstants from seedsigner.gui.screens import RET_CODE__POWER_BUTTON, RET_CODE__BACK_BUTTON from seedsigner.gui.screens.screen import BaseScreen, DireWarningScreen, LargeButtonScreen, PowerOffScreen, PowerOffNotRequiredScreen, ResetScreen, WarningScreen +from seedsigner.models.settings import Settings from seedsigner.models.threads import BaseThread -from seedsigner.models import Settings class BackStackView: @@ -46,7 +48,6 @@ def __init__(self) -> None: # Import here to avoid circular imports from seedsigner.controller import Controller from seedsigner.gui import Renderer - from seedsigner.models import Settings self.controller: Controller = Controller.get_instance() self.settings = Settings.get_instance() diff --git a/tests/base.py b/tests/base.py index 72bd55f3..708d64f7 100644 --- a/tests/base.py +++ b/tests/base.py @@ -14,7 +14,7 @@ from seedsigner.controller import Controller, FlowBasedTestException, StopFlowBasedTest from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON, RET_CODE__POWER_BUTTON -from seedsigner.models import Settings +from seedsigner.models.settings import Settings from seedsigner.views.view import Destination, MainMenuView, View diff --git a/tests/test_bip85.py b/tests/test_bip85.py index c9f67a31..5ade3917 100644 --- a/tests/test_bip85.py +++ b/tests/test_bip85.py @@ -1,6 +1,6 @@ import pytest from mock import MagicMock -from seedsigner.models import Seed +from seedsigner.models.seed import Seed from embit import bip39 from seedsigner.models.settings import SettingsConstants diff --git a/tests/test_decodepsbtqr.py b/tests/test_decodepsbtqr.py index ef9baa4a..9b7d6981 100644 --- a/tests/test_decodepsbtqr.py +++ b/tests/test_decodepsbtqr.py @@ -1,7 +1,7 @@ -import pytest -from mock import MagicMock -from seedsigner.models import Seed, DecodeQR, DecodeQRStatus, QRType, PSBTParser -from embit import psbt, bip39 +from seedsigner.models.decode_qr import DecodeQR, DecodeQRStatus +from seedsigner.models.psbt_parser import PSBTParser +from seedsigner.models.qr_type import QRType +from seedsigner.models.seed import Seed from seedsigner.models.settings_definition import SettingsConstants diff --git a/tests/test_encodepsbtqr.py b/tests/test_encodepsbtqr.py index 0dc5c0f4..75ed796f 100644 --- a/tests/test_encodepsbtqr.py +++ b/tests/test_encodepsbtqr.py @@ -1,6 +1,5 @@ -import pytest -from mock import MagicMock -from seedsigner.models import EncodeQR, QRType +from seedsigner.models.encode_qr import EncodeQR +from seedsigner.models.qr_type import QRType from embit import psbt from binascii import a2b_base64 diff --git a/tests/test_flows_settings.py b/tests/test_flows_settings.py index 8c603170..1fb394a2 100644 --- a/tests/test_flows_settings.py +++ b/tests/test_flows_settings.py @@ -3,9 +3,8 @@ # Must import test base before the Controller from base import FlowTest, FlowStep -from seedsigner.models import SettingsDefinition from seedsigner.models.settings import Settings -from seedsigner.models.settings_definition import SettingsConstants +from seedsigner.models.settings_definition import SettingsDefinition, SettingsConstants from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON from seedsigner.views.view import MainMenuView from seedsigner.views import settings_views diff --git a/tests/test_psbt_parser.py b/tests/test_psbt_parser.py index e0fb5a84..4de00533 100644 --- a/tests/test_psbt_parser.py +++ b/tests/test_psbt_parser.py @@ -1,6 +1,5 @@ -import pytest -from mock import MagicMock -from seedsigner.models import PSBTParser, Seed +from seedsigner.models.psbt_parser import PSBTParser +from seedsigner.models.seed import Seed from embit import psbt from binascii import a2b_base64 from seedsigner.models.settings_definition import SettingsConstants diff --git a/tests/test_seed.py b/tests/test_seed.py index cbfa8964..a7e082ee 100644 --- a/tests/test_seed.py +++ b/tests/test_seed.py @@ -1,7 +1,4 @@ -import pytest -from mock import MagicMock -from seedsigner.models import Seed -from embit import bip39 +from seedsigner.models.seed import Seed from seedsigner.models.settings import SettingsConstants From 01aaf69f1559b304c81f56d7c3ea5d6c7c87fe3d Mon Sep 17 00:00:00 2001 From: kdmukai Date: Sun, 23 Jul 2023 18:33:00 -0500 Subject: [PATCH 2/5] initial cleanup --- src/main.py | 47 ++---------------------------------- src/seedsigner/controller.py | 42 +++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/src/main.py b/src/main.py index 100c3154..75611efc 100644 --- a/src/main.py +++ b/src/main.py @@ -5,55 +5,12 @@ start = time() from seedsigner.controller import Controller print("main.py time elapsed to import Controller:", time() - start) + # Get the one and only Controller instance and start our main loop +start = time() controller = Controller.get_instance() print("main.py time elapsed to import and initialize Controller:", time() - start) - -start = time() -from seedsigner.models.threads import BaseThread -class BackgroundImportThread(BaseThread): - def run(self): - start = time() - from importlib import import_module - - # import seedsigner.hardware.buttons # slowly imports GPIO along the way - - def time_import(module_name): - last = time() - import_module(module_name) - print(time() - last, module_name) - - time_import('embit') - time_import('seedsigner.helpers.embit_utils') - - # Do costly initializations - time_import('seedsigner.models.seed_storage') - from seedsigner.models.seed_storage import SeedStorage - Controller.get_instance().storage = SeedStorage() - - # Get MainMenuView ready to respond quickly - time_import('seedsigner.views.scan_views') - - time_import('seedsigner.views.seed_views') - - time_import('seedsigner.views.tools_views') - - time_import('seedsigner.views.settings_views') - - # Lowest priority costly initializations - # time_import('picamera') - # time_import('picamera.array') - # time_import('seedsigner.hardware.pivideostream') - - print("Total BackgroundImportThread import time:", time() - start) - - exit() -background_import_thread = BackgroundImportThread() -background_import_thread.start() -print("main.py time elapsed through BackgroundImportThread.start():", time() - start) - - print("*" * 80) print("\nmain.py OVERALL time elapsed until Controller.start():", time() - master_start) print("\n" + "*" * 80) diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index a9a07b98..8dedabb7 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -2,14 +2,14 @@ from time import time +from seedsigner.models.threads import BaseThread + start = time() print("Starting Controller import...") import logging import traceback -# from embit.descriptor import Descriptor -# from embit.psbt import PSBT from PIL.Image import Image from seedsigner.views.view import Destination @@ -52,6 +52,43 @@ class FlowBasedTestException(Exception): pass +class BackgroundImportThread(BaseThread): + def run(self): + start = time() + from importlib import import_module + + # import seedsigner.hardware.buttons # slowly imports GPIO along the way + + def time_import(module_name): + last = time() + import_module(module_name) + print(time() - last, module_name) + + time_import('embit') + time_import('seedsigner.helpers.embit_utils') + + # Do costly initializations + time_import('seedsigner.models.seed_storage') + from seedsigner.models.seed_storage import SeedStorage + Controller.get_instance().storage = SeedStorage() + + # Get MainMenuView ready to respond quickly + time_import('seedsigner.views.scan_views') + + time_import('seedsigner.views.seed_views') + + time_import('seedsigner.views.tools_views') + + time_import('seedsigner.views.settings_views') + + # Lowest priority costly initializations + # time_import('picamera') + # time_import('picamera.array') + # time_import('seedsigner.hardware.pivideostream') + + print("Total BackgroundImportThread import time:", time() - start) + + class Controller(Singleton): """ @@ -166,7 +203,6 @@ def configure_instance(cls, disable_hardware=False): background_import_thread.start() print("Time elapsed through BackgroundImportThread.start():", time() - start) - return cls._instance From 32953ee56e4bcde96634278bd4433b3bb2f94c95 Mon Sep 17 00:00:00 2001 From: kdmukai Date: Sun, 30 Jul 2023 08:59:34 -0500 Subject: [PATCH 3/5] tentative fix to race condition in test suite --- src/seedsigner/controller.py | 48 +++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index 8dedabb7..5a326977 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -1,10 +1,10 @@ print("controller.py") -from time import time +import time from seedsigner.models.threads import BaseThread -start = time() +start = time.time() print("Starting Controller import...") import logging @@ -20,7 +20,7 @@ logger = logging.getLogger(__name__) -print("Total Controller import time:", time() - start) +print("Total Controller import time:", time.time() - start) class BackStack(list[Destination]): @@ -54,15 +54,15 @@ class FlowBasedTestException(Exception): class BackgroundImportThread(BaseThread): def run(self): - start = time() + start = time.time() from importlib import import_module # import seedsigner.hardware.buttons # slowly imports GPIO along the way def time_import(module_name): - last = time() + last = time.time() import_module(module_name) - print(time() - last, module_name) + print(time.time() - last, module_name) time_import('embit') time_import('seedsigner.helpers.embit_utils') @@ -70,7 +70,7 @@ def time_import(module_name): # Do costly initializations time_import('seedsigner.models.seed_storage') from seedsigner.models.seed_storage import SeedStorage - Controller.get_instance().storage = SeedStorage() + Controller.get_instance()._storage = SeedStorage() # Get MainMenuView ready to respond quickly time_import('seedsigner.views.scan_views') @@ -86,7 +86,7 @@ def time_import(module_name): # time_import('picamera.array') # time_import('seedsigner.hardware.pivideostream') - print("Total BackgroundImportThread import time:", time() - start) + print("Total BackgroundImportThread import time:", time.time() - start) @@ -112,7 +112,7 @@ class Controller(Singleton): # Declare class member vars with type hints to enable richer IDE support throughout # the code. - storage: 'SeedStorage' = None # TODO: Rename "storage" to something more indicative of its temp, in-memory state + _storage: 'SeedStorage' = None # TODO: Rename "storage" to something more indicative of its temp, in-memory state settings: Settings = None # TODO: Refactor these flow-related attrs that survive across multiple Screens. @@ -166,7 +166,7 @@ def configure_instance(cls, disable_hardware=False): each time you try to re-initialize a Controller. """ - start = time() + start = time.time() from seedsigner.gui.renderer import Renderer from seedsigner.hardware.microsd import MicroSD @@ -196,12 +196,12 @@ def configure_instance(cls, disable_hardware=False): # Other behavior constants controller.screensaver_activation_ms = 120 * 1000 - print(f"Controller configured in {time() - start:.3f}s") + print(f"Controller configured in {time.time() - start:.3f}s") - start = time() + start = time.time() background_import_thread = BackgroundImportThread() background_import_thread.start() - print("Time elapsed through BackgroundImportThread.start():", time() - start) + print("Time elapsed through BackgroundImportThread.start():", time.time() - start) return cls._instance @@ -210,6 +210,20 @@ def configure_instance(cls, disable_hardware=False): def camera(self): from .hardware.camera import Camera return Camera.get_instance() + + + @property + def storage(self): + start = time.time() + did_sleep = False + while not self._storage: + # Wait for the BackgroundImportThread to finish initializing the storage. + # This is a rare timing issue that likely only occurs in the test suite. + did_sleep = True + time.sleep(0.001) + if did_sleep: + print(f"Controller.storage waited {time.time() - start:.3f}s for BackgroundImportThread") + return self._storage def get_seed(self, seed_num: int) -> 'Seed': @@ -251,15 +265,15 @@ def start(self, initial_destination: Destination = None) -> None: from .views import MainMenuView, BackStackView from .views.screensaver import OpeningSplashScreen - start = time() + start = time.time() opening_splash = OpeningSplashScreen() - print(f"Instantiating opening splash screen took {time() - start:.3f}s") - start = time() + print(f"Instantiating opening splash screen took {time.time() - start:.3f}s") + start = time.time() opening_splash.start() - print(f"Opening splash screen took {time() - start:.3f}s") + print(f"Opening splash screen took {time.time() - start:.3f}s") """ Class references can be stored as variables in python! From ac5c4d919b199084f0633878e43a946c7fa0369d Mon Sep 17 00:00:00 2001 From: kdmukai Date: Sat, 5 Aug 2023 10:55:55 -0500 Subject: [PATCH 4/5] Clean up to prep PR for final review --- src/main.py | 11 ------- src/seedsigner/controller.py | 47 ++++----------------------- src/seedsigner/helpers/embit_utils.py | 1 - src/seedsigner/models/decode_qr.py | 2 -- src/seedsigner/models/encode_qr.py | 2 -- src/seedsigner/models/psbt_parser.py | 2 -- src/seedsigner/models/seed.py | 1 - src/seedsigner/models/seed_storage.py | 2 -- src/seedsigner/views/psbt_views.py | 2 -- src/seedsigner/views/seed_views.py | 7 ++-- src/seedsigner/views/tools_views.py | 2 -- 11 files changed, 9 insertions(+), 70 deletions(-) diff --git a/src/main.py b/src/main.py index 75611efc..384d5711 100644 --- a/src/main.py +++ b/src/main.py @@ -1,17 +1,6 @@ -from time import time -master_start = time() - - -start = time() from seedsigner.controller import Controller -print("main.py time elapsed to import Controller:", time() - start) # Get the one and only Controller instance and start our main loop -start = time() controller = Controller.get_instance() -print("main.py time elapsed to import and initialize Controller:", time() - start) -print("*" * 80) -print("\nmain.py OVERALL time elapsed until Controller.start():", time() - master_start) -print("\n" + "*" * 80) controller.start() diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index 5a326977..2ec8c570 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -1,26 +1,18 @@ -print("controller.py") - import time - -from seedsigner.models.threads import BaseThread - -start = time.time() -print("Starting Controller import...") - import logging + import traceback from PIL.Image import Image +from seedsigner.models.settings import Settings +from seedsigner.models.singleton import Singleton +from seedsigner.models.threads import BaseThread from seedsigner.views.view import Destination -from .models.settings import Settings -from .models.singleton import Singleton - logger = logging.getLogger(__name__) -print("Total Controller import time:", time.time() - start) class BackStack(list[Destination]): @@ -52,9 +44,9 @@ class FlowBasedTestException(Exception): pass + class BackgroundImportThread(BaseThread): def run(self): - start = time.time() from importlib import import_module # import seedsigner.hardware.buttons # slowly imports GPIO along the way @@ -62,7 +54,7 @@ def run(self): def time_import(module_name): last = time.time() import_module(module_name) - print(time.time() - last, module_name) + # print(time.time() - last, module_name) time_import('embit') time_import('seedsigner.helpers.embit_utils') @@ -81,13 +73,6 @@ def time_import(module_name): time_import('seedsigner.views.settings_views') - # Lowest priority costly initializations - # time_import('picamera') - # time_import('picamera.array') - # time_import('seedsigner.hardware.pivideostream') - - print("Total BackgroundImportThread import time:", time.time() - start) - class Controller(Singleton): @@ -166,7 +151,6 @@ def configure_instance(cls, disable_hardware=False): each time you try to re-initialize a Controller. """ - start = time.time() from seedsigner.gui.renderer import Renderer from seedsigner.hardware.microsd import MicroSD @@ -195,13 +179,9 @@ def configure_instance(cls, disable_hardware=False): # Other behavior constants controller.screensaver_activation_ms = 120 * 1000 - - print(f"Controller configured in {time.time() - start:.3f}s") - start = time.time() background_import_thread = BackgroundImportThread() background_import_thread.start() - print("Time elapsed through BackgroundImportThread.start():", time.time() - start) return cls._instance @@ -214,15 +194,10 @@ def camera(self): @property def storage(self): - start = time.time() - did_sleep = False while not self._storage: # Wait for the BackgroundImportThread to finish initializing the storage. # This is a rare timing issue that likely only occurs in the test suite. - did_sleep = True time.sleep(0.001) - if did_sleep: - print(f"Controller.storage waited {time.time() - start:.3f}s for BackgroundImportThread") return self._storage @@ -265,15 +240,7 @@ def start(self, initial_destination: Destination = None) -> None: from .views import MainMenuView, BackStackView from .views.screensaver import OpeningSplashScreen - start = time.time() - - opening_splash = OpeningSplashScreen() - - print(f"Instantiating opening splash screen took {time.time() - start:.3f}s") - start = time.time() - opening_splash.start() - - print(f"Opening splash screen took {time.time() - start:.3f}s") + OpeningSplashScreen().start() """ Class references can be stored as variables in python! diff --git a/src/seedsigner/helpers/embit_utils.py b/src/seedsigner/helpers/embit_utils.py index a1655f20..bdd95b7f 100644 --- a/src/seedsigner/helpers/embit_utils.py +++ b/src/seedsigner/helpers/embit_utils.py @@ -1,4 +1,3 @@ -print("embit_utils.py") import embit from embit import bip32 from embit.bip32 import HDKey diff --git a/src/seedsigner/models/decode_qr.py b/src/seedsigner/models/decode_qr.py index 3c8d3ad7..f5e30302 100644 --- a/src/seedsigner/models/decode_qr.py +++ b/src/seedsigner/models/decode_qr.py @@ -1,5 +1,3 @@ -print("decode_qr.py") - import base64 import json import logging diff --git a/src/seedsigner/models/encode_qr.py b/src/seedsigner/models/encode_qr.py index caef52f5..0e6493d5 100644 --- a/src/seedsigner/models/encode_qr.py +++ b/src/seedsigner/models/encode_qr.py @@ -1,5 +1,3 @@ -print("encode_qr.py") - import math from embit import bip32 diff --git a/src/seedsigner/models/psbt_parser.py b/src/seedsigner/models/psbt_parser.py index 84979ba8..f431a3c6 100644 --- a/src/seedsigner/models/psbt_parser.py +++ b/src/seedsigner/models/psbt_parser.py @@ -1,5 +1,3 @@ -print("psbt_parser.py") - from binascii import hexlify from embit import psbt, script, ec, bip32 from embit.descriptor import Descriptor diff --git a/src/seedsigner/models/seed.py b/src/seedsigner/models/seed.py index 108755ad..51eb8555 100644 --- a/src/seedsigner/models/seed.py +++ b/src/seedsigner/models/seed.py @@ -1,4 +1,3 @@ -print("seed.py") import unicodedata from binascii import hexlify diff --git a/src/seedsigner/models/seed_storage.py b/src/seedsigner/models/seed_storage.py index 6d2379f1..dcc30a7a 100644 --- a/src/seedsigner/models/seed_storage.py +++ b/src/seedsigner/models/seed_storage.py @@ -1,5 +1,3 @@ -print("seed_storage.py") - from typing import List from seedsigner.models.seed import Seed, InvalidSeedException from seedsigner.models.settings_definition import SettingsConstants diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py index 7d43d3b4..faf74bcd 100644 --- a/src/seedsigner/views/psbt_views.py +++ b/src/seedsigner/views/psbt_views.py @@ -1,5 +1,3 @@ -print("psbt_views.py") - import logging from embit.psbt import PSBT diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 46949669..db5a9a0c 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -1,5 +1,3 @@ -print("seed_views.py") - import embit import random import time @@ -12,10 +10,10 @@ from seedsigner.controller import Controller from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerCustomIconConstants -from seedsigner.helpers import embit_utils from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen, WarningScreen, DireWarningScreen, seed_screens) from seedsigner.gui.screens.screen import LargeIconStatusScreen, QRDisplayScreen +from seedsigner.helpers import embit_utils from seedsigner.models.decode_qr import DecodeQR from seedsigner.models.encode_qr import EncodeQR from seedsigner.models.psbt_parser import PSBTParser @@ -26,8 +24,7 @@ from seedsigner.models.threads import BaseThread, ThreadsafeCounter from seedsigner.views.psbt_views import PSBTChangeDetailsView from seedsigner.views.scan_views import ScanView - -from .view import NotYetImplementedView, View, Destination, BackStackView, MainMenuView +from seedsigner.views.view import NotYetImplementedView, View, Destination, BackStackView, MainMenuView diff --git a/src/seedsigner/views/tools_views.py b/src/seedsigner/views/tools_views.py index 88b3ee88..6cd0c0bd 100644 --- a/src/seedsigner/views/tools_views.py +++ b/src/seedsigner/views/tools_views.py @@ -1,5 +1,3 @@ -print("tools_views.py") - import hashlib import os From 260d282a2f8dbc1dac2f0da874b3a3b43584f20e Mon Sep 17 00:00:00 2001 From: kdmukai Date: Sat, 5 Aug 2023 11:05:28 -0500 Subject: [PATCH 5/5] further cleanup --- src/main.py | 4 +--- src/seedsigner/views/view.py | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main.py b/src/main.py index 384d5711..740faf30 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,4 @@ from seedsigner.controller import Controller # Get the one and only Controller instance and start our main loop -controller = Controller.get_instance() - -controller.start() +Controller.get_instance().start() diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index 53315411..3faa39ba 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -1,5 +1,3 @@ -print("view.py") - from dataclasses import dataclass from typing import Type