Plugins call with cmd_only:
- pass temporary config to Plugins - load only enabled plugins - parse the command line again after plugins are loaded
This commit is contained in:
@@ -1594,18 +1594,6 @@ def plugin_command(s, plugin_name = None):
|
||||
return func_wrapper
|
||||
return decorator
|
||||
|
||||
def remove_disabled_plugin_commands(config: SimpleConfig, plugins_with_commands: set[str]):
|
||||
"""Removes registered commands of plugins that are not enabled in the config."""
|
||||
global known_commands
|
||||
global plugin_commands
|
||||
assert len(known_commands) > 0, "known_commands should not be empty, called too early?"
|
||||
for plugin_name in plugins_with_commands:
|
||||
if not config.get(f'enable_plugin_{plugin_name}'):
|
||||
registered_commands: set[str] = plugin_commands[plugin_name]
|
||||
assert len(registered_commands) > 0, "plugin command registered with the invalid plugin name"
|
||||
for command_name in registered_commands:
|
||||
del known_commands[command_name]
|
||||
delattr(Commands, command_name)
|
||||
|
||||
def eval_bool(x: str) -> bool:
|
||||
if x == 'false': return False
|
||||
@@ -1890,5 +1878,6 @@ def get_parser():
|
||||
group.add_argument(k, nargs='?', help=v)
|
||||
|
||||
# 'gui' is the default command
|
||||
# note: set_default_subparser modifies sys.argv
|
||||
parser.set_default_subparser('gui')
|
||||
return parser
|
||||
|
||||
@@ -65,19 +65,20 @@ class Plugins(DaemonThread):
|
||||
pkgpath = os.path.dirname(plugins.__file__)
|
||||
|
||||
@profiler
|
||||
def __init__(self, config: SimpleConfig = None, gui_name = None, cmd_only: bool = False):
|
||||
def __init__(self, config: SimpleConfig, gui_name = None, cmd_only: bool = False):
|
||||
self.config = config
|
||||
self.cmd_only = cmd_only # type: bool
|
||||
self.internal_plugin_metadata = {}
|
||||
self.external_plugin_metadata = {}
|
||||
self.loaded_command_modules = set() # type: set[str]
|
||||
if cmd_only:
|
||||
# only import the command modules of plugins
|
||||
Logger.__init__(self)
|
||||
self.find_plugins()
|
||||
return
|
||||
DaemonThread.__init__(self)
|
||||
self.device_manager = DeviceMgr(config)
|
||||
self.name = 'Plugins' # set name of thread
|
||||
self.config = config
|
||||
self.hw_wallets = {}
|
||||
self.plugins = {} # type: Dict[str, BasePlugin]
|
||||
self.gui_name = gui_name
|
||||
@@ -86,15 +87,6 @@ class Plugins(DaemonThread):
|
||||
self.add_jobs(self.device_manager.thread_jobs())
|
||||
self.start()
|
||||
|
||||
def __getattr__(self, item):
|
||||
# to prevent accessing of a cmd_only instance of this class
|
||||
if self.cmd_only:
|
||||
if item == 'logger':
|
||||
# if something tries to access logger and it is not initialized it gets initialized here
|
||||
Logger.__init__(self)
|
||||
return self.logger
|
||||
raise Exception(f"This instance of Plugins is only for command importing, cannot access {item}")
|
||||
|
||||
@property
|
||||
def descriptions(self):
|
||||
return dict(list(self.internal_plugin_metadata.items()) + list(self.external_plugin_metadata.items()))
|
||||
@@ -108,6 +100,8 @@ class Plugins(DaemonThread):
|
||||
# we exclude the ones packaged as *code*, here:
|
||||
if loader.__class__.__qualname__ == "PyiFrozenImporter":
|
||||
continue
|
||||
if self.cmd_only and self.config.get('enable_plugin_' + name) is not True:
|
||||
continue
|
||||
full_name = f'electrum.plugins.{name}' + ('.commands' if self.cmd_only else '')
|
||||
spec = importlib.util.find_spec(full_name)
|
||||
if spec is None:
|
||||
@@ -233,6 +227,8 @@ class Plugins(DaemonThread):
|
||||
spec = zipfile.find_spec(name)
|
||||
module = self.exec_module_from_spec(spec, module_path)
|
||||
if self.cmd_only:
|
||||
if self.config.get('enable_plugin_' + name) is not True:
|
||||
continue
|
||||
spec2 = importlib.util.find_spec(module_path + '.commands')
|
||||
if spec2 is None: # no commands module in this plugin
|
||||
continue
|
||||
|
||||
25
run_electrum
25
run_electrum
@@ -103,7 +103,7 @@ from electrum.storage import WalletStorage
|
||||
from electrum.util import print_msg, print_stderr, json_encode, json_decode, UserCancelled
|
||||
from electrum.util import InvalidPassword
|
||||
from electrum.plugin import Plugins
|
||||
from electrum.commands import get_parser, known_commands, Commands, config_variables, remove_disabled_plugin_commands
|
||||
from electrum.commands import get_parser, known_commands, Commands, config_variables
|
||||
from electrum import daemon
|
||||
from electrum import keystore
|
||||
from electrum.util import create_and_start_event_loop, UserFacingException, JsonRPCError
|
||||
@@ -340,8 +340,6 @@ def main():
|
||||
sys.argv[i] = input("Enter argument:")
|
||||
elif arg == ':':
|
||||
sys.argv[i] = prompt_password('Enter argument (will not echo):', confirm=False)
|
||||
# load (only) the commands modules of plugins so their commands are registered
|
||||
plugin_commands = Plugins(cmd_only=True)
|
||||
|
||||
# config is an object passed to the various constructors (wallet, interface, gui)
|
||||
if is_android:
|
||||
@@ -360,15 +358,23 @@ def main():
|
||||
# ~hack for easier regtest builds. pkgname subject to change.
|
||||
config_options['regtest'] = True
|
||||
else:
|
||||
# save sys args for next parser
|
||||
saved_sys_argv = sys.argv[:]
|
||||
# disable help, the next parser will display it
|
||||
if '-h' in sys.argv:
|
||||
sys.argv.remove('-h')
|
||||
# parse first without plugins
|
||||
config_options = parse_command_line()
|
||||
tmp_config = SimpleConfig(config_options)
|
||||
# load (only) the commands modules of plugins so their commands are registered
|
||||
plugin_commands = Plugins(tmp_config, cmd_only=True)
|
||||
# re-parse command line
|
||||
sys.argv = saved_sys_argv[:]
|
||||
config_options = parse_command_line()
|
||||
|
||||
config = SimpleConfig(config_options)
|
||||
cmdname = config.get('cmd')
|
||||
|
||||
# now that we know which plugins are enabled we can remove commands of disabled plugins again
|
||||
# enabled plugins depend on the datadir which is parsed by argparse, so they can only be unloaded afterwards
|
||||
remove_disabled_plugin_commands(config, plugin_commands.loaded_command_modules)
|
||||
|
||||
# set language as early as possible
|
||||
# Note: we are already too late for strings that are declared in the global scope
|
||||
# of an already imported module. However, the GUI and the plugins at least have
|
||||
@@ -500,10 +506,7 @@ def handle_cmd(*, cmdname: str, config: 'SimpleConfig', config_options: dict):
|
||||
else:
|
||||
# command line
|
||||
configure_logging(config, log_to_file=False) # don't spam logfiles for each client-side RPC, but support "-v"
|
||||
cmd = known_commands.get(cmdname)
|
||||
if not cmd:
|
||||
print_stderr("unknown command:", cmdname)
|
||||
sys_exit(1)
|
||||
cmd = known_commands[cmdname]
|
||||
wallet_path = config.get_wallet_path()
|
||||
if not config.NETWORK_OFFLINE:
|
||||
init_cmdline(config_options, wallet_path, rpcserver=True, config=config)
|
||||
|
||||
Reference in New Issue
Block a user