Create a Plugins class
Encapsulates plugin logic and removes global variable ugliness.
This commit is contained in:
parent
2c67de8f64
commit
49797c3094
11
electrum
11
electrum
|
@ -79,7 +79,7 @@ if is_bundle or is_local or is_android:
|
||||||
from electrum import util
|
from electrum import util
|
||||||
from electrum import SimpleConfig, Network, Wallet, WalletStorage
|
from electrum import SimpleConfig, Network, Wallet, WalletStorage
|
||||||
from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword
|
from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword
|
||||||
from electrum.plugins import init_plugins, run_hook, always_hook
|
from electrum.plugins import Plugins, run_hook, always_hook
|
||||||
from electrum.commands import get_parser, known_commands, Commands, config_variables
|
from electrum.commands import get_parser, known_commands, Commands, config_variables
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,12 +97,12 @@ def prompt_password(prompt, confirm=True):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def init_gui(config, network):
|
def init_gui(config, network, plugins):
|
||||||
gui_name = config.get('gui', 'qt')
|
gui_name = config.get('gui', 'qt')
|
||||||
if gui_name in ['lite', 'classic']:
|
if gui_name in ['lite', 'classic']:
|
||||||
gui_name = 'qt'
|
gui_name = 'qt'
|
||||||
gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui'])
|
gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui'])
|
||||||
gui = gui.ElectrumGui(config, network)
|
gui = gui.ElectrumGui(config, network, plugins)
|
||||||
return gui
|
return gui
|
||||||
|
|
||||||
|
|
||||||
|
@ -493,9 +493,10 @@ if __name__ == '__main__':
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# initialize plugins.
|
# initialize plugins.
|
||||||
|
plugins = None
|
||||||
if not is_android:
|
if not is_android:
|
||||||
gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline'
|
gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline'
|
||||||
init_plugins(config, is_bundle or is_local or is_android, gui_name)
|
plugins = Plugins(config, is_bundle or is_local or is_android, gui_name)
|
||||||
|
|
||||||
# get password if needed
|
# get password if needed
|
||||||
if cmd_name not in ['gui', 'daemon']:
|
if cmd_name not in ['gui', 'daemon']:
|
||||||
|
@ -527,7 +528,7 @@ if __name__ == '__main__':
|
||||||
network.start()
|
network.start()
|
||||||
server = NetworkServer(config, network)
|
server = NetworkServer(config, network)
|
||||||
server.start()
|
server.start()
|
||||||
server.gui = init_gui(config, network)
|
server.gui = init_gui(config, network, plugins)
|
||||||
server.gui.main()
|
server.gui.main()
|
||||||
elif cmd_name == 'daemon':
|
elif cmd_name == 'daemon':
|
||||||
subcommand = config.get('subcommand')
|
subcommand = config.get('subcommand')
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# To create a new GUI, please add its code to this directory.
|
# To create a new GUI, please add its code to this directory.
|
||||||
# Two objects must be passed to the ElectrumGui: config and network
|
# Three objects are passed to the ElectrumGui: config, network and plugins
|
||||||
# The Wallet object is instanciated by the GUI
|
# The Wallet object is instanciated by the GUI
|
||||||
|
|
||||||
# Notifications about network events are sent to the GUI by using network.register_callback()
|
# Notifications about network events are sent to the GUI by using network.register_callback()
|
||||||
|
|
|
@ -904,7 +904,7 @@ config = None
|
||||||
|
|
||||||
class ElectrumGui:
|
class ElectrumGui:
|
||||||
|
|
||||||
def __init__(self, _config, _network):
|
def __init__(self, _config, _network, plugins):
|
||||||
global wallet, network, contacts, config
|
global wallet, network, contacts, config
|
||||||
network = _network
|
network = _network
|
||||||
config = _config
|
config = _config
|
||||||
|
@ -1018,5 +1018,3 @@ class ElectrumGui:
|
||||||
else:
|
else:
|
||||||
seed = modal_input('Mnemonic', 'please enter your code')
|
seed = modal_input('Mnemonic', 'please enter your code')
|
||||||
return str(seed)
|
return str(seed)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1284,7 +1284,7 @@ class ElectrumWindow:
|
||||||
|
|
||||||
class ElectrumGui():
|
class ElectrumGui():
|
||||||
|
|
||||||
def __init__(self, config, network):
|
def __init__(self, config, network, plugins):
|
||||||
self.network = network
|
self.network = network
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class RequestHandler(SimpleJSONRPCRequestHandler):
|
||||||
|
|
||||||
class ElectrumGui:
|
class ElectrumGui:
|
||||||
|
|
||||||
def __init__(self, config, network):
|
def __init__(self, config, network, plugins):
|
||||||
self.network = network
|
self.network = network
|
||||||
self.config = config
|
self.config = config
|
||||||
storage = WalletStorage(self.config.get_wallet_path())
|
storage = WalletStorage(self.config.get_wallet_path())
|
||||||
|
|
|
@ -58,10 +58,11 @@ class OpenFileEventFilter(QObject):
|
||||||
|
|
||||||
class ElectrumGui:
|
class ElectrumGui:
|
||||||
|
|
||||||
def __init__(self, config, network):
|
def __init__(self, config, network, plugins):
|
||||||
set_language(config.get('language'))
|
set_language(config.get('language'))
|
||||||
self.network = network
|
self.network = network
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.plugins = plugins
|
||||||
self.windows = []
|
self.windows = []
|
||||||
self.efilter = OpenFileEventFilter(self.windows)
|
self.efilter = OpenFileEventFilter(self.windows)
|
||||||
self.app = QApplication(sys.argv)
|
self.app = QApplication(sys.argv)
|
||||||
|
|
|
@ -2811,12 +2811,12 @@ class ElectrumWindow(QMainWindow):
|
||||||
|
|
||||||
|
|
||||||
def plugins_dialog(self):
|
def plugins_dialog(self):
|
||||||
from electrum.plugins import plugins, descriptions, is_available, loader
|
|
||||||
|
|
||||||
self.pluginsdialog = d = QDialog(self)
|
self.pluginsdialog = d = QDialog(self)
|
||||||
d.setWindowTitle(_('Electrum Plugins'))
|
d.setWindowTitle(_('Electrum Plugins'))
|
||||||
d.setModal(1)
|
d.setModal(1)
|
||||||
|
|
||||||
|
plugins = self.gui_object.plugins
|
||||||
|
|
||||||
vbox = QVBoxLayout(d)
|
vbox = QVBoxLayout(d)
|
||||||
|
|
||||||
# plugins
|
# plugins
|
||||||
|
@ -2828,40 +2828,34 @@ class ElectrumWindow(QMainWindow):
|
||||||
|
|
||||||
w = QWidget()
|
w = QWidget()
|
||||||
scroll.setWidget(w)
|
scroll.setWidget(w)
|
||||||
w.setMinimumHeight(len(plugins)*35)
|
w.setMinimumHeight(plugins.count() * 35)
|
||||||
|
|
||||||
grid = QGridLayout()
|
grid = QGridLayout()
|
||||||
grid.setColumnStretch(0,1)
|
grid.setColumnStretch(0,1)
|
||||||
w.setLayout(grid)
|
w.setLayout(grid)
|
||||||
|
|
||||||
def do_toggle(cb, name, w):
|
def do_toggle(cb, name, w):
|
||||||
p = plugins.get(name)
|
p = plugins.toggle_enabled(self.config, name)
|
||||||
if p:
|
if p:
|
||||||
p.disable()
|
# FIXME: this is hosed for multiple windows
|
||||||
p.close()
|
|
||||||
plugins.pop(name)
|
|
||||||
else:
|
|
||||||
module = loader(name)
|
|
||||||
plugins[name] = p = module.Plugin(self.config, name)
|
|
||||||
p.enable()
|
|
||||||
p.wallet = self.wallet
|
p.wallet = self.wallet
|
||||||
p.load_wallet(self.wallet, self)
|
p.load_wallet(self.wallet, self)
|
||||||
p.init_qt(self.gui_object)
|
p.init_qt(self.gui_object)
|
||||||
r = p.is_enabled()
|
enabled = p is not None
|
||||||
cb.setChecked(r)
|
cb.setChecked(enabled)
|
||||||
if w: w.setEnabled(r)
|
if w: w.setEnabled(enabled)
|
||||||
|
|
||||||
def mk_toggle(cb, name, w):
|
def mk_toggle(cb, name, w):
|
||||||
return lambda: do_toggle(cb, name, w)
|
return lambda: do_toggle(cb, name, w)
|
||||||
|
|
||||||
for i, descr in enumerate(descriptions):
|
for i, descr in enumerate(plugins.descriptions):
|
||||||
name = descr['name']
|
name = descr['name']
|
||||||
p = plugins.get(name)
|
p = plugins.get(name)
|
||||||
if descr.get('registers_wallet_type'):
|
if descr.get('registers_wallet_type'):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
cb = QCheckBox(descr['fullname'])
|
cb = QCheckBox(descr['fullname'])
|
||||||
cb.setEnabled(is_available(name, self.wallet))
|
cb.setEnabled(plugins.is_available(name, self.wallet))
|
||||||
cb.setChecked(p is not None and p.is_enabled())
|
cb.setChecked(p is not None and p.is_enabled())
|
||||||
grid.addWidget(cb, i, 0)
|
grid.addWidget(cb, i, 0)
|
||||||
if p and p.requires_settings():
|
if p and p.requires_settings():
|
||||||
|
|
|
@ -12,7 +12,7 @@ import sys, getpass, datetime
|
||||||
|
|
||||||
class ElectrumGui:
|
class ElectrumGui:
|
||||||
|
|
||||||
def __init__(self, config, network):
|
def __init__(self, config, network, plugins):
|
||||||
self.network = network
|
self.network = network
|
||||||
self.config = config
|
self.config = config
|
||||||
storage = WalletStorage(config.get_wallet_path())
|
storage = WalletStorage(config.get_wallet_path())
|
||||||
|
|
|
@ -12,7 +12,7 @@ import tty, sys
|
||||||
|
|
||||||
class ElectrumGui:
|
class ElectrumGui:
|
||||||
|
|
||||||
def __init__(self, config, network):
|
def __init__(self, config, network, plugins):
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.network = network
|
self.network = network
|
||||||
|
@ -486,4 +486,3 @@ class ElectrumGui:
|
||||||
break
|
break
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
122
lib/plugins.py
122
lib/plugins.py
|
@ -26,12 +26,68 @@ from util import *
|
||||||
from i18n import _
|
from i18n import _
|
||||||
from util import print_error, profiler
|
from util import print_error, profiler
|
||||||
|
|
||||||
plugins = {}
|
class Plugins:
|
||||||
descriptions = []
|
|
||||||
loader = None
|
|
||||||
|
|
||||||
def is_available(name, w):
|
@profiler
|
||||||
for d in descriptions:
|
def __init__(self, config, is_local, gui_name):
|
||||||
|
if is_local:
|
||||||
|
find = imp.find_module('plugins')
|
||||||
|
plugins = imp.load_module('electrum_plugins', *find)
|
||||||
|
self.pathname = find[1]
|
||||||
|
else:
|
||||||
|
plugins = __import__('electrum_plugins')
|
||||||
|
self.pathname = None
|
||||||
|
|
||||||
|
self.plugins = {}
|
||||||
|
self.descriptions = plugins.descriptions
|
||||||
|
for item in self.descriptions:
|
||||||
|
name = item['name']
|
||||||
|
if gui_name not in item.get('available_for', []):
|
||||||
|
continue
|
||||||
|
x = item.get('registers_wallet_type')
|
||||||
|
if x:
|
||||||
|
self.register_wallet_type(name, x)
|
||||||
|
if config.get('use_' + name):
|
||||||
|
self.load_plugin(config, name)
|
||||||
|
|
||||||
|
def print_error(self, *msg):
|
||||||
|
print_error("[%s]" % self.__class__.__name__, *msg)
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
return self.plugins.get(name)
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return len(self.plugins)
|
||||||
|
|
||||||
|
def load_plugin(self, config, name):
|
||||||
|
full_name = 'electrum_plugins.' + name
|
||||||
|
try:
|
||||||
|
if self.pathname: # local
|
||||||
|
path = os.path.join(self.pathname, name + '.py')
|
||||||
|
p = imp.load_source(full_name, path)
|
||||||
|
else:
|
||||||
|
p = __import__(full_name, fromlist=['electrum_plugins'])
|
||||||
|
plugin = p.Plugin(config, name)
|
||||||
|
self.plugins[name] = plugin
|
||||||
|
self.print_error("loaded", name)
|
||||||
|
return plugin
|
||||||
|
except Exception:
|
||||||
|
print_msg(_("Error: cannot initialize plugin"), name)
|
||||||
|
traceback.print_exc(file=sys.stdout)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def toggle_enabled(self, config, name):
|
||||||
|
p = self.get(name)
|
||||||
|
config.set_key('use_' + name, p is None, True)
|
||||||
|
if p:
|
||||||
|
self.plugins.pop(name)
|
||||||
|
p.close()
|
||||||
|
self.print_error("closed", name)
|
||||||
|
return None
|
||||||
|
return self.load_plugin(config, name)
|
||||||
|
|
||||||
|
def is_available(self, name, w):
|
||||||
|
for d in self.descriptions:
|
||||||
if d.get('name') == name:
|
if d.get('name') == name:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -48,49 +104,16 @@ def is_available(name, w):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def wallet_plugin_loader(self, config, name):
|
||||||
|
if self.plugins.get(name) is None:
|
||||||
|
self.load_plugin(config, name)
|
||||||
|
return self.plugins[name]
|
||||||
|
|
||||||
def plugin_loader(config, name):
|
def register_wallet_type(self, name, x):
|
||||||
global plugins
|
|
||||||
if plugins.get(name) is None:
|
|
||||||
print_error(_("Loading plugin by constructor:"), name)
|
|
||||||
p = loader(name)
|
|
||||||
plugins[name] = p.Plugin(config, name)
|
|
||||||
return plugins[name]
|
|
||||||
|
|
||||||
@profiler
|
|
||||||
def init_plugins(config, is_local, gui_name):
|
|
||||||
global plugins, descriptions, loader
|
|
||||||
if is_local:
|
|
||||||
fp, pathname, description = imp.find_module('plugins')
|
|
||||||
electrum_plugins = imp.load_module('electrum_plugins', fp, pathname, description)
|
|
||||||
loader = lambda name: imp.load_source('electrum_plugins.' + name, os.path.join(pathname, name + '.py'))
|
|
||||||
else:
|
|
||||||
electrum_plugins = __import__('electrum_plugins')
|
|
||||||
loader = lambda name: __import__('electrum_plugins.' + name, fromlist=['electrum_plugins'])
|
|
||||||
|
|
||||||
def register_wallet_type(name, x):
|
|
||||||
import wallet
|
import wallet
|
||||||
x += (lambda: plugin_loader(config, name),)
|
x += (lambda: self.wallet_plugin_loader(config, name),)
|
||||||
wallet.wallet_types.append(x)
|
wallet.wallet_types.append(x)
|
||||||
|
|
||||||
descriptions = electrum_plugins.descriptions
|
|
||||||
for item in descriptions:
|
|
||||||
name = item['name']
|
|
||||||
if gui_name not in item.get('available_for', []):
|
|
||||||
continue
|
|
||||||
x = item.get('registers_wallet_type')
|
|
||||||
if x:
|
|
||||||
register_wallet_type(name, x)
|
|
||||||
if not config.get('use_' + name):
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
p = loader(name)
|
|
||||||
plugins[name] = p.Plugin(config, name)
|
|
||||||
except Exception:
|
|
||||||
print_msg(_("Error: cannot initialize plugin"), name)
|
|
||||||
traceback.print_exc(file=sys.stdout)
|
|
||||||
|
|
||||||
|
|
||||||
hook_names = set()
|
hook_names = set()
|
||||||
hooks = {}
|
hooks = {}
|
||||||
|
|
||||||
|
@ -157,14 +180,6 @@ class BasePlugin:
|
||||||
def requires_settings(self):
|
def requires_settings(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def enable(self):
|
|
||||||
self.set_enabled(True)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def disable(self):
|
|
||||||
self.set_enabled(False)
|
|
||||||
return True
|
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def load_wallet(self, wallet, window): pass
|
def load_wallet(self, wallet, window): pass
|
||||||
|
|
||||||
|
@ -179,8 +194,5 @@ class BasePlugin:
|
||||||
def is_available(self):
|
def is_available(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_enabled(self, enabled):
|
|
||||||
self.config.set_key('use_'+self.name, enabled, True)
|
|
||||||
|
|
||||||
def settings_dialog(self):
|
def settings_dialog(self):
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in New Issue