From 3e7d4749cf5399f524e02428c810526d6af9b5b0 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 6 Apr 2024 11:35:57 +0200 Subject: [PATCH] turn classmethod 'find_all_plugins' into an instance method. change 'use_' prefix to 'enable_plugin_' --- electrum/plugin.py | 100 ++++++++++++------------ electrum/plugins/swapserver/__init__.py | 2 +- tests/regtest.py | 2 +- 3 files changed, 53 insertions(+), 51 deletions(-) diff --git a/electrum/plugin.py b/electrum/plugin.py index aad9bb896..32ff34757 100644 --- a/electrum/plugin.py +++ b/electrum/plugin.py @@ -57,7 +57,6 @@ class Plugins(DaemonThread): LOGGING_SHORTCUT = 'p' pkgpath = os.path.dirname(plugins.__file__) - _all_found_plugins = None # type: Optional[Dict[str, dict]] @profiler def __init__(self, config: SimpleConfig, gui_name): @@ -66,48 +65,44 @@ class Plugins(DaemonThread): self.config = config self.hw_wallets = {} self.plugins = {} # type: Dict[str, BasePlugin] + self.internal_plugin_metadata = {} self.gui_name = gui_name - self.descriptions = {} self.device_manager = DeviceMgr(config) + self.find_internal_plugins() self.load_plugins() self.add_jobs(self.device_manager.thread_jobs()) self.start() - @classmethod - def find_all_plugins(cls) -> Mapping[str, dict]: - """Return a map of all found plugins: name -> description. - Note that plugins not available for the current GUI are also included. - """ - if cls._all_found_plugins is None: - cls._all_found_plugins = dict() - iter_modules = list(pkgutil.iter_modules([cls.pkgpath])) - for loader, name, ispkg in iter_modules: - # FIXME pyinstaller binaries are packaging each built-in plugin twice: - # once as data and once as code. To honor the "no duplicates" rule below, - # we exclude the ones packaged as *code*, here: - if loader.__class__.__qualname__ == "FrozenImporter": - continue - full_name = f'electrum.plugins.{name}' - spec = importlib.util.find_spec(full_name) - if spec is None: # pkgutil found it but importlib can't ?! - raise Exception(f"Error pre-loading {full_name}: no spec") - try: - module = importlib.util.module_from_spec(spec) - # sys.modules needs to be modified for relative imports to work - # see https://stackoverflow.com/a/50395128 - sys.modules[spec.name] = module - spec.loader.exec_module(module) - except Exception as e: - raise Exception(f"Error pre-loading {full_name}: {repr(e)}") from e - d = module.__dict__ - if name in cls._all_found_plugins: - _logger.info(f"Found the following plugin modules: {iter_modules=}") - raise Exception(f"duplicate plugins? for {name=}") - cls._all_found_plugins[name] = d - return cls._all_found_plugins + @property + def descriptions(self): + return dict(list(self.internal_plugin_metadata.items())) - def load_plugins(self): - for name, d in self.find_all_plugins().items(): + def find_internal_plugins(self) -> Mapping[str, dict]: + """Populates self.internal_plugin_metadata + """ + iter_modules = list(pkgutil.iter_modules([self.pkgpath])) + for loader, name, ispkg in iter_modules: + # FIXME pyinstaller binaries are packaging each built-in plugin twice: + # once as data and once as code. To honor the "no duplicates" rule below, + # we exclude the ones packaged as *code*, here: + if loader.__class__.__qualname__ == "FrozenImporter": + continue + full_name = f'electrum.plugins.{name}' + spec = importlib.util.find_spec(full_name) + if spec is None: # pkgutil found it but importlib can't ?! + raise Exception(f"Error pre-loading {full_name}: no spec") + try: + module = importlib.util.module_from_spec(spec) + # sys.modules needs to be modified for relative imports to work + # see https://stackoverflow.com/a/50395128 + sys.modules[spec.name] = module + spec.loader.exec_module(module) + except Exception as e: + raise Exception(f"Error pre-loading {full_name}: {repr(e)}") from e + d = module.__dict__ + if 'fullname' not in d: + continue + d['display_name'] = d['fullname'] gui_good = self.gui_name in d.get('available_for', []) if not gui_good: continue @@ -117,8 +112,20 @@ class Plugins(DaemonThread): details = d.get('registers_keystore') if details: self.register_keystore(name, gui_good, details) - self.descriptions[name] = d - if not d.get('requires_wallet_type') and self.config.get('use_' + name): + if d.get('requires_wallet_type'): + # trustedcoin will not be added to list + continue + if name in self.internal_plugin_metadata: + _logger.info(f"Found the following plugin modules: {iter_modules=}") + raise Exception(f"duplicate plugins? for {name=}") + self.internal_plugin_metadata[name] = d + + def load_plugins(self): + self.load_internal_plugins() + + def load_internal_plugins(self): + for name, d in self.internal_plugin_metadata.items(): + if self.config.get('enable_plugin_' + name) is True: try: self.load_plugin(name) except BaseException as e: @@ -156,14 +163,14 @@ class Plugins(DaemonThread): self.remove_jobs(plugin.thread_jobs()) def enable(self, name: str) -> 'BasePlugin': - self.config.set_key('use_' + name, True, save=True) + self.config.set_key('enable_plugin_' + name, True, save=True) p = self.get(name) if p: return p return self.load_plugin(name) def disable(self, name: str) -> None: - self.config.set_key('use_' + name, False, save=True) + self.config.set_key('enable_plugin_' + name, False, save=True) p = self.get(name) if not p: return @@ -173,12 +180,7 @@ class Plugins(DaemonThread): @classmethod def is_plugin_enabler_config_key(cls, key: str) -> bool: - if not key.startswith('use_'): - return False - # note: the 'use_' prefix is not sufficient to check, there are - # non-plugin-related config keys that also have it... hence: - name = key[4:] - return name in cls.find_all_plugins() + return key.startswith('enable_plugin_') def toggle(self, name: str) -> Optional['BasePlugin']: p = self.get(name) @@ -204,7 +206,7 @@ class Plugins(DaemonThread): if gui_good: try: p = self.get_plugin(name) - if p.is_enabled(): + if p.is_available(): out.append(HardwarePluginToScan(name=name, description=details[2], plugin=p, @@ -276,7 +278,7 @@ class BasePlugin(Logger): self.parent = parent # type: Plugins # The plugins object self.name = name self.config = config - self.wallet = None + self.wallet = None # fixme: this field should not exist Logger.__init__(self) # add self to hooks for k in dir(self): @@ -313,7 +315,7 @@ class BasePlugin(Logger): return [] def is_enabled(self): - return self.is_available() and self.config.get('use_'+self.name) is True + return self.is_available() and self.config.get('enable_plugin_'+self.name) is True def is_available(self): return True diff --git a/electrum/plugins/swapserver/__init__.py b/electrum/plugins/swapserver/__init__.py index 1a7d3547b..ff236f6ef 100644 --- a/electrum/plugins/swapserver/__init__.py +++ b/electrum/plugins/swapserver/__init__.py @@ -6,7 +6,7 @@ Submarine swap server for an Electrum daemon. Example setup: - electrum -o setconfig use_swapserver True + electrum -o setconfig enable_plugin_swapserver True electrum -o setconfig swapserver_port 5455 electrum daemon -v diff --git a/tests/regtest.py b/tests/regtest.py index 68327fd4a..b7a94d64b 100644 --- a/tests/regtest.py +++ b/tests/regtest.py @@ -82,7 +82,7 @@ class TestLightningSwapserver(TestLightning): }, 'bob': { 'lightning_listen': 'localhost:9735', - 'use_swapserver': 'true', + 'enable_plugin_swapserver': 'true', } }