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 SimpleConfig, Network, Wallet, WalletStorage
|
||||
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
|
||||
|
||||
|
||||
|
@ -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')
|
||||
if gui_name in ['lite', 'classic']:
|
||||
gui_name = 'qt'
|
||||
gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui'])
|
||||
gui = gui.ElectrumGui(config, network)
|
||||
gui = gui.ElectrumGui(config, network, plugins)
|
||||
return gui
|
||||
|
||||
|
||||
|
@ -493,9 +493,10 @@ if __name__ == '__main__':
|
|||
sys.exit(1)
|
||||
|
||||
# initialize plugins.
|
||||
plugins = None
|
||||
if not is_android:
|
||||
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
|
||||
if cmd_name not in ['gui', 'daemon']:
|
||||
|
@ -527,7 +528,7 @@ if __name__ == '__main__':
|
|||
network.start()
|
||||
server = NetworkServer(config, network)
|
||||
server.start()
|
||||
server.gui = init_gui(config, network)
|
||||
server.gui = init_gui(config, network, plugins)
|
||||
server.gui.main()
|
||||
elif cmd_name == 'daemon':
|
||||
subcommand = config.get('subcommand')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# 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
|
||||
|
||||
# Notifications about network events are sent to the GUI by using network.register_callback()
|
||||
|
|
|
@ -904,7 +904,7 @@ config = None
|
|||
|
||||
class ElectrumGui:
|
||||
|
||||
def __init__(self, _config, _network):
|
||||
def __init__(self, _config, _network, plugins):
|
||||
global wallet, network, contacts, config
|
||||
network = _network
|
||||
config = _config
|
||||
|
@ -1018,5 +1018,3 @@ class ElectrumGui:
|
|||
else:
|
||||
seed = modal_input('Mnemonic', 'please enter your code')
|
||||
return str(seed)
|
||||
|
||||
|
||||
|
|
|
@ -1284,7 +1284,7 @@ class ElectrumWindow:
|
|||
|
||||
class ElectrumGui():
|
||||
|
||||
def __init__(self, config, network):
|
||||
def __init__(self, config, network, plugins):
|
||||
self.network = network
|
||||
self.config = config
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class RequestHandler(SimpleJSONRPCRequestHandler):
|
|||
|
||||
class ElectrumGui:
|
||||
|
||||
def __init__(self, config, network):
|
||||
def __init__(self, config, network, plugins):
|
||||
self.network = network
|
||||
self.config = config
|
||||
storage = WalletStorage(self.config.get_wallet_path())
|
||||
|
|
|
@ -58,10 +58,11 @@ class OpenFileEventFilter(QObject):
|
|||
|
||||
class ElectrumGui:
|
||||
|
||||
def __init__(self, config, network):
|
||||
def __init__(self, config, network, plugins):
|
||||
set_language(config.get('language'))
|
||||
self.network = network
|
||||
self.config = config
|
||||
self.plugins = plugins
|
||||
self.windows = []
|
||||
self.efilter = OpenFileEventFilter(self.windows)
|
||||
self.app = QApplication(sys.argv)
|
||||
|
|
|
@ -2811,12 +2811,12 @@ class ElectrumWindow(QMainWindow):
|
|||
|
||||
|
||||
def plugins_dialog(self):
|
||||
from electrum.plugins import plugins, descriptions, is_available, loader
|
||||
|
||||
self.pluginsdialog = d = QDialog(self)
|
||||
d.setWindowTitle(_('Electrum Plugins'))
|
||||
d.setModal(1)
|
||||
|
||||
plugins = self.gui_object.plugins
|
||||
|
||||
vbox = QVBoxLayout(d)
|
||||
|
||||
# plugins
|
||||
|
@ -2828,40 +2828,34 @@ class ElectrumWindow(QMainWindow):
|
|||
|
||||
w = QWidget()
|
||||
scroll.setWidget(w)
|
||||
w.setMinimumHeight(len(plugins)*35)
|
||||
w.setMinimumHeight(plugins.count() * 35)
|
||||
|
||||
grid = QGridLayout()
|
||||
grid.setColumnStretch(0,1)
|
||||
w.setLayout(grid)
|
||||
|
||||
def do_toggle(cb, name, w):
|
||||
p = plugins.get(name)
|
||||
p = plugins.toggle_enabled(self.config, name)
|
||||
if p:
|
||||
p.disable()
|
||||
p.close()
|
||||
plugins.pop(name)
|
||||
else:
|
||||
module = loader(name)
|
||||
plugins[name] = p = module.Plugin(self.config, name)
|
||||
p.enable()
|
||||
# FIXME: this is hosed for multiple windows
|
||||
p.wallet = self.wallet
|
||||
p.load_wallet(self.wallet, self)
|
||||
p.init_qt(self.gui_object)
|
||||
r = p.is_enabled()
|
||||
cb.setChecked(r)
|
||||
if w: w.setEnabled(r)
|
||||
enabled = p is not None
|
||||
cb.setChecked(enabled)
|
||||
if w: w.setEnabled(enabled)
|
||||
|
||||
def mk_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']
|
||||
p = plugins.get(name)
|
||||
if descr.get('registers_wallet_type'):
|
||||
continue
|
||||
try:
|
||||
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())
|
||||
grid.addWidget(cb, i, 0)
|
||||
if p and p.requires_settings():
|
||||
|
|
|
@ -12,7 +12,7 @@ import sys, getpass, datetime
|
|||
|
||||
class ElectrumGui:
|
||||
|
||||
def __init__(self, config, network):
|
||||
def __init__(self, config, network, plugins):
|
||||
self.network = network
|
||||
self.config = config
|
||||
storage = WalletStorage(config.get_wallet_path())
|
||||
|
|
|
@ -12,7 +12,7 @@ import tty, sys
|
|||
|
||||
class ElectrumGui:
|
||||
|
||||
def __init__(self, config, network):
|
||||
def __init__(self, config, network, plugins):
|
||||
|
||||
self.config = config
|
||||
self.network = network
|
||||
|
@ -486,4 +486,3 @@ class ElectrumGui:
|
|||
break
|
||||
|
||||
return out
|
||||
|
||||
|
|
150
lib/plugins.py
150
lib/plugins.py
|
@ -26,70 +26,93 @@ from util import *
|
|||
from i18n import _
|
||||
from util import print_error, profiler
|
||||
|
||||
plugins = {}
|
||||
descriptions = []
|
||||
loader = None
|
||||
class Plugins:
|
||||
|
||||
def is_available(name, w):
|
||||
for d in descriptions:
|
||||
if d.get('name') == name:
|
||||
break
|
||||
else:
|
||||
return False
|
||||
deps = d.get('requires', [])
|
||||
for dep, s in deps:
|
||||
@profiler
|
||||
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:
|
||||
__import__(dep)
|
||||
except ImportError:
|
||||
return False
|
||||
wallet_types = d.get('requires_wallet_type')
|
||||
if wallet_types:
|
||||
if w.wallet_type not in wallet_types:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def plugin_loader(config, name):
|
||||
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
|
||||
x += (lambda: plugin_loader(config, name),)
|
||||
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)
|
||||
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:
|
||||
break
|
||||
else:
|
||||
return False
|
||||
deps = d.get('requires', [])
|
||||
for dep, s in deps:
|
||||
try:
|
||||
__import__(dep)
|
||||
except ImportError:
|
||||
return False
|
||||
wallet_types = d.get('requires_wallet_type')
|
||||
if wallet_types:
|
||||
if w.wallet_type not in wallet_types:
|
||||
return False
|
||||
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 register_wallet_type(self, name, x):
|
||||
import wallet
|
||||
x += (lambda: self.wallet_plugin_loader(config, name),)
|
||||
wallet.wallet_types.append(x)
|
||||
|
||||
hook_names = set()
|
||||
hooks = {}
|
||||
|
@ -157,14 +180,6 @@ class BasePlugin:
|
|||
def requires_settings(self):
|
||||
return False
|
||||
|
||||
def enable(self):
|
||||
self.set_enabled(True)
|
||||
return True
|
||||
|
||||
def disable(self):
|
||||
self.set_enabled(False)
|
||||
return True
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet, window): pass
|
||||
|
||||
|
@ -179,8 +194,5 @@ class BasePlugin:
|
|||
def is_available(self):
|
||||
return True
|
||||
|
||||
def set_enabled(self, enabled):
|
||||
self.config.set_key('use_'+self.name, enabled, True)
|
||||
|
||||
def settings_dialog(self):
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue