1
1
#!/usr/bin/env python
2
2
#
3
3
# Electrum - lightweight Bitcoin client
4
- # Copyright (C) 2015 Thomas Voegtlin
4
+ # Copyright (C) 2015-2024 Thomas Voegtlin
5
5
#
6
6
# Permission is hereby granted, free of charge, to any person
7
7
# obtaining a copy of this software and associated documentation files
22
22
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23
23
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
24
# SOFTWARE.
25
+
25
26
import os
26
27
import pkgutil
27
28
import importlib .util
28
29
import time
29
30
import threading
30
31
import traceback
31
32
import sys
32
- import json
33
+ import aiohttp
34
+
33
35
from typing import (NamedTuple , Any , Union , TYPE_CHECKING , Optional , Tuple ,
34
- Dict , Iterable , List , Sequence , Callable , TypeVar , Mapping , Set )
36
+ Dict , Iterable , List , Sequence , Callable , TypeVar , Mapping )
35
37
import concurrent
36
38
import zipimport
37
39
from concurrent import futures
38
40
from functools import wraps , partial
39
- from enum import IntEnum
40
- from packaging .version import parse as parse_version
41
- from electrum .version import ELECTRUM_VERSION
42
41
43
42
from .i18n import _
44
43
from .util import (profiler , DaemonThread , UserCancelled , ThreadJob , UserFacingException )
59
58
hooks = {}
60
59
61
60
62
-
63
61
class Plugins (DaemonThread ):
64
62
65
63
LOGGING_SHORTCUT = 'p'
@@ -89,7 +87,7 @@ def __init__(self, config: SimpleConfig, gui_name):
89
87
def descriptions (self ):
90
88
return dict (list (self .internal_plugin_metadata .items ()) + list (self .external_plugin_metadata .items ()))
91
89
92
- def find_internal_plugins (self ) -> Mapping [ str , dict ] :
90
+ def find_internal_plugins (self ):
93
91
"""Populates self.internal_plugin_metadata
94
92
"""
95
93
iter_modules = list (pkgutil .iter_modules ([self .pkgpath ]))
@@ -165,10 +163,9 @@ def check_plugin_hash(self, name: str)-> bool:
165
163
return True
166
164
167
165
async def download_external_plugin (self , name ):
168
- import aiohttp
169
166
metadata = self .external_plugin_metadata .get (name )
170
167
if metadata is None :
171
- raise Exception ("unknown external plugin %s" % name )
168
+ raise Exception (f "unknown external plugin { name } " )
172
169
url = metadata ['download_url' ]
173
170
filename = self .external_plugin_path (name )
174
171
async with aiohttp .ClientSession () as session :
@@ -179,7 +176,7 @@ async def download_external_plugin(self, name):
179
176
fd .write (chunk )
180
177
if not self .check_plugin_hash (name ):
181
178
os .unlink (filename )
182
- raise Exception ("wrong plugin hash %s" % name )
179
+ raise Exception (f "wrong plugin hash { name } " )
183
180
184
181
def load_external_plugin (self , name ):
185
182
if name in self .plugins :
@@ -188,31 +185,30 @@ def load_external_plugin(self, name):
188
185
# on startup, or added by manual user installation after that point.
189
186
metadata = self .external_plugin_metadata .get (name )
190
187
if metadata is None :
191
- self .logger .exception ("attempted to load unknown external plugin %s" % name )
188
+ self .logger .exception (f "attempted to load unknown external plugin { name } " )
192
189
return
193
190
filename = self .external_plugin_path (name )
194
191
if not os .path .exists (filename ):
195
192
return
196
193
if not self .check_plugin_hash (name ):
197
- self .logger .exception ("wrong hash for plugin '%s'" % name )
194
+ self .logger .exception (f "wrong hash for plugin '{ name } '" )
198
195
os .unlink (filename )
199
196
return
200
197
try :
201
198
zipfile = zipimport .zipimporter (filename )
202
199
except zipimport .ZipImportError :
203
- self .logger .exception ("unable to load zip plugin '%s'" % filename )
200
+ self .logger .exception (f "unable to load zip plugin '{ filename } '" )
204
201
return
205
202
try :
206
203
module = zipfile .load_module (name )
207
204
except zipimport .ZipImportError as e :
208
205
self .logger .exception (f"unable to load zip plugin '{ filename } ' package '{ name } '" )
209
206
return
210
- sys .modules ['electrum_external_plugins.' + name ] = module
207
+ sys .modules ['electrum_external_plugins.' + name ] = module
211
208
full_name = f'electrum_external_plugins.{ name } .{ self .gui_name } '
212
209
spec = importlib .util .find_spec (full_name )
213
210
if spec is None :
214
- raise RuntimeError ("%s implementation for %s plugin not found"
215
- % (self .gui_name , name ))
211
+ raise RuntimeError (f"{ self .gui_name } implementation for { name } plugin not found" )
216
212
module = importlib .util .module_from_spec (spec )
217
213
self ._register_module (spec , module )
218
214
if sys .version_info >= (3 , 10 ):
@@ -248,7 +244,7 @@ def load_external_plugins(self):
248
244
try :
249
245
self .load_external_plugin (name )
250
246
except BaseException as e :
251
- traceback .print_exc (file = sys .stdout ) # shouldn't this be... suppressed unless -v?
247
+ traceback .print_exc (file = sys .stdout ) # shouldn't this be... suppressed unless -v?
252
248
self .logger .exception (f"cannot initialize plugin { name } { e !r} " )
253
249
254
250
def get (self , name ):
@@ -271,11 +267,10 @@ def load_plugin(self, name) -> 'BasePlugin':
271
267
def load_internal_plugin (self , name ) -> 'BasePlugin' :
272
268
if name in self .plugins :
273
269
return self .plugins [name ]
274
- full_name = f'electrum.plugins.{ name } ' + f' .{ self .gui_name } '
270
+ full_name = f'electrum.plugins.{ name } .{ self .gui_name } '
275
271
spec = importlib .util .find_spec (full_name )
276
272
if spec is None :
277
- raise RuntimeError ("%s implementation for %s plugin not found"
278
- % (self .gui_name , name ))
273
+ raise RuntimeError (f"{ self .gui_name } implementation for { name } plugin not found" )
279
274
try :
280
275
module = importlib .util .module_from_spec (spec )
281
276
spec .loader .exec_module (module )
@@ -350,6 +345,7 @@ def get_hardware_support(self):
350
345
def register_wallet_type (self , name , gui_good , wallet_type ):
351
346
from .wallet import register_wallet_type , register_constructor
352
347
self .logger .info (f"registering wallet type { (wallet_type , name )} " )
348
+
353
349
def loader ():
354
350
plugin = self .get_plugin (name )
355
351
register_constructor (wallet_type , plugin .wallet_class )
@@ -358,6 +354,7 @@ def loader():
358
354
359
355
def register_keystore (self , name , gui_good , details ):
360
356
from .keystore import register_keystore
357
+
361
358
def dynamic_constructor (d ):
362
359
return self .get_plugin (name ).keystore_class (d )
363
360
if details [0 ] == 'hardware' :
@@ -406,7 +403,7 @@ def __init__(self, parent, config: 'SimpleConfig', name):
406
403
self .parent = parent # type: Plugins # The plugins object
407
404
self .name = name
408
405
self .config = config
409
- self .wallet = None # fixme: this field should not exist
406
+ self .wallet = None # fixme: this field should not exist
410
407
Logger .__init__ (self )
411
408
# add self to hooks
412
409
for k in dir (self ):
@@ -443,7 +440,7 @@ def thread_jobs(self):
443
440
return []
444
441
445
442
def is_enabled (self ):
446
- return self .is_available () and self .config .get ('enable_plugin_' + self .name ) is True
443
+ return self .is_available () and self .config .get ('enable_plugin_' + self .name ) is True
447
444
448
445
def is_available (self ):
449
446
return True
@@ -466,6 +463,7 @@ def read_file(self, filename: str) -> bytes:
466
463
s = myfile .read ()
467
464
return s
468
465
466
+
469
467
class DeviceUnpairableError (UserFacingException ): pass
470
468
class HardwarePluginLibraryUnavailable (Exception ): pass
471
469
class CannotAutoSelectDevice (Exception ): pass
@@ -551,7 +549,7 @@ def assert_runs_in_hwd_thread():
551
549
552
550
553
551
class DeviceMgr (ThreadJob ):
554
- ''' Manages hardware clients. A client communicates over a hardware
552
+ """ Manages hardware clients. A client communicates over a hardware
555
553
channel with the device.
556
554
557
555
In addition to tracking device HID IDs, the device manager tracks
@@ -579,7 +577,7 @@ class DeviceMgr(ThreadJob):
579
577
the HID IDs.
580
578
581
579
This plugin is thread-safe. Currently only devices supported by
582
- hidapi are implemented.'''
580
+ hidapi are implemented."""
583
581
584
582
def __init__ (self , config : SimpleConfig ):
585
583
ThreadJob .__init__ (self )
@@ -691,7 +689,7 @@ def client_for_keystore(self, plugin: 'HW_PluginBase', handler: Optional['Hardwa
691
689
allow_user_interaction : bool = True ) -> Optional ['HardwareClientBase' ]:
692
690
self .logger .info ("getting client for keystore" )
693
691
if handler is None :
694
- raise Exception (_ ("Handler not found for" ) + ' ' + plugin .name + '\n ' + _ ("A library is probably missing." ))
692
+ raise Exception (_ ("Handler not found for {}" ). format ( plugin .name ) + '\n ' + _ ("A library is probably missing." ))
695
693
handler .update_status (False )
696
694
pcode = keystore .pairing_code ()
697
695
client = None
@@ -756,7 +754,7 @@ def force_pair_keystore(
756
754
try :
757
755
client_xpub = client .get_xpub (derivation , xtype )
758
756
except (UserCancelled , RuntimeError ):
759
- # Bad / cancelled PIN / passphrase
757
+ # Bad / cancelled PIN / passphrase
760
758
client_xpub = None
761
759
if client_xpub == xpub :
762
760
keystore .opportunistically_fill_in_missing_info_from_device (client )
@@ -928,8 +926,7 @@ def scan_devices(self) -> Sequence['Device']:
928
926
try :
929
927
new_devices = f ()
930
928
except BaseException as e :
931
- self .logger .error ('custom device enum failed. func {}, error {}'
932
- .format (str (f ), repr (e )))
929
+ self .logger .error (f'custom device enum failed. func { str (f )} , error { e !r} ' )
933
930
else :
934
931
devices .extend (new_devices )
935
932
0 commit comments