URI: 
       twizard hww: scan devices fewer times and move away from GUI thread - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 4b1d8353040513f6a3e371b7385711eb9f146a70
   DIR parent 01dac92e19481690e31b06632c1b1833468cee0b
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Thu,  9 Apr 2020 18:00:35 +0200
       
       wizard hww: scan devices fewer times and move away from GUI thread
       
       Diffstat:
         M electrum/base_wizard.py             |       7 +++----
         M electrum/plugin.py                  |      17 +++++++++--------
         M electrum/plugins/coldcard/coldcard… |       1 +
         M electrum/plugins/digitalbitbox/dig… |       1 +
         M electrum/plugins/hw_wallet/plugin.… |       6 ++++--
         M electrum/plugins/keepkey/keepkey.py |       1 +
         M electrum/plugins/ledger/ledger.py   |       1 +
         M electrum/plugins/safe_t/safe_t.py   |       1 +
         M electrum/plugins/trezor/trezor.py   |       1 +
       
       9 files changed, 22 insertions(+), 14 deletions(-)
       ---
   DIR diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py
       t@@ -348,7 +348,7 @@ class BaseWizard(Logger):
                assert isinstance(self.plugin, HW_PluginBase)
                devmgr = self.plugins.device_manager
                try:
       -            self.plugin.setup_device(device_info, self, purpose)
       +            client = self.plugin.setup_device(device_info, self, purpose)
                except OSError as e:
                    self.show_error(_('We encountered an error while connecting to your device:')
                                    + '\n' + str(e) + '\n'
       t@@ -376,19 +376,18 @@ class BaseWizard(Logger):
                    self.show_error(str(e))
                    self.choose_hw_device(purpose, storage=storage)
                    return
       +
                if purpose == HWD_SETUP_NEW_WALLET:
                    def f(derivation, script_type):
                        derivation = normalize_bip32_derivation(derivation)
                        self.run('on_hw_derivation', name, device_info, derivation, script_type)
                    self.derivation_and_script_type_dialog(f)
                elif purpose == HWD_SETUP_DECRYPT_WALLET:
       -            client = devmgr.client_by_id(device_info.device.id_)
                    password = client.get_password_for_storage_encryption()
                    try:
                        storage.decrypt(password)
                    except InvalidPassword:
                        # try to clear session so that user can type another passphrase
       -                client = devmgr.client_by_id(device_info.device.id_)
                        if hasattr(client, 'clear_session'):  # FIXME not all hw wallet plugins have this
                            client.clear_session()
                        raise
       t@@ -435,7 +434,7 @@ class BaseWizard(Logger):
                assert isinstance(self.plugin, HW_PluginBase)
                try:
                    xpub = self.plugin.get_xpub(device_info.device.id_, derivation, xtype, self)
       -            client = devmgr.client_by_id(device_info.device.id_)
       +            client = devmgr.client_by_id(device_info.device.id_, scan_now=False)
                    if not client: raise Exception("failed to find client for device id")
                    root_fingerprint = client.request_root_fingerprint_from_device()
                    label = client.label()  # use this as device_info.label might be outdated!
   DIR diff --git a/electrum/plugin.py b/electrum/plugin.py
       t@@ -401,7 +401,7 @@ class DeviceMgr(ThreadJob):
            def create_client(self, device: 'Device', handler: Optional['HardwareHandlerBase'],
                              plugin: 'HW_PluginBase') -> Optional['HardwareClientBase']:
                # Get from cache first
       -        client = self.client_lookup(device.id_)
       +        client = self._client_by_id(device.id_)
                if client:
                    return client
                client = plugin.create_client(device, handler)
       t@@ -437,7 +437,7 @@ class DeviceMgr(ThreadJob):
                    self._close_client(id_)
        
            def _close_client(self, id_):
       -        client = self.client_lookup(id_)
       +        client = self._client_by_id(id_)
                self.clients.pop(client, None)
                if client:
                    client.close()
       t@@ -446,19 +446,20 @@ class DeviceMgr(ThreadJob):
                with self.lock:
                    self.xpub_ids[xpub] = id_
        
       -    def client_lookup(self, id_) -> Optional['HardwareClientBase']:
       +    def _client_by_id(self, id_) -> Optional['HardwareClientBase']:
                with self.lock:
                    for client, (path, client_id) in self.clients.items():
                        if client_id == id_:
                            return client
                return None
        
       -    def client_by_id(self, id_) -> Optional['HardwareClientBase']:
       +    def client_by_id(self, id_, *, scan_now: bool = True) -> Optional['HardwareClientBase']:
                '''Returns a client for the device ID if one is registered.  If
                a device is wiped or in bootloader mode pairing is impossible;
                in such cases we communicate by device ID and not wallet.'''
       -        self.scan_devices()
       -        return self.client_lookup(id_)
       +        if scan_now:
       +            self.scan_devices()
       +        return self._client_by_id(id_)
        
            @with_scan_lock
            def client_for_keystore(self, plugin: 'HW_PluginBase', handler: Optional['HardwareHandlerBase'],
       t@@ -495,7 +496,7 @@ class DeviceMgr(ThreadJob):
            def client_by_xpub(self, plugin: 'HW_PluginBase', xpub, handler: 'HardwareHandlerBase',
                               devices: Sequence['Device']) -> Optional['HardwareClientBase']:
                _id = self.xpub_id(xpub)
       -        client = self.client_lookup(_id)
       +        client = self._client_by_id(_id)
                if client:
                    # An unpaired client might have another wallet's handler
                    # from a prior scan.  Replace to fix dialog parenting.
       t@@ -511,7 +512,7 @@ class DeviceMgr(ThreadJob):
                # The wallet has not been previously paired, so let the user
                # choose an unpaired device and compare its first address.
                xtype = bip32.xpub_type(xpub)
       -        client = self.client_lookup(info.device.id_)
       +        client = self._client_by_id(info.device.id_)
                if client and client.is_pairable():
                    # See comment above for same code
                    client.handler = handler
   DIR diff --git a/electrum/plugins/coldcard/coldcard.py b/electrum/plugins/coldcard/coldcard.py
       t@@ -524,6 +524,7 @@ class ColdcardPlugin(HW_PluginBase):
            def setup_device(self, device_info, wizard, purpose):
                device_id = device_info.device.id_
                client = self.scan_and_create_client_for_device(device_id=device_id, wizard=wizard)
       +        return client
        
            def get_xpub(self, device_id, derivation, xtype, wizard):
                # this seems to be part of the pairing process only, not during normal ops?
   DIR diff --git a/electrum/plugins/digitalbitbox/digitalbitbox.py b/electrum/plugins/digitalbitbox/digitalbitbox.py
       t@@ -705,6 +705,7 @@ class DigitalBitboxPlugin(HW_PluginBase):
                    client.setupRunning = True
                wizard.run_task_without_blocking_gui(
                    task=lambda: client.get_xpub("m/44'/0'", 'standard'))
       +        return client
        
        
            def is_mobile_paired(self):
   DIR diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py
       t@@ -25,6 +25,7 @@
        # SOFTWARE.
        
        from typing import TYPE_CHECKING, Dict, List, Union, Tuple, Sequence, Optional, Type
       +from functools import partial
        
        from electrum.plugin import BasePlugin, hook, Device, DeviceMgr, DeviceInfo
        from electrum.i18n import _
       t@@ -67,14 +68,15 @@ class HW_PluginBase(BasePlugin):
        
            def scan_and_create_client_for_device(self, *, device_id: str, wizard: 'BaseWizard') -> 'HardwareClientBase':
                devmgr = self.device_manager()
       -        client = devmgr.client_by_id(device_id)
       +        client = wizard.run_task_without_blocking_gui(
       +            task=partial(devmgr.client_by_id, device_id))
                if client is None:
                    raise UserFacingException(_('Failed to create a client for this device.') + '\n' +
                                              _('Make sure it is in the correct state.'))
                client.handler = self.create_handler(wizard)
                return client
        
       -    def setup_device(self, device_info: DeviceInfo, wizard: 'BaseWizard', purpose):
       +    def setup_device(self, device_info: DeviceInfo, wizard: 'BaseWizard', purpose) -> 'HardwareClientBase':
                """Called when creating a new wallet or when using the device to decrypt
                an existing wallet. Select the device to use.  If the device is
                uninitialized, go through the initialization process.
   DIR diff --git a/electrum/plugins/keepkey/keepkey.py b/electrum/plugins/keepkey/keepkey.py
       t@@ -283,6 +283,7 @@ class KeepKeyPlugin(HW_PluginBase):
                wizard.run_task_without_blocking_gui(
                    task=lambda: client.get_xpub("m", 'standard'))
                client.used()
       +        return client
        
            def get_xpub(self, device_id, derivation, xtype, wizard):
                if xtype not in self.SUPPORTED_XTYPES:
   DIR diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py
       t@@ -610,6 +610,7 @@ class LedgerPlugin(HW_PluginBase):
                client = self.scan_and_create_client_for_device(device_id=device_id, wizard=wizard)
                wizard.run_task_without_blocking_gui(
                    task=lambda: client.get_xpub("m/44'/0'", 'standard'))  # TODO replace by direct derivation once Nano S > 1.1
       +        return client
        
            def get_xpub(self, device_id, derivation, xtype, wizard):
                if xtype not in self.SUPPORTED_XTYPES:
   DIR diff --git a/electrum/plugins/safe_t/safe_t.py b/electrum/plugins/safe_t/safe_t.py
       t@@ -257,6 +257,7 @@ class SafeTPlugin(HW_PluginBase):
                wizard.run_task_without_blocking_gui(
                    task=lambda: client.get_xpub("m", 'standard'))
                client.used()
       +        return client
        
            def get_xpub(self, device_id, derivation, xtype, wizard):
                if xtype not in self.SUPPORTED_XTYPES:
   DIR diff --git a/electrum/plugins/trezor/trezor.py b/electrum/plugins/trezor/trezor.py
       t@@ -288,6 +288,7 @@ class TrezorPlugin(HW_PluginBase):
                wizard.run_task_without_blocking_gui(
                    task=lambda: client.get_xpub('m', 'standard', creating=is_creating_wallet))
                client.used()
       +        return client
        
            def get_xpub(self, device_id, derivation, xtype, wizard):
                if xtype not in self.SUPPORTED_XTYPES: