Create a Plugins class

Encapsulates plugin logic and removes global variable ugliness.
This commit is contained in:
Neil Booth 2015-09-03 12:02:03 +09:00
parent 2c67de8f64
commit 49797c3094
10 changed files with 208 additions and 203 deletions

View File

@ -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')

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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())

View File

@ -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)

View File

@ -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():

View File

@ -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())

View File

@ -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

View File

@ -26,12 +26,68 @@ 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:
@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:
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:
@ -48,49 +104,16 @@ def is_available(name, w):
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 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):
def register_wallet_type(self, name, x):
import wallet
x += (lambda: plugin_loader(config, name),)
x += (lambda: self.wallet_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)
except Exception:
print_msg(_("Error: cannot initialize plugin"), name)
traceback.print_exc(file=sys.stdout)
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