You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
5.9 KiB
205 lines
5.9 KiB
#!/usr/bin/env python |
|
# |
|
# Electrum - lightweight Bitcoin client |
|
# Copyright (C) 2015 Thomas Voegtlin |
|
# |
|
# This program is free software: you can redistribute it and/or modify |
|
# it under the terms of the GNU General Public License as published by |
|
# the Free Software Foundation, either version 3 of the License, or |
|
# (at your option) any later version. |
|
# |
|
# This program is distributed in the hope that it will be useful, |
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
# GNU General Public License for more details. |
|
# |
|
# You should have received a copy of the GNU General Public License |
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
import traceback |
|
import sys |
|
import os |
|
import imp |
|
import pkgutil |
|
import time |
|
|
|
from util import * |
|
from i18n import _ |
|
from util import profiler, PrintError, DaemonThread |
|
|
|
class Plugins(DaemonThread): |
|
|
|
@profiler |
|
def __init__(self, config, is_local, gui_name): |
|
DaemonThread.__init__(self) |
|
if is_local: |
|
find = imp.find_module('plugins') |
|
plugins = imp.load_module('electrum_plugins', *find) |
|
else: |
|
plugins = __import__('electrum_plugins') |
|
self.pkgpath = os.path.dirname(plugins.__file__) |
|
self.plugins = {} |
|
self.gui_name = gui_name |
|
self.descriptions = [] |
|
for loader, name, ispkg in pkgutil.iter_modules([self.pkgpath]): |
|
m = loader.find_module(name).load_module(name) |
|
d = m.__dict__ |
|
if gui_name not in d.get('available_for', []): |
|
continue |
|
self.descriptions.append(d) |
|
x = d.get('registers_wallet_type') |
|
if x: |
|
self.register_wallet_type(config, name, x) |
|
if not d.get('requires_wallet_type') and config.get('use_' + name): |
|
self.load_plugin(config, name) |
|
|
|
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 + '.' + self.gui_name |
|
try: |
|
p = pkgutil.find_loader(full_name).load_module(full_name) |
|
plugin = p.Plugin(self, config, name) |
|
self.add_jobs(plugin.thread_jobs()) |
|
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 close_plugin(self, plugin): |
|
self.remove_jobs(plugin.thread_jobs()) |
|
|
|
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, config, name, x): |
|
import wallet |
|
x += (lambda: self.wallet_plugin_loader(config, name),) |
|
wallet.wallet_types.append(x) |
|
|
|
def run(self): |
|
jobs = [job for plugin in self.plugins.values() |
|
for job in plugin.thread_jobs()] |
|
self.add_jobs(jobs) |
|
while self.is_running(): |
|
time.sleep(0.1) |
|
self.run_jobs() |
|
self.print_error("stopped") |
|
|
|
|
|
hook_names = set() |
|
hooks = {} |
|
|
|
def hook(func): |
|
hook_names.add(func.func_name) |
|
return func |
|
|
|
def run_hook(name, *args): |
|
return _run_hook(name, False, *args) |
|
|
|
def always_hook(name, *args): |
|
return _run_hook(name, True, *args) |
|
|
|
def _run_hook(name, always, *args): |
|
results = [] |
|
f_list = hooks.get(name, []) |
|
for p, f in f_list: |
|
if name == 'load_wallet': |
|
p.wallet = args[0] # For for p.is_enabled() below |
|
if always or p.is_enabled(): |
|
try: |
|
r = f(*args) |
|
except Exception: |
|
print_error("Plugin error") |
|
traceback.print_exc(file=sys.stdout) |
|
r = False |
|
if r: |
|
results.append(r) |
|
|
|
if results: |
|
assert len(results) == 1, results |
|
return results[0] |
|
|
|
|
|
class BasePlugin(PrintError): |
|
|
|
def __init__(self, parent, config, name): |
|
self.parent = parent # The plugins object |
|
self.name = name |
|
self.config = config |
|
self.wallet = None |
|
# add self to hooks |
|
for k in dir(self): |
|
if k in hook_names: |
|
l = hooks.get(k, []) |
|
l.append((self, getattr(self, k))) |
|
hooks[k] = l |
|
|
|
def diagnostic_name(self): |
|
return self.name |
|
|
|
def close(self): |
|
# remove self from hooks |
|
for k in dir(self): |
|
if k in hook_names: |
|
l = hooks.get(k, []) |
|
l.remove((self, getattr(self, k))) |
|
hooks[k] = l |
|
self.parent.close_plugin(self) |
|
self.on_close() |
|
|
|
def on_close(self): |
|
pass |
|
|
|
def requires_settings(self): |
|
return False |
|
|
|
def thread_jobs(self): |
|
return [] |
|
|
|
def is_enabled(self): |
|
return self.is_available() and self.config.get('use_'+self.name) is True |
|
|
|
def is_available(self): |
|
return True |
|
|
|
def settings_dialog(self): |
|
pass |
|
|
|
|