URI: 
       thardware wallets: better handle label collision when selecting device - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 56c3de0e1eb28f6017286f51c37c5c45b86690bd
   DIR parent 6e3875ceabf75ee96e722358bcff33f883dddbf5
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Sun, 17 Nov 2019 01:15:44 +0100
       
       hardware wallets: better handle label collision when selecting device
       
       related: #5759
       
       Diffstat:
         M electrum/plugin.py                  |      26 +++++++++++++++++++-------
         M electrum/plugins/hw_wallet/plugin.… |       7 ++++++-
       
       2 files changed, 25 insertions(+), 8 deletions(-)
       ---
   DIR diff --git a/electrum/plugin.py b/electrum/plugin.py
       t@@ -314,6 +314,9 @@ class HardwarePluginToScan(NamedTuple):
            exception: Optional[Exception]
        
        
       +PLACEHOLDER_HW_CLIENT_LABELS = {"", " "}
       +
       +
        class DeviceMgr(ThreadJob):
            '''Manages hardware clients.  A client communicates over a hardware
            channel with the device.
       t@@ -507,7 +510,7 @@ class DeviceMgr(ThreadJob):
                      'its seed (and passphrase, if any).  Otherwise all bitcoins you '
                      'receive will be unspendable.').format(plugin.device))
        
       -    def unpaired_device_infos(self, handler, plugin: 'HW_PluginBase', devices=None,
       +    def unpaired_device_infos(self, handler, plugin: 'HW_PluginBase', devices: List['Device'] = None,
                                      include_failing_clients=False):
                '''Returns a list of DeviceInfo objects: one for each connected,
                unpaired device accepted by the plugin.'''
       t@@ -537,7 +540,7 @@ class DeviceMgr(ThreadJob):
                return infos
        
            def select_device(self, plugin: 'HW_PluginBase', handler,
       -                      keystore: 'Hardware_KeyStore', devices=None) -> 'DeviceInfo':
       +                      keystore: 'Hardware_KeyStore', devices: List['Device'] = None) -> 'DeviceInfo':
                '''Ask the user to select a device to use if there is more than one,
                and return the DeviceInfo for the device.'''
                while True:
       t@@ -557,12 +560,21 @@ class DeviceMgr(ThreadJob):
                    devices = None
                if len(infos) == 1:
                    return infos[0]
       -        # select device by label
       -        for info in infos:
       -            if info.label == keystore.label:
       -                return info
       +        # select device by label automatically;
       +        # but only if not a placeholder label and only if there is no collision
       +        device_labels = [info.label for info in infos]
       +        if (keystore.label not in PLACEHOLDER_HW_CLIENT_LABELS
       +                and device_labels.count(keystore.label) == 1):
       +            for info in infos:
       +                if info.label == keystore.label:
       +                    return info
       +        # ask user to select device
                msg = _("Please select which {} device to use:").format(plugin.device)
       -        descriptions = [str(info.label) + ' (%s)'%(_("initialized") if info.initialized else _("wiped")) for info in infos]
       +        descriptions = ["{label} ({init}, {transport})"
       +                        .format(label=info.label,
       +                                init=(_("initialized") if info.initialized else _("wiped")),
       +                                transport=info.device.transport_ui_string)
       +                        for info in infos]
                c = handler.query_choice(msg, descriptions)
                if c is None:
                    raise UserCancelled()
   DIR diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py
       t@@ -160,7 +160,12 @@ class HardwareClientBase:
                raise NotImplementedError()
        
            def label(self) -> str:
       -        """The name given by the user to the device."""
       +        """The name given by the user to the device.
       +
       +        Note: labels are shown to the user to help distinguish their devices,
       +        and they are also used as a fallback to distinguish devices programmatically.
       +        So ideally, different devices would have different labels.
       +        """
                raise NotImplementedError()
        
            def has_usable_connection_with_device(self) -> bool: