Browse Source

whitespace: top-level modules

The MMGen Project 4 months ago
parent
commit
b5600fc563
43 changed files with 1962 additions and 1982 deletions
  1. 51 50
      mmgen/addr.py
  2. 25 25
      mmgen/addrdata.py
  3. 59 59
      mmgen/addrfile.py
  4. 9 9
      mmgen/addrgen.py
  5. 81 81
      mmgen/addrlist.py
  6. 80 80
      mmgen/autosign.py
  7. 24 26
      mmgen/base_obj.py
  8. 47 48
      mmgen/baseconv.py
  9. 27 27
      mmgen/bip39.py
  10. 129 132
      mmgen/cfg.py
  11. 55 55
      mmgen/cfgfile.py
  12. 35 35
      mmgen/color.py
  13. 77 85
      mmgen/crypto.py
  14. 101 101
      mmgen/daemon.py
  15. 6 6
      mmgen/derive.py
  16. 27 27
      mmgen/devinit.py
  17. 66 66
      mmgen/devtools.py
  18. 1 1
      mmgen/exception.py
  19. 28 28
      mmgen/filename.py
  20. 56 56
      mmgen/fileutil.py
  21. 19 19
      mmgen/flags.py
  22. 36 36
      mmgen/key.py
  23. 21 25
      mmgen/keygen.py
  24. 29 29
      mmgen/led.py
  25. 8 8
      mmgen/main.py
  26. 55 55
      mmgen/mn_entry.py
  27. 76 76
      mmgen/msg.py
  28. 86 86
      mmgen/obj.py
  29. 36 36
      mmgen/objmethods.py
  30. 5 5
      mmgen/opts.py
  31. 51 51
      mmgen/passwdlist.py
  32. 64 64
      mmgen/protocol.py
  33. 11 11
      mmgen/pyversion.py
  34. 106 106
      mmgen/rpc.py
  35. 26 26
      mmgen/seed.py
  36. 71 74
      mmgen/seedsplit.py
  37. 34 34
      mmgen/sha2.py
  38. 47 47
      mmgen/subseed.py
  39. 37 37
      mmgen/term.py
  40. 18 18
      mmgen/ui.py
  41. 101 101
      mmgen/util.py
  42. 25 25
      mmgen/util2.py
  43. 16 16
      mmgen/xmrseed.py

+ 51 - 50
mmgen/addr.py

@@ -22,29 +22,29 @@ addr: MMGen address-related types
 
 
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .objmethods import HiliteStr,InitErrors,MMGenObject
-from .obj import ImmutableAttr,MMGenIdx,get_obj
+from .objmethods import HiliteStr, InitErrors, MMGenObject
+from .obj import ImmutableAttr, MMGenIdx, get_obj
 from .seed import SeedID
 from .seed import SeedID
 from . import color as color_mod
 from . import color as color_mod
 
 
 ati = namedtuple('addrtype_info',
 ati = namedtuple('addrtype_info',
-	['name','pubkey_type','compressed','gen_method','addr_fmt','wif_label','extra_attrs','desc'])
+	['name', 'pubkey_type', 'compressed', 'gen_method', 'addr_fmt', 'wif_label', 'extra_attrs', 'desc'])
 
 
-class MMGenAddrType(HiliteStr,InitErrors,MMGenObject):
+class MMGenAddrType(HiliteStr, InitErrors, MMGenObject):
 	width = 1
 	width = 1
 	trunc_ok = False
 	trunc_ok = False
 	color = 'blue'
 	color = 'blue'
 
 
 	name        = ImmutableAttr(str)
 	name        = ImmutableAttr(str)
 	pubkey_type = ImmutableAttr(str)
 	pubkey_type = ImmutableAttr(str)
-	compressed  = ImmutableAttr(bool,set_none_ok=True)
-	gen_method  = ImmutableAttr(str,set_none_ok=True)
-	addr_fmt    = ImmutableAttr(str,set_none_ok=True)
-	wif_label   = ImmutableAttr(str,set_none_ok=True)
-	extra_attrs = ImmutableAttr(tuple,set_none_ok=True)
+	compressed  = ImmutableAttr(bool, set_none_ok=True)
+	gen_method  = ImmutableAttr(str, set_none_ok=True)
+	addr_fmt    = ImmutableAttr(str, set_none_ok=True)
+	wif_label   = ImmutableAttr(str, set_none_ok=True)
+	extra_attrs = ImmutableAttr(tuple, set_none_ok=True)
 	desc        = ImmutableAttr(str)
 	desc        = ImmutableAttr(str)
 
 
-	pkh_fmts = ('p2pkh','bech32','ethereum')
+	pkh_fmts = ('p2pkh', 'bech32', 'ethereum')
 	mmtypes = {
 	mmtypes = {
 		'L': ati('legacy',    'std', False,'p2pkh',   'p2pkh',   'wif', (), 'Legacy uncompressed address'),
 		'L': ati('legacy',    'std', False,'p2pkh',   'p2pkh',   'wif', (), 'Legacy uncompressed address'),
 		'C': ati('compressed','std', True, 'p2pkh',   'p2pkh',   'wif', (), 'Compressed P2PKH address'),
 		'C': ati('compressed','std', True, 'p2pkh',   'p2pkh',   'wif', (), 'Compressed P2PKH address'),
@@ -54,34 +54,35 @@ class MMGenAddrType(HiliteStr,InitErrors,MMGenObject):
 		'Z': ati('zcash_z','zcash_z',False,'zcash_z', 'zcash_z', 'wif',     ('viewkey',),      'Zcash z-address'),
 		'Z': ati('zcash_z','zcash_z',False,'zcash_z', 'zcash_z', 'wif',     ('viewkey',),      'Zcash z-address'),
 		'M': ati('monero', 'monero', False,'monero',  'monero',  'spendkey',('viewkey','wallet_passwd'),'Monero address'),
 		'M': ati('monero', 'monero', False,'monero',  'monero',  'spendkey',('viewkey','wallet_passwd'),'Monero address'),
 	}
 	}
-	def __new__(cls,proto,id_str,errmsg=None):
-		if isinstance(id_str,cls):
+	def __new__(cls, proto, id_str, errmsg=None):
+		if isinstance(id_str, cls):
 			return id_str
 			return id_str
 		try:
 		try:
-			id_str = id_str.replace('-','_')
-			for k,v in cls.mmtypes.items():
-				if id_str in (k,v.name):
+			id_str = id_str.replace('-', '_')
+			for k, v in cls.mmtypes.items():
+				if id_str in (k, v.name):
 					if id_str == v.name:
 					if id_str == v.name:
 						id_str = k
 						id_str = k
-					me = str.__new__(cls,id_str)
+					me = str.__new__(cls, id_str)
 					for k in v._fields:
 					for k in v._fields:
-						setattr(me,k,getattr(v,k))
+						setattr(me, k, getattr(v, k))
 					if me not in proto.mmtypes + ('P',):
 					if me not in proto.mmtypes + ('P',):
 						raise ValueError(f'{me.name!r}: invalid address type for {proto.name} protocol')
 						raise ValueError(f'{me.name!r}: invalid address type for {proto.name} protocol')
 					me.proto = proto
 					me.proto = proto
 					return me
 					return me
 			raise ValueError(f'{id_str}: unrecognized address type for protocol {proto.name}')
 			raise ValueError(f'{id_str}: unrecognized address type for protocol {proto.name}')
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail( e,
+			return cls.init_fail(
+				e,
 				f"{errmsg or ''}{id_str!r}: invalid value for {cls.__name__} ({e!s})",
 				f"{errmsg or ''}{id_str!r}: invalid value for {cls.__name__} ({e!s})",
-				preformat = True )
+				preformat = True)
 
 
 	@classmethod
 	@classmethod
 	def get_names(cls):
 	def get_names(cls):
 		return [v.name for v in cls.mmtypes.values()]
 		return [v.name for v in cls.mmtypes.values()]
 
 
-def is_mmgen_addrtype(proto,id_str):
-	return get_obj( MMGenAddrType, proto=proto, id_str=id_str, silent=True, return_bool=True )
+def is_mmgen_addrtype(proto, id_str):
+	return get_obj(MMGenAddrType, proto=proto, id_str=id_str, silent=True, return_bool=True)
 
 
 class MMGenPasswordType(MMGenAddrType):
 class MMGenPasswordType(MMGenAddrType):
 	mmtypes = {
 	mmtypes = {
@@ -92,58 +93,58 @@ class AddrIdx(MMGenIdx):
 	max_digits = 7
 	max_digits = 7
 
 
 def is_addr_idx(s):
 def is_addr_idx(s):
-	return get_obj( AddrIdx, n=s, silent=True, return_bool=True )
+	return get_obj(AddrIdx, n=s, silent=True, return_bool=True)
 
 
-class AddrListID(HiliteStr,InitErrors,MMGenObject):
+class AddrListID(HiliteStr, InitErrors, MMGenObject):
 	width = 10
 	width = 10
 	trunc_ok = False
 	trunc_ok = False
 	color = 'yellow'
 	color = 'yellow'
-	def __new__(cls,sid=None,mmtype=None,proto=None,id_str=None):
+	def __new__(cls, sid=None, mmtype=None, proto=None, id_str=None):
 		try:
 		try:
 			if id_str:
 			if id_str:
-				a,b = id_str.split(':')
+				a, b = id_str.split(':')
 				sid = SeedID(sid=a)
 				sid = SeedID(sid=a)
 				try:
 				try:
-					mmtype = MMGenAddrType( proto=proto, id_str=b )
+					mmtype = MMGenAddrType(proto=proto, id_str=b)
 				except:
 				except:
-					mmtype = MMGenPasswordType( proto=proto, id_str=b )
+					mmtype = MMGenPasswordType(proto=proto, id_str=b)
 			else:
 			else:
-				assert isinstance(sid,SeedID), f'{sid!r} not a SeedID instance'
-				if not isinstance(mmtype,(MMGenAddrType,MMGenPasswordType)):
+				assert isinstance(sid, SeedID), f'{sid!r} not a SeedID instance'
+				if not isinstance(mmtype, (MMGenAddrType, MMGenPasswordType)):
 					raise ValueError(f'{mmtype!r}: not an instance of MMGenAddrType or MMGenPasswordType')
 					raise ValueError(f'{mmtype!r}: not an instance of MMGenAddrType or MMGenPasswordType')
-			me = str.__new__(cls,sid+':'+mmtype)
+			me = str.__new__(cls, sid+':'+mmtype)
 			me.sid = sid
 			me.sid = sid
 			me.mmtype = mmtype
 			me.mmtype = mmtype
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
 			return cls.init_fail(e, f'sid={sid}, mmtype={mmtype}')
 			return cls.init_fail(e, f'sid={sid}, mmtype={mmtype}')
 
 
-def is_addrlist_id(proto,s):
-	return get_obj( AddrListID, proto=proto, id_str=s, silent=False, return_bool=True )
+def is_addrlist_id(proto, s):
+	return get_obj(AddrListID, proto=proto, id_str=s, silent=False, return_bool=True)
 
 
-class MMGenID(HiliteStr,InitErrors,MMGenObject):
+class MMGenID(HiliteStr, InitErrors, MMGenObject):
 	color = 'orange'
 	color = 'orange'
 	width = 0
 	width = 0
 	trunc_ok = False
 	trunc_ok = False
-	def __new__(cls,proto,id_str):
+	def __new__(cls, proto, id_str):
 		try:
 		try:
 			ss = str(id_str).split(':')
 			ss = str(id_str).split(':')
-			assert len(ss) in (2,3),'not 2 or 3 colon-separated items'
-			t = proto.addr_type((ss[1],proto.dfl_mmtype)[len(ss)==2])
-			me = str.__new__(cls,f'{ss[0]}:{t}:{ss[-1]}')
+			assert len(ss) in (2, 3), 'not 2 or 3 colon-separated items'
+			t = proto.addr_type((ss[1], proto.dfl_mmtype)[len(ss)==2])
+			me = str.__new__(cls, f'{ss[0]}:{t}:{ss[-1]}')
 			me.sid = SeedID(sid=ss[0])
 			me.sid = SeedID(sid=ss[0])
 			me.idx = AddrIdx(ss[-1])
 			me.idx = AddrIdx(ss[-1])
 			me.mmtype = t
 			me.mmtype = t
 			assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}'
 			assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}'
-			me.al_id = str.__new__(AddrListID,me.sid+':'+me.mmtype) # checks already done
+			me.al_id = str.__new__(AddrListID, me.sid+':'+me.mmtype) # checks already done
 			me.sort_key = f'{me.sid}:{me.mmtype}:{me.idx:0{me.idx.max_digits}}'
 			me.sort_key = f'{me.sid}:{me.mmtype}:{me.idx:0{me.idx.max_digits}}'
 			me.proto = proto
 			me.proto = proto
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,id_str)
+			return cls.init_fail(e, id_str)
 
 
-def is_mmgen_id(proto,s):
-	return get_obj( MMGenID, proto=proto, id_str=s, silent=True, return_bool=True )
+def is_mmgen_id(proto, s):
+	return get_obj(MMGenID, proto=proto, id_str=s, silent=True, return_bool=True)
 
 
 class CoinAddr(HiliteStr, InitErrors, MMGenObject):
 class CoinAddr(HiliteStr, InitErrors, MMGenObject):
 	color = 'cyan'
 	color = 'cyan'
@@ -152,7 +153,7 @@ class CoinAddr(HiliteStr, InitErrors, MMGenObject):
 	trunc_ok = False
 	trunc_ok = False
 
 
 	def __new__(cls, proto, addr):
 	def __new__(cls, proto, addr):
-		if isinstance(addr,cls):
+		if isinstance(addr, cls):
 			return addr
 			return addr
 		try:
 		try:
 			ap = proto.decode_addr(addr)
 			ap = proto.decode_addr(addr)
@@ -171,18 +172,18 @@ class CoinAddr(HiliteStr, InitErrors, MMGenObject):
 			me.proto = proto
 			me.proto = proto
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,addr,objname=f'{proto.cls_name} address')
+			return cls.init_fail(e, addr, objname=f'{proto.cls_name} address')
 
 
 	@property
 	@property
 	def parsed(self):
 	def parsed(self):
-		if not hasattr(self,'_parsed'):
-			self._parsed = self.proto.parse_addr(self.ver_bytes,self.bytes,self.addr_fmt)
+		if not hasattr(self, '_parsed'):
+			self._parsed = self.proto.parse_addr(self.ver_bytes, self.bytes, self.addr_fmt)
 		return self._parsed
 		return self._parsed
 
 
 	# reimplement some HiliteStr methods:
 	# reimplement some HiliteStr methods:
 	@classmethod
 	@classmethod
-	def fmtc(cls,s,width,color=False):
-		return super().fmtc( s=s[:width-2]+'..' if len(s) > width else s, width=width, color=color )
+	def fmtc(cls, s, width, color=False):
+		return super().fmtc(s=s[:width-2]+'..' if len(s) > width else s, width=width, color=color)
 
 
 	def fmt(self, view_pref, width, color=False):
 	def fmt(self, view_pref, width, color=False):
 		s = self.views[view_pref]
 		s = self.views[view_pref]
@@ -191,11 +192,11 @@ class CoinAddr(HiliteStr, InitErrors, MMGenObject):
 	def hl(self, view_pref, color=True):
 	def hl(self, view_pref, color=True):
 		return getattr(color_mod, self.color)(self.views[view_pref]) if color else self.views[view_pref]
 		return getattr(color_mod, self.color)(self.views[view_pref]) if color else self.views[view_pref]
 
 
-def is_coin_addr(proto,s):
-	return get_obj( CoinAddr, proto=proto, addr=s, silent=True, return_bool=True )
+def is_coin_addr(proto, s):
+	return get_obj(CoinAddr, proto=proto, addr=s, silent=True, return_bool=True)
 
 
 class TokenAddr(CoinAddr):
 class TokenAddr(CoinAddr):
 	color = 'blue'
 	color = 'blue'
 
 
-def ViewKey(proto,viewkey_str):
+def ViewKey(proto, viewkey_str):
 	return proto.viewkey(viewkey_str)
 	return proto.viewkey(viewkey_str)

+ 25 - 25
mmgen/addrdata.py

@@ -21,15 +21,15 @@ addrdata: MMGen AddrData and related classes
 """
 """
 
 
 from .cfg import gc
 from .cfg import gc
-from .util import fmt,die
+from .util import fmt, die
 from .base_obj import AsyncInit
 from .base_obj import AsyncInit
-from .obj import MMGenObject,MMGenDict,get_obj
-from .addr import MMGenID,AddrListID
-from .addrlist import AddrListEntry,AddrListData,AddrList
+from .obj import MMGenObject, MMGenDict, get_obj
+from .addr import MMGenID, AddrListID
+from .addrlist import AddrListEntry, AddrListData, AddrList
 
 
 class AddrData(MMGenObject):
 class AddrData(MMGenObject):
 
 
-	def __init__(self,proto,*args,**kwargs):
+	def __init__(self, proto, *args, **kwargs):
 		self.al_ids = {}
 		self.al_ids = {}
 		self.proto = proto
 		self.proto = proto
 		self.rpc = None
 		self.rpc = None
@@ -37,63 +37,63 @@ class AddrData(MMGenObject):
 	def seed_ids(self):
 	def seed_ids(self):
 		return list(self.al_ids.keys())
 		return list(self.al_ids.keys())
 
 
-	def addrlist(self,al_id):
+	def addrlist(self, al_id):
 		# TODO: Validate al_id
 		# TODO: Validate al_id
 		if al_id in self.al_ids:
 		if al_id in self.al_ids:
 			return self.al_ids[al_id]
 			return self.al_ids[al_id]
 
 
-	def mmaddr2coinaddr(self,mmaddr):
-		al_id,idx = MMGenID(self.proto,mmaddr).rsplit(':',1)
+	def mmaddr2coinaddr(self, mmaddr):
+		al_id, idx = MMGenID(self.proto, mmaddr).rsplit(':', 1)
 		coinaddr = ''
 		coinaddr = ''
 		if al_id in self.al_ids:
 		if al_id in self.al_ids:
 			coinaddr = self.addrlist(al_id).coinaddr(int(idx))
 			coinaddr = self.addrlist(al_id).coinaddr(int(idx))
 		return coinaddr or None
 		return coinaddr or None
 
 
-	def coinaddr2mmaddr(self,coinaddr):
+	def coinaddr2mmaddr(self, coinaddr):
 		d = self.make_reverse_dict([coinaddr])
 		d = self.make_reverse_dict([coinaddr])
 		return (list(d.values())[0][0]) if d else None
 		return (list(d.values())[0][0]) if d else None
 
 
-	def add(self,addrlist):
-		if isinstance(addrlist,AddrList):
+	def add(self, addrlist):
+		if isinstance(addrlist, AddrList):
 			self.al_ids[addrlist.al_id] = addrlist
 			self.al_ids[addrlist.al_id] = addrlist
 			return True
 			return True
 		else:
 		else:
 			raise TypeError(f'Error: object {addrlist!r} is not an instance of AddrList')
 			raise TypeError(f'Error: object {addrlist!r} is not an instance of AddrList')
 
 
-	def make_reverse_dict(self,coinaddrs):
+	def make_reverse_dict(self, coinaddrs):
 		d = MMGenDict()
 		d = MMGenDict()
 		for al_id in self.al_ids:
 		for al_id in self.al_ids:
 			d.update(self.al_ids[al_id].make_reverse_dict_addrlist(coinaddrs))
 			d.update(self.al_ids[al_id].make_reverse_dict_addrlist(coinaddrs))
 		return d
 		return d
 
 
-class TwAddrData(AddrData,metaclass=AsyncInit):
+class TwAddrData(AddrData, metaclass=AsyncInit):
 
 
-	def __new__(cls,cfg,proto,*args,**kwargs):
-		return MMGenObject.__new__(proto.base_proto_subclass(cls,'addrdata'))
+	def __new__(cls, cfg, proto, *args, **kwargs):
+		return MMGenObject.__new__(proto.base_proto_subclass(cls, 'addrdata'))
 
 
-	async def __init__(self,cfg,proto,twctl=None):
+	async def __init__(self, cfg, proto, twctl=None):
 		from .rpc import rpc_init
 		from .rpc import rpc_init
 		from .tw.shared import TwLabel
 		from .tw.shared import TwLabel
 		from .seed import SeedID
 		from .seed import SeedID
 		self.cfg = cfg
 		self.cfg = cfg
 		self.proto = proto
 		self.proto = proto
-		self.rpc = await rpc_init(cfg,proto)
+		self.rpc = await rpc_init(cfg, proto)
 		self.al_ids = {}
 		self.al_ids = {}
 		twd = await self.get_tw_data(twctl)
 		twd = await self.get_tw_data(twctl)
-		out,i = {},0
-		for acct,addr_array in twd:
-			l = get_obj(TwLabel,proto=self.proto,text=acct,silent=True)
+		out, i = {}, 0
+		for acct, addr_array in twd:
+			l = get_obj(TwLabel, proto=self.proto, text=acct, silent=True)
 			if l and l.mmid.type == 'mmgen':
 			if l and l.mmid.type == 'mmgen':
 				obj = l.mmid.obj
 				obj = l.mmid.obj
 				if len(addr_array) != 1:
 				if len(addr_array) != 1:
-					message = self.msgs['multiple_acct_addrs'].strip().format( acct=acct, proj=gc.proj_name )
-					die(3, fmt( message, indent='  ' ))
+					message = self.msgs['multiple_acct_addrs'].strip().format(acct=acct, proj=gc.proj_name)
+					die(3, fmt(message, indent='  '))
 				al_id = AddrListID(
 				al_id = AddrListID(
 					sid = SeedID(sid=obj.sid),
 					sid = SeedID(sid=obj.sid),
-					mmtype = self.proto.addr_type(obj.mmtype) )
+					mmtype = self.proto.addr_type(obj.mmtype))
 				if al_id not in out:
 				if al_id not in out:
 					out[al_id] = []
 					out[al_id] = []
-				out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],comment=l.comment))
+				out[al_id].append(AddrListEntry(self.proto, idx=obj.idx, addr=addr_array[0], comment=l.comment))
 				i += 1
 				i += 1
 
 
 		self.cfg._util.vmsg(f'{i} {gc.proj_name} addresses found, {len(twd)} accounts total')
 		self.cfg._util.vmsg(f'{i} {gc.proj_name} addresses found, {len(twd)} accounts total')
@@ -103,5 +103,5 @@ class TwAddrData(AddrData,metaclass=AsyncInit):
 				self.cfg,
 				self.cfg,
 				self.proto,
 				self.proto,
 				al_id = al_id,
 				al_id = al_id,
-				adata = AddrListData(sorted( out[al_id], key=lambda a: a.idx ))
+				adata = AddrListData(sorted(out[al_id], key=lambda a: a.idx))
 			))
 			))

+ 59 - 59
mmgen/addrfile.py

@@ -21,13 +21,13 @@ addrfile: Address and password file classes for the MMGen suite
 """
 """
 
 
 from .cfg import gc
 from .cfg import gc
-from .util import msg,die,capfirst
+from .util import msg, die, capfirst
 from .protocol import init_proto
 from .protocol import init_proto
-from .obj import MMGenObject,TwComment,WalletPassword,MMGenPWIDString
-from .seed import SeedID,is_seed_id
+from .obj import MMGenObject, TwComment, WalletPassword, MMGenPWIDString
+from .seed import SeedID, is_seed_id
 from .key import PrivKey
 from .key import PrivKey
-from .addr import ViewKey,AddrListID,MMGenAddrType,MMGenPasswordType,is_addr_idx
-from .addrlist import KeyList,AddrListData
+from .addr import ViewKey, AddrListID, MMGenAddrType, MMGenPasswordType, is_addr_idx
+from .addrlist import KeyList, AddrListData
 
 
 class AddrFile(MMGenObject):
 class AddrFile(MMGenObject):
 	desc        = 'addresses'
 	desc        = 'addresses'
@@ -43,7 +43,7 @@ class AddrFile(MMGenObject):
 # The label may contain any printable ASCII symbol.
 # The label may contain any printable ASCII symbol.
 """
 """
 
 
-	def __init__(self,parent):
+	def __init__(self, parent):
 		self.parent = parent
 		self.parent = parent
 		self.cfg    = parent.cfg
 		self.cfg    = parent.cfg
 		self.infile = None
 		self.infile = None
@@ -53,7 +53,7 @@ class AddrFile(MMGenObject):
 		from .crypto import Crypto
 		from .crypto import Crypto
 		self.fmt_data = Crypto(self.cfg).mmgen_encrypt(
 		self.fmt_data = Crypto(self.cfg).mmgen_encrypt(
 			data = self.fmt_data.encode(),
 			data = self.fmt_data.encode(),
-			desc = f'new {self.parent.desc} list' )
+			desc = f'new {self.parent.desc} list')
 		self.ext += f'.{Crypto.mmenc_ext}'
 		self.ext += f'.{Crypto.mmenc_ext}'
 
 
 	@property
 	@property
@@ -61,7 +61,7 @@ class AddrFile(MMGenObject):
 		return '{}{}.{}'.format(
 		return '{}{}.{}'.format(
 			self.parent.id_str,
 			self.parent.id_str,
 			('.' + self.parent.proto.network) if self.parent.proto.testnet else '',
 			('.' + self.parent.proto.network) if self.parent.proto.testnet else '',
-			self.ext )
+			self.ext)
 
 
 	def write(
 	def write(
 			self,
 			self,
@@ -92,9 +92,9 @@ class AddrFile(MMGenObject):
 		)
 		)
 		return self.parent.al_id.sid + (' ' if lbl_p2 else '') + lbl_p2
 		return self.parent.al_id.sid + (' ' if lbl_p2 else '') + lbl_p2
 
 
-	def format(self,add_comments=False):
+	def format(self, add_comments=False):
 		p = self.parent
 		p = self.parent
-		if p.gen_passwds and p.pw_fmt in ('bip39','xmrseed'):
+		if p.gen_passwds and p.pw_fmt in ('bip39', 'xmrseed'):
 			desc_pfx = f'{p.pw_fmt.upper()} '
 			desc_pfx = f'{p.pw_fmt.upper()} '
 			hdr2 = ''
 			hdr2 = ''
 		else:
 		else:
@@ -102,9 +102,9 @@ class AddrFile(MMGenObject):
 			hdr2 = self.text_label_header
 			hdr2 = self.text_label_header
 		out = [
 		out = [
 			f'# {gc.proj_name} {desc_pfx}{p.desc} file\n#\n'
 			f'# {gc.proj_name} {desc_pfx}{p.desc} file\n#\n'
-			+ self.header.strip().format( pnm=gc.proj_name )
+			+ self.header.strip().format(pnm=gc.proj_name)
 			+ '\n'
 			+ '\n'
-			+ hdr2.lstrip().format( n=TwComment.max_screen_width )
+			+ hdr2.lstrip().format(n=TwComment.max_screen_width)
 			+ '#\n'
 			+ '#\n'
 		]
 		]
 
 
@@ -113,41 +113,41 @@ class AddrFile(MMGenObject):
 			out.append('# Record this value to a secure location.\n')
 			out.append('# Record this value to a secure location.\n')
 
 
 		lbl = self.make_label()
 		lbl = self.make_label()
-		self.parent.dmsg_sc('lbl',lbl[9:])
+		self.parent.dmsg_sc('lbl', lbl[9:])
 		out.append(f'{lbl} {{')
 		out.append(f'{lbl} {{')
 
 
 		fs = '  {:<%s}  {:<34}{}' % len(str(p.data[-1].idx))
 		fs = '  {:<%s}  {:<34}{}' % len(str(p.data[-1].idx))
 		for e in p.data:
 		for e in p.data:
 			c = ' ' + e.comment if add_comments and e.comment else ''
 			c = ' ' + e.comment if add_comments and e.comment else ''
 			if type(p) is KeyList:
 			if type(p) is KeyList:
-				out.append(fs.format( e.idx, f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c ))
+				out.append(fs.format(e.idx, f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c))
 			elif type(p).__name__ == 'PasswordList':
 			elif type(p).__name__ == 'PasswordList':
-				out.append(fs.format(e.idx,e.passwd,c))
+				out.append(fs.format(e.idx, e.passwd, c))
 			else: # First line with idx
 			else: # First line with idx
 				out.append(fs.format(e.idx, e.addr.views[e.addr.view_pref], c))
 				out.append(fs.format(e.idx, e.addr.views[e.addr.view_pref], c))
 				if p.has_keys:
 				if p.has_keys:
 					if self.cfg.b16:
 					if self.cfg.b16:
-						out.append(fs.format( '', f'orig_hex: {e.sec.orig_bytes.hex()}', c ))
+						out.append(fs.format('', f'orig_hex: {e.sec.orig_bytes.hex()}', c))
 					if type(self) is not ViewKeyAddrFile:
 					if type(self) is not ViewKeyAddrFile:
-						out.append(fs.format( '', f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c ))
-					for k in ('viewkey','wallet_passwd'):
-						v = getattr(e,k)
+						out.append(fs.format('', f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c))
+					for k in ('viewkey', 'wallet_passwd'):
+						v = getattr(e, k)
 						if v:
 						if v:
-							out.append(fs.format( '', f'{k}: {v}', c ))
+							out.append(fs.format('', f'{k}: {v}', c))
 
 
 		out.append('}')
 		out.append('}')
 		self.fmt_data = '\n'.join([l.rstrip() for l in out]) + '\n'
 		self.fmt_data = '\n'.join([l.rstrip() for l in out]) + '\n'
 		return self.fmt_data
 		return self.fmt_data
 
 
-	def get_line(self,lines):
-		ret = lines.pop(0).split(None,2)
+	def get_line(self, lines):
+		ret = lines.pop(0).split(None, 2)
 		self.line_ctr += 1
 		self.line_ctr += 1
 		if ret[0] == 'orig_hex:': # hacky
 		if ret[0] == 'orig_hex:': # hacky
-			ret = lines.pop(0).split(None,2)
+			ret = lines.pop(0).split(None, 2)
 			self.line_ctr += 1
 			self.line_ctr += 1
 		return ret if len(ret) == 3 else ret + ['']
 		return ret if len(ret) == 3 else ret + ['']
 
 
-	def parse_file_body(self,lines):
+	def parse_file_body(self, lines):
 
 
 		p = self.parent
 		p = self.parent
 		ret = AddrListData()
 		ret = AddrListData()
@@ -155,39 +155,39 @@ class AddrFile(MMGenObject):
 		iifs = "{!r}: invalid identifier [expected '{}:']"
 		iifs = "{!r}: invalid identifier [expected '{}:']"
 
 
 		while lines:
 		while lines:
-			idx,addr,comment = self.get_line(lines)
+			idx, addr, comment = self.get_line(lines)
 
 
 			assert is_addr_idx(idx), f'invalid address index {idx!r}'
 			assert is_addr_idx(idx), f'invalid address index {idx!r}'
 			p.check_format(addr)
 			p.check_format(addr)
 
 
-			a = le(**{ 'proto': p.proto, 'idx':int(idx), p.main_attr:addr, 'comment':comment })
+			a = le(**{'proto': p.proto, 'idx':int(idx), p.main_attr:addr, 'comment':comment})
 
 
-			if p.has_keys: # order: wif,(orig_hex),viewkey,wallet_passwd
+			if p.has_keys: # order: wif, (orig_hex), viewkey, wallet_passwd
 				if type(self) is not ViewKeyAddrFile:
 				if type(self) is not ViewKeyAddrFile:
 					d = self.get_line(lines)
 					d = self.get_line(lines)
-					assert d[0] == p.al_id.mmtype.wif_label+':', iifs.format(d[0],p.al_id.mmtype.wif_label)
-					a.sec = PrivKey(proto=p.proto,wif=d[1])
-				for k,dtype,add_proto in (
-					('viewkey',ViewKey,True),
-					('wallet_passwd',WalletPassword,False) ):
+					assert d[0] == p.al_id.mmtype.wif_label+':', iifs.format(d[0], p.al_id.mmtype.wif_label)
+					a.sec = PrivKey(proto=p.proto, wif=d[1])
+				for k, dtype, add_proto in (
+					('viewkey', ViewKey, True),
+					('wallet_passwd', WalletPassword, False)):
 					if k in p.al_id.mmtype.extra_attrs:
 					if k in p.al_id.mmtype.extra_attrs:
 						d = self.get_line(lines)
 						d = self.get_line(lines)
-						assert d[0] == k+':', iifs.format(d[0],k)
-						setattr(a,k,dtype( *((p.proto,d[1]) if add_proto else (d[1],)) ) )
+						assert d[0] == k+':', iifs.format(d[0], k)
+						setattr(a, k, dtype(*((p.proto, d[1]) if add_proto else (d[1],))))
 
 
 			ret.append(a)
 			ret.append(a)
 
 
 		if type(self) is not ViewKeyAddrFile and p.has_keys and p.ka_validity_chk is not False:
 		if type(self) is not ViewKeyAddrFile and p.has_keys and p.ka_validity_chk is not False:
 
 
 			def verify_keys():
 			def verify_keys():
-				from .addrgen import KeyGenerator,AddrGenerator
-				kg = KeyGenerator( self.cfg, p.proto, p.al_id.mmtype.pubkey_type )
-				ag = AddrGenerator( self.cfg, p.proto, p.al_id.mmtype )
+				from .addrgen import KeyGenerator, AddrGenerator
+				kg = KeyGenerator(self.cfg, p.proto, p.al_id.mmtype.pubkey_type)
+				ag = AddrGenerator(self.cfg, p.proto, p.al_id.mmtype)
 				llen = len(ret)
 				llen = len(ret)
 				qmsg_r = p.cfg._util.qmsg_r
 				qmsg_r = p.cfg._util.qmsg_r
-				for n,e in enumerate(ret):
+				for n, e in enumerate(ret):
 					qmsg_r(f'\rVerifying keys {n+1}/{llen}')
 					qmsg_r(f'\rVerifying keys {n+1}/{llen}')
-					assert e.addr == ag.to_addr(kg.gen_data(e.sec)),(
+					assert e.addr == ag.to_addr(kg.gen_data(e.sec)), (
 						f'Key doesn’t match address!\n  {e.sec.wif}\n  {e.addr}')
 						f'Key doesn’t match address!\n  {e.sec.wif}\n  {e.addr}')
 				p.cfg._util.qmsg(' - done')
 				p.cfg._util.qmsg(' - done')
 
 
@@ -195,12 +195,12 @@ class AddrFile(MMGenObject):
 				verify_keys()
 				verify_keys()
 			else:
 			else:
 				from .ui import keypress_confirm
 				from .ui import keypress_confirm
-				if keypress_confirm( p.cfg, 'Check key-to-address validity?' ):
+				if keypress_confirm(p.cfg, 'Check key-to-address validity?'):
 					verify_keys()
 					verify_keys()
 
 
 		return ret
 		return ret
 
 
-	def parse_file(self,fn,buf=[],exit_on_error=True):
+	def parse_file(self, fn, buf=[], exit_on_error=True):
 
 
 		def parse_addrfile_label(lbl):
 		def parse_addrfile_label(lbl):
 			"""
 			"""
@@ -232,7 +232,7 @@ class AddrFile(MMGenObject):
 				network = 'mainnet'
 				network = 'mainnet'
 
 
 			from .proto.btc.params import mainnet
 			from .proto.btc.params import mainnet
-			if lbl in [MMGenAddrType(mainnet,key).name for key in mainnet.mmtypes]:
+			if lbl in [MMGenAddrType(mainnet, key).name for key in mainnet.mmtypes]:
 				coin, mmtype_key = ('BTC', lbl)
 				coin, mmtype_key = ('BTC', lbl)
 			elif ':' in lbl: # first component is coin, second is mmtype_key
 			elif ':' in lbl: # first component is coin, second is mmtype_key
 				coin, mmtype_key = lbl.split(':')
 				coin, mmtype_key = lbl.split(':')
@@ -249,7 +249,7 @@ class AddrFile(MMGenObject):
 		p = self.parent
 		p = self.parent
 
 
 		from .fileutil import get_lines_from_file
 		from .fileutil import get_lines_from_file
-		lines = get_lines_from_file( p.cfg, fn, p.desc+' data', trim_comments=True )
+		lines = get_lines_from_file(p.cfg, fn, p.desc+' data', trim_comments=True)
 
 
 		try:
 		try:
 			assert len(lines) >= 3, f'Too few lines in address file ({len(lines)})'
 			assert len(lines) >= 3, f'Too few lines in address file ({len(lines)})'
@@ -267,15 +267,15 @@ class AddrFile(MMGenObject):
 				p.set_pw_fmt(ss[0])
 				p.set_pw_fmt(ss[0])
 				p.set_pw_len(ss[1])
 				p.set_pw_len(ss[1])
 				p.pw_id_str = MMGenPWIDString(ls.pop())
 				p.pw_id_str = MMGenPWIDString(ls.pop())
-				modname,funcname = p.pw_info[p.pw_fmt].chk_func.split('.')
+				modname, funcname = p.pw_info[p.pw_fmt].chk_func.split('.')
 				import importlib
 				import importlib
-				p.chk_func = getattr(importlib.import_module('mmgen.'+modname),funcname)
-				proto = init_proto( p.cfg, 'btc' ) # FIXME: dummy protocol
-				mmtype = MMGenPasswordType(proto,'P')
+				p.chk_func = getattr(importlib.import_module('mmgen.'+modname), funcname)
+				proto = init_proto(p.cfg, 'btc') # FIXME: dummy protocol
+				mmtype = MMGenPasswordType(proto, 'P')
 			elif len(ls) == 1:
 			elif len(ls) == 1:
-				proto,mmtype = parse_addrfile_label(ls[0])
+				proto, mmtype = parse_addrfile_label(ls[0])
 			elif len(ls) == 0:
 			elif len(ls) == 0:
-				proto = init_proto( p.cfg, 'btc' )
+				proto = init_proto(p.cfg, 'btc')
 				mmtype = proto.addr_type('L')
 				mmtype = proto.addr_type('L')
 			else:
 			else:
 				raise ValueError(f'{lines[0]}: Invalid first line for {p.gen_desc} file {fn!r}')
 				raise ValueError(f'{lines[0]}: Invalid first line for {p.gen_desc} file {fn!r}')
@@ -289,22 +289,22 @@ class AddrFile(MMGenObject):
 					raise ValueError(
 					raise ValueError(
 						f'{p.desc} file is '
 						f'{p.desc} file is '
 						+ f'{proto.base_coin} {proto.network} but protocol is '
 						+ f'{proto.base_coin} {proto.network} but protocol is '
-						+ f'{p.proto.base_coin} {p.proto.network}' )
+						+ f'{p.proto.base_coin} {p.proto.network}')
 
 
 			p.base_coin = proto.base_coin
 			p.base_coin = proto.base_coin
 			p.network = proto.network
 			p.network = proto.network
-			p.al_id = AddrListID( sid=SeedID(sid=sid), mmtype=mmtype )
+			p.al_id = AddrListID(sid=SeedID(sid=sid), mmtype=mmtype)
 
 
 			data = self.parse_file_body(lines[1:-1])
 			data = self.parse_file_body(lines[1:-1])
-			assert isinstance(data,list),'Invalid file body data'
+			assert isinstance(data, list), 'Invalid file body data'
 		except Exception as e:
 		except Exception as e:
 			m = 'Invalid data in {} list file {!r}{} ({!s})'.format(
 			m = 'Invalid data in {} list file {!r}{} ({!s})'.format(
 				p.desc,
 				p.desc,
 				self.infile,
 				self.infile,
 				(f', content line {self.line_ctr}' if self.line_ctr else ''),
 				(f', content line {self.line_ctr}' if self.line_ctr else ''),
-				e )
+				e)
 			if exit_on_error:
 			if exit_on_error:
-				die(3,m)
+				die(3, m)
 			else:
 			else:
 				msg(m)
 				msg(m)
 				return False
 				return False
@@ -339,13 +339,13 @@ class PasswordFile(AddrFile):
 # password.  The label may contain any printable ASCII symbol.
 # password.  The label may contain any printable ASCII symbol.
 """
 """
 
 
-	def get_line(self,lines):
+	def get_line(self, lines):
 
 
 		self.line_ctr += 1
 		self.line_ctr += 1
 		p = self.parent
 		p = self.parent
 
 
-		if p.pw_fmt in ('bip39','xmrseed'):
-			ret = lines.pop(0).split(None,p.pw_len+1)
+		if p.pw_fmt in ('bip39', 'xmrseed'):
+			ret = lines.pop(0).split(None, p.pw_len+1)
 			if len(ret) > p.pw_len+1:
 			if len(ret) > p.pw_len+1:
 				m1 = f'extraneous text {ret[p.pw_len+1]!r} found after password'
 				m1 = f'extraneous text {ret[p.pw_len+1]!r} found after password'
 				m2 = '[bare comments not allowed in BIP39 password files]'
 				m2 = '[bare comments not allowed in BIP39 password files]'
@@ -353,10 +353,10 @@ class PasswordFile(AddrFile):
 			elif len(ret) < p.pw_len+1:
 			elif len(ret) < p.pw_len+1:
 				m = f'invalid password length {len(ret)-1}'
 				m = f'invalid password length {len(ret)-1}'
 			else:
 			else:
-				return (ret[0],' '.join(ret[1:p.pw_len+1]),'')
+				return (ret[0], ' '.join(ret[1:p.pw_len+1]), '')
 			raise ValueError(m)
 			raise ValueError(m)
 		else:
 		else:
-			ret = lines.pop(0).split(None,2)
+			ret = lines.pop(0).split(None, 2)
 			return ret if len(ret) == 3 else ret + ['']
 			return ret if len(ret) == 3 else ret + ['']
 
 
 	def make_label(self):
 	def make_label(self):

+ 9 - 9
mmgen/addrgen.py

@@ -24,19 +24,19 @@ from .keygen import KeyGenerator # convenience import
 
 
 # decorator for to_addr() and to_viewkey()
 # decorator for to_addr() and to_viewkey()
 def check_data(orig_func):
 def check_data(orig_func):
-	def f(self,data):
+	def f(self, data):
 		assert data.pubkey_type == self.pubkey_type, 'addrgen.py:check_data() pubkey_type mismatch'
 		assert data.pubkey_type == self.pubkey_type, 'addrgen.py:check_data() pubkey_type mismatch'
-		assert data.compressed == self.compressed,(
+		assert data.compressed == self.compressed, (
 	f'addrgen.py:check_data() expected compressed={self.compressed} but got compressed={data.compressed}'
 	f'addrgen.py:check_data() expected compressed={self.compressed} but got compressed={data.compressed}'
 		)
 		)
-		return orig_func(self,data)
+		return orig_func(self, data)
 	return f
 	return f
 
 
 class addr_generator:
 class addr_generator:
 
 
 	class base:
 	class base:
 
 
-		def __init__(self,cfg,proto,addr_type):
+		def __init__(self, cfg, proto, addr_type):
 			self.proto = proto
 			self.proto = proto
 			self.pubkey_type = addr_type.pubkey_type
 			self.pubkey_type = addr_type.pubkey_type
 			self.compressed = addr_type.compressed
 			self.compressed = addr_type.compressed
@@ -44,12 +44,12 @@ class addr_generator:
 
 
 	class keccak(base):
 	class keccak(base):
 
 
-		def __init__(self,cfg,proto,addr_type):
-			super().__init__(cfg,proto,addr_type)
+		def __init__(self, cfg, proto, addr_type):
+			super().__init__(cfg, proto, addr_type)
 			from .util2 import get_keccak
 			from .util2 import get_keccak
 			self.keccak_256 = get_keccak(cfg)
 			self.keccak_256 = get_keccak(cfg)
 
 
-def AddrGenerator(cfg,proto,addr_type):
+def AddrGenerator(cfg, proto, addr_type):
 	"""
 	"""
 	factory function returning an address generator for the specified address type
 	factory function returning an address generator for the specified address type
 	"""
 	"""
@@ -67,7 +67,7 @@ def AddrGenerator(cfg,proto,addr_type):
 	from .addr import MMGenAddrType
 	from .addr import MMGenAddrType
 
 
 	if type(addr_type) is str:
 	if type(addr_type) is str:
-		addr_type = MMGenAddrType(proto=proto,id_str=addr_type)
+		addr_type = MMGenAddrType(proto=proto, id_str=addr_type)
 	elif type(addr_type) is MMGenAddrType:
 	elif type(addr_type) is MMGenAddrType:
 		assert addr_type in proto.mmtypes, f'{addr_type}: invalid address type for coin {proto.coin}'
 		assert addr_type in proto.mmtypes, f'{addr_type}: invalid address type for coin {proto.coin}'
 	else:
 	else:
@@ -76,4 +76,4 @@ def AddrGenerator(cfg,proto,addr_type):
 	import importlib
 	import importlib
 	return getattr(
 	return getattr(
 		importlib.import_module(f'mmgen.proto.{package_map[addr_type.name]}.addrgen'),
 		importlib.import_module(f'mmgen.proto.{package_map[addr_type.name]}.addrgen'),
-		addr_type.name )(cfg,proto,addr_type)
+		addr_type.name)(cfg, proto, addr_type)

+ 81 - 81
mmgen/addrlist.py

@@ -20,17 +20,17 @@
 addrlist: Address list classes for the MMGen suite
 addrlist: Address list classes for the MMGen suite
 """
 """
 
 
-from .util import suf,make_chksum_N,Msg,die
-from .objmethods import MMGenObject,HiliteStr,InitErrors
-from .obj import MMGenListItem,ListItemAttr,MMGenDict,TwComment,WalletPassword
+from .util import suf, make_chksum_N, Msg, die
+from .objmethods import MMGenObject, HiliteStr, InitErrors
+from .obj import MMGenListItem, ListItemAttr, MMGenDict, TwComment, WalletPassword
 from .key import PrivKey
 from .key import PrivKey
-from .addr import MMGenID,MMGenAddrType,CoinAddr,AddrIdx,AddrListID,ViewKey
+from .addr import MMGenID, MMGenAddrType, CoinAddr, AddrIdx, AddrListID, ViewKey
 
 
-class AddrIdxList(tuple,InitErrors,MMGenObject):
+class AddrIdxList(tuple, InitErrors, MMGenObject):
 
 
 	max_len = 1000000
 	max_len = 1000000
 
 
-	def __new__(cls,fmt_str=None,idx_list=None,sep=','):
+	def __new__(cls, fmt_str=None, idx_list=None, sep=','):
 		try:
 		try:
 			if fmt_str:
 			if fmt_str:
 				def gen():
 				def gen():
@@ -45,9 +45,9 @@ class AddrIdxList(tuple,InitErrors,MMGenObject):
 						else:
 						else:
 							raise ValueError(f'{i}: invalid range')
 							raise ValueError(f'{i}: invalid range')
 				idx_list = tuple(gen())
 				idx_list = tuple(gen())
-			return tuple.__new__(cls,sorted({AddrIdx(i) for i in (idx_list or [])}))
+			return tuple.__new__(cls, sorted({AddrIdx(i) for i in (idx_list or [])}))
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,idx_list or fmt_str)
+			return cls.init_fail(e, idx_list or fmt_str)
 
 
 	@property
 	@property
 	def id_str(self):
 	def id_str(self):
@@ -74,36 +74,36 @@ class AddrIdxList(tuple,InitErrors,MMGenObject):
 
 
 class AddrListEntryBase(MMGenListItem):
 class AddrListEntryBase(MMGenListItem):
 	invalid_attrs = {'proto'}
 	invalid_attrs = {'proto'}
-	def __init__(self,proto,**kwargs):
+	def __init__(self, proto, **kwargs):
 		self.__dict__['proto'] = proto
 		self.__dict__['proto'] = proto
-		MMGenListItem.__init__(self,**kwargs)
+		MMGenListItem.__init__(self, **kwargs)
 
 
 class AddrListEntry(AddrListEntryBase):
 class AddrListEntry(AddrListEntryBase):
-	addr          = ListItemAttr(CoinAddr,include_proto=True)
-	addr_p2pkh    = ListItemAttr(CoinAddr,include_proto=True)
+	addr          = ListItemAttr(CoinAddr, include_proto=True)
+	addr_p2pkh    = ListItemAttr(CoinAddr, include_proto=True)
 	idx           = ListItemAttr(AddrIdx) # not present in flat addrlists
 	idx           = ListItemAttr(AddrIdx) # not present in flat addrlists
-	comment       = ListItemAttr(TwComment,reassign_ok=True)
-	sec           = ListItemAttr(PrivKey,include_proto=True)
-	viewkey       = ListItemAttr(ViewKey,include_proto=True)
+	comment       = ListItemAttr(TwComment, reassign_ok=True)
+	sec           = ListItemAttr(PrivKey, include_proto=True)
+	viewkey       = ListItemAttr(ViewKey, include_proto=True)
 	wallet_passwd = ListItemAttr(WalletPassword)
 	wallet_passwd = ListItemAttr(WalletPassword)
 
 
 class AddrListChksum(HiliteStr):
 class AddrListChksum(HiliteStr):
 	color = 'pink'
 	color = 'pink'
 	trunc_ok = False
 	trunc_ok = False
 
 
-	def __new__(cls,addrlist):
+	def __new__(cls, addrlist):
 		ea = addrlist.al_id.mmtype.extra_attrs or () # add viewkey and passwd to the mix, if present
 		ea = addrlist.al_id.mmtype.extra_attrs or () # add viewkey and passwd to the mix, if present
 		lines = [' '.join(
 		lines = [' '.join(
 					addrlist.chksum_rec_f(e) +
 					addrlist.chksum_rec_f(e) +
-					tuple(getattr(e,a) for a in ea if getattr(e,a))
+					tuple(getattr(e, a) for a in ea if getattr(e, a))
 				) for e in addrlist.data]
 				) for e in addrlist.data]
-		return str.__new__(cls,make_chksum_N(' '.join(lines), nchars=16, sep=True))
+		return str.__new__(cls, make_chksum_N(' '.join(lines), nchars=16, sep=True))
 
 
 class AddrListIDStr(HiliteStr):
 class AddrListIDStr(HiliteStr):
 	color = 'green'
 	color = 'green'
 	trunc_ok = False
 	trunc_ok = False
 
 
-	def __new__(cls,addrlist,fmt_str=None):
+	def __new__(cls, addrlist, fmt_str=None):
 		idxs = [e.idx for e in addrlist.data]
 		idxs = [e.idx for e in addrlist.data]
 		prev = idxs[0]
 		prev = idxs[0]
 		ret = [prev]
 		ret = [prev]
@@ -116,7 +116,7 @@ class AddrListIDStr(HiliteStr):
 					ret.extend(['-', prev])
 					ret.extend(['-', prev])
 				ret.extend([',', i])
 				ret.extend([',', i])
 			prev = i
 			prev = i
-		s = ''.join(map(str,ret))
+		s = ''.join(map(str, ret))
 
 
 		if fmt_str:
 		if fmt_str:
 			ret = fmt_str.format(s)
 			ret = fmt_str.format(s)
@@ -127,14 +127,14 @@ class AddrListIDStr(HiliteStr):
 			ret = '{}{}{}[{}]'.format(
 			ret = '{}{}{}[{}]'.format(
 				addrlist.al_id.sid,
 				addrlist.al_id.sid,
 				(f'-{coin}', '')[coin == 'BTC'],
 				(f'-{coin}', '')[coin == 'BTC'],
-				(f'-{mmtype}', '')[mmtype in ('L','E')],
+				(f'-{mmtype}', '')[mmtype in ('L', 'E')],
 				s)
 				s)
 
 
-		addrlist.dmsg_sc('id_str',ret[8:].split('[')[0])
+		addrlist.dmsg_sc('id_str', ret[8:].split('[')[0])
 
 
-		return str.__new__(cls,ret)
+		return str.__new__(cls, ret)
 
 
-class AddrListData(list,MMGenObject):
+class AddrListData(list, MMGenObject):
 	pass
 	pass
 
 
 class AddrList(MMGenObject): # Address info for a single seed ID
 class AddrList(MMGenObject): # Address info for a single seed ID
@@ -149,10 +149,10 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 	has_keys     = False
 	has_keys     = False
 	chksum_rec_f = lambda foo, e: (str(e.idx), e.addr.views[e.addr.view_pref])
 	chksum_rec_f = lambda foo, e: (str(e.idx), e.addr.views[e.addr.view_pref])
 
 
-	def dmsg_sc(self,desc,data):
+	def dmsg_sc(self, desc, data):
 		Msg(f'sc_debug_{desc}: {data}')
 		Msg(f'sc_debug_{desc}: {data}')
 
 
-	def noop(self,desc,data):
+	def noop(self, desc, data):
 		pass
 		pass
 
 
 	def __init__(
 	def __init__(
@@ -171,7 +171,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 			key_address_validity_check = None, # None=prompt user, True=check without prompt, False=skip check
 			key_address_validity_check = None, # None=prompt user, True=check without prompt, False=skip check
 			skip_chksum = False,
 			skip_chksum = False,
 			skip_chksum_msg = False,
 			skip_chksum_msg = False,
-			add_p2pkh = False ):
+			add_p2pkh = False):
 
 
 		self.cfg = cfg
 		self.cfg = cfg
 		self.ka_validity_chk = key_address_validity_check
 		self.ka_validity_chk = key_address_validity_check
@@ -183,9 +183,9 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 			self.dmsg_sc = self.noop
 			self.dmsg_sc = self.noop
 
 
 		if seed and addr_idxs:   # data from seed + idxs
 		if seed and addr_idxs:   # data from seed + idxs
-			self.al_id = AddrListID( sid=seed.sid, mmtype=MMGenAddrType(proto, mmtype or proto.dfl_mmtype) )
+			self.al_id = AddrListID(sid=seed.sid, mmtype=MMGenAddrType(proto, mmtype or proto.dfl_mmtype))
 			src = 'gen'
 			src = 'gen'
-			adata = self.generate(seed, addr_idxs if isinstance(addr_idxs,AddrIdxList) else AddrIdxList(addr_idxs))
+			adata = self.generate(seed, addr_idxs if isinstance(addr_idxs, AddrIdxList) else AddrIdxList(addr_idxs))
 			do_chksum = True
 			do_chksum = True
 		elif addrfile:           # data from MMGen address file
 		elif addrfile:           # data from MMGen address file
 			self.infile = addrfile
 			self.infile = addrfile
@@ -196,21 +196,21 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 		elif addrlist:           # data from flat address list
 		elif addrlist:           # data from flat address list
 			self.al_id = None
 			self.al_id = None
 			from .util import remove_dups
 			from .util import remove_dups
-			addrlist = remove_dups(addrlist,edesc='address',desc='address list')
-			adata = AddrListData([AddrListEntry(proto=proto,addr=a) for a in addrlist])
+			addrlist = remove_dups(addrlist, edesc='address', desc='address list')
+			adata = AddrListData([AddrListEntry(proto=proto, addr=a) for a in addrlist])
 		elif keylist:            # data from flat key list
 		elif keylist:            # data from flat key list
 			self.al_id = None
 			self.al_id = None
 			from .util import remove_dups
 			from .util import remove_dups
-			keylist = remove_dups(keylist,edesc='key',desc='key list',hide=True)
-			adata = AddrListData([AddrListEntry(proto=proto,sec=PrivKey(proto=proto,wif=k)) for k in keylist])
+			keylist = remove_dups(keylist, edesc='key', desc='key list', hide=True)
+			adata = AddrListData([AddrListEntry(proto=proto, sec=PrivKey(proto=proto, wif=k)) for k in keylist])
 		elif seed or addr_idxs:
 		elif seed or addr_idxs:
-			die(3,'Must specify both seed and addr indexes')
+			die(3, 'Must specify both seed and addr indexes')
 		elif al_id or adata:
 		elif al_id or adata:
-			die(3,'Must specify both al_id and adata')
+			die(3, 'Must specify both al_id and adata')
 		else:
 		else:
-			die(3,f'Incorrect arguments for {type(self).__name__}')
+			die(3, f'Incorrect arguments for {type(self).__name__}')
 
 
-		# al_id,adata now set
+		# al_id, adata now set
 		self.data = adata
 		self.data = adata
 		self.num_addrs = len(adata)
 		self.num_addrs = len(adata)
 		self.fmt_data = ''
 		self.fmt_data = ''
@@ -221,7 +221,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 
 
 		if type(self) is ViewKeyAddrList:
 		if type(self) is ViewKeyAddrList:
 			if not 'viewkey' in self.al_id.mmtype.extra_attrs:
 			if not 'viewkey' in self.al_id.mmtype.extra_attrs:
-				die(1,f'viewkeys not supported for address type {self.al_id.mmtype.desc!r}')
+				die(1, f'viewkeys not supported for address type {self.al_id.mmtype.desc!r}')
 
 
 		self.id_str = AddrListIDStr(self)
 		self.id_str = AddrListIDStr(self)
 
 
@@ -233,30 +233,30 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 			if not skip_chksum_msg:
 			if not skip_chksum_msg:
 				self.do_chksum_msg(record=src=='gen')
 				self.do_chksum_msg(record=src=='gen')
 
 
-	def do_chksum_msg(self,record):
+	def do_chksum_msg(self, record):
 		chk = 'Check this value against your records'
 		chk = 'Check this value against your records'
 		rec = f'Record this checksum: it will be used to verify the {self.desc} file in the future'
 		rec = f'Record this checksum: it will be used to verify the {self.desc} file in the future'
 		self.cfg._util.qmsg(
 		self.cfg._util.qmsg(
 			f'Checksum for {self.desc} data {self.id_str.hl()}: {self.chksum.hl()}\n' +
 			f'Checksum for {self.desc} data {self.id_str.hl()}: {self.chksum.hl()}\n' +
-			(chk,rec)[record] )
+			(chk, rec)[record])
 
 
-	def generate(self,seed,addr_idxs):
+	def generate(self, seed, addr_idxs):
 
 
 		seed = self.scramble_seed(seed.data)
 		seed = self.scramble_seed(seed.data)
-		self.dmsg_sc('seed',seed[:8].hex())
+		self.dmsg_sc('seed', seed[:8].hex())
 
 
 		mmtype = self.al_id.mmtype
 		mmtype = self.al_id.mmtype
 
 
-		gen_wallet_passwd = type(self) in (KeyAddrList,ViewKeyAddrList) and 'wallet_passwd' in mmtype.extra_attrs
-		gen_viewkey       = type(self) in (KeyAddrList,ViewKeyAddrList) and 'viewkey' in mmtype.extra_attrs
+		gen_wallet_passwd = type(self) in (KeyAddrList, ViewKeyAddrList) and 'wallet_passwd' in mmtype.extra_attrs
+		gen_viewkey       = type(self) in (KeyAddrList, ViewKeyAddrList) and 'viewkey' in mmtype.extra_attrs
 
 
 		if self.gen_addrs:
 		if self.gen_addrs:
 			from .keygen import KeyGenerator
 			from .keygen import KeyGenerator
 			from .addrgen import AddrGenerator
 			from .addrgen import AddrGenerator
-			kg = KeyGenerator( self.cfg, self.proto, mmtype.pubkey_type )
-			ag = AddrGenerator( self.cfg, self.proto, mmtype )
+			kg = KeyGenerator(self.cfg, self.proto, mmtype.pubkey_type)
+			ag = AddrGenerator(self.cfg, self.proto, mmtype)
 			if self.add_p2pkh:
 			if self.add_p2pkh:
-				ag2 = AddrGenerator( self.cfg, self.proto, 'compressed' )
+				ag2 = AddrGenerator(self.cfg, self.proto, 'compressed')
 
 
 		from .derive import derive_coin_privkey_bytes
 		from .derive import derive_coin_privkey_bytes
 
 
@@ -265,19 +265,19 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 		out = AddrListData()
 		out = AddrListData()
 		CR = '\n' if self.cfg.debug_addrlist else '\r'
 		CR = '\n' if self.cfg.debug_addrlist else '\r'
 
 
-		for pk_bytes in derive_coin_privkey_bytes(seed,addr_idxs):
+		for pk_bytes in derive_coin_privkey_bytes(seed, addr_idxs):
 
 
 			if not self.cfg.debug:
 			if not self.cfg.debug:
 				self.cfg._util.qmsg_r(
 				self.cfg._util.qmsg_r(
-					f'{CR}Generating {self.gen_desc} #{pk_bytes.idx} ({pk_bytes.pos} of {t_addrs})' )
+					f'{CR}Generating {self.gen_desc} #{pk_bytes.idx} ({pk_bytes.pos} of {t_addrs})')
 
 
-			e = le( proto=self.proto, idx=pk_bytes.idx )
+			e = le(proto=self.proto, idx=pk_bytes.idx)
 
 
 			e.sec = PrivKey(
 			e.sec = PrivKey(
 				self.proto,
 				self.proto,
 				pk_bytes.data,
 				pk_bytes.data,
 				compressed  = mmtype.compressed,
 				compressed  = mmtype.compressed,
-				pubkey_type = mmtype.pubkey_type )
+				pubkey_type = mmtype.pubkey_type)
 
 
 			if self.gen_addrs:
 			if self.gen_addrs:
 				data = kg.gen_data(e.sec)
 				data = kg.gen_data(e.sec)
@@ -288,7 +288,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 					e.viewkey = ag.to_viewkey(data)
 					e.viewkey = ag.to_viewkey(data)
 				if gen_wallet_passwd:
 				if gen_wallet_passwd:
 					e.wallet_passwd = self.gen_wallet_passwd(
 					e.wallet_passwd = self.gen_wallet_passwd(
-						e.viewkey.encode() if type(self) is ViewKeyAddrList else e.sec )
+						e.viewkey.encode() if type(self) is ViewKeyAddrList else e.sec)
 			elif self.gen_passwds:
 			elif self.gen_passwds:
 				e.passwd = self.gen_passwd(e.sec) # TODO - own type
 				e.passwd = self.gen_passwd(e.sec) # TODO - own type
 
 
@@ -299,32 +299,32 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 			self.al_id.hl(),
 			self.al_id.hl(),
 			t_addrs,
 			t_addrs,
 			self.gen_desc,
 			self.gen_desc,
-			suf(t_addrs,self.gen_desc_pl),
-			' ' * 15 ))
+			suf(t_addrs, self.gen_desc_pl),
+			' ' * 15))
 
 
 		return out
 		return out
 
 
-	def gen_wallet_passwd(self,privbytes):
+	def gen_wallet_passwd(self, privbytes):
 		from .proto.btc.common import hash256
 		from .proto.btc.common import hash256
-		return WalletPassword( hash256(privbytes)[:16].hex() )
+		return WalletPassword(hash256(privbytes)[:16].hex())
 
 
-	def check_format(self,addr):
+	def check_format(self, addr):
 		return True # format is checked when added to list entry object
 		return True # format is checked when added to list entry object
 
 
-	def scramble_seed(self,seed):
+	def scramble_seed(self, seed):
 		is_btcfork = self.proto.base_coin == 'BTC'
 		is_btcfork = self.proto.base_coin == 'BTC'
 		if is_btcfork and self.al_id.mmtype == 'L' and not self.proto.testnet:
 		if is_btcfork and self.al_id.mmtype == 'L' and not self.proto.testnet:
-			self.dmsg_sc('str','(none)')
+			self.dmsg_sc('str', '(none)')
 			return seed
 			return seed
 		if self.proto.base_coin == 'ETH':
 		if self.proto.base_coin == 'ETH':
 			scramble_key = self.proto.coin.lower()
 			scramble_key = self.proto.coin.lower()
 		else:
 		else:
-			scramble_key = (self.proto.coin.lower()+':','')[is_btcfork] + self.al_id.mmtype.name
+			scramble_key = (self.proto.coin.lower()+':', '')[is_btcfork] + self.al_id.mmtype.name
 		from .crypto import Crypto
 		from .crypto import Crypto
 		if self.proto.testnet:
 		if self.proto.testnet:
 			scramble_key += ':' + self.proto.network
 			scramble_key += ':' + self.proto.network
-		self.dmsg_sc('str',scramble_key)
-		return Crypto(self.cfg).scramble_seed(seed,scramble_key.encode())
+		self.dmsg_sc('str', scramble_key)
+		return Crypto(self.cfg).scramble_seed(seed, scramble_key.encode())
 
 
 	def idxs(self):
 	def idxs(self):
 		return [e.idx for e in self.data]
 		return [e.idx for e in self.data]
@@ -333,7 +333,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 		return [f'{self.al_id.sid}:{e.idx}' for e in self.data]
 		return [f'{self.al_id.sid}:{e.idx}' for e in self.data]
 
 
 	def addrpairs(self):
 	def addrpairs(self):
-		return [(e.idx,e.addr) for e in self.data]
+		return [(e.idx, e.addr) for e in self.data]
 
 
 	def coinaddrs(self):
 	def coinaddrs(self):
 		return [e.addr for e in self.data]
 		return [e.addr for e in self.data]
@@ -341,57 +341,57 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 	def comments(self):
 	def comments(self):
 		return [e.comment for e in self.data]
 		return [e.comment for e in self.data]
 
 
-	def entry(self,idx):
+	def entry(self, idx):
 		for e in self.data:
 		for e in self.data:
 			if idx == e.idx:
 			if idx == e.idx:
 				return e
 				return e
 
 
-	def coinaddr(self,idx):
+	def coinaddr(self, idx):
 		for e in self.data:
 		for e in self.data:
 			if idx == e.idx:
 			if idx == e.idx:
 				return e.addr
 				return e.addr
 
 
-	def comment(self,idx):
+	def comment(self, idx):
 		for e in self.data:
 		for e in self.data:
 			if idx == e.idx:
 			if idx == e.idx:
 				return e.comment
 				return e.comment
 
 
-	def set_comment(self,idx,comment):
+	def set_comment(self, idx, comment):
 		for e in self.data:
 		for e in self.data:
 			if idx == e.idx:
 			if idx == e.idx:
 				e.comment = comment
 				e.comment = comment
 
 
-	def make_reverse_dict_addrlist(self,coinaddrs):
+	def make_reverse_dict_addrlist(self, coinaddrs):
 		d = MMGenDict()
 		d = MMGenDict()
 		b = coinaddrs
 		b = coinaddrs
 		for e in self.data:
 		for e in self.data:
 			try:
 			try:
-				d[b[b.index(e.addr)]] = ( MMGenID(self.proto, f'{self.al_id}:{e.idx}'), e.comment )
+				d[b[b.index(e.addr)]] = (MMGenID(self.proto, f'{self.al_id}:{e.idx}'), e.comment)
 			except ValueError:
 			except ValueError:
 				pass
 				pass
 		return d
 		return d
 
 
-	def add_wifs(self,key_list):
+	def add_wifs(self, key_list):
 		"""
 		"""
 		Match WIF keys in a flat list to addresses in self by generating all
 		Match WIF keys in a flat list to addresses in self by generating all
 		possible addresses for each key.
 		possible addresses for each key.
 		"""
 		"""
-		def gen_addr(pk,t):
+		def gen_addr(pk, t):
 			at = self.proto.addr_type(t)
 			at = self.proto.addr_type(t)
 			from .keygen import KeyGenerator
 			from .keygen import KeyGenerator
 			from .addrgen import AddrGenerator
 			from .addrgen import AddrGenerator
-			kg = KeyGenerator( self.cfg, self.proto, at.pubkey_type )
-			ag = AddrGenerator( self.cfg, self.proto, at )
+			kg = KeyGenerator(self.cfg, self.proto, at.pubkey_type)
+			ag = AddrGenerator(self.cfg, self.proto, at)
 			return ag.to_addr(kg.gen_data(pk))
 			return ag.to_addr(kg.gen_data(pk))
 
 
-		compressed_types = set(self.proto.mmtypes) - {'L','E'}
-		uncompressed_types = set(self.proto.mmtypes) & {'L','E'}
+		compressed_types = set(self.proto.mmtypes) - {'L', 'E'}
+		uncompressed_types = set(self.proto.mmtypes) & {'L', 'E'}
 
 
 		def gen():
 		def gen():
 			for wif in key_list:
 			for wif in key_list:
-				pk = PrivKey(proto=self.proto,wif=wif)
+				pk = PrivKey(proto=self.proto, wif=wif)
 				for t in (compressed_types if pk.compressed else uncompressed_types):
 				for t in (compressed_types if pk.compressed else uncompressed_types):
-					yield ( gen_addr(pk,t), pk )
+					yield (gen_addr(pk, t), pk)
 
 
 		addrs4keys = dict(gen())
 		addrs4keys = dict(gen())
 
 
@@ -399,14 +399,14 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 			if d.addr in addrs4keys:
 			if d.addr in addrs4keys:
 				d.sec = addrs4keys[d.addr]
 				d.sec = addrs4keys[d.addr]
 
 
-	def list_missing(self,attr):
-		return [d.addr for d in self.data if not getattr(d,attr)]
+	def list_missing(self, attr):
+		return [d.addr for d in self.data if not getattr(d, attr)]
 
 
 	@property
 	@property
 	def file(self):
 	def file(self):
-		if not hasattr(self,'_file'):
+		if not hasattr(self, '_file'):
 			from . import addrfile
 			from . import addrfile
-			self._file = getattr( addrfile, type(self).__name__.replace('List','File') )(self)
+			self._file = getattr(addrfile, type(self).__name__.replace('List', 'File'))(self)
 		return self._file
 		return self._file
 
 
 class KeyAddrList(AddrList):
 class KeyAddrList(AddrList):

+ 80 - 80
mmgen/autosign.py

@@ -20,7 +20,7 @@ from subprocess import run, PIPE, DEVNULL
 from .cfg import Config
 from .cfg import Config
 from .util import msg, msg_r, ymsg, rmsg, gmsg, bmsg, die, suf, fmt, fmt_list, is_int, have_sudo, capfirst
 from .util import msg, msg_r, ymsg, rmsg, gmsg, bmsg, die, suf, fmt, fmt_list, is_int, have_sudo, capfirst
 from .color import yellow, red, orange, brown, blue
 from .color import yellow, red, orange, brown, blue
-from .wallet import Wallet,get_wallet_cls
+from .wallet import Wallet, get_wallet_cls
 from .addrlist import AddrIdxList
 from .addrlist import AddrIdxList
 from .filename import find_file_in_dir
 from .filename import find_file_in_dir
 from .ui import keypress_confirm
 from .ui import keypress_confirm
@@ -135,10 +135,10 @@ class Signable:
 		multiple_ok = True
 		multiple_ok = True
 		action_desc = 'signed'
 		action_desc = 'signed'
 
 
-		def __init__(self,parent):
+		def __init__(self, parent):
 			self.parent = parent
 			self.parent = parent
 			self.cfg = parent.cfg
 			self.cfg = parent.cfg
-			self.dir = getattr(parent,self.dir_name)
+			self.dir = getattr(parent, self.dir_name)
 			self.name = type(self).__name__
 			self.name = type(self).__name__
 
 
 		@property
 		@property
@@ -152,21 +152,21 @@ class Signable:
 
 
 		@property
 		@property
 		def unsigned(self):
 		def unsigned(self):
-			return self._unprocessed( '_unsigned', self.rawext, self.sigext )
+			return self._unprocessed('_unsigned', self.rawext, self.sigext)
 
 
 		@property
 		@property
 		def unsubmitted(self):
 		def unsubmitted(self):
-			return self._unprocessed( '_unsubmitted', self.sigext, self.subext )
+			return self._unprocessed('_unsubmitted', self.sigext, self.subext)
 
 
 		@property
 		@property
 		def unsubmitted_raw(self):
 		def unsubmitted_raw(self):
-			return self._unprocessed( '_unsubmitted_raw', self.rawext, self.subext )
+			return self._unprocessed('_unsubmitted_raw', self.rawext, self.subext)
 
 
 		unsent = unsubmitted
 		unsent = unsubmitted
 		unsent_raw = unsubmitted_raw
 		unsent_raw = unsubmitted_raw
 
 
-		def _unprocessed(self,attrname,rawext,sigext):
-			if not hasattr(self,attrname):
+		def _unprocessed(self, attrname, rawext, sigext):
+			if not hasattr(self, attrname):
 				dirlist = sorted(self.dir.iterdir())
 				dirlist = sorted(self.dir.iterdir())
 				names = {f.name for f in dirlist}
 				names = {f.name for f in dirlist}
 				setattr(
 				setattr(
@@ -174,13 +174,13 @@ class Signable:
 					attrname,
 					attrname,
 					tuple(f for f in dirlist
 					tuple(f for f in dirlist
 						if f.name.endswith('.' + rawext)
 						if f.name.endswith('.' + rawext)
-							and f.name[:-len(rawext)] + sigext not in names) )
-			return getattr(self,attrname)
+							and f.name[:-len(rawext)] + sigext not in names))
+			return getattr(self, attrname)
 
 
-		def print_bad_list(self,bad_files):
+		def print_bad_list(self, bad_files):
 			msg('\n{a}\n{b}'.format(
 			msg('\n{a}\n{b}'.format(
 				a = red(f'Failed {self.desc}s:'),
 				a = red(f'Failed {self.desc}s:'),
-				b = '  {}\n'.format('\n  '.join(self.gen_bad_list(sorted(bad_files,key=lambda f: f.name))))
+				b = '  {}\n'.format('\n  '.join(self.gen_bad_list(sorted(bad_files, key=lambda f: f.name))))
 			))
 			))
 
 
 		def die_wrong_num_txs(self, tx_type, msg=None, desc=None, show_dir=False):
 		def die_wrong_num_txs(self, tx_type, msg=None, desc=None, show_dir=False):
@@ -255,7 +255,7 @@ class Signable:
 		dir_name = 'tx_dir'
 		dir_name = 'tx_dir'
 		fail_msg = 'failed to sign'
 		fail_msg = 'failed to sign'
 
 
-		async def sign(self,f):
+		async def sign(self, f):
 			from .tx import UnsignedTX
 			from .tx import UnsignedTX
 			tx1 = UnsignedTX(
 			tx1 = UnsignedTX(
 					cfg       = self.cfg,
 					cfg       = self.cfg,
@@ -263,7 +263,7 @@ class Signable:
 					automount = self.name=='automount_transaction')
 					automount = self.name=='automount_transaction')
 			if tx1.proto.sign_mode == 'daemon':
 			if tx1.proto.sign_mode == 'daemon':
 				from .rpc import rpc_init
 				from .rpc import rpc_init
-				tx1.rpc = await rpc_init( self.cfg, tx1.proto, ignore_wallet=True )
+				tx1.rpc = await rpc_init(self.cfg, tx1.proto, ignore_wallet=True)
 			from .tx.sign import txsign
 			from .tx.sign import txsign
 			tx2 = await txsign(
 			tx2 = await txsign(
 					cfg_parm    = self.cfg,
 					cfg_parm    = self.cfg,
@@ -278,7 +278,7 @@ class Signable:
 			else:
 			else:
 				return False
 				return False
 
 
-		def print_summary(self,signables):
+		def print_summary(self, signables):
 
 
 			if self.cfg.full_summary:
 			if self.cfg.full_summary:
 				bmsg('\nAutosign summary:\n')
 				bmsg('\nAutosign summary:\n')
@@ -289,22 +289,22 @@ class Signable:
 				for tx in signables:
 				for tx in signables:
 					non_mmgen = [o for o in tx.outputs if not o.mmid]
 					non_mmgen = [o for o in tx.outputs if not o.mmid]
 					if non_mmgen:
 					if non_mmgen:
-						yield (tx,non_mmgen)
+						yield (tx, non_mmgen)
 
 
 			body = list(gen())
 			body = list(gen())
 
 
 			if body:
 			if body:
 				bmsg('\nAutosign summary:')
 				bmsg('\nAutosign summary:')
 				fs = '{}  {} {}'
 				fs = '{}  {} {}'
-				t_wid,a_wid = 6,44
+				t_wid, a_wid = 6, 44
 
 
 				def gen():
 				def gen():
-					yield fs.format('TX ID ','Non-MMGen outputs'+' '*(a_wid-17),'Amount')
+					yield fs.format('TX ID ', 'Non-MMGen outputs'+' '*(a_wid-17), 'Amount')
 					yield fs.format('-'*t_wid, '-'*a_wid, '-'*7)
 					yield fs.format('-'*t_wid, '-'*a_wid, '-'*7)
-					for tx,non_mmgen in body:
+					for tx, non_mmgen in body:
 						for nm in non_mmgen:
 						for nm in non_mmgen:
 							yield fs.format(
 							yield fs.format(
-								tx.txid.fmt( width=t_wid, color=True ) if nm is non_mmgen[0] else ' '*t_wid,
+								tx.txid.fmt(width=t_wid, color=True) if nm is non_mmgen[0] else ' '*t_wid,
 								nm.addr.fmt(nm.addr.view_pref, width=a_wid, color=True),
 								nm.addr.fmt(nm.addr.view_pref, width=a_wid, color=True),
 								nm.amt.hl() + ' ' + yellow(tx.coin))
 								nm.amt.hl() + ' ' + yellow(tx.coin))
 
 
@@ -312,7 +312,7 @@ class Signable:
 			else:
 			else:
 				msg('\nNo non-MMGen outputs')
 				msg('\nNo non-MMGen outputs')
 
 
-		def gen_bad_list(self,bad_files):
+		def gen_bad_list(self, bad_files):
 			for f in bad_files:
 			for f in bad_files:
 				yield red(f.name)
 				yield red(f.name)
 
 
@@ -326,12 +326,12 @@ class Signable:
 
 
 	class xmr_signable(transaction): # mixin class
 	class xmr_signable(transaction): # mixin class
 
 
-		def need_daemon_restart(self,m,new_idx):
+		def need_daemon_restart(self, m, new_idx):
 			old_idx = self.parent.xmr_cur_wallet_idx
 			old_idx = self.parent.xmr_cur_wallet_idx
 			self.parent.xmr_cur_wallet_idx = new_idx
 			self.parent.xmr_cur_wallet_idx = new_idx
 			return old_idx != new_idx or m.wd.state != 'ready'
 			return old_idx != new_idx or m.wd.state != 'ready'
 
 
-		def print_summary(self,signables):
+		def print_summary(self, signables):
 			bmsg('\nAutosign summary:')
 			bmsg('\nAutosign summary:')
 			msg('\n'.join(s.get_info(indent='  ') for s in signables) + self.summary_footer)
 			msg('\n'.join(s.get_info(indent='  ') for s in signables) + self.summary_footer)
 
 
@@ -342,7 +342,7 @@ class Signable:
 		multiple_ok = False
 		multiple_ok = False
 		summary_footer = ''
 		summary_footer = ''
 
 
-		async def sign(self,f):
+		async def sign(self, f):
 			from . import xmrwallet
 			from . import xmrwallet
 			from .xmrwallet.file.tx import MoneroMMGenTX
 			from .xmrwallet.file.tx import MoneroMMGenTX
 			tx1 = MoneroMMGenTX.Completed(self.parent.xmrwallet_cfg, f)
 			tx1 = MoneroMMGenTX.Completed(self.parent.xmrwallet_cfg, f)
@@ -351,7 +351,7 @@ class Signable:
 				self.parent.xmrwallet_cfg,
 				self.parent.xmrwallet_cfg,
 				infile  = str(self.parent.wallet_files[0]), # MMGen wallet file
 				infile  = str(self.parent.wallet_files[0]), # MMGen wallet file
 				wallets = str(tx1.src_wallet_idx))
 				wallets = str(tx1.src_wallet_idx))
-			tx2 = await m.main( f, restart_daemon=self.need_daemon_restart(m,tx1.src_wallet_idx) )
+			tx2 = await m.main(f, restart_daemon=self.need_daemon_restart(m, tx1.src_wallet_idx))
 			tx2.write(ask_write=False)
 			tx2.write(ask_write=False)
 			return tx2
 			return tx2
 
 
@@ -370,7 +370,7 @@ class Signable:
 				f for f in super().unsigned
 				f for f in super().unsigned
 					if not json.loads(f.read_text())['MoneroMMGenWalletOutputsFile']['data']['imported'])
 					if not json.loads(f.read_text())['MoneroMMGenWalletOutputsFile']['data']['imported'])
 
 
-		async def sign(self,f):
+		async def sign(self, f):
 			from . import xmrwallet
 			from . import xmrwallet
 			wallet_idx = xmrwallet.op_cls('wallet').get_idx_from_fn(f)
 			wallet_idx = xmrwallet.op_cls('wallet').get_idx_from_fn(f)
 			m = xmrwallet.op(
 			m = xmrwallet.op(
@@ -378,7 +378,7 @@ class Signable:
 				self.parent.xmrwallet_cfg,
 				self.parent.xmrwallet_cfg,
 				infile  = str(self.parent.wallet_files[0]), # MMGen wallet file
 				infile  = str(self.parent.wallet_files[0]), # MMGen wallet file
 				wallets = str(wallet_idx))
 				wallets = str(wallet_idx))
-			obj = await m.main(f, wallet_idx, restart_daemon=self.need_daemon_restart(m,wallet_idx))
+			obj = await m.main(f, wallet_idx, restart_daemon=self.need_daemon_restart(m, wallet_idx))
 			obj.write(quiet=not obj.data.sign)
 			obj.write(quiet=not obj.data.sign)
 			self.action_desc = 'imported and signed' if obj.data.sign else 'imported'
 			self.action_desc = 'imported and signed' if obj.data.sign else 'imported'
 			return obj
 			return obj
@@ -390,26 +390,26 @@ class Signable:
 		dir_name = 'msg_dir'
 		dir_name = 'msg_dir'
 		fail_msg = 'failed to sign or signed incompletely'
 		fail_msg = 'failed to sign or signed incompletely'
 
 
-		async def sign(self,f):
-			from .msg import UnsignedMsg,SignedMsg
-			m = UnsignedMsg( self.cfg, infile=f )
+		async def sign(self, f):
+			from .msg import UnsignedMsg, SignedMsg
+			m = UnsignedMsg(self.cfg, infile=f)
 			await m.sign(wallet_files=self.parent.wallet_files[:], passwd_file=str(self.parent.keyfile))
 			await m.sign(wallet_files=self.parent.wallet_files[:], passwd_file=str(self.parent.keyfile))
-			m = SignedMsg( self.cfg, data=m.__dict__ )
+			m = SignedMsg(self.cfg, data=m.__dict__)
 			m.write_to_file(
 			m.write_to_file(
 				outdir = self.dir.resolve(),
 				outdir = self.dir.resolve(),
-				ask_overwrite = False )
+				ask_overwrite = False)
 			if m.data.get('failed_sids'):
 			if m.data.get('failed_sids'):
-				die('MsgFileFailedSID',f'Failed Seed IDs: {fmt_list(m.data["failed_sids"],fmt="bare")}')
+				die('MsgFileFailedSID', f'Failed Seed IDs: {fmt_list(m.data["failed_sids"], fmt="bare")}')
 			return m
 			return m
 
 
-		def print_summary(self,signables):
+		def print_summary(self, signables):
 			gmsg('\nSigned message files:')
 			gmsg('\nSigned message files:')
 			for message in signables:
 			for message in signables:
 				gmsg('  ' + message.signed_filename)
 				gmsg('  ' + message.signed_filename)
 
 
-		def gen_bad_list(self,bad_files):
+		def gen_bad_list(self, bad_files):
 			for f in bad_files:
 			for f in bad_files:
-				sigfile = f.parent / ( f.name[:-len(self.rawext)] + self.sigext )
+				sigfile = f.parent / (f.name[:-len(self.rawext)] + self.sigext)
 				yield orange(sigfile.name) if sigfile.exists() else red(f.name)
 				yield orange(sigfile.name) if sigfile.exists() else red(f.name)
 
 
 class Autosign:
 class Autosign:
@@ -453,13 +453,13 @@ class Autosign:
 	def init_fixup(self): # see test/overlay/fakemods/mmgen/autosign.py
 	def init_fixup(self): # see test/overlay/fakemods/mmgen/autosign.py
 		pass
 		pass
 
 
-	def __init__(self,cfg,cmd=None):
+	def __init__(self, cfg, cmd=None):
 
 
 		if cfg.mnemonic_fmt:
 		if cfg.mnemonic_fmt:
 			if cfg.mnemonic_fmt not in self.mn_fmts:
 			if cfg.mnemonic_fmt not in self.mn_fmts:
-				die(1,'{!r}: invalid mnemonic format (must be one of: {})'.format(
+				die(1, '{!r}: invalid mnemonic format (must be one of: {})'.format(
 					cfg.mnemonic_fmt,
 					cfg.mnemonic_fmt,
-					fmt_list( self.mn_fmts, fmt='no_spc' ) ))
+					fmt_list(self.mn_fmts, fmt='no_spc')))
 
 
 		if sys.platform == 'linux':
 		if sys.platform == 'linux':
 			self.dfl_mountpoint = f'/mnt/{self.linux_mount_subdir}'
 			self.dfl_mountpoint = f'/mnt/{self.linux_mount_subdir}'
@@ -507,11 +507,11 @@ class Autosign:
 
 
 		self.keyfile = self.mountpoint / 'autosign.key'
 		self.keyfile = self.mountpoint / 'autosign.key'
 
 
-		if any(k in cfg._uopts for k in ('help','longhelp')):
+		if any(k in cfg._uopts for k in ('help', 'longhelp')):
 			return
 			return
 
 
 		if 'coin' in cfg._uopts:
 		if 'coin' in cfg._uopts:
-			die(1,'--coin option not supported with this command.  Use --coins instead')
+			die(1, '--coin option not supported with this command.  Use --coins instead')
 
 
 		self.coins = cfg.coins.upper().split(',') if cfg.coins else []
 		self.coins = cfg.coins.upper().split(',') if cfg.coins else []
 
 
@@ -539,7 +539,7 @@ class Autosign:
 			self.dirs |= self.xmr_dirs
 			self.dirs |= self.xmr_dirs
 			self.signables += Signable.xmr_signables
 			self.signables += Signable.xmr_signables
 
 
-		for name,path in self.dirs.items():
+		for name, path in self.dirs.items():
 			setattr(self, name, self.mountpoint / path)
 			setattr(self, name, self.mountpoint / path)
 
 
 		self.swap = SwapMgr(self.cfg, ignore_zram=True)
 		self.swap = SwapMgr(self.cfg, ignore_zram=True)
@@ -553,28 +553,28 @@ class Autosign:
 				from .rpc import rpc_init
 				from .rpc import rpc_init
 				from .exception import SocketError
 				from .exception import SocketError
 				try:
 				try:
-					await rpc_init( self.cfg, proto, ignore_wallet=True )
+					await rpc_init(self.cfg, proto, ignore_wallet=True)
 				except SocketError as e:
 				except SocketError as e:
 					from .daemon import CoinDaemon
 					from .daemon import CoinDaemon
-					d = CoinDaemon( self.cfg, proto=proto, test_suite=self.cfg.test_suite )
+					d = CoinDaemon(self.cfg, proto=proto, test_suite=self.cfg.test_suite)
 					die(2,
 					die(2,
 						f'\n{e}\nIs the {d.coind_name} daemon ({d.exec_fn}) running '
 						f'\n{e}\nIs the {d.coind_name} daemon ({d.exec_fn}) running '
-						+ 'and listening on the correct port?' )
+						+ 'and listening on the correct port?')
 
 
 	@property
 	@property
 	def wallet_files(self):
 	def wallet_files(self):
 
 
-		if not hasattr(self,'_wallet_files'):
+		if not hasattr(self, '_wallet_files'):
 
 
 			try:
 			try:
 				dirlist = self.wallet_dir.iterdir()
 				dirlist = self.wallet_dir.iterdir()
 			except:
 			except:
-				die(1,f"Cannot open wallet directory '{self.wallet_dir}'. Did you run ‘mmgen-autosign setup’?")
+				die(1, f"Cannot open wallet directory '{self.wallet_dir}'. Did you run ‘mmgen-autosign setup’?")
 
 
 			self._wallet_files = [f for f in dirlist if f.suffix == '.mmdat']
 			self._wallet_files = [f for f in dirlist if f.suffix == '.mmdat']
 
 
 			if not self._wallet_files:
 			if not self._wallet_files:
-				die(1,'No wallet files present!')
+				die(1, 'No wallet files present!')
 
 
 		return self._wallet_files
 		return self._wallet_files
 
 
@@ -595,7 +595,7 @@ class Autosign:
 
 
 		if sys.platform == 'linux' and not self.mountpoint.is_dir():
 		if sys.platform == 'linux' and not self.mountpoint.is_dir():
 			def do_die(m):
 			def do_die(m):
-				die(1,'\n' + yellow(fmt(m.strip(),indent='  ')))
+				die(1, '\n' + yellow(fmt(m.strip(), indent='  ')))
 			if Path(self.old_dfl_mountpoint).is_dir():
 			if Path(self.old_dfl_mountpoint).is_dir():
 				do_die(self.old_dfl_mountpoint_errmsg)
 				do_die(self.old_dfl_mountpoint_errmsg)
 			else:
 			else:
@@ -607,14 +607,14 @@ class Autosign:
 				if not silent:
 				if not silent:
 					msg(f"Mounting '{self.mountpoint}'")
 					msg(f"Mounting '{self.mountpoint}'")
 			else:
 			else:
-				die(1,f'Unable to mount device {self.dev_label} at {self.mountpoint}')
+				die(1, f'Unable to mount device {self.dev_label} at {self.mountpoint}')
 
 
 		for dirname in self.dirs:
 		for dirname in self.dirs:
 			check_or_create(dirname)
 			check_or_create(dirname)
 
 
 	def do_umount(self, silent=False, verbose=False):
 	def do_umount(self, silent=False, verbose=False):
 		if self.mountpoint.is_mount():
 		if self.mountpoint.is_mount():
-			run( ['sync'], check=True )
+			run(['sync'], check=True)
 			if not silent:
 			if not silent:
 				msg(f"Unmounting '{self.mountpoint}'")
 				msg(f"Unmounting '{self.mountpoint}'")
 			redir = None if verbose else DEVNULL
 			redir = None if verbose else DEVNULL
@@ -634,8 +634,8 @@ class Autosign:
 
 
 		return not fails
 		return not fails
 
 
-	async def sign_all(self,target_name):
-		target = getattr(Signable,target_name)(self)
+	async def sign_all(self, target_name):
+		target = getattr(Signable, target_name)(self)
 		if target.unsigned:
 		if target.unsigned:
 			good = []
 			good = []
 			bad = []
 			bad = []
@@ -675,7 +675,7 @@ class Autosign:
 				self.led.set('busy')
 				self.led.set('busy')
 			ret = [await self.sign_all(signable) for signable in self.signables]
 			ret = [await self.sign_all(signable) for signable in self.signables]
 			for val in ret:
 			for val in ret:
-				if isinstance(val,str):
+				if isinstance(val, str):
 					msg(val)
 					msg(val)
 			if self.cfg.test_suite_autosign_threaded:
 			if self.cfg.test_suite_autosign_threaded:
 				await asyncio.sleep(0.3)
 				await asyncio.sleep(0.3)
@@ -701,15 +701,15 @@ class Autosign:
 		desc = f"key file '{self.keyfile}'"
 		desc = f"key file '{self.keyfile}'"
 		msg('Creating ' + desc)
 		msg('Creating ' + desc)
 		try:
 		try:
-			self.keyfile.write_text( os.urandom(32).hex() )
+			self.keyfile.write_text(os.urandom(32).hex())
 			self.keyfile.chmod(0o400)
 			self.keyfile.chmod(0o400)
 		except:
 		except:
-			die(2,'Unable to write ' + desc)
+			die(2, 'Unable to write ' + desc)
 		msg('Wrote ' + desc)
 		msg('Wrote ' + desc)
 
 
-	def gen_key(self,no_unmount=False):
+	def gen_key(self, no_unmount=False):
 		if not self.device_inserted:
 		if not self.device_inserted:
-			die(1,'Removable device not present!')
+			die(1, 'Removable device not present!')
 		self.do_mount()
 		self.do_mount()
 		self.wipe_encryption_key()
 		self.wipe_encryption_key()
 		self.create_key()
 		self.create_key()
@@ -756,7 +756,7 @@ class Autosign:
 			try:
 			try:
 				self.wallet_dir.stat()
 				self.wallet_dir.stat()
 			except:
 			except:
-				die(2,f"Unable to create wallet directory '{self.wallet_dir}'")
+				die(2, f"Unable to create wallet directory '{self.wallet_dir}'")
 
 
 		self.gen_key(no_unmount=True)
 		self.gen_key(no_unmount=True)
 
 
@@ -768,20 +768,20 @@ class Autosign:
 		remove_wallet_dir()
 		remove_wallet_dir()
 		create_wallet_dir()
 		create_wallet_dir()
 
 
-		wf = find_file_in_dir( get_wallet_cls('mmgen'), self.cfg.data_dir )
+		wf = find_file_in_dir(get_wallet_cls('mmgen'), self.cfg.data_dir)
 		if wf and keypress_confirm(
 		if wf and keypress_confirm(
 				cfg         = self.cfg,
 				cfg         = self.cfg,
 				prompt      = f"Default wallet '{wf}' found.\nUse default wallet for autosigning?",
 				prompt      = f"Default wallet '{wf}' found.\nUse default wallet for autosigning?",
-				default_yes = True ):
-			ss_in = Wallet( Config(), wf )
+				default_yes = True):
+			ss_in = Wallet(Config(), wf)
 		else:
 		else:
-			ss_in = Wallet( self.cfg, in_fmt=self.mn_fmts[self.cfg.mnemonic_fmt or self.dfl_mn_fmt] )
-		ss_out = Wallet( self.cfg, ss=ss_in, passwd_file=str(self.keyfile) )
-		ss_out.write_to_file( desc='autosign wallet', outdir=self.wallet_dir )
+			ss_in = Wallet(self.cfg, in_fmt=self.mn_fmts[self.cfg.mnemonic_fmt or self.dfl_mn_fmt])
+		ss_out = Wallet(self.cfg, ss=ss_in, passwd_file=str(self.keyfile))
+		ss_out.write_to_file(desc='autosign wallet', outdir=self.wallet_dir)
 
 
 	@property
 	@property
 	def xmrwallet_cfg(self):
 	def xmrwallet_cfg(self):
-		if not hasattr(self,'_xmrwallet_cfg'):
+		if not hasattr(self, '_xmrwallet_cfg'):
 			self._xmrwallet_cfg = Config({
 			self._xmrwallet_cfg = Config({
 				'_clone': self.cfg,
 				'_clone': self.cfg,
 				'coin': 'xmr',
 				'coin': 'xmr',
@@ -820,29 +820,29 @@ class Autosign:
 			nonlocal count
 			nonlocal count
 			msg_r('.')
 			msg_r('.')
 			from .fileutil import shred_file
 			from .fileutil import shred_file
-			shred_file( f, verbose=self.cfg.verbose )
+			shred_file(f, verbose=self.cfg.verbose)
 			count += 1
 			count += 1
 
 
 		def clean_dir(s_name):
 		def clean_dir(s_name):
 
 
-			def clean_files(rawext,sigext):
+			def clean_files(rawext, sigext):
 				for f in s.dir.iterdir():
 				for f in s.dir.iterdir():
 					if s.clean_all and (f.name.endswith(f'.{rawext}') or f.name.endswith(f'.{sigext}')):
 					if s.clean_all and (f.name.endswith(f'.{rawext}') or f.name.endswith(f'.{sigext}')):
 						do_shred(f)
 						do_shred(f)
 					elif f.name.endswith(f'.{sigext}'):
 					elif f.name.endswith(f'.{sigext}'):
-						raw = f.parent / ( f.name[:-len(sigext)] + rawext )
+						raw = f.parent / (f.name[:-len(sigext)] + rawext)
 						if raw.is_file():
 						if raw.is_file():
 							do_shred(raw)
 							do_shred(raw)
 
 
-			s = getattr(Signable,s_name)(self)
+			s = getattr(Signable, s_name)(self)
 
 
 			msg_r(f"Cleaning directory '{s.dir}'..")
 			msg_r(f"Cleaning directory '{s.dir}'..")
 
 
 			if s.dir.is_dir():
 			if s.dir.is_dir():
-				clean_files( s.rawext, s.sigext )
-				if hasattr(s,'subext'):
-					clean_files( s.rawext, s.subext )
-					clean_files( s.sigext, s.subext )
+				clean_files(s.rawext, s.sigext)
+				if hasattr(s, 'subext'):
+					clean_files(s.rawext, s.subext)
+					clean_files(s.sigext, s.subext)
 
 
 			msg('done' if s.dir.is_dir() else 'skipped (no dir)')
 			msg('done' if s.dir.is_dir() else 'skipped (no dir)')
 
 
@@ -887,7 +887,7 @@ class Autosign:
 				msg_r('.')
 				msg_r('.')
 				n += 1
 				n += 1
 
 
-	def at_exit(self,exit_val,message=None):
+	def at_exit(self, exit_val, message=None):
 		if message:
 		if message:
 			msg(message)
 			msg(message)
 		self.led.stop()
 		self.led.stop()
@@ -895,16 +895,16 @@ class Autosign:
 
 
 	def init_exit_handler(self):
 	def init_exit_handler(self):
 
 
-		def handler(arg1,arg2):
-			self.at_exit(1,'\nCleaning up...')
+		def handler(arg1, arg2):
+			self.at_exit(1, '\nCleaning up...')
 
 
 		import signal
 		import signal
-		signal.signal( signal.SIGTERM, handler )
-		signal.signal( signal.SIGINT, handler )
+		signal.signal(signal.SIGTERM, handler)
+		signal.signal(signal.SIGINT, handler)
 
 
 	def init_led(self):
 	def init_led(self):
 		from .led import LEDControl
 		from .led import LEDControl
 		self.led = LEDControl(
 		self.led = LEDControl(
 			enabled = self.cfg.led,
 			enabled = self.cfg.led,
-			simulate = self.cfg.test_suite_autosign_led_simulate )
+			simulate = self.cfg.test_suite_autosign_led_simulate)
 		self.led.set('off')
 		self.led.set('off')

+ 24 - 26
mmgen/base_obj.py

@@ -21,14 +21,14 @@ base_obj: base objects with no internal imports for the MMGen suite
 """
 """
 
 
 class AsyncInit(type):
 class AsyncInit(type):
-	async def __call__(cls,*args,**kwargs):
-		instance = cls.__new__(cls,*args,**kwargs)
-		await type(instance).__init__(instance,*args,**kwargs)
+	async def __call__(cls, *args, **kwargs):
+		instance = cls.__new__(cls, *args, **kwargs)
+		await type(instance).__init__(instance, *args, **kwargs)
 		return instance
 		return instance
 
 
 class AttrCtrlMeta(type):
 class AttrCtrlMeta(type):
-	def __call__(cls,*args,**kwargs):
-		instance = super().__call__(*args,**kwargs)
+	def __call__(cls, *args, **kwargs):
+		instance = super().__call__(*args, **kwargs)
 		if instance._autolock:
 		if instance._autolock:
 			instance._lock()
 			instance._lock()
 		return instance
 		return instance
@@ -55,38 +55,38 @@ class AttrCtrl(metaclass=AttrCtrlMeta):
 	def _lock(self):
 	def _lock(self):
 		self._locked = True
 		self._locked = True
 
 
-	def __getattr__(self,name):
+	def __getattr__(self, name):
 		if self._locked and self._default_to_none:
 		if self._locked and self._default_to_none:
 			return None
 			return None
 		else:
 		else:
 			raise AttributeError(f'{type(self).__name__} object has no attribute {name!r}')
 			raise AttributeError(f'{type(self).__name__} object has no attribute {name!r}')
 
 
-	def __setattr__(self,name,value):
+	def __setattr__(self, name, value):
 
 
 		if self._locked:
 		if self._locked:
 			assert name != '_locked', 'lock can be set only once'
 			assert name != '_locked', 'lock can be set only once'
 
 
-			def do_error(name,value,ref_val):
+			def do_error(name, value, ref_val):
 				raise AttributeError(
 				raise AttributeError(
 					f'{value!r}: invalid value for attribute {name!r}'
 					f'{value!r}: invalid value for attribute {name!r}'
 					+ ' of {} object (must be of type {}, not {})'.format(
 					+ ' of {} object (must be of type {}, not {})'.format(
 						type(self).__name__,
 						type(self).__name__,
 						type(ref_val).__name__,
 						type(ref_val).__name__,
-						type(value).__name__ ) )
+						type(value).__name__))
 
 
-			if not (name in self.__dict__ or hasattr(type(self),name)):
+			if not (name in self.__dict__ or hasattr(type(self), name)):
 				raise AttributeError(f'{type(self).__name__} object has no attribute {name!r}')
 				raise AttributeError(f'{type(self).__name__} object has no attribute {name!r}')
 
 
-			ref_val = getattr(type(self),name) if self._use_class_attr else getattr(self,name)
+			ref_val = getattr(type(self), name) if self._use_class_attr else getattr(self, name)
 
 
 			if (
 			if (
 				(name not in self._skip_type_check)
 				(name not in self._skip_type_check)
 				and (ref_val is not None)
 				and (ref_val is not None)
-				and not isinstance(value,type(ref_val))
+				and not isinstance(value, type(ref_val))
 			):
 			):
-				do_error(name,value,ref_val)
+				do_error(name, value, ref_val)
 
 
-		return object.__setattr__(self,name,value)
+		return object.__setattr__(self, name, value)
 
 
 	def __delattr__(self, name):
 	def __delattr__(self, name):
 		if self._locked and not name in self._delete_ok:
 		if self._locked and not name in self._delete_ok:
@@ -107,25 +107,23 @@ class Lockable(AttrCtrl):
 	_reset_ok = ()
 	_reset_ok = ()
 
 
 	def _lock(self):
 	def _lock(self):
-		for name in ('_set_ok','_reset_ok'):
-			for attr in getattr(self,name):
-				assert hasattr(self,attr), (
-					f'attribute {attr!r} in {name!r} not found in {type(self).__name__} object {id(self)}' )
+		for name in ('_set_ok', '_reset_ok'):
+			for attr in getattr(self, name):
+				assert hasattr(self, attr), (
+					f'attribute {attr!r} in {name!r} not found in {type(self).__name__} object {id(self)}')
 		super()._lock()
 		super()._lock()
 
 
-	def __setattr__(self,name,value):
-		if self._locked and (name in self.__dict__ or hasattr(type(self),name)):
-			val = getattr(self,name)
+	def __setattr__(self, name, value):
+		if self._locked and (name in self.__dict__ or hasattr(type(self), name)):
+			val = getattr(self, name)
 			if name not in (self._set_ok + self._reset_ok):
 			if name not in (self._set_ok + self._reset_ok):
 				raise AttributeError(f'attribute {name!r} of {type(self).__name__} object is read-only')
 				raise AttributeError(f'attribute {name!r} of {type(self).__name__} object is read-only')
 			elif name not in self._reset_ok:
 			elif name not in self._reset_ok:
-				#print(self.__dict__)
 				if not (
 				if not (
 					(val != 0 and not val) or
 					(val != 0 and not val) or
-					(self._use_class_attr and name not in self.__dict__) ):
+					(self._use_class_attr and name not in self.__dict__)):
 					raise AttributeError(
 					raise AttributeError(
 						f'attribute {name!r} of {type(self).__name__} object is already set,'
 						f'attribute {name!r} of {type(self).__name__} object is already set,'
-						+ ' and resetting is forbidden' )
-			# else name is in (_set_ok + _reset_ok) -- allow name to be in both lists
+						+ ' and resetting is forbidden')
 
 
-		return AttrCtrl.__setattr__(self,name,value)
+		return AttrCtrl.__setattr__(self, name, value)

+ 47 - 48
mmgen/baseconv.py

@@ -32,23 +32,22 @@ def is_b32_str(s):
 
 
 def is_mmgen_mnemonic(s):
 def is_mmgen_mnemonic(s):
 	try:
 	try:
-		baseconv('mmgen').tobytes(s.split(),pad='seed')
+		baseconv('mmgen').tobytes(s.split(), pad='seed')
 		return True
 		return True
 	except:
 	except:
 		return False
 		return False
 
 
 class baseconv:
 class baseconv:
 	mn_base = 1626
 	mn_base = 1626
-	dt = namedtuple('desc_tuple',['short','long'])
+	dt = namedtuple('desc_tuple', ['short', 'long'])
 	constants = {
 	constants = {
 	'desc': {
 	'desc': {
-		'b58':   dt('base58',            'base58-encoded data'),
-		'b32':   dt('MMGen base32',      'MMGen base32-encoded data created using simple base conversion'),
-		'b16':   dt('hexadecimal string','base16 (hexadecimal) string data'),
-		'b10':   dt('base10 string',     'base10 (decimal) string data'),
-		'b8':    dt('base8 string',      'base8 (octal) string data'),
-		'b6d':   dt('base6d (die roll)', 'base6 data using the digits from one to six'),
-#		'tirosh':('Tirosh mnemonic',   'base1626 mnemonic using truncated Tirosh wordlist'), # not used by wallet
+		'b58':   dt('base58',             'base58-encoded data'),
+		'b32':   dt('MMGen base32',       'MMGen base32-encoded data created using simple base conversion'),
+		'b16':   dt('hexadecimal string', 'base16 (hexadecimal) string data'),
+		'b10':   dt('base10 string',      'base10 (decimal) string data'),
+		'b8':    dt('base8 string',       'base8 (octal) string data'),
+		'b6d':   dt('base6d (die roll)',  'base6 data using the digits from one to six'),
 		'mmgen': dt('MMGen native mnemonic',
 		'mmgen': dt('MMGen native mnemonic',
 		'MMGen native mnemonic seed phrase created using old Electrum wordlist and simple base conversion'),
 		'MMGen native mnemonic seed phrase created using old Electrum wordlist and simple base conversion'),
 	},
 	},
@@ -68,18 +67,18 @@ class baseconv:
 #		'tirosh1633': '1a5faeff' # tirosh list is 1633 words long!
 #		'tirosh1633': '1a5faeff' # tirosh list is 1633 words long!
 	},
 	},
 	'seedlen_map': {
 	'seedlen_map': {
-		'b58': { 16:22, 24:33, 32:44 },
-		'b6d': { 16:50, 24:75, 32:100 },
-		'mmgen': { 16:12, 24:18, 32:24 },
+		'b58':   {16:22, 24:33, 32:44},
+		'b6d':   {16:50, 24:75, 32:100},
+		'mmgen': {16:12, 24:18, 32:24},
 	},
 	},
 	'seedlen_map_rev': {
 	'seedlen_map_rev': {
-		'b58': { 22:16, 33:24, 44:32 },
-		'b6d': { 50:16, 75:24, 100:32 },
-		'mmgen': { 12:16, 18:24, 24:32 },
+		'b58':   {22:16, 33:24, 44:32},
+		'b6d':   {50:16, 75:24, 100:32},
+		'mmgen': {12:16, 18:24, 24:32},
 	}
 	}
 	}
 	}
 
 
-	def __init__(self,wl_id):
+	def __init__(self, wl_id):
 
 
 		if wl_id == 'mmgen':
 		if wl_id == 'mmgen':
 			from .wordlist.electrum import words
 			from .wordlist.electrum import words
@@ -87,9 +86,9 @@ class baseconv:
 		elif wl_id not in self.constants['digits']:
 		elif wl_id not in self.constants['digits']:
 			raise ValueError(f'{wl_id}: unrecognized mnemonic ID')
 			raise ValueError(f'{wl_id}: unrecognized mnemonic ID')
 
 
-		for k,v in self.constants.items():
+		for k, v in self.constants.items():
 			if wl_id in v:
 			if wl_id in v:
-				setattr(self,k,v[wl_id])
+				setattr(self, k, v[wl_id])
 
 
 		self.wl_id = wl_id
 		self.wl_id = wl_id
 
 
@@ -98,23 +97,23 @@ class baseconv:
 
 
 	def get_wordlist_chksum(self):
 	def get_wordlist_chksum(self):
 		from hashlib import sha256
 		from hashlib import sha256
-		return sha256( ' '.join(self.digits).encode() ).hexdigest()[:8]
+		return sha256(' '.join(self.digits).encode()).hexdigest()[:8]
 
 
-	def check_wordlist(self,cfg):
+	def check_wordlist(self, cfg):
 
 
 		wl = self.digits
 		wl = self.digits
 		ret = f'Wordlist: {self.wl_id}\nLength: {len(wl)} words'
 		ret = f'Wordlist: {self.wl_id}\nLength: {len(wl)} words'
 		new_chksum = self.get_wordlist_chksum()
 		new_chksum = self.get_wordlist_chksum()
 
 
-		cfg._util.compare_chksums( new_chksum, 'generated', self.wl_chksum, 'saved', die_on_fail=True )
+		cfg._util.compare_chksums(new_chksum, 'generated', self.wl_chksum, 'saved', die_on_fail=True)
 
 
 		if tuple(sorted(wl)) == wl:
 		if tuple(sorted(wl)) == wl:
 			return ret + '\nList is sorted'
 			return ret + '\nList is sorted'
 		else:
 		else:
-			die(3,'ERROR: List is not sorted!')
+			die(3, 'ERROR: List is not sorted!')
 
 
 	@staticmethod
 	@staticmethod
-	def get_pad(pad,seed_pad_func):
+	def get_pad(pad, seed_pad_func):
 		"""
 		"""
 		'pad' argument to baseconv conversion methods must be either None, 'seed' or an integer.
 		'pad' argument to baseconv conversion methods must be either None, 'seed' or an integer.
 		If None, output of minimum (but never zero) length will be produced.
 		If None, output of minimum (but never zero) length will be produced.
@@ -128,72 +127,72 @@ class baseconv:
 		elif pad == 'seed':
 		elif pad == 'seed':
 			return seed_pad_func()
 			return seed_pad_func()
 		else:
 		else:
-			die('BaseConversionPadError',f"{pad!r}: illegal value for 'pad' (must be None,'seed' or int)")
+			die('BaseConversionPadError', f"{pad!r}: illegal value for 'pad' (must be None, 'seed' or int)")
 
 
-	def tohex(self,words_arg,pad=None):
+	def tohex(self, words_arg, pad=None):
 		"convert string or list data of instance base to a hexadecimal string"
 		"convert string or list data of instance base to a hexadecimal string"
 		return self.tobytes(words_arg, pad//2 if type(pad) is int else pad).hex()
 		return self.tobytes(words_arg, pad//2 if type(pad) is int else pad).hex()
 
 
-	def tobytes(self,words_arg,pad=None):
+	def tobytes(self, words_arg, pad=None):
 		"convert string or list data of instance base to byte string"
 		"convert string or list data of instance base to byte string"
 
 
-		words = words_arg if isinstance(words_arg,(list,tuple)) else tuple(words_arg.strip())
+		words = words_arg if isinstance(words_arg, (list, tuple)) else tuple(words_arg.strip())
 		desc = self.desc.short
 		desc = self.desc.short
 
 
 		if len(words) == 0:
 		if len(words) == 0:
-			die('BaseConversionError',f'empty {desc} data')
+			die('BaseConversionError', f'empty {desc} data')
 
 
 		def get_seed_pad():
 		def get_seed_pad():
-			assert hasattr(self,'seedlen_map_rev'), f'seed padding not supported for base {self.wl_id!r}'
+			assert hasattr(self, 'seedlen_map_rev'), f'seed padding not supported for base {self.wl_id!r}'
 			d = self.seedlen_map_rev
 			d = self.seedlen_map_rev
 			if not len(words) in d:
 			if not len(words) in d:
-				die( 'BaseConversionError',
-					f'{len(words)}: invalid length for seed-padded {desc} data in base conversion' )
+				die('BaseConversionError',
+					f'{len(words)}: invalid length for seed-padded {desc} data in base conversion')
 			return d[len(words)]
 			return d[len(words)]
 
 
-		pad_val = max(self.get_pad(pad,get_seed_pad),1)
+		pad_val = max(self.get_pad(pad, get_seed_pad), 1)
 		wl = self.digits
 		wl = self.digits
 		base = len(wl)
 		base = len(wl)
 
 
 		if not set(words) <= set(wl):
 		if not set(words) <= set(wl):
-			die( 'BaseConversionError',
-				( 'seed data' if pad == 'seed' else f'{words_arg!r}:' ) +
-				f' not in {desc} format' )
+			die('BaseConversionError',
+				('seed data' if pad == 'seed' else f'{words_arg!r}:') +
+				f' not in {desc} format')
 
 
 		ret = sum(wl.index(words[::-1][i])*(base**i) for i in range(len(words)))
 		ret = sum(wl.index(words[::-1][i])*(base**i) for i in range(len(words)))
 		bl = ret.bit_length()
 		bl = ret.bit_length()
-		return ret.to_bytes(max(pad_val,bl//8+bool(bl%8)),'big')
+		return ret.to_bytes(max(pad_val, bl//8+bool(bl%8)), 'big')
 
 
-	def fromhex(self,hexstr,pad=None,tostr=False):
+	def fromhex(self, hexstr, pad=None, tostr=False):
 		"convert a hexadecimal string to a list or string data of instance base"
 		"convert a hexadecimal string to a list or string data of instance base"
 
 
 		from .util import is_hex_str
 		from .util import is_hex_str
 		if not is_hex_str(hexstr):
 		if not is_hex_str(hexstr):
-			die( 'HexadecimalStringError',
-				( 'seed data' if pad == 'seed' else f'{hexstr!r}:' ) +
-				' not a hexadecimal string' )
+			die('HexadecimalStringError',
+				('seed data' if pad == 'seed' else f'{hexstr!r}:') +
+				' not a hexadecimal string')
 
 
-		return self.frombytes( bytes.fromhex(hexstr), pad, tostr )
+		return self.frombytes(bytes.fromhex(hexstr), pad, tostr)
 
 
-	def frombytes(self,bytestr,pad=None,tostr=False):
+	def frombytes(self, bytestr, pad=None, tostr=False):
 		"convert byte string to list or string data of instance base"
 		"convert byte string to list or string data of instance base"
 
 
 		if not bytestr:
 		if not bytestr:
-			die( 'BaseConversionError', 'empty data not allowed in base conversion' )
+			die('BaseConversionError', 'empty data not allowed in base conversion')
 
 
 		def get_seed_pad():
 		def get_seed_pad():
-			assert hasattr(self,'seedlen_map'), f'seed padding not supported for base {self.wl_id!r}'
+			assert hasattr(self, 'seedlen_map'), f'seed padding not supported for base {self.wl_id!r}'
 			d = self.seedlen_map
 			d = self.seedlen_map
 			if not len(bytestr) in d:
 			if not len(bytestr) in d:
-				die( 'SeedLengthError',
-					f'{len(bytestr)}: invalid byte length for seed data in seed-padded base conversion' )
+				die('SeedLengthError',
+					f'{len(bytestr)}: invalid byte length for seed data in seed-padded base conversion')
 			return d[len(bytestr)]
 			return d[len(bytestr)]
 
 
-		pad = max(self.get_pad(pad,get_seed_pad),1)
+		pad = max(self.get_pad(pad, get_seed_pad), 1)
 		wl = self.digits
 		wl = self.digits
 
 
 		def gen():
 		def gen():
-			num = int.from_bytes(bytestr,'big')
+			num = int.from_bytes(bytestr, 'big')
 			base = len(wl)
 			base = len(wl)
 			while num:
 			while num:
 				yield num % base
 				yield num % base

+ 27 - 27
mmgen/bip39.py

@@ -23,21 +23,21 @@ bip39.py - Data and routines for BIP39 mnemonic seed phrases
 from hashlib import sha256
 from hashlib import sha256
 
 
 from .baseconv import baseconv
 from .baseconv import baseconv
-from .util import is_hex_str,die
+from .util import is_hex_str, die
 
 
 def is_bip39_mnemonic(s):
 def is_bip39_mnemonic(s):
-	return bool( bip39().tohex(s.split()) )
+	return bool(bip39().tohex(s.split()))
 
 
 # implements a subset of the baseconv API
 # implements a subset of the baseconv API
 class bip39(baseconv):
 class bip39(baseconv):
 
 
 	desc            = baseconv.dt('BIP39 mnemonic', 'BIP39 mnemonic seed phrase')
 	desc            = baseconv.dt('BIP39 mnemonic', 'BIP39 mnemonic seed phrase')
 	wl_chksum       = 'f18b9a84'
 	wl_chksum       = 'f18b9a84'
-	seedlen_map     = { 16:12, 24:18, 32:24 }
-	seedlen_map_rev = { 12:16, 18:24, 24:32 }
+	seedlen_map     = {16:12, 24:18, 32:24}
+	seedlen_map_rev = {12:16, 18:24, 24:32}
 
 
 	from collections import namedtuple
 	from collections import namedtuple
-	bc = namedtuple('bip39_constants',['chk_len','mn_len'])
+	bc = namedtuple('bip39_constants', ['chk_len', 'mn_len'])
 	#    ENT   CS  MS
 	#    ENT   CS  MS
 	constants = {
 	constants = {
 		128: bc(4, 12),
 		128: bc(4, 12),
@@ -47,21 +47,21 @@ class bip39(baseconv):
 		256: bc(8, 24),
 		256: bc(8, 24),
 	}
 	}
 
 
-	def __init__(self,wl_id='bip39'):
+	def __init__(self, wl_id='bip39'):
 		assert wl_id == 'bip39', "initialize with 'bip39' for compatibility with baseconv API"
 		assert wl_id == 'bip39', "initialize with 'bip39' for compatibility with baseconv API"
 		from .wordlist.bip39 import words
 		from .wordlist.bip39 import words
 		self.digits = words
 		self.digits = words
 		self.wl_id = 'bip39'
 		self.wl_id = 'bip39'
 
 
 	@classmethod
 	@classmethod
-	def nwords2seedlen(cls,nwords,in_bytes=False,in_hex=False):
-		for k,v in cls.constants.items():
+	def nwords2seedlen(cls, nwords, in_bytes=False, in_hex=False):
+		for k, v in cls.constants.items():
 			if v.mn_len == nwords:
 			if v.mn_len == nwords:
 				return k//8 if in_bytes else k//4 if in_hex else k
 				return k//8 if in_bytes else k//4 if in_hex else k
-		die( 'MnemonicError', f'{nwords!r}: invalid word length for BIP39 mnemonic' )
+		die('MnemonicError', f'{nwords!r}: invalid word length for BIP39 mnemonic')
 
 
 	@classmethod
 	@classmethod
-	def seedlen2nwords(cls,seed_len,in_bytes=False,in_hex=False):
+	def seedlen2nwords(cls, seed_len, in_bytes=False, in_hex=False):
 		seed_bits = seed_len * 8 if in_bytes else seed_len * 4 if in_hex else seed_len
 		seed_bits = seed_len * 8 if in_bytes else seed_len * 4 if in_hex else seed_len
 		try:
 		try:
 			return cls.constants[seed_bits].mn_len
 			return cls.constants[seed_bits].mn_len
@@ -71,47 +71,47 @@ class bip39(baseconv):
 	def tohex(self, words_arg, pad=None):
 	def tohex(self, words_arg, pad=None):
 		return self.tobytes(words_arg, pad=pad).hex()
 		return self.tobytes(words_arg, pad=pad).hex()
 
 
-	def tobytes(self,words_arg,pad=None):
-		assert isinstance(words_arg,(list,tuple)),'words_arg must be list or tuple'
-		assert pad in (None,'seed'), f"{pad}: invalid 'pad' argument (must be None or 'seed')"
+	def tobytes(self, words_arg, pad=None):
+		assert isinstance(words_arg, (list, tuple)), 'words_arg must be list or tuple'
+		assert pad in (None, 'seed'), f"{pad}: invalid 'pad' argument (must be None or 'seed')"
 
 
 		wl = self.digits
 		wl = self.digits
 
 
-		for n,w in enumerate(words_arg):
+		for n, w in enumerate(words_arg):
 			if w not in wl:
 			if w not in wl:
-				die( 'MnemonicError', f'word #{n+1} is not in the BIP39 word list' )
+				die('MnemonicError', f'word #{n+1} is not in the BIP39 word list')
 
 
 		res = ''.join(f'{wl.index(w):011b}' for w in words_arg)
 		res = ''.join(f'{wl.index(w):011b}' for w in words_arg)
 
 
-		for k,v in self.constants.items():
+		for k, v in self.constants.items():
 			if len(words_arg) == v.mn_len:
 			if len(words_arg) == v.mn_len:
 				bitlen = k
 				bitlen = k
 				break
 				break
 		else:
 		else:
-			die( 'MnemonicError', f'{len(words_arg)}: invalid BIP39 seed phrase length' )
+			die('MnemonicError', f'{len(words_arg)}: invalid BIP39 seed phrase length')
 
 
 		seed_bin = res[:bitlen]
 		seed_bin = res[:bitlen]
 		chk_bin = res[bitlen:]
 		chk_bin = res[bitlen:]
 
 
-		seed_hex = f'{int(seed_bin,2):0{bitlen//4}x}'
+		seed_hex = f'{int(seed_bin, 2):0{bitlen//4}x}'
 		seed_bytes = bytes.fromhex(seed_hex)
 		seed_bytes = bytes.fromhex(seed_hex)
 
 
 		chk_len = self.constants[bitlen].chk_len
 		chk_len = self.constants[bitlen].chk_len
 		chk_hex_chk = sha256(seed_bytes).hexdigest()
 		chk_hex_chk = sha256(seed_bytes).hexdigest()
-		chk_bin_chk = f'{int(chk_hex_chk,16):0256b}'[:chk_len]
+		chk_bin_chk = f'{int(chk_hex_chk, 16):0256b}'[:chk_len]
 
 
 		if chk_bin != chk_bin_chk:
 		if chk_bin != chk_bin_chk:
-			die( 'MnemonicError', f'invalid BIP39 seed phrase checksum ({chk_bin} != {chk_bin_chk})' )
+			die('MnemonicError', f'invalid BIP39 seed phrase checksum ({chk_bin} != {chk_bin_chk})')
 
 
 		return seed_bytes
 		return seed_bytes
 
 
 	def fromhex(self, hexstr, pad=None, tostr=False):
 	def fromhex(self, hexstr, pad=None, tostr=False):
-		assert is_hex_str(hexstr),'seed data not a hexadecimal string'
+		assert is_hex_str(hexstr), 'seed data not a hexadecimal string'
 		return self.frombytes(bytes.fromhex(hexstr), pad=pad, tostr=tostr)
 		return self.frombytes(bytes.fromhex(hexstr), pad=pad, tostr=tostr)
 
 
 	def frombytes(self, seed_bytes, pad=None, tostr=False):
 	def frombytes(self, seed_bytes, pad=None, tostr=False):
-		assert tostr is False,"'tostr' must be False for 'bip39'"
-		assert pad in (None,'seed'), f"{pad}: invalid 'pad' argument (must be None or 'seed')"
+		assert tostr is False, "'tostr' must be False for 'bip39'"
+		assert pad in (None, 'seed'), f"{pad}: invalid 'pad' argument (must be None or 'seed')"
 
 
 		wl = self.digits
 		wl = self.digits
 		bitlen = len(seed_bytes) * 8
 		bitlen = len(seed_bytes) * 8
@@ -121,14 +121,14 @@ class bip39(baseconv):
 
 
 		chk_hex = sha256(seed_bytes).hexdigest()
 		chk_hex = sha256(seed_bytes).hexdigest()
 
 
-		seed_bin = f'{int(seed_bytes.hex(),16):0{bitlen}b}'
-		chk_bin  = f'{int(chk_hex,16):0256b}'
+		seed_bin = f'{int(seed_bytes.hex(), 16):0{bitlen}b}'
+		chk_bin  = f'{int(chk_hex, 16):0256b}'
 
 
 		res = seed_bin + chk_bin
 		res = seed_bin + chk_bin
 
 
-		return tuple(wl[int(res[i*11:(i+1)*11],2)] for i in range(c.mn_len))
+		return tuple(wl[int(res[i*11:(i+1)*11], 2)] for i in range(c.mn_len))
 
 
-	def generate_seed(self,words_arg,passwd=''):
+	def generate_seed(self, words_arg, passwd=''):
 
 
 		self.tohex(words_arg) # validate
 		self.tohex(words_arg) # validate
 
 

+ 129 - 132
mmgen/cfg.py

@@ -20,15 +20,15 @@
 cfg: Configuration classes for the MMGen suite
 cfg: Configuration classes for the MMGen suite
 """
 """
 
 
-import sys,os
+import sys, os
 from collections import namedtuple
 from collections import namedtuple
 from .base_obj import Lockable
 from .base_obj import Lockable
 
 
-def die(*args,**kwargs):
+def die(*args, **kwargs):
 	from .util import die
 	from .util import die
-	die(*args,**kwargs)
+	die(*args, **kwargs)
 
 
-def die2(exit_val,s):
+def die2(exit_val, s):
 	sys.stderr.write(s+'\n')
 	sys.stderr.write(s+'\n')
 	sys.exit(exit_val)
 	sys.exit(exit_val)
 
 
@@ -86,16 +86,16 @@ class GlobalConstants(Lockable):
 	cmd_caps = cmd_caps_data.get(prog_id)
 	cmd_caps = cmd_caps_data.get(prog_id)
 
 
 	if sys.platform not in ('linux', 'win32', 'darwin'):
 	if sys.platform not in ('linux', 'win32', 'darwin'):
-		die2(1,f'{sys.platform!r}: platform not supported by {proj_name}')
+		die2(1, f'{sys.platform!r}: platform not supported by {proj_name}')
 
 
 	if os.getenv('HOME'):   # Linux, MSYS2, or macOS
 	if os.getenv('HOME'):   # Linux, MSYS2, or macOS
 		home_dir = os.getenv('HOME')
 		home_dir = os.getenv('HOME')
 	elif sys.platform == 'win32': # Windows without MSYS2 - not supported
 	elif sys.platform == 'win32': # Windows without MSYS2 - not supported
-		die2(1,f'$HOME not set!  {proj_name} for Windows must be run in MSYS2 environment')
+		die2(1, f'$HOME not set!  {proj_name} for Windows must be run in MSYS2 environment')
 	else:
 	else:
-		die2(2,'$HOME is not set!  Unable to determine home directory')
+		die2(2, '$HOME is not set!  Unable to determine home directory')
 
 
-	def get_mmgen_data_file(self,filename,package='mmgen'):
+	def get_mmgen_data_file(self, filename, package='mmgen'):
 		"""
 		"""
 		this is an expensive import, so do only when required
 		this is an expensive import, so do only when required
 		"""
 		"""
@@ -111,7 +111,7 @@ class GlobalConstants(Lockable):
 			from importlib.resources import files # Python 3.9 and above
 			from importlib.resources import files # Python 3.9 and above
 		except ImportError:
 		except ImportError:
 			from importlib_resources import files
 			from importlib_resources import files
-		return files(package).joinpath('data',filename).read_text()
+		return files(package).joinpath('data', filename).read_text()
 
 
 	@property
 	@property
 	def version(self):
 	def version(self):
@@ -144,7 +144,7 @@ class Config(Lockable):
 	  3 - config file
 	  3 - config file
 	"""
 	"""
 	_autolock = False
 	_autolock = False
-	_set_ok = ('usr_randchars','_proto')
+	_set_ok = ('usr_randchars', '_proto')
 	_reset_ok = ('accept_defaults',)
 	_reset_ok = ('accept_defaults',)
 	_delete_ok = ('_opts',)
 	_delete_ok = ('_opts',)
 	_use_class_attr = True
 	_use_class_attr = True
@@ -210,7 +210,7 @@ class Config(Lockable):
 	pager           = False
 	pager           = False
 	columns         = 0
 	columns         = 0
 	color = bool(
 	color = bool(
-		( sys.stdout.isatty() and not os.getenv('MMGEN_TEST_SUITE_PEXPECT') ) or
+		(sys.stdout.isatty() and not os.getenv('MMGEN_TEST_SUITE_PEXPECT')) or
 		os.getenv('MMGEN_TEST_SUITE_ENABLE_COLOR')
 		os.getenv('MMGEN_TEST_SUITE_ENABLE_COLOR')
 	)
 	)
 
 
@@ -256,7 +256,7 @@ class Config(Lockable):
 	exit_after               = ''
 	exit_after               = ''
 	resuming                 = False
 	resuming                 = False
 	skipping_deps            = False
 	skipping_deps            = False
-	test_datadir             = os.path.join('test','data_dir' + ('','-α')[bool(os.getenv('MMGEN_DEBUG_UTF8'))])
+	test_datadir             = os.path.join('test', 'data_dir' + ('', '-α')[bool(os.getenv('MMGEN_DEBUG_UTF8'))])
 
 
 	mnemonic_entry_modes = {}
 	mnemonic_entry_modes = {}
 
 
@@ -273,12 +273,12 @@ class Config(Lockable):
 	)
 	)
 
 
 	_incompatible_opts = (
 	_incompatible_opts = (
-		('help','longhelp'),
-		('bob','alice','carol'),
-		('label','keep_label'),
-		('tx_id','info'),
-		('tx_id','terse_info'),
-		('autosign','outdir'),
+		('help', 'longhelp'),
+		('bob', 'alice', 'carol'),
+		('label', 'keep_label'),
+		('tx_id', 'info'),
+		('tx_id', 'terse_info'),
+		('autosign', 'outdir'),
 	)
 	)
 
 
 	_cfg_file_opts = (
 	_cfg_file_opts = (
@@ -319,7 +319,7 @@ class Config(Lockable):
 		'ltc_ignore_daemon_version',
 		'ltc_ignore_daemon_version',
 		'xmr_ignore_daemon_version',
 		'xmr_ignore_daemon_version',
 		'eth_mainnet_chain_names',
 		'eth_mainnet_chain_names',
-		'eth_testnet_chain_names' )
+		'eth_testnet_chain_names')
 
 
 	# Supported environmental vars
 	# Supported environmental vars
 	# The corresponding attributes (lowercase, without 'mmgen_') must exist in the class.
 	# The corresponding attributes (lowercase, without 'mmgen_') must exist in the class.
@@ -377,10 +377,10 @@ class Config(Lockable):
 		'contract_data',
 		'contract_data',
 	)
 	)
 	# Auto-typechecked and auto-set opts - first value in list is the default
 	# Auto-typechecked and auto-set opts - first value in list is the default
-	_ov = namedtuple('autoset_opt_info',['type','choices'])
+	_ov = namedtuple('autoset_opt_info', ['type', 'choices'])
 	_autoset_opts = {
 	_autoset_opts = {
-		'fee_estimate_mode': _ov('nocase_pfx', ['conservative','economical']),
-		'rpc_backend':       _ov('nocase_pfx', ['auto','httplib','curl','aiohttp','requests']),
+		'fee_estimate_mode': _ov('nocase_pfx', ['conservative', 'economical']),
+		'rpc_backend':       _ov('nocase_pfx', ['auto', 'httplib', 'curl', 'aiohttp', 'requests']),
 	}
 	}
 
 
 	_auto_typeset_opts = {
 	_auto_typeset_opts = {
@@ -414,13 +414,13 @@ class Config(Lockable):
 		"""
 		"""
 		location of mmgen.cfg
 		location of mmgen.cfg
 		"""
 		"""
-		if not hasattr(self,'_data_dir_root'):
+		if not hasattr(self, '_data_dir_root'):
 			if self._data_dir_root_override:
 			if self._data_dir_root_override:
 				self._data_dir_root = os.path.normpath(os.path.abspath(self._data_dir_root_override))
 				self._data_dir_root = os.path.normpath(os.path.abspath(self._data_dir_root_override))
 			elif self.test_suite:
 			elif self.test_suite:
 				self._data_dir_root = self.test_datadir
 				self._data_dir_root = self.test_datadir
 			else:
 			else:
-				self._data_dir_root = os.path.join(gc.home_dir,'.'+gc.proj_name.lower())
+				self._data_dir_root = os.path.join(gc.home_dir, '.'+gc.proj_name.lower())
 		return self._data_dir_root
 		return self._data_dir_root
 
 
 	@property
 	@property
@@ -428,12 +428,12 @@ class Config(Lockable):
 		"""
 		"""
 		location of wallet and other data - same as data_dir_root for mainnet
 		location of wallet and other data - same as data_dir_root for mainnet
 		"""
 		"""
-		if not hasattr(self,'_data_dir'):
+		if not hasattr(self, '_data_dir'):
 			self._data_dir = os.path.normpath(os.path.join(*{
 			self._data_dir = os.path.normpath(os.path.join(*{
 				'regtest': (self.data_dir_root, 'regtest', (self.regtest_user or 'none')),
 				'regtest': (self.data_dir_root, 'regtest', (self.regtest_user or 'none')),
 				'testnet': (self.data_dir_root, 'testnet'),
 				'testnet': (self.data_dir_root, 'testnet'),
 				'mainnet': (self.data_dir_root,),
 				'mainnet': (self.data_dir_root,),
-			}[self.network] ))
+			}[self.network]))
 		return self._data_dir
 		return self._data_dir
 
 
 	def __init__(
 	def __init__(
@@ -457,7 +457,7 @@ class Config(Lockable):
 		if opts_data or parsed_opts or process_opts:
 		if opts_data or parsed_opts or process_opts:
 			assert cfg is None, (
 			assert cfg is None, (
 				'Config(): ‘cfg’ cannot be used simultaneously with ' +
 				'Config(): ‘cfg’ cannot be used simultaneously with ' +
-				'‘opts_data’, ‘parsed_opts’ or ‘process_opts’' )
+				'‘opts_data’, ‘parsed_opts’ or ‘process_opts’')
 			from .opts import UserOpts
 			from .opts import UserOpts
 			UserOpts(
 			UserOpts(
 				cfg          = self,
 				cfg          = self,
@@ -472,26 +472,26 @@ class Config(Lockable):
 				self._uopts = {}
 				self._uopts = {}
 			else:
 			else:
 				if '_clone' in cfg:
 				if '_clone' in cfg:
-					assert isinstance( cfg['_clone'], Config )
+					assert isinstance(cfg['_clone'], Config)
 					self._cloned = cfg['_clone'].__dict__
 					self._cloned = cfg['_clone'].__dict__
-					for k,v in self._cloned.items():
+					for k, v in self._cloned.items():
 						if not k.startswith('_'):
 						if not k.startswith('_'):
-							setattr(self,k,v)
+							setattr(self, k, v)
 					del cfg['_clone']
 					del cfg['_clone']
 				self._uopts = cfg
 				self._uopts = cfg
 			self._uopt_desc = 'configuration option'
 			self._uopt_desc = 'configuration option'
 
 
 		self._data_dir_root_override = self._cloned.pop(
 		self._data_dir_root_override = self._cloned.pop(
 			'_data_dir_root_override',
 			'_data_dir_root_override',
-			self._uopts.pop('data_dir',None))
+			self._uopts.pop('data_dir', None))
 
 
-		if parse_only and not any(k in self._uopts for k in ['help','longhelp']):
+		if parse_only and not any(k in self._uopts for k in ['help', 'longhelp']):
 			return
 			return
 
 
 		# Step 2: set cfg from user-supplied data, skipping auto opts; set type from corresponding
 		# Step 2: set cfg from user-supplied data, skipping auto opts; set type from corresponding
 		#         class attribute, if it exists:
 		#         class attribute, if it exists:
 		auto_opts = tuple(self._autoset_opts) + tuple(self._auto_typeset_opts)
 		auto_opts = tuple(self._autoset_opts) + tuple(self._auto_typeset_opts)
-		for key,val in self._uopts.items():
+		for key, val in self._uopts.items():
 			assert key.isascii() and key.isidentifier() and key[0] != '_', (
 			assert key.isascii() and key.isidentifier() and key[0] != '_', (
 				f'{key!r}: malformed configuration option')
 				f'{key!r}: malformed configuration option')
 			assert key not in self._forbidden_opts, (
 			assert key not in self._forbidden_opts, (
@@ -524,13 +524,13 @@ class Config(Lockable):
 		# Step 4: set cfg from cfgfile, skipping already-set opts and auto opts; save set opts and auto
 		# Step 4: set cfg from cfgfile, skipping already-set opts and auto opts; save set opts and auto
 		#         opts to be set:
 		#         opts to be set:
 		# requires ‘data_dir_root’, ‘test_suite_cfgtest’
 		# requires ‘data_dir_root’, ‘test_suite_cfgtest’
-		self._cfgfile_opts = self._set_cfg_from_cfg_file( self._envopts, need_proto )
+		self._cfgfile_opts = self._set_cfg_from_cfg_file(self._envopts, need_proto)
 
 
 		# Step 5: set autoset opts from user-supplied data, cfgfile data, or default values, in that order:
 		# Step 5: set autoset opts from user-supplied data, cfgfile data, or default values, in that order:
-		self._set_autoset_opts( self._cfgfile_opts.autoset )
+		self._set_autoset_opts(self._cfgfile_opts.autoset)
 
 
 		# Step 6: set auto typeset opts from user-supplied data or cfgfile data, in that order:
 		# Step 6: set auto typeset opts from user-supplied data or cfgfile data, in that order:
-		self._set_auto_typeset_opts( self._cfgfile_opts.auto_typeset )
+		self._set_auto_typeset_opts(self._cfgfile_opts.auto_typeset)
 
 
 		if self.regtest or self.bob or self.alice or self.carol or gc.prog_name == f'{gc.proj_id}-regtest':
 		if self.regtest or self.bob or self.alice or self.carol or gc.prog_name == f'{gc.proj_id}-regtest':
 			self.network = 'regtest'
 			self.network = 'regtest'
@@ -567,7 +567,7 @@ class Config(Lockable):
 		if need_proto:
 		if need_proto:
 			from .protocol import init_proto_from_cfg, warn_trustlevel
 			from .protocol import init_proto_from_cfg, warn_trustlevel
 			# requires the default-to-none behavior, so do after the lock:
 			# requires the default-to-none behavior, so do after the lock:
-			self._proto = init_proto_from_cfg(self,need_amt=need_amt)
+			self._proto = init_proto_from_cfg(self, need_amt=need_amt)
 
 
 		if self._opts and not caller_post_init:
 		if self._opts and not caller_post_init:
 			self._post_init()
 			self._post_init()
@@ -590,41 +590,38 @@ class Config(Lockable):
 		sys.exit(1) # called only on bad invocation
 		sys.exit(1) # called only on bad invocation
 
 
 	def _set_cfg_from_env(self):
 	def _set_cfg_from_env(self):
-		for name,val in ((k,v) for k,v in os.environ.items() if k.startswith('MMGEN_')):
+		for name, val in ((k, v) for k, v in os.environ.items() if k.startswith('MMGEN_')):
 			if name == 'MMGEN_DEBUG_ALL':
 			if name == 'MMGEN_DEBUG_ALL':
 				continue
 				continue
 			if name in self._env_opts:
 			if name in self._env_opts:
 				if val: # ignore empty string values; string value of '0' or 'false' sets variable to False
 				if val: # ignore empty string values; string value of '0' or 'false' sets variable to False
 					disable = name.startswith('MMGEN_DISABLE_')
 					disable = name.startswith('MMGEN_DISABLE_')
-					gname = name[(6,14)[disable]:].lower()
+					gname = name[(6, 14)[disable]:].lower()
 					if gname in self._uopts: # don’t touch attr if already set by user
 					if gname in self._uopts: # don’t touch attr if already set by user
 						continue
 						continue
-					if hasattr(self,gname):
+					if hasattr(self, gname):
 						setattr(
 						setattr(
 							self,
 							self,
 							gname,
 							gname,
-							conv_type( name, val, getattr(self,gname), 'environment var', invert_bool=disable ))
+							conv_type(name, val, getattr(self, gname), 'environment var', invert_bool=disable))
 						yield gname
 						yield gname
 					else:
 					else:
 						raise ValueError(f'Name {gname!r} not present in globals')
 						raise ValueError(f'Name {gname!r} not present in globals')
 			else:
 			else:
 				raise ValueError(f'{name!r} is not a valid MMGen environment variable')
 				raise ValueError(f'{name!r} is not a valid MMGen environment variable')
 
 
-	def _set_cfg_from_cfg_file(
-			self,
-			env_cfg,
-			need_proto ):
+	def _set_cfg_from_cfg_file(self, env_cfg, need_proto):
 
 
-		_ret = namedtuple('cfgfile_opts',['non_auto','autoset','auto_typeset'])
+		_ret = namedtuple('cfgfile_opts', ['non_auto', 'autoset', 'auto_typeset'])
 
 
 		if not self._use_cfg_file:
 		if not self._use_cfg_file:
-			return _ret( (), {}, {} )
+			return _ret((), {}, {})
 
 
 		# check for changes in system template file (term must be initialized)
 		# check for changes in system template file (term must be initialized)
 		from .cfgfile import mmgen_cfg_file
 		from .cfgfile import mmgen_cfg_file
-		mmgen_cfg_file(self,'sample')
+		mmgen_cfg_file(self, 'sample')
 
 
-		ucfg = mmgen_cfg_file(self,'usr')
+		ucfg = mmgen_cfg_file(self, 'usr')
 
 
 		self._cfgfile_fn = ucfg.fn
 		self._cfgfile_fn = ucfg.fn
 
 
@@ -642,9 +639,9 @@ class Config(Lockable):
 				if ns[0] in gc.core_coins:
 				if ns[0] in gc.core_coins:
 					if not need_proto:
 					if not need_proto:
 						continue
 						continue
-					nse,tn = (
-						(ns[2:],ns[1]=='testnet') if len(ns) > 2 and ns[1] in ('mainnet','testnet') else
-						(ns[1:],False)
+					nse, tn = (
+						(ns[2:], ns[1]=='testnet') if len(ns) > 2 and ns[1] in ('mainnet', 'testnet') else
+						(ns[1:], False)
 					)
 					)
 					# no instance yet, so override _class_ attr:
 					# no instance yet, so override _class_ attr:
 					cls = init_proto(self, ns[0], tn, need_amt=True, return_cls=True)
 					cls = init_proto(self, ns[0], tn, need_amt=True, return_cls=True)
@@ -652,26 +649,26 @@ class Config(Lockable):
 				else:
 				else:
 					cls = self
 					cls = self
 					attr = d.name
 					attr = d.name
-				refval = getattr(cls,attr)
-				val = ucfg.parse_value(d.value,refval)
+				refval = getattr(cls, attr)
+				val = ucfg.parse_value(d.value, refval)
 				if not val:
 				if not val:
-					die( 'CfgFileParseError', f'Parse error in file {ucfg.fn!r}, line {d.lineno}' )
-				val_conv = conv_type( attr, val, refval, 'configuration file option', src=ucfg.fn )
+					die('CfgFileParseError', f'Parse error in file {ucfg.fn!r}, line {d.lineno}')
+				val_conv = conv_type(attr, val, refval, 'configuration file option', src=ucfg.fn)
 				if not attr in already_set:
 				if not attr in already_set:
-					setattr(cls,attr,val_conv)
+					setattr(cls, attr, val_conv)
 					non_auto_opts.append(attr)
 					non_auto_opts.append(attr)
 			elif d.name in self._autoset_opts:
 			elif d.name in self._autoset_opts:
 				autoset_opts[d.name] = d.value
 				autoset_opts[d.name] = d.value
 			elif d.name in self._auto_typeset_opts:
 			elif d.name in self._auto_typeset_opts:
 				auto_typeset_opts[d.name] = d.value
 				auto_typeset_opts[d.name] = d.value
 			else:
 			else:
-				die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' )
+				die('CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}')
 
 
-		return _ret( tuple(non_auto_opts), autoset_opts, auto_typeset_opts )
+		return _ret(tuple(non_auto_opts), autoset_opts, auto_typeset_opts)
 
 
-	def _set_autoset_opts(self,cfgfile_autoset_opts):
+	def _set_autoset_opts(self, cfgfile_autoset_opts):
 
 
-		def get_autoset_opt(key,val,src):
+		def get_autoset_opt(key, val, src):
 
 
 			def die_on_err(desc):
 			def die_on_err(desc):
 				from .util import fmt_list
 				from .util import fmt_list
@@ -680,11 +677,11 @@ class Config(Lockable):
 					'{a!r}: invalid {b} (not {c}: {d})'.format(
 					'{a!r}: invalid {b} (not {c}: {d})'.format(
 						a = val,
 						a = val,
 						b = {
 						b = {
-							'cmdline': f'parameter for option --{key.replace("_","-")}',
+							'cmdline': f'parameter for option --{key.replace("_", "-")}',
 							'cfgfile': f'value for cfg file option {key!r}'
 							'cfgfile': f'value for cfg file option {key!r}'
 						}[src],
 						}[src],
 						c = desc,
 						c = desc,
-						d = fmt_list(data.choices) ))
+						d = fmt_list(data.choices)))
 
 
 			class opt_type:
 			class opt_type:
 
 
@@ -703,7 +700,7 @@ class Config(Lockable):
 
 
 			data = self._autoset_opts[key]
 			data = self._autoset_opts[key]
 
 
-			return getattr(opt_type,data.type)()
+			return getattr(opt_type, data.type)()
 
 
 		# Check autoset opts, setting if unset
 		# Check autoset opts, setting if unset
 		for key in self._autoset_opts:
 		for key in self._autoset_opts:
@@ -711,27 +708,27 @@ class Config(Lockable):
 			if key in self._cloned:
 			if key in self._cloned:
 				continue
 				continue
 
 
-			assert not hasattr(self,key), f'autoset opt {key!r} is already set, but it shouldn’t be!'
+			assert not hasattr(self, key), f'autoset opt {key!r} is already set, but it shouldn’t be!'
 
 
 			if key in self._uopts:
 			if key in self._uopts:
-				val,src = (self._uopts[key],'cmdline')
+				val, src = (self._uopts[key], 'cmdline')
 			elif key in cfgfile_autoset_opts:
 			elif key in cfgfile_autoset_opts:
-				val,src = (cfgfile_autoset_opts[key],'cfgfile')
+				val, src = (cfgfile_autoset_opts[key], 'cfgfile')
 			else:
 			else:
 				val = None
 				val = None
 
 
 			if val is None:
 			if val is None:
 				setattr(self, key, self._autoset_opts[key].choices[0])
 				setattr(self, key, self._autoset_opts[key].choices[0])
 			else:
 			else:
-				setattr(self, key, get_autoset_opt(key,val,src=src))
+				setattr(self, key, get_autoset_opt(key, val, src=src))
 
 
-	def _set_auto_typeset_opts(self,cfgfile_auto_typeset_opts):
+	def _set_auto_typeset_opts(self, cfgfile_auto_typeset_opts):
 
 
-		def do_set(key,val,ref_type):
-			assert not hasattr(self,key), f'{key!r} is in cfg!'
-			setattr(self,key,None if val is None else ref_type(val))
+		def do_set(key, val, ref_type):
+			assert not hasattr(self, key), f'{key!r} is in cfg!'
+			setattr(self, key, None if val is None else ref_type(val))
 
 
-		for key,ref_type in self._auto_typeset_opts.items():
+		for key, ref_type in self._auto_typeset_opts.items():
 			if key in self._uopts:
 			if key in self._uopts:
 				do_set(key, self._uopts[key], ref_type)
 				do_set(key, self._uopts[key], ref_type)
 			elif key in cfgfile_auto_typeset_opts:
 			elif key in cfgfile_auto_typeset_opts:
@@ -739,18 +736,18 @@ class Config(Lockable):
 
 
 	def _die_on_incompatible_opts(self):
 	def _die_on_incompatible_opts(self):
 		for group in self._incompatible_opts:
 		for group in self._incompatible_opts:
-			bad = [k for k in self.__dict__ if k in group and getattr(self,k) is not None]
+			bad = [k for k in self.__dict__ if k in group and getattr(self, k) is not None]
 			if len(bad) > 1:
 			if len(bad) > 1:
-				die(1,'Conflicting options: {}'.format(', '.join(map(fmt_opt,bad))))
+				die(1, 'Conflicting options: {}'.format(', '.join(map(fmt_opt, bad))))
 
 
-	def _set_quiet(self,val):
+	def _set_quiet(self, val):
 		from .util import Util
 		from .util import Util
 		self.__dict__['quiet'] = val
 		self.__dict__['quiet'] = val
 		self.__dict__['_util'] = Util(self) # qmsg, qmsg_r
 		self.__dict__['_util'] = Util(self) # qmsg, qmsg_r
 
 
 def check_opts(cfg): # Raises exception if any check fails
 def check_opts(cfg): # Raises exception if any check fails
 
 
-	from .util import is_int,Msg
+	from .util import is_int, Msg
 
 
 	def get_desc(desc_pfx=''):
 	def get_desc(desc_pfx=''):
 		return (
 		return (
@@ -760,18 +757,18 @@ def check_opts(cfg): # Raises exception if any check fails
 					if name in cfg._uopts and 'command-line' in cfg._uopt_desc else
 					if name in cfg._uopts and 'command-line' in cfg._uopt_desc else
 				f'value for configuration option {name!r}'
 				f'value for configuration option {name!r}'
 			)
 			)
-			+ ( ' from environment' if name in cfg._envopts else '')
+			+ (' from environment' if name in cfg._envopts else '')
 			+ (f' in {cfg._cfgfile_fn!r}' if name in cfg._cfgfile_opts.non_auto else '')
 			+ (f' in {cfg._cfgfile_fn!r}' if name in cfg._cfgfile_opts.non_auto else '')
 		)
 		)
 
 
-	def display_opt(name,val='',beg='For selected',end=':\n'):
+	def display_opt(name, val='', beg='For selected', end=':\n'):
 		from .util import msg_r
 		from .util import msg_r
 		msg_r('{} option {!r}{}'.format(
 		msg_r('{} option {!r}{}'.format(
 			beg,
 			beg,
 			f'{fmt_opt(name)}={val}' if val else fmt_opt(name),
 			f'{fmt_opt(name)}={val}' if val else fmt_opt(name),
-			end ))
+			end))
 
 
-	def opt_compares(val,op_str,target):
+	def opt_compares(val, op_str, target):
 		import operator
 		import operator
 		if not {
 		if not {
 			'<':  operator.lt,
 			'<':  operator.lt,
@@ -779,24 +776,24 @@ def check_opts(cfg): # Raises exception if any check fails
 			'>':  operator.gt,
 			'>':  operator.gt,
 			'>=': operator.ge,
 			'>=': operator.ge,
 			'=':  operator.eq,
 			'=':  operator.eq,
-		}[op_str](val,target):
-			die( 'UserOptError', f'{val}: invalid {get_desc()} (not {op_str} {target})' )
+		}[op_str](val, target):
+			die('UserOptError', f'{val}: invalid {get_desc()} (not {op_str} {target})')
 
 
-	def opt_is_int(val,desc_pfx=''):
+	def opt_is_int(val, desc_pfx=''):
 		if not is_int(val):
 		if not is_int(val):
-			die( 'UserOptError', f'{val!r}: invalid {get_desc(desc_pfx)} (not an integer)' )
+			die('UserOptError', f'{val!r}: invalid {get_desc(desc_pfx)} (not an integer)')
 
 
-	def opt_is_in_list(val,tlist,desc_pfx=''):
+	def opt_is_in_list(val, tlist, desc_pfx=''):
 		if val not in tlist:
 		if val not in tlist:
-			q,sep = (('',','),("'","','"))[isinstance(tlist[0],str)]
-			die( 'UserOptError', '{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format(
+			q, sep = (('', ','), ("'", "','"))[isinstance(tlist[0], str)]
+			die('UserOptError', '{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format(
 				v = val,
 				v = val,
 				w = get_desc(desc_pfx),
 				w = get_desc(desc_pfx),
 				q = q,
 				q = q,
-				o = sep.join(map(str,sorted(tlist))) ))
+				o = sep.join(map(str, sorted(tlist)))))
 
 
 	def opt_unrecognized():
 	def opt_unrecognized():
-		die( 'UserOptError', f'{val!r}: unrecognized {get_desc()}' )
+		die('UserOptError', f'{val!r}: unrecognized {get_desc()}')
 
 
 	class check_funcs:
 	class check_funcs:
 
 
@@ -808,32 +805,32 @@ def check_opts(cfg): # Raises exception if any check fails
 			if name == 'out_fmt':
 			if name == 'out_fmt':
 				p = 'hidden_incog_output_params'
 				p = 'hidden_incog_output_params'
 
 
-				if wd.type == 'incog_hidden' and not getattr(cfg,p):
-					die( 'UserOptError',
+				if wd.type == 'incog_hidden' and not getattr(cfg, p):
+					die('UserOptError',
 						'Hidden incog format output requested.  ' +
 						'Hidden incog format output requested.  ' +
-						f'You must supply a file and offset with the {fmt_opt(p)!r} option' )
+						f'You must supply a file and offset with the {fmt_opt(p)!r} option')
 
 
 				if wd.base_type == 'incog_base' and cfg.old_incog_fmt:
 				if wd.base_type == 'incog_base' and cfg.old_incog_fmt:
-					display_opt(name,val,beg='Selected',end=' ')
-					display_opt('old_incog_fmt',beg='conflicts with',end=':\n')
-					die( 'UserOptError', 'Export to old incog wallet format unsupported' )
+					display_opt(name, val, beg='Selected', end=' ')
+					display_opt('old_incog_fmt', beg='conflicts with', end=':\n')
+					die('UserOptError', 'Export to old incog wallet format unsupported')
 				elif wd.type == 'brain':
 				elif wd.type == 'brain':
-					die( 'UserOptError', 'Output to brainwallet format unsupported' )
+					die('UserOptError', 'Output to brainwallet format unsupported')
 
 
 		out_fmt = in_fmt
 		out_fmt = in_fmt
 
 
 		def hidden_incog_input_params():
 		def hidden_incog_input_params():
-			a = val.rsplit(',',1) # permit comma in filename
+			a = val.rsplit(',', 1) # permit comma in filename
 			if len(a) != 2:
 			if len(a) != 2:
-				display_opt(name,val)
-				die( 'UserOptError', 'Option requires two comma-separated arguments' )
+				display_opt(name, val)
+				die('UserOptError', 'Option requires two comma-separated arguments')
 
 
-			fn,offset = a
+			fn, offset = a
 			opt_is_int(offset)
 			opt_is_int(offset)
 
 
-			from .fileutil import check_infile,check_outdir,check_outfile
+			from .fileutil import check_infile, check_outdir, check_outfile
 			if name == 'hidden_incog_input_params':
 			if name == 'hidden_incog_input_params':
-				check_infile(fn,blkdev_ok=True)
+				check_infile(fn, blkdev_ok=True)
 				key2 = 'in_fmt'
 				key2 = 'in_fmt'
 			else:
 			else:
 				try:
 				try:
@@ -843,52 +840,52 @@ def check_opts(cfg): # Raises exception if any check fails
 					if b:
 					if b:
 						check_outdir(b)
 						check_outdir(b)
 				else:
 				else:
-					check_outfile(fn,blkdev_ok=True)
+					check_outfile(fn, blkdev_ok=True)
 				key2 = 'out_fmt'
 				key2 = 'out_fmt'
 
 
-			if hasattr(cfg,key2):
-				val2 = getattr(cfg,key2)
+			if hasattr(cfg, key2):
+				val2 = getattr(cfg, key2)
 				from .wallet import get_wallet_data
 				from .wallet import get_wallet_data
 				wd = get_wallet_data('incog_hidden')
 				wd = get_wallet_data('incog_hidden')
 				if val2 and val2 not in wd.fmt_codes:
 				if val2 and val2 not in wd.fmt_codes:
-					die( 'UserOptError', f'Option conflict:\n  {fmt_opt(name)}, with\n  {fmt_opt(key2)}={val2}' )
+					die('UserOptError', f'Option conflict:\n  {fmt_opt(name)}, with\n  {fmt_opt(key2)}={val2}')
 
 
 		hidden_incog_output_params = hidden_incog_input_params
 		hidden_incog_output_params = hidden_incog_input_params
 
 
 		def subseeds():
 		def subseeds():
 			from .subseed import SubSeedIdxRange
 			from .subseed import SubSeedIdxRange
-			opt_compares(val,'>=',SubSeedIdxRange.min_idx)
-			opt_compares(val,'<=',SubSeedIdxRange.max_idx)
+			opt_compares(val, '>=', SubSeedIdxRange.min_idx)
+			opt_compares(val, '<=', SubSeedIdxRange.max_idx)
 
 
 		def seed_len():
 		def seed_len():
 			from .seed import Seed
 			from .seed import Seed
-			opt_is_in_list(int(val),Seed.lens)
+			opt_is_in_list(int(val), Seed.lens)
 
 
 		def hash_preset():
 		def hash_preset():
 			from .crypto import Crypto
 			from .crypto import Crypto
-			opt_is_in_list(val,list(Crypto.hash_presets.keys()))
+			opt_is_in_list(val, list(Crypto.hash_presets.keys()))
 
 
 		def brain_params():
 		def brain_params():
 			a = val.split(',')
 			a = val.split(',')
 			if len(a) != 2:
 			if len(a) != 2:
-				display_opt(name,val)
-				die( 'UserOptError', 'Option requires two comma-separated arguments' )
+				display_opt(name, val)
+				die('UserOptError', 'Option requires two comma-separated arguments')
 
 
-			opt_is_int( a[0], desc_pfx='seed length' )
+			opt_is_int(a[0], desc_pfx='seed length')
 			from .seed import Seed
 			from .seed import Seed
-			opt_is_in_list( int(a[0]), Seed.lens, desc_pfx='seed length' )
+			opt_is_in_list(int(a[0]), Seed.lens, desc_pfx='seed length')
 
 
 			from .crypto import Crypto
 			from .crypto import Crypto
-			opt_is_in_list( a[1], list(Crypto.hash_presets.keys()), desc_pfx='hash preset' )
+			opt_is_in_list(a[1], list(Crypto.hash_presets.keys()), desc_pfx='hash preset')
 
 
 		def usr_randchars():
 		def usr_randchars():
 			if val != 0:
 			if val != 0:
-				opt_compares(val,'>=',cfg.min_urandchars)
-				opt_compares(val,'<=',cfg.max_urandchars)
+				opt_compares(val, '>=', cfg.min_urandchars)
+				opt_compares(val, '<=', cfg.max_urandchars)
 
 
 		def tx_confs():
 		def tx_confs():
 			opt_is_int(val)
 			opt_is_int(val)
-			opt_compares(int(val),'>=',1)
+			opt_compares(int(val), '>=', 1)
 
 
 		def vsize_adj():
 		def vsize_adj():
 			from .util import ymsg
 			from .util import ymsg
@@ -896,19 +893,19 @@ def check_opts(cfg): # Raises exception if any check fails
 
 
 		def daemon_id():
 		def daemon_id():
 			from .daemon import CoinDaemon
 			from .daemon import CoinDaemon
-			opt_is_in_list(val,CoinDaemon.all_daemon_ids())
+			opt_is_in_list(val, CoinDaemon.all_daemon_ids())
 
 
 		def locktime():
 		def locktime():
 			opt_is_int(val)
 			opt_is_int(val)
-			opt_compares(int(val),'>',0)
+			opt_compares(int(val), '>', 0)
 
 
 		def columns():
 		def columns():
-			opt_compares(val,'>',10)
+			opt_compares(val, '>', 10)
 
 
 	# TODO: add checks for token, rbf, tx_fee
 	# TODO: add checks for token, rbf, tx_fee
 	check_funcs_names = tuple(check_funcs.__dict__)
 	check_funcs_names = tuple(check_funcs.__dict__)
 	for name in tuple(cfg._uopts) + cfg._envopts + cfg._cfgfile_opts.non_auto:
 	for name in tuple(cfg._uopts) + cfg._envopts + cfg._cfgfile_opts.non_auto:
-		val = getattr(cfg,name)
+		val = getattr(cfg, name)
 		if name in cfg._infile_opts:
 		if name in cfg._infile_opts:
 			from .fileutil import check_infile
 			from .fileutil import check_infile
 			check_infile(val) # file exists and is readable - dies on error
 			check_infile(val) # file exists and is readable - dies on error
@@ -916,19 +913,19 @@ def check_opts(cfg): # Raises exception if any check fails
 			from .fileutil import check_outdir
 			from .fileutil import check_outdir
 			check_outdir(val) # dies on error
 			check_outdir(val) # dies on error
 		elif name in check_funcs_names:
 		elif name in check_funcs_names:
-			getattr(check_funcs,name)()
+			getattr(check_funcs, name)()
 		elif cfg.debug:
 		elif cfg.debug:
 			Msg(f'check_opts(): No test for config opt {name!r}')
 			Msg(f'check_opts(): No test for config opt {name!r}')
 
 
 def fmt_opt(o):
 def fmt_opt(o):
-	return '--' + o.replace('_','-')
+	return '--' + o.replace('_', '-')
 
 
 def opt_postproc_debug(cfg):
 def opt_postproc_debug(cfg):
-	none_opts = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg,k) is None]
+	none_opts = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg, k) is None]
 	from .util import Msg
 	from .util import Msg
 	Msg('\n    Configuration opts:')
 	Msg('\n    Configuration opts:')
 	for e in [d for d in dir(cfg) if d[:2] != '__']:
 	for e in [d for d in dir(cfg) if d[:2] != '__']:
-		Msg(f'        {e:<20}: {getattr(cfg,e)}')
+		Msg(f'        {e:<20}: {getattr(cfg, e)}')
 	Msg("    Configuration opts set to 'None':")
 	Msg("    Configuration opts set to 'None':")
 	Msg('        {}\n'.format('\n        '.join(none_opts)))
 	Msg('        {}\n'.format('\n        '.join(none_opts)))
 	Msg('\n=== end opts.py debug ===\n')
 	Msg('\n=== end opts.py debug ===\n')
@@ -939,21 +936,21 @@ def conv_type(
 		refval,
 		refval,
 		desc,
 		desc,
 		invert_bool = False,
 		invert_bool = False,
-		src         = None ):
+		src         = None):
 
 
 	def do_fail():
 	def do_fail():
-		die(1,'{a!r}: invalid value for {b} {c!r}{d} (must be of type {e!r})'.format(
+		die(1, '{a!r}: invalid value for {b} {c!r}{d} (must be of type {e!r})'.format(
 			a = val,
 			a = val,
 			b = desc,
 			b = desc,
 			c = fmt_opt(name) if 'command-line' in desc else name,
 			c = fmt_opt(name) if 'command-line' in desc else name,
 			d = f' in {src!r}' if src else '',
 			d = f' in {src!r}' if src else '',
-			e = type(refval).__name__ ))
+			e = type(refval).__name__))
 
 
 	if type(refval) is bool:
 	if type(refval) is bool:
 		v = str(val).lower()
 		v = str(val).lower()
 		ret = (
 		ret = (
-			True  if v in ('true','yes','1','on') else
-			False if v in ('false','no','none','0','off','') else
+			True  if v in ('true', 'yes', '1', 'on') else
+			False if v in ('false', 'no', 'none', '0', 'off', '') else
 			None
 			None
 		)
 		)
 		return do_fail() if ret is None else (not ret) if invert_bool else ret
 		return do_fail() if ret is None else (not ret) if invert_bool else ret

+ 55 - 55
mmgen/cfgfile.py

@@ -20,13 +20,13 @@
 cfgfile: API for the MMGen runtime configuration file and related files
 cfgfile: API for the MMGen runtime configuration file and related files
 """
 """
 
 
-import os,re
+import os, re
 from collections import namedtuple
 from collections import namedtuple
 
 
 from .cfg import gc
 from .cfg import gc
-from .util import msg,ymsg,suf,fmt,fmt_list,oneshot_warning,strip_comment,capfirst,die
+from .util import msg, ymsg, suf, fmt, fmt_list, oneshot_warning, strip_comment, capfirst, die
 
 
-def mmgen_cfg_file(cfg,id_str):
+def mmgen_cfg_file(cfg, id_str):
 	return cfg_file.get_cls_by_id(id_str)(cfg)
 	return cfg_file.get_cls_by_id(id_str)(cfg)
 
 
 class cfg_file:
 class cfg_file:
@@ -35,62 +35,62 @@ class cfg_file:
 	write_ok = False
 	write_ok = False
 	warn_missing = True
 	warn_missing = True
 	write_metadata = False
 	write_metadata = False
-	line_data = namedtuple('cfgfile_line',['name','value','lineno','chunk'])
+	line_data = namedtuple('cfgfile_line', ['name', 'value', 'lineno', 'chunk'])
 	fn_base = 'mmgen.cfg'
 	fn_base = 'mmgen.cfg'
 
 
 	class warn_missing_file(oneshot_warning):
 	class warn_missing_file(oneshot_warning):
 		color = 'yellow' # has no effect, as color not initialized yet
 		color = 'yellow' # has no effect, as color not initialized yet
 		message = '{} not found at {!r}'
 		message = '{} not found at {!r}'
 
 
-	def get_data(self,fn):
+	def get_data(self, fn):
 		try:
 		try:
 			with open(fn) as fp:
 			with open(fn) as fp:
 				return fp.read().splitlines()
 				return fp.read().splitlines()
 		except:
 		except:
 			if self.warn_missing:
 			if self.warn_missing:
-				self.warn_missing_file( div=fn, fmt_args=(self.desc,fn) )
+				self.warn_missing_file(div=fn, fmt_args=(self.desc, fn))
 			return ''
 			return ''
 
 
-	def copy_system_data(self,fn):
+	def copy_system_data(self, fn):
 		assert self.write_ok, f'writing to file {fn!r} not allowed!'
 		assert self.write_ok, f'writing to file {fn!r} not allowed!'
-		src = mmgen_cfg_file(self.cfg,'sys')
+		src = mmgen_cfg_file(self.cfg, 'sys')
 		if src.data:
 		if src.data:
 			data = src.data + src.make_metadata() if self.write_metadata else src.data
 			data = src.data + src.make_metadata() if self.write_metadata else src.data
 			try:
 			try:
-				with open(fn,'w') as fp:
+				with open(fn, 'w') as fp:
 					fp.write('\n'.join(data)+'\n')
 					fp.write('\n'.join(data)+'\n')
-				os.chmod(fn,0o600)
+				os.chmod(fn, 0o600)
 			except:
 			except:
-				die(2,f'ERROR: unable to write to {fn!r}')
+				die(2, f'ERROR: unable to write to {fn!r}')
 
 
-	def parse_value(self,value,refval):
-		if isinstance(refval,dict):
-			m = re.fullmatch(r'((\s+\w+:\S+)+)',' '+value) # expect one or more colon-separated values
+	def parse_value(self, value, refval):
+		if isinstance(refval, dict):
+			m = re.fullmatch(r'((\s+\w+:\S+)+)', ' '+value) # expect one or more colon-separated values
 			if m:
 			if m:
 				return dict([i.split(':') for i in m[1].split()])
 				return dict([i.split(':') for i in m[1].split()])
-		elif isinstance(refval,(list,tuple)):
-			m = re.fullmatch(r'((\s+\S+)+)',' '+value)     # expect single value or list
+		elif isinstance(refval, (list, tuple)):
+			m = re.fullmatch(r'((\s+\S+)+)', ' '+value)     # expect single value or list
 			if m:
 			if m:
 				ret = m[1].split()
 				ret = m[1].split()
-				return ret if isinstance(refval,list) else tuple(ret)
+				return ret if isinstance(refval, list) else tuple(ret)
 		else:
 		else:
 			return value
 			return value
 
 
 	def get_lines(self):
 	def get_lines(self):
 		def gen_lines():
 		def gen_lines():
-			for lineno,line in enumerate(self.data,1):
+			for lineno, line in enumerate(self.data, 1):
 				line = strip_comment(line)
 				line = strip_comment(line)
 				if line == '':
 				if line == '':
 					continue
 					continue
-				m = re.fullmatch(r'(\w+)(\s+)(.*)',line)
+				m = re.fullmatch(r'(\w+)(\s+)(.*)', line)
 				if m:
 				if m:
-					yield self.line_data(m[1],m[3],lineno,None)
+					yield self.line_data(m[1], m[3], lineno, None)
 				else:
 				else:
-					die( 'CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}' )
+					die('CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}')
 		return gen_lines()
 		return gen_lines()
 
 
 	@classmethod
 	@classmethod
-	def get_cls_by_id(cls,id_str):
+	def get_cls_by_id(cls, id_str):
 		d = {
 		d = {
 			'usr':    CfgFileUsr,
 			'usr':    CfgFileUsr,
 			'sys':    CfgFileSampleSys,
 			'sys':    CfgFileSampleSys,
@@ -101,13 +101,13 @@ class cfg_file:
 class cfg_file_sample(cfg_file):
 class cfg_file_sample(cfg_file):
 
 
 	@classmethod
 	@classmethod
-	def cls_make_metadata(cls,data):
+	def cls_make_metadata(cls, data):
 		return [f'# Version {cls.cur_ver} {cls.compute_chksum(data)}']
 		return [f'# Version {cls.cur_ver} {cls.compute_chksum(data)}']
 
 
 	@staticmethod
 	@staticmethod
 	def compute_chksum(data):
 	def compute_chksum(data):
 		import hashlib
 		import hashlib
-		return hashlib.new('ripemd160','\n'.join(data).encode()).hexdigest()
+		return hashlib.new('ripemd160', '\n'.join(data).encode()).hexdigest()
 
 
 	@property
 	@property
 	def computed_chksum(self):
 	def computed_chksum(self):
@@ -124,19 +124,19 @@ class cfg_file_sample(cfg_file):
 		- last line is metadata line of the form '# Version VER_NUM HASH'
 		- last line is metadata line of the form '# Version VER_NUM HASH'
 		"""
 		"""
 
 
-		def process_chunk(chunk,lineno):
-			m = re.fullmatch(r'(#\s*)(\w+)(\s+)(.*)',chunk[-1])
+		def process_chunk(chunk, lineno):
+			m = re.fullmatch(r'(#\s*)(\w+)(\s+)(.*)', chunk[-1])
 			if m:
 			if m:
-				return self.line_data(m[2],m[4],lineno,chunk)
+				return self.line_data(m[2], m[4], lineno, chunk)
 			else:
 			else:
-				die( 'CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}' )
+				die('CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}')
 
 
 		def gen_chunks(lines):
 		def gen_chunks(lines):
 			hdr = True
 			hdr = True
 			chunk = []
 			chunk = []
 			in_chunk = False
 			in_chunk = False
 
 
-			for lineno,line in enumerate(lines,1):
+			for lineno, line in enumerate(lines, 1):
 
 
 				if line.startswith('##'):
 				if line.startswith('##'):
 					hdr = False
 					hdr = False
@@ -150,17 +150,17 @@ class cfg_file_sample(cfg_file):
 				elif line.startswith('#'):
 				elif line.startswith('#'):
 					if in_chunk is False:
 					if in_chunk is False:
 						if chunk:
 						if chunk:
-							yield process_chunk(chunk,last_nonblank)
+							yield process_chunk(chunk, last_nonblank)
 						chunk = [line]
 						chunk = [line]
 						in_chunk = True
 						in_chunk = True
 					else:
 					else:
 						chunk.append(line)
 						chunk.append(line)
 					last_nonblank = lineno
 					last_nonblank = lineno
 				else:
 				else:
-					die( 'CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}' )
+					die('CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}')
 
 
 			if chunk:
 			if chunk:
-				yield process_chunk(chunk,last_nonblank)
+				yield process_chunk(chunk, last_nonblank)
 
 
 		return list(gen_chunks(self.data))
 		return list(gen_chunks(self.data))
 
 
@@ -169,9 +169,9 @@ class CfgFileUsr(cfg_file):
 	warn_missing = False
 	warn_missing = False
 	write_ok = True
 	write_ok = True
 
 
-	def __init__(self,cfg):
+	def __init__(self, cfg):
 		self.cfg = cfg
 		self.cfg = cfg
-		self.fn = os.path.join(cfg.data_dir_root,self.fn_base)
+		self.fn = os.path.join(cfg.data_dir_root, self.fn_base)
 		self.data = self.get_data(self.fn)
 		self.data = self.get_data(self.fn)
 		if not self.data:
 		if not self.data:
 			self.copy_system_data(self.fn)
 			self.copy_system_data(self.fn)
@@ -180,15 +180,15 @@ class CfgFileSampleSys(cfg_file_sample):
 	desc = 'system sample configuration file'
 	desc = 'system sample configuration file'
 	test_fn_subdir = 'usr.local.share'
 	test_fn_subdir = 'usr.local.share'
 
 
-	def __init__(self,cfg):
+	def __init__(self, cfg):
 		self.cfg = cfg
 		self.cfg = cfg
 		if self.cfg.test_suite_cfgtest:
 		if self.cfg.test_suite_cfgtest:
-			self.fn = os.path.join(cfg.data_dir_root,self.test_fn_subdir,self.fn_base)
+			self.fn = os.path.join(cfg.data_dir_root, self.test_fn_subdir, self.fn_base)
 			with open(self.fn) as fp:
 			with open(self.fn) as fp:
 				self.data = fp.read().splitlines()
 				self.data = fp.read().splitlines()
 		else:
 		else:
 			# self.fn is used for error msgs only, so file need not exist on filesystem
 			# self.fn is used for error msgs only, so file need not exist on filesystem
-			self.fn = os.path.join(os.path.dirname(__file__),'data',self.fn_base)
+			self.fn = os.path.join(os.path.dirname(__file__), 'data', self.fn_base)
 			self.data = gc.get_mmgen_data_file(self.fn_base).splitlines()
 			self.data = gc.get_mmgen_data_file(self.fn_base).splitlines()
 
 
 	def make_metadata(self):
 	def make_metadata(self):
@@ -204,12 +204,12 @@ class CfgFileSampleUsr(cfg_file_sample):
 	out_of_date_fs = 'File {!r} is out of date - replacing'
 	out_of_date_fs = 'File {!r} is out of date - replacing'
 	altered_by_user_fs = 'File {!r} was altered by user - replacing'
 	altered_by_user_fs = 'File {!r} was altered by user - replacing'
 
 
-	def __init__(self,cfg):
+	def __init__(self, cfg):
 		self.cfg = cfg
 		self.cfg = cfg
-		self.fn = os.path.join(cfg.data_dir_root,f'{self.fn_base}.sample')
+		self.fn = os.path.join(cfg.data_dir_root, f'{self.fn_base}.sample')
 		self.data = self.get_data(self.fn)
 		self.data = self.get_data(self.fn)
 
 
-		src = mmgen_cfg_file(cfg,'sys')
+		src = mmgen_cfg_file(cfg, 'sys')
 
 
 		if not src.data:
 		if not src.data:
 			return
 			return
@@ -217,7 +217,7 @@ class CfgFileSampleUsr(cfg_file_sample):
 		if self.data:
 		if self.data:
 			if self.parse_metadata():
 			if self.parse_metadata():
 				if self.chksum == self.computed_chksum:
 				if self.chksum == self.computed_chksum:
-					diff = self.diff(self.get_lines(),src.get_lines())
+					diff = self.diff(self.get_lines(), src.get_lines())
 					if not diff:
 					if not diff:
 						return
 						return
 					self.show_changes(diff)
 					self.show_changes(diff)
@@ -230,14 +230,14 @@ class CfgFileSampleUsr(cfg_file_sample):
 
 
 	def parse_metadata(self):
 	def parse_metadata(self):
 		if self.data:
 		if self.data:
-			m = re.match(r'# Version (\d+) ([a-f0-9]{40})$',self.data[-1])
+			m = re.match(r'# Version (\d+) ([a-f0-9]{40})$', self.data[-1])
 			if m:
 			if m:
 				self.ver = m[1]
 				self.ver = m[1]
 				self.chksum = m[2]
 				self.chksum = m[2]
 				self.data = self.data[:-1] # remove metadata line
 				self.data = self.data[:-1] # remove metadata line
 				return True
 				return True
 
 
-	def diff(self,a_tup,b_tup): # a=user, b=system
+	def diff(self, a_tup, b_tup): # a=user, b=system
 		a = [i.name for i in a_tup]#[3:] # Debug
 		a = [i.name for i in a_tup]#[3:] # Debug
 		b = [i.name for i in b_tup]#[:-2] # Debug
 		b = [i.name for i in b_tup]#[:-2] # Debug
 		removed = set(a) - set(b)
 		removed = set(a) - set(b)
@@ -250,34 +250,34 @@ class CfgFileSampleUsr(cfg_file_sample):
 		else:
 		else:
 			return None
 			return None
 
 
-	def show_changes(self,diff):
+	def show_changes(self, diff):
 		ymsg('Warning: configuration file options have changed!\n')
 		ymsg('Warning: configuration file options have changed!\n')
-		for desc in ('added','removed'):
+		for desc in ('added', 'removed'):
 			data = diff[desc]
 			data = diff[desc]
 			if data:
 			if data:
-				opts = fmt_list([i.name for i in data],fmt='bare')
-				msg(f'  The following option{suf(data,verb="has")} been {desc}:\n    {opts}\n')
+				opts = fmt_list([i.name for i in data], fmt='bare')
+				msg(f'  The following option{suf(data, verb="has")} been {desc}:\n    {opts}\n')
 				if desc == 'removed' and data:
 				if desc == 'removed' and data:
-					uc = mmgen_cfg_file(self.cfg,'usr')
+					uc = mmgen_cfg_file(self.cfg, 'usr')
 					usr_names = [i.name for i in uc.get_lines()]
 					usr_names = [i.name for i in uc.get_lines()]
 					rm_names = [i.name for i in data]
 					rm_names = [i.name for i in data]
 					bad = sorted(set(usr_names).intersection(rm_names))
 					bad = sorted(set(usr_names).intersection(rm_names))
 					if bad:
 					if bad:
 						m = f"""
 						m = f"""
-							The following removed option{suf(bad,verb='is')} set in {uc.fn!r}
+							The following removed option{suf(bad, verb='is')} set in {uc.fn!r}
 							and must be deleted or commented out:
 							and must be deleted or commented out:
-							{'  ' + fmt_list(bad,fmt='bare')}
+							{'  ' + fmt_list(bad, fmt='bare')}
 						"""
 						"""
-						ymsg(fmt(m,indent='  ',strip_char='\t'))
+						ymsg(fmt(m, indent='  ', strip_char='\t'))
 
 
-		from .ui import keypress_confirm,do_pager
+		from .ui import keypress_confirm, do_pager
 		while True:
 		while True:
-			if not keypress_confirm( self.cfg, self.details_confirm_prompt, no_nl=True ):
+			if not keypress_confirm(self.cfg, self.details_confirm_prompt, no_nl=True):
 				return
 				return
 
 
 			def get_details():
 			def get_details():
-				for desc,data in diff.items():
-					sep,sep2 = ('\n  ','\n\n  ')
+				for desc, data in diff.items():
+					sep, sep2 = ('\n  ', '\n\n  ')
 					if data:
 					if data:
 						yield (
 						yield (
 							f'{capfirst(desc)} section{suf(data)}:'
 							f'{capfirst(desc)} section{suf(data)}:'

+ 35 - 35
mmgen/color.py

@@ -21,27 +21,27 @@ color: color handling for the MMGen suite
 """
 """
 
 
 _colors = {
 _colors = {
-	'black':       (  232,      (30,0) ),
-	'red':         (  210,      (31,1) ),
-	'green':       (  121,      (32,1) ),
-	'yellow':      (  229,      (33,1) ),
-	'blue':        (  75,       (34,1) ),
-	'magenta':     (  205,      (35,1) ),
-	'cyan':        (  122,      (36,1) ),
-
-	'gray':        (  246,      (30,1) ),
-	'orange':      (  216,      (31,1) ),
-	'purple':      (  141,      (35,1) ),
-	'pink':        (  218,      (35,1) ),
-
-	'melon':       (  222,      (33,1) ),
-	'brown':       (  173,      (33,0) ),
-	'grndim':      (  108,      (32,0) ),
-
-	'redbg':       ( (232,210), (30,101) ),
-	'grnbg':       ( (232,121), (30,102) ),
-	'yelbg':       ( (232,229), (30,103) ),
-	'blubg':       ( (232,75),  (30,104) ),
+	'black':   (232,        (30, 0)),
+	'red':     (210,        (31, 1)),
+	'green':   (121,        (32, 1)),
+	'yellow':  (229,        (33, 1)),
+	'blue':    (75,         (34, 1)),
+	'magenta': (205,        (35, 1)),
+	'cyan':    (122,        (36, 1)),
+
+	'gray':    (246,        (30, 1)),
+	'orange':  (216,        (31, 1)),
+	'purple':  (141,        (35, 1)),
+	'pink':    (218,        (35, 1)),
+
+	'melon':   (222,        (33, 1)),
+	'brown':   (173,        (33, 0)),
+	'grndim':  (108,        (32, 0)),
+
+	'redbg':   ((232, 210), (30, 101)),
+	'grnbg':   ((232, 121), (30, 102)),
+	'yelbg':   ((232, 229), (30, 103)),
+	'blubg':   ((232, 75),  (30, 104)),
 }
 }
 
 
 def nocolor(s):
 def nocolor(s):
@@ -52,16 +52,16 @@ def set_vt100():
 	import sys
 	import sys
 	if sys.platform == 'win32':
 	if sys.platform == 'win32':
 		from subprocess import run
 		from subprocess import run
-		run([],shell=True)
+		run([], shell=True)
 
 
 def get_terminfo_colors(term=None):
 def get_terminfo_colors(term=None):
-	from subprocess import run,PIPE
-	cmd = ['infocmp','-0']
+	from subprocess import run, PIPE
+	cmd = ['infocmp', '-0']
 	if term:
 	if term:
 		cmd.append(term)
 		cmd.append(term)
 
 
 	try:
 	try:
-		cmdout = run(cmd,stdout=PIPE,check=True).stdout.decode()
+		cmdout = run(cmd, stdout=PIPE, check=True).stdout.decode()
 	except:
 	except:
 		set_vt100()
 		set_vt100()
 		return None
 		return None
@@ -72,12 +72,12 @@ def get_terminfo_colors(term=None):
 		if s.isdecimal():
 		if s.isdecimal():
 			return int(s)
 			return int(s)
 		elif s.startswith('0x') and is_hex_str(s[2:]):
 		elif s.startswith('0x') and is_hex_str(s[2:]):
-			return int(s[2:],16)
+			return int(s[2:], 16)
 		else:
 		else:
 			return None
 			return None
 
 
 def init_color(num_colors='auto'):
 def init_color(num_colors='auto'):
-	assert num_colors in ('auto',8,16,256,0)
+	assert num_colors in ('auto', 8, 16, 256, 0)
 
 
 	import sys
 	import sys
 	self = sys.modules[__name__]
 	self = sys.modules[__name__]
@@ -98,19 +98,19 @@ def init_color(num_colors='auto'):
 	if num_colors == 0:
 	if num_colors == 0:
 		ncc = (lambda s: s).__code__
 		ncc = (lambda s: s).__code__
 		for c in _colors:
 		for c in _colors:
-			getattr(self,c).__code__ = ncc
+			getattr(self, c).__code__ = ncc
 	elif num_colors == 256:
 	elif num_colors == 256:
-		for c,e in _colors.items():
+		for c, e in _colors.items():
 			start = (
 			start = (
 				'\033[38;5;{};1m'.format(e[0]) if type(e[0]) == int else
 				'\033[38;5;{};1m'.format(e[0]) if type(e[0]) == int else
-				'\033[38;5;{};48;5;{};1m'.format(*e[0]) )
-			getattr(self,c).__code__ = eval(f'(lambda s: "{start}" + s + "{reset}").__code__')
-	elif num_colors in (8,16):
-		for c,e in _colors.items():
+				'\033[38;5;{};48;5;{};1m'.format(*e[0]))
+			getattr(self, c).__code__ = eval(f'(lambda s: "{start}" + s + "{reset}").__code__')
+	elif num_colors in (8, 16):
+		for c, e in _colors.items():
 			start = (
 			start = (
 				'\033[{}m'.format(e[1][0]) if e[1][1] == 0 else
 				'\033[{}m'.format(e[1][0]) if e[1][1] == 0 else
-				'\033[{};{}m'.format(*e[1]) )
-			getattr(self,c).__code__ = eval(f'(lambda s: "{start}" + s + "{reset}").__code__')
+				'\033[{};{}m'.format(*e[1]))
+			getattr(self, c).__code__ = eval(f'(lambda s: "{start}" + s + "{reset}").__code__')
 
 
 	set_vt100()
 	set_vt100()
 
 

+ 77 - 85
mmgen/crypto.py

@@ -24,15 +24,7 @@ import os
 from collections import namedtuple
 from collections import namedtuple
 
 
 from .cfg import gc
 from .cfg import gc
-from .util import (
-	msg,
-	msg_r,
-	ymsg,
-	fmt,
-	die,
-	make_chksum_8,
-	oneshot_warning,
-)
+from .util import msg, msg_r, ymsg, fmt, die, make_chksum_8, oneshot_warning
 
 
 class Crypto:
 class Crypto:
 
 
@@ -41,7 +33,7 @@ class Crypto:
 
 
 	salt_len       = 16
 	salt_len       = 16
 	aesctr_iv_len  = 16
 	aesctr_iv_len  = 16
-	aesctr_dfl_iv  = int.to_bytes(1,aesctr_iv_len,'big')
+	aesctr_dfl_iv  = int.to_bytes(1, aesctr_iv_len, 'big')
 	hincog_chk_len = 8
 	hincog_chk_len = 8
 
 
 	mmenc_salt_len = 32
 	mmenc_salt_len = 32
@@ -50,7 +42,7 @@ class Crypto:
 	# Scrypt params: 'id_num': [N, r, p] (N is an exponent of two)
 	# Scrypt params: 'id_num': [N, r, p] (N is an exponent of two)
 	# NB: hashlib.scrypt in Python (>=v3.6) supports max N value of 14.  This means that
 	# NB: hashlib.scrypt in Python (>=v3.6) supports max N value of 14.  This means that
 	# for hash presets > 3 the standalone scrypt library must be used!
 	# for hash presets > 3 the standalone scrypt library must be used!
-	_hp = namedtuple('scrypt_preset',['N','r','p'])
+	_hp = namedtuple('scrypt_preset', ['N', 'r', 'p'])
 	hash_presets = {
 	hash_presets = {
 		'1': _hp(12, 8, 1),
 		'1': _hp(12, 8, 1),
 		'2': _hp(13, 8, 4),
 		'2': _hp(13, 8, 4),
@@ -63,60 +55,60 @@ class Crypto:
 
 
 	class pwfile_reuse_warning(oneshot_warning):
 	class pwfile_reuse_warning(oneshot_warning):
 		message = 'Reusing passphrase from file {!r} at user request'
 		message = 'Reusing passphrase from file {!r} at user request'
-		def __init__(self,fn):
-			oneshot_warning.__init__(self,div=fn,fmt_args=[fn],reverse=True)
+		def __init__(self, fn):
+			oneshot_warning.__init__(self, div=fn, fmt_args=[fn], reverse=True)
 
 
-	def pwfile_used(self,passwd_file):
-		if hasattr(self,'_pwfile_used'):
+	def pwfile_used(self, passwd_file):
+		if hasattr(self, '_pwfile_used'):
 			self.pwfile_reuse_warning(passwd_file)
 			self.pwfile_reuse_warning(passwd_file)
 			return True
 			return True
 		else:
 		else:
 			self._pwfile_used = True
 			self._pwfile_used = True
 			return False
 			return False
 
 
-	def __init__(self,cfg):
+	def __init__(self, cfg):
 		self.cfg = cfg
 		self.cfg = cfg
 		self.util = cfg._util
 		self.util = cfg._util
 
 
-	def get_hash_params(self,hash_preset):
+	def get_hash_params(self, hash_preset):
 		if hash_preset in self.hash_presets:
 		if hash_preset in self.hash_presets:
-			return self.hash_presets[hash_preset] # N,r,p
+			return self.hash_presets[hash_preset] # N, r, p
 		else: # Shouldn't be here
 		else: # Shouldn't be here
-			die(3,f"{hash_preset}: invalid 'hash_preset' value")
+			die(3, f"{hash_preset}: invalid 'hash_preset' value")
 
 
-	def sha256_rounds(self,s):
+	def sha256_rounds(self, s):
 		from hashlib import sha256
 		from hashlib import sha256
 		for _ in range(self.scramble_hash_rounds):
 		for _ in range(self.scramble_hash_rounds):
 			s = sha256(s).digest()
 			s = sha256(s).digest()
 		return s
 		return s
 
 
-	def scramble_seed(self,seed,scramble_key):
+	def scramble_seed(self, seed, scramble_key):
 		import hmac
 		import hmac
-		step1 = hmac.digest(seed,scramble_key,'sha256')
+		step1 = hmac.digest(seed, scramble_key, 'sha256')
 		if self.cfg.debug:
 		if self.cfg.debug:
 			msg(f'Seed:  {seed.hex()!r}\nScramble key: {scramble_key}\nScrambled seed: {step1.hex()}\n')
 			msg(f'Seed:  {seed.hex()!r}\nScramble key: {scramble_key}\nScrambled seed: {step1.hex()}\n')
 		return self.sha256_rounds(step1)
 		return self.sha256_rounds(step1)
 
 
-	def encrypt_seed(self,data,key,desc='seed'):
-		return self.encrypt_data(data,key,desc=desc)
+	def encrypt_seed(self, data, key, desc='seed'):
+		return self.encrypt_data(data, key, desc=desc)
 
 
-	def decrypt_seed(self,enc_seed,key,seed_id,key_id):
+	def decrypt_seed(self, enc_seed, key, seed_id, key_id):
 		self.util.vmsg_r('Checking key...')
 		self.util.vmsg_r('Checking key...')
 		chk1 = make_chksum_8(key)
 		chk1 = make_chksum_8(key)
 		if key_id:
 		if key_id:
-			if not self.util.compare_chksums(key_id,'key ID',chk1,'computed'):
+			if not self.util.compare_chksums(key_id, 'key ID', chk1, 'computed'):
 				msg('Incorrect passphrase or hash preset')
 				msg('Incorrect passphrase or hash preset')
 				return False
 				return False
 
 
-		dec_seed = self.decrypt_data(enc_seed,key,desc='seed')
+		dec_seed = self.decrypt_data(enc_seed, key, desc='seed')
 		chk2     = make_chksum_8(dec_seed)
 		chk2     = make_chksum_8(dec_seed)
 		if seed_id:
 		if seed_id:
-			if self.util.compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
+			if self.util.compare_chksums(seed_id, 'Seed ID', chk2, 'decrypted seed'):
 				self.util.qmsg('Passphrase is OK')
 				self.util.qmsg('Passphrase is OK')
 			else:
 			else:
 				if not self.cfg.debug:
 				if not self.cfg.debug:
 					msg_r('Checking key ID...')
 					msg_r('Checking key ID...')
-					if self.util.compare_chksums(key_id,'key ID',chk1,'computed'):
+					if self.util.compare_chksums(key_id, 'key ID', chk1, 'computed'):
 						msg('Key ID is correct but decryption of seed failed')
 						msg('Key ID is correct but decryption of seed failed')
 					else:
 					else:
 						msg('Incorrect passphrase or hash preset')
 						msg('Incorrect passphrase or hash preset')
@@ -130,26 +122,26 @@ class Crypto:
 			self,
 			self,
 			data,
 			data,
 			key,
 			key,
-			iv = aesctr_dfl_iv,
-			desc = 'data',
+			iv     = aesctr_dfl_iv,
+			desc   = 'data',
 			verify = True,
 			verify = True,
-			silent = False ):
+			silent = False):
 
 
-		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
+		from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 		from cryptography.hazmat.backends import default_backend
 		from cryptography.hazmat.backends import default_backend
 		if not silent:
 		if not silent:
 			self.util.vmsg(f'Encrypting {desc}')
 			self.util.vmsg(f'Encrypting {desc}')
-		c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
+		c = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
 		encryptor = c.encryptor()
 		encryptor = c.encryptor()
 		enc_data = encryptor.update(data) + encryptor.finalize()
 		enc_data = encryptor.update(data) + encryptor.finalize()
 
 
 		if verify:
 		if verify:
 			self.util.vmsg_r(f'Performing a test decryption of the {desc}...')
 			self.util.vmsg_r(f'Performing a test decryption of the {desc}...')
-			c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
+			c = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
 			encryptor = c.encryptor()
 			encryptor = c.encryptor()
 			dec_data = encryptor.update(enc_data) + encryptor.finalize()
 			dec_data = encryptor.update(enc_data) + encryptor.finalize()
 			if dec_data != data:
 			if dec_data != data:
-				die(2,f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
+				die(2, f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
 			if not silent:
 			if not silent:
 				self.util.vmsg('done')
 				self.util.vmsg('done')
 
 
@@ -159,13 +151,13 @@ class Crypto:
 			self,
 			self,
 			enc_data,
 			enc_data,
 			key,
 			key,
-			iv = aesctr_dfl_iv,
-			desc = 'data' ):
+			iv   = aesctr_dfl_iv,
+			desc = 'data'):
 
 
-		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
+		from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 		from cryptography.hazmat.backends import default_backend
 		from cryptography.hazmat.backends import default_backend
 		self.util.vmsg_r(f'Decrypting {desc} with key...')
 		self.util.vmsg_r(f'Decrypting {desc} with key...')
-		c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
+		c = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
 		encryptor = c.encryptor()
 		encryptor = c.encryptor()
 		return encryptor.update(enc_data) + encryptor.finalize()
 		return encryptor.update(enc_data) + encryptor.finalize()
 
 
@@ -174,13 +166,13 @@ class Crypto:
 			passwd,
 			passwd,
 			salt,
 			salt,
 			hash_preset,
 			hash_preset,
-			buflen = 32 ):
+			buflen = 32):
 
 
 		# Buflen arg is for brainwallets only, which use this function to generate
 		# Buflen arg is for brainwallets only, which use this function to generate
 		# the seed directly.
 		# the seed directly.
 		ps = self.get_hash_params(hash_preset)
 		ps = self.get_hash_params(hash_preset)
 
 
-		if isinstance(passwd,str):
+		if isinstance(passwd, str):
 			passwd = passwd.encode()
 			passwd = passwd.encode()
 
 
 		def do_hashlib_scrypt():
 		def do_hashlib_scrypt():
@@ -192,7 +184,7 @@ class Crypto:
 				r        = ps.r,
 				r        = ps.r,
 				p        = ps.p,
 				p        = ps.p,
 				maxmem   = 0,
 				maxmem   = 0,
-				dklen    = buflen )
+				dklen    = buflen)
 
 
 		def do_standalone_scrypt():
 		def do_standalone_scrypt():
 			import scrypt
 			import scrypt
@@ -202,7 +194,7 @@ class Crypto:
 				N        = 2**ps.N,
 				N        = 2**ps.N,
 				r        = ps.r,
 				r        = ps.r,
 				p        = ps.p,
 				p        = ps.p,
-				buflen   = buflen )
+				buflen   = buflen)
 
 
 		if int(hash_preset) > 3:
 		if int(hash_preset) > 3:
 			msg_r('Hashing passphrase, please wait...')
 			msg_r('Hashing passphrase, please wait...')
@@ -210,7 +202,7 @@ class Crypto:
 		# hashlib.scrypt doesn't support N > 14 (hash preset > 3)
 		# hashlib.scrypt doesn't support N > 14 (hash preset > 3)
 		ret = (
 		ret = (
 			do_standalone_scrypt() if ps.N > 14 or self.cfg.force_standalone_scrypt_module else
 			do_standalone_scrypt() if ps.N > 14 or self.cfg.force_standalone_scrypt_module else
-			do_hashlib_scrypt() )
+			do_hashlib_scrypt())
 
 
 		if int(hash_preset) > 3:
 		if int(hash_preset) > 3:
 			msg_r('\b'*34 + ' '*34 + '\b'*34)
 			msg_r('\b'*34 + ' '*34 + '\b'*34)
@@ -222,19 +214,19 @@ class Crypto:
 			passwd,
 			passwd,
 			salt,
 			salt,
 			hash_preset,
 			hash_preset,
-			desc = 'encryption key',
+			desc      = 'encryption key',
 			from_what = 'passphrase',
 			from_what = 'passphrase',
-			verbose = False ):
+			verbose   = False):
 
 
 		if self.cfg.verbose or verbose:
 		if self.cfg.verbose or verbose:
 			msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...")
 			msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...")
-		key = self.scrypt_hash_passphrase(passwd,salt,hash_preset)
+		key = self.scrypt_hash_passphrase(passwd, salt, hash_preset)
 		if self.cfg.verbose or verbose:
 		if self.cfg.verbose or verbose:
 			msg('done')
 			msg('done')
 		self.util.dmsg(f'Key: {key.hex()}')
 		self.util.dmsg(f'Key: {key.hex()}')
 		return key
 		return key
 
 
-	def _get_random_data_from_user(self,uchars=None,desc='data'):
+	def _get_random_data_from_user(self, uchars=None, desc='data'):
 
 
 		if uchars is None:
 		if uchars is None:
 			uchars = self.cfg.usr_randchars
 			uchars = self.cfg.usr_randchars
@@ -260,8 +252,8 @@ class Crypto:
 		"""
 		"""
 
 
 		msg(f'Enter {uchars} random symbols' if self.cfg.quiet else
 		msg(f'Enter {uchars} random symbols' if self.cfg.quiet else
-			'\n' + fmt(info1,indent='  ') +
-			'\n' + fmt(info2) )
+			'\n' + fmt(info1, indent='  ') +
+			'\n' + fmt(info2))
 
 
 		import time
 		import time
 		from .term import get_char_raw
 		from .term import get_char_raw
@@ -272,7 +264,7 @@ class Crypto:
 			key_data += get_char_raw(f'\rYou may begin typing.  {uchars-i} symbols left: ')
 			key_data += get_char_raw(f'\rYou may begin typing.  {uchars-i} symbols left: ')
 			time_data.append(time.time())
 			time_data.append(time.time())
 
 
-		msg_r( '\r' if self.cfg.quiet else f'\rThank you.  That’s enough.{" "*18}\n\n' )
+		msg_r('\r' if self.cfg.quiet else f'\rThank you.  That’s enough.{" "*18}\n\n')
 
 
 		time_data = [f'{t:.22f}'.rstrip('0') for t in time_data]
 		time_data = [f'{t:.22f}'.rstrip('0') for t in time_data]
 
 
@@ -287,24 +279,24 @@ class Crypto:
 			msg(f'USER ENTROPY (user input + keystroke timings):\n{ret}')
 			msg(f'USER ENTROPY (user input + keystroke timings):\n{ret}')
 
 
 		from .ui import line_input
 		from .ui import line_input
-		line_input( self.cfg, 'User random data successfully acquired.  Press ENTER to continue: ' )
+		line_input(self.cfg, 'User random data successfully acquired.  Press ENTER to continue: ')
 
 
 		return ret.encode()
 		return ret.encode()
 
 
-	def get_random(self,length):
+	def get_random(self, length):
 
 
 		os_rand = os.urandom(length)
 		os_rand = os.urandom(length)
 		assert len(os_rand) == length, f'OS random number generator returned {len(os_rand)} (!= {length}) bytes!'
 		assert len(os_rand) == length, f'OS random number generator returned {len(os_rand)} (!= {length}) bytes!'
 
 
 		return self.add_user_random(
 		return self.add_user_random(
 			rand_bytes = os_rand,
 			rand_bytes = os_rand,
-			desc       = 'from your operating system' )
+			desc       = 'from your operating system')
 
 
 	def add_user_random(
 	def add_user_random(
 			self,
 			self,
 			rand_bytes,
 			rand_bytes,
 			desc,
 			desc,
-			urand = {'data':b'', 'counter':0} ):
+			urand = {'data':b'', 'counter':0}):
 
 
 		assert type(rand_bytes) is bytes, 'add_user_random_chk1'
 		assert type(rand_bytes) is bytes, 'add_user_random_chk1'
 
 
@@ -323,12 +315,12 @@ class Crypto:
 			import hmac
 			import hmac
 			key = hmac.digest(
 			key = hmac.digest(
 				urand['data'],
 				urand['data'],
-				os_rand + int.to_bytes(urand['counter'],8,'big'),
-				'sha256' )
+				os_rand + int.to_bytes(urand['counter'], 8, 'big'),
+				'sha256')
 
 
 			msg(f'Encrypting random data {desc} with ephemeral key #{urand["counter"]}')
 			msg(f'Encrypting random data {desc} with ephemeral key #{urand["counter"]}')
 
 
-			return self.encrypt_data( data=rand_bytes, key=key, desc=desc, verify=False, silent=True )
+			return self.encrypt_data(data=rand_bytes, key=key, desc=desc, verify=False, silent=True)
 		else:
 		else:
 			return rand_bytes
 			return rand_bytes
 
 
@@ -336,15 +328,15 @@ class Crypto:
 			self,
 			self,
 			old_preset = gc.dfl_hash_preset,
 			old_preset = gc.dfl_hash_preset,
 			data_desc  = 'data',
 			data_desc  = 'data',
-			prompt     = None ):
+			prompt     = None):
 
 
 		prompt = prompt or (
 		prompt = prompt or (
-			f'Enter hash preset for {data_desc},\n' +
-			f'or hit ENTER to accept the default value ({old_preset!r}): ' )
+			f'Enter hash preset for {data_desc}, \n' +
+			f'or hit ENTER to accept the default value ({old_preset!r}): ')
 
 
 		from .ui import line_input
 		from .ui import line_input
 		while True:
 		while True:
-			ret = line_input( self.cfg, prompt )
+			ret = line_input(self.cfg, prompt)
 			if ret:
 			if ret:
 				if ret in self.hash_presets:
 				if ret in self.hash_presets:
 					return ret
 					return ret
@@ -353,7 +345,7 @@ class Crypto:
 			else:
 			else:
 				return old_preset
 				return old_preset
 
 
-	def get_new_passphrase(self,data_desc,hash_preset,passwd_file,pw_desc='passphrase'):
+	def get_new_passphrase(self, data_desc, hash_preset, passwd_file, pw_desc='passphrase'):
 		message = f"""
 		message = f"""
 				You must choose a passphrase to encrypt your {data_desc} with.
 				You must choose a passphrase to encrypt your {data_desc} with.
 				A key will be generated from your passphrase using a hash preset of '{hash_preset}'.
 				A key will be generated from your passphrase using a hash preset of '{hash_preset}'.
@@ -366,72 +358,72 @@ class Crypto:
 				cfg    = self.cfg,
 				cfg    = self.cfg,
 				infile = passwd_file,
 				infile = passwd_file,
 				desc   = f'{pw_desc} for {data_desc}',
 				desc   = f'{pw_desc} for {data_desc}',
-				quiet  = self.pwfile_used(passwd_file) ))
+				quiet  = self.pwfile_used(passwd_file)))
 		else:
 		else:
-			self.util.qmsg('\n'+fmt(message,indent='  '))
+			self.util.qmsg('\n'+fmt(message, indent='  '))
 			from .ui import get_words_from_user
 			from .ui import get_words_from_user
 			if self.cfg.echo_passphrase:
 			if self.cfg.echo_passphrase:
-				pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
+				pw = ' '.join(get_words_from_user(self.cfg, f'Enter {pw_desc} for {data_desc}: '))
 			else:
 			else:
 				for _ in range(gc.passwd_max_tries):
 				for _ in range(gc.passwd_max_tries):
-					pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
-					pw_chk = ' '.join(get_words_from_user( self.cfg, f'Repeat {pw_desc}: ' ))
+					pw = ' '.join(get_words_from_user(self.cfg, f'Enter {pw_desc} for {data_desc}: '))
+					pw_chk = ' '.join(get_words_from_user(self.cfg, f'Repeat {pw_desc}: '))
 					self.util.dmsg(f'Passphrases: [{pw}] [{pw_chk}]')
 					self.util.dmsg(f'Passphrases: [{pw}] [{pw_chk}]')
 					if pw == pw_chk:
 					if pw == pw_chk:
 						self.util.vmsg('Passphrases match')
 						self.util.vmsg('Passphrases match')
 						break
 						break
 					msg('Passphrases do not match.  Try again.')
 					msg('Passphrases do not match.  Try again.')
 				else:
 				else:
-					die(2,f'User failed to duplicate passphrase in {gc.passwd_max_tries} attempts')
+					die(2, f'User failed to duplicate passphrase in {gc.passwd_max_tries} attempts')
 
 
 		if pw == '':
 		if pw == '':
 			self.util.qmsg('WARNING: Empty passphrase')
 			self.util.qmsg('WARNING: Empty passphrase')
 
 
 		return pw
 		return pw
 
 
-	def get_passphrase(self,data_desc,passwd_file,pw_desc='passphrase'):
+	def get_passphrase(self, data_desc, passwd_file, pw_desc='passphrase'):
 		if passwd_file:
 		if passwd_file:
 			from .fileutil import get_words_from_file
 			from .fileutil import get_words_from_file
 			return ' '.join(get_words_from_file(
 			return ' '.join(get_words_from_file(
 				cfg    = self.cfg,
 				cfg    = self.cfg,
 				infile = passwd_file,
 				infile = passwd_file,
 				desc   = f'{pw_desc} for {data_desc}',
 				desc   = f'{pw_desc} for {data_desc}',
-				quiet  = self.pwfile_used(passwd_file) ))
+				quiet  = self.pwfile_used(passwd_file)))
 		else:
 		else:
 			from .ui import get_words_from_user
 			from .ui import get_words_from_user
-			return ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
+			return ' '.join(get_words_from_user(self.cfg, f'Enter {pw_desc} for {data_desc}: '))
 
 
-	def mmgen_encrypt(self,data,desc='data',hash_preset=None):
+	def mmgen_encrypt(self, data, desc='data', hash_preset=None):
 		salt  = self.get_random(self.mmenc_salt_len)
 		salt  = self.get_random(self.mmenc_salt_len)
 		iv    = self.get_random(self.aesctr_iv_len)
 		iv    = self.get_random(self.aesctr_iv_len)
 		nonce = self.get_random(self.mmenc_nonce_len)
 		nonce = self.get_random(self.mmenc_nonce_len)
 		hp    = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
 		hp    = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
-		m     = ('user-requested','default')[hp=='3']
+		m     = ('user-requested', 'default')[hp=='3']
 		self.util.vmsg(f'Encrypting {desc}')
 		self.util.vmsg(f'Encrypting {desc}')
 		self.util.qmsg(f'Using {m} hash preset of {hp!r}')
 		self.util.qmsg(f'Using {m} hash preset of {hp!r}')
 		passwd = self.get_new_passphrase(
 		passwd = self.get_new_passphrase(
 			data_desc = desc,
 			data_desc = desc,
 			hash_preset = hp,
 			hash_preset = hp,
-			passwd_file = self.cfg.passwd_file )
-		key    = self.make_key(passwd,salt,hp)
+			passwd_file = self.cfg.passwd_file)
+		key    = self.make_key(passwd, salt, hp)
 		from hashlib import sha256
 		from hashlib import sha256
-		enc_d  = self.encrypt_data( sha256(nonce+data).digest() + nonce + data, key, iv, desc=desc )
+		enc_d  = self.encrypt_data(sha256(nonce+data).digest() + nonce + data, key, iv, desc=desc)
 		return salt+iv+enc_d
 		return salt+iv+enc_d
 
 
-	def mmgen_decrypt(self,data,desc='data',hash_preset=None):
+	def mmgen_decrypt(self, data, desc='data', hash_preset=None):
 		self.util.vmsg(f'Preparing to decrypt {desc}')
 		self.util.vmsg(f'Preparing to decrypt {desc}')
 		dstart = self.mmenc_salt_len + self.aesctr_iv_len
 		dstart = self.mmenc_salt_len + self.aesctr_iv_len
 		salt   = data[:self.mmenc_salt_len]
 		salt   = data[:self.mmenc_salt_len]
 		iv     = data[self.mmenc_salt_len:dstart]
 		iv     = data[self.mmenc_salt_len:dstart]
 		enc_d  = data[dstart:]
 		enc_d  = data[dstart:]
 		hp     = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
 		hp     = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
-		m  = ('user-requested','default')[hp=='3']
+		m  = ('user-requested', 'default')[hp=='3']
 		self.util.qmsg(f'Using {m} hash preset of {hp!r}')
 		self.util.qmsg(f'Using {m} hash preset of {hp!r}')
 		passwd = self.get_passphrase(
 		passwd = self.get_passphrase(
 			data_desc = desc,
 			data_desc = desc,
-			passwd_file = self.cfg.passwd_file )
-		key    = self.make_key(passwd,salt,hp)
-		dec_d  = self.decrypt_data( enc_d, key, iv, desc )
+			passwd_file = self.cfg.passwd_file)
+		key    = self.make_key(passwd, salt, hp)
+		dec_d  = self.decrypt_data(enc_d, key, iv, desc)
 		sha256_len = 32
 		sha256_len = 32
 		from hashlib import sha256
 		from hashlib import sha256
 		if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
 		if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
@@ -441,9 +433,9 @@ class Crypto:
 			msg('Incorrect passphrase or hash preset')
 			msg('Incorrect passphrase or hash preset')
 			return False
 			return False
 
 
-	def mmgen_decrypt_retry(self,d,desc='data'):
+	def mmgen_decrypt_retry(self, d, desc='data'):
 		while True:
 		while True:
-			d_dec = self.mmgen_decrypt(d,desc)
+			d_dec = self.mmgen_decrypt(d, desc)
 			if d_dec:
 			if d_dec:
 				return d_dec
 				return d_dec
 			msg('Trying again...')
 			msg('Trying again...')

+ 101 - 101
mmgen/daemon.py

@@ -20,17 +20,17 @@
 daemon: Daemon control interface for the MMGen suite
 daemon: Daemon control interface for the MMGen suite
 """
 """
 
 
-import sys,os,time,importlib
-from subprocess import run,PIPE,CompletedProcess
+import sys, os, time, importlib
+from subprocess import run, PIPE, CompletedProcess
 from collections import namedtuple
 from collections import namedtuple
 
 
 from .base_obj import Lockable
 from .base_obj import Lockable
 from .color import set_vt100
 from .color import set_vt100
-from .util import msg,Msg_r,die,remove_dups,oneshot_warning,fmt_list
-from .flags import ClassFlags,ClassOpts
+from .util import msg, Msg_r, die, remove_dups, oneshot_warning, fmt_list
+from .flags import ClassFlags, ClassOpts
 
 
-_dd = namedtuple('daemon_data',['coind_name','coind_version','coind_version_str']) # latest tested version
-_nw = namedtuple('coin_networks',['mainnet','testnet','regtest'])
+_dd = namedtuple('daemon_data', ['coind_name', 'coind_version', 'coind_version_str']) # latest tested version
+_nw = namedtuple('coin_networks', ['mainnet', 'testnet', 'regtest'])
 
 
 class Daemon(Lockable):
 class Daemon(Lockable):
 
 
@@ -47,10 +47,10 @@ class Daemon(Lockable):
 	private_port = None
 	private_port = None
 	avail_opts = ()
 	avail_opts = ()
 	avail_flags = () # like opts, but can be set or unset after instantiation
 	avail_flags = () # like opts, but can be set or unset after instantiation
-	_reset_ok = ('debug','wait','pids')
+	_reset_ok = ('debug', 'wait', 'pids')
 	version_info_arg = '--version'
 	version_info_arg = '--version'
 
 
-	def __init__(self,cfg,opts=None,flags=None):
+	def __init__(self, cfg, opts=None, flags=None):
 
 
 		self.cfg = cfg
 		self.cfg = cfg
 		self.platform = sys.platform
 		self.platform = sys.platform
@@ -58,40 +58,40 @@ class Daemon(Lockable):
 			self.use_pidfile = False
 			self.use_pidfile = False
 			self.use_threads = True
 			self.use_threads = True
 
 
-		self.opt = ClassOpts(self,opts)
-		self.flag = ClassFlags(self,flags)
+		self.opt = ClassOpts(self, opts)
+		self.flag = ClassFlags(self, flags)
 		self.debug = self.debug or cfg.debug_daemon
 		self.debug = self.debug or cfg.debug_daemon
 
 
-	def exec_cmd_thread(self,cmd):
+	def exec_cmd_thread(self, cmd):
 		import threading
 		import threading
-		tname = ('exec_cmd','exec_cmd_win_console')[self.platform == 'win32' and self.new_console_mswin]
-		t = threading.Thread(target=getattr(self,tname),args=(cmd,))
+		tname = ('exec_cmd', 'exec_cmd_win_console')[self.platform == 'win32' and self.new_console_mswin]
+		t = threading.Thread(target=getattr(self, tname), args=(cmd,))
 		t.daemon = True
 		t.daemon = True
 		t.start()
 		t.start()
 		if self.platform == 'win32':
 		if self.platform == 'win32':
 			Msg_r(' \b') # blocks w/o this...crazy
 			Msg_r(' \b') # blocks w/o this...crazy
 		return True
 		return True
 
 
-	def exec_cmd_win_console(self,cmd):
-		from subprocess import Popen,CREATE_NEW_CONSOLE,STARTUPINFO,STARTF_USESHOWWINDOW,SW_HIDE
-		si = STARTUPINFO(dwFlags=STARTF_USESHOWWINDOW,wShowWindow=SW_HIDE)
-		p = Popen(cmd,creationflags=CREATE_NEW_CONSOLE,startupinfo=si)
+	def exec_cmd_win_console(self, cmd):
+		from subprocess import Popen, CREATE_NEW_CONSOLE, STARTUPINFO, STARTF_USESHOWWINDOW, SW_HIDE
+		si = STARTUPINFO(dwFlags=STARTF_USESHOWWINDOW, wShowWindow=SW_HIDE)
+		p = Popen(cmd, creationflags=CREATE_NEW_CONSOLE, startupinfo=si)
 		p.wait()
 		p.wait()
 
 
-	def exec_cmd(self,cmd,is_daemon=False,check_retcode=False):
-		out = (PIPE,None)[is_daemon and self.opt.no_daemonize]
+	def exec_cmd(self, cmd, is_daemon=False, check_retcode=False):
+		out = (PIPE, None)[is_daemon and self.opt.no_daemonize]
 		try:
 		try:
-			cp = run(cmd,check=False,stdout=out,stderr=out)
+			cp = run(cmd, check=False, stdout=out, stderr=out)
 		except OSError as e:
 		except OSError as e:
-			die( 'MMGenCalledProcessError', f'Error starting executable: {type(e).__name__} [Errno {e.errno}]' )
+			die('MMGenCalledProcessError', f'Error starting executable: {type(e).__name__} [Errno {e.errno}]')
 		set_vt100()
 		set_vt100()
 		if check_retcode and cp.returncode:
 		if check_retcode and cp.returncode:
-			die(1,str(cp))
+			die(1, str(cp))
 		if self.debug:
 		if self.debug:
 			print(cp)
 			print(cp)
 		return cp
 		return cp
 
 
-	def run_cmd(self,cmd,silent=False,is_daemon=False,check_retcode=False):
+	def run_cmd(self, cmd, silent=False, is_daemon=False, check_retcode=False):
 
 
 		if self.debug:
 		if self.debug:
 			msg('\n\n')
 			msg('\n\n')
@@ -100,14 +100,14 @@ class Daemon(Lockable):
 			msg(f'Starting {self.desc} on port {self.bind_port}')
 			msg(f'Starting {self.desc} on port {self.bind_port}')
 
 
 		if self.debug:
 		if self.debug:
-			msg(f'\nExecuting:\n{fmt_list(cmd,fmt="col",indent="  ")}\n')
+			msg(f'\nExecuting:\n{fmt_list(cmd, fmt="col", indent="  ")}\n')
 
 
 		if self.use_threads and is_daemon and not self.opt.no_daemonize:
 		if self.use_threads and is_daemon and not self.opt.no_daemonize:
 			ret = self.exec_cmd_thread(cmd)
 			ret = self.exec_cmd_thread(cmd)
 		else:
 		else:
-			ret = self.exec_cmd(cmd,is_daemon,check_retcode)
+			ret = self.exec_cmd(cmd, is_daemon, check_retcode)
 
 
-		if isinstance(ret,CompletedProcess):
+		if isinstance(ret, CompletedProcess):
 			if ret.stdout and (self.debug or not silent):
 			if ret.stdout and (self.debug or not silent):
 				msg(ret.stdout.decode().rstrip())
 				msg(ret.stdout.decode().rstrip())
 			if ret.stderr and (self.debug or (ret.returncode and not silent)):
 			if ret.stderr and (self.debug or (ret.returncode and not silent)):
@@ -124,7 +124,7 @@ class Daemon(Lockable):
 			# Assumes only one running instance of given daemon.  If multiple daemons are running,
 			# Assumes only one running instance of given daemon.  If multiple daemons are running,
 			# the first PID in the list is returned and self.pids is set to the PID list.
 			# the first PID in the list is returned and self.pids is set to the PID list.
 			ss = f'{self.exec_fn}.exe'
 			ss = f'{self.exec_fn}.exe'
-			cp = self.run_cmd(['ps','-Wl'],silent=True)
+			cp = self.run_cmd(['ps', '-Wl'], silent=True)
 			self.pids = ()
 			self.pids = ()
 			# use Windows, not Cygwin, PID
 			# use Windows, not Cygwin, PID
 			pids = tuple(line.split()[3] for line in cp.stdout.decode().splitlines() if ss in line)
 			pids = tuple(line.split()[3] for line in cp.stdout.decode().splitlines() if ss in line)
@@ -134,10 +134,10 @@ class Daemon(Lockable):
 				return pids[0]
 				return pids[0]
 		elif self.platform in ('linux', 'darwin'):
 		elif self.platform in ('linux', 'darwin'):
 			ss = ' '.join(self.start_cmd)
 			ss = ' '.join(self.start_cmd)
-			cp = self.run_cmd(['pgrep','-f',ss],silent=True)
+			cp = self.run_cmd(['pgrep', '-f', ss], silent=True)
 			if cp.stdout:
 			if cp.stdout:
 				return cp.stdout.strip().decode()
 				return cp.stdout.strip().decode()
-		die(2,f'{ss!r} not found in process list, cannot determine PID')
+		die(2, f'{ss!r} not found in process list, cannot determine PID')
 
 
 	@property
 	@property
 	def bind_port(self):
 	def bind_port(self):
@@ -147,7 +147,7 @@ class Daemon(Lockable):
 	def state(self):
 	def state(self):
 		if self.debug:
 		if self.debug:
 			msg(f'Testing port {self.bind_port}')
 			msg(f'Testing port {self.bind_port}')
-		return 'ready' if self.test_socket('localhost',self.bind_port) else 'stopped'
+		return 'ready' if self.test_socket('localhost', self.bind_port) else 'stopped'
 
 
 	@property
 	@property
 	def start_cmds(self):
 	def start_cmds(self):
@@ -156,17 +156,17 @@ class Daemon(Lockable):
 	@property
 	@property
 	def stop_cmd(self):
 	def stop_cmd(self):
 		return (
 		return (
-			['kill','-Wf',self.pid] if self.platform == 'win32' else
-			['kill','-9',self.pid] if self.force_kill else
-			['kill',self.pid] )
+			['kill', '-Wf', self.pid] if self.platform == 'win32' else
+			['kill', '-9', self.pid] if self.force_kill else
+			['kill', self.pid])
 
 
-	def cmd(self,action,*args,**kwargs):
-		return getattr(self,action)(*args,**kwargs)
+	def cmd(self, action, *args, **kwargs):
+		return getattr(self, action)(*args, **kwargs)
 
 
-	def cli(self,*cmds,silent=False):
-		return self.run_cmd(self.cli_cmd(*cmds),silent=silent)
+	def cli(self, *cmds, silent=False):
+		return self.run_cmd(self.cli_cmd(*cmds), silent=silent)
 
 
-	def state_msg(self,extra_text=None):
+	def state_msg(self, extra_text=None):
 		try:
 		try:
 			pid = self.pid
 			pid = self.pid
 		except:
 		except:
@@ -176,12 +176,12 @@ class Daemon(Lockable):
 			f'{self.desc} {extra_text}running',
 			f'{self.desc} {extra_text}running',
 			'pid N/A' if pid is None or self.pids or self.state == 'stopped' else f'pid {pid}',
 			'pid N/A' if pid is None or self.pids or self.state == 'stopped' else f'pid {pid}',
 			f'port {self.bind_port}',
 			f'port {self.bind_port}',
-			w = 60 )
+			w = 60)
 
 
 	def pre_start(self):
 	def pre_start(self):
 		pass
 		pass
 
 
-	def start(self,quiet=False,silent=False):
+	def start(self, quiet=False, silent=False):
 		if self.state == 'ready':
 		if self.state == 'ready':
 			if not (quiet or silent):
 			if not (quiet or silent):
 				msg(self.state_msg(extra_text='already'))
 				msg(self.state_msg(extra_text='already'))
@@ -193,24 +193,24 @@ class Daemon(Lockable):
 
 
 		if not silent:
 		if not silent:
 			msg(f'Starting {self.desc} on port {self.bind_port}')
 			msg(f'Starting {self.desc} on port {self.bind_port}')
-		ret = self.run_cmd(self.start_cmd,silent=True,is_daemon=True,check_retcode=True)
+		ret = self.run_cmd(self.start_cmd, silent=True, is_daemon=True, check_retcode=True)
 
 
 		if self.wait:
 		if self.wait:
 			self.wait_for_state('ready')
 			self.wait_for_state('ready')
 
 
 		return ret
 		return ret
 
 
-	def stop(self,quiet=False,silent=False):
+	def stop(self, quiet=False, silent=False):
 		if self.state == 'ready':
 		if self.state == 'ready':
 			if not silent:
 			if not silent:
 				msg(f'Stopping {self.desc} on port {self.bind_port}')
 				msg(f'Stopping {self.desc} on port {self.bind_port}')
 			if self.force_kill:
 			if self.force_kill:
 				run(['sync'])
 				run(['sync'])
-			ret = self.run_cmd(self.stop_cmd,silent=True)
+			ret = self.run_cmd(self.stop_cmd, silent=True)
 
 
 			if self.pids:
 			if self.pids:
 				msg('Warning: multiple PIDs [{}] -- we may be stopping the wrong instance'.format(
 				msg('Warning: multiple PIDs [{}] -- we may be stopping the wrong instance'.format(
-					fmt_list(self.pids,fmt='bare')
+					fmt_list(self.pids, fmt='bare')
 				))
 				))
 			if self.wait:
 			if self.wait:
 				self.wait_for_state('stopped')
 				self.wait_for_state('stopped')
@@ -221,49 +221,49 @@ class Daemon(Lockable):
 				msg(f'{self.desc} on port {self.bind_port} not running')
 				msg(f'{self.desc} on port {self.bind_port} not running')
 			return True
 			return True
 
 
-	def restart(self,silent=False):
+	def restart(self, silent=False):
 		self.stop(silent=silent)
 		self.stop(silent=silent)
 		return self.start(silent=silent)
 		return self.start(silent=silent)
 
 
-	def test_socket(self,host,port,timeout=10):
+	def test_socket(self, host, port, timeout=10):
 		import socket
 		import socket
 		try:
 		try:
-			socket.create_connection((host,port),timeout=timeout).close()
+			socket.create_connection((host, port), timeout=timeout).close()
 		except:
 		except:
 			return False
 			return False
 		else:
 		else:
 			return True
 			return True
 
 
-	def wait_for_state(self,req_state):
+	def wait_for_state(self, req_state):
 		for _ in range(300):
 		for _ in range(300):
 			if self.state == req_state:
 			if self.state == req_state:
 				return True
 				return True
 			time.sleep(0.2)
 			time.sleep(0.2)
-		die(2,f'Wait for state {req_state!r} timeout exceeded for {self.desc} (port {self.bind_port})')
+		die(2, f'Wait for state {req_state!r} timeout exceeded for {self.desc} (port {self.bind_port})')
 
 
 	@classmethod
 	@classmethod
 	def get_exec_version_str(cls):
 	def get_exec_version_str(cls):
 		try:
 		try:
-			cp = run([cls.exec_fn,cls.version_info_arg],stdout=PIPE,stderr=PIPE,check=True)
+			cp = run([cls.exec_fn, cls.version_info_arg], stdout=PIPE, stderr=PIPE, check=True)
 		except Exception as e:
 		except Exception as e:
-			die(2,f'{e}\nUnable to execute {cls.exec_fn}')
+			die(2, f'{e}\nUnable to execute {cls.exec_fn}')
 
 
 		if cp.returncode:
 		if cp.returncode:
-			die(2,f'Unable to execute {cls.exec_fn}')
+			die(2, f'Unable to execute {cls.exec_fn}')
 		else:
 		else:
 			res = cp.stdout.decode().splitlines()
 			res = cp.stdout.decode().splitlines()
-			return ( res[0] if len(res) == 1 else [s for s in res if 'ersion' in s][0] ).strip()
+			return (res[0] if len(res) == 1 else [s for s in res if 'ersion' in s][0]).strip()
 
 
 class RPCDaemon(Daemon):
 class RPCDaemon(Daemon):
 
 
 	avail_opts = ('no_daemonize',)
 	avail_opts = ('no_daemonize',)
 
 
-	def __init__(self,cfg,opts=None,flags=None):
-		super().__init__(cfg,opts=opts,flags=flags)
+	def __init__(self, cfg, opts=None, flags=None):
+		super().__init__(cfg, opts=opts, flags=flags)
 		self.desc = '{} {} {}RPC daemon'.format(
 		self.desc = '{} {} {}RPC daemon'.format(
 			self.rpc_type,
 			self.rpc_type,
-			getattr(self.proto.network_names,self.proto.network),
-			'test suite ' if self.test_suite else '' )
+			getattr(self.proto.network_names, self.proto.network),
+			'test suite ' if self.test_suite else '')
 		self._set_ok += ('usr_daemon_args',)
 		self._set_ok += ('usr_daemon_args',)
 		self.usr_daemon_args = []
 		self.usr_daemon_args = []
 
 
@@ -272,22 +272,22 @@ class RPCDaemon(Daemon):
 		return [self.exec_fn] + self.daemon_args + self.usr_daemon_args
 		return [self.exec_fn] + self.daemon_args + self.usr_daemon_args
 
 
 class CoinDaemon(Daemon):
 class CoinDaemon(Daemon):
-	networks = ('mainnet','testnet','regtest')
+	networks = ('mainnet', 'testnet', 'regtest')
 	cfg_file_hdr = ''
 	cfg_file_hdr = ''
 	avail_flags = ('keep_cfg_file',)
 	avail_flags = ('keep_cfg_file',)
-	avail_opts = ('no_daemonize','online')
+	avail_opts = ('no_daemonize', 'online')
 	testnet_dir = None
 	testnet_dir = None
 	test_suite_port_shift = 1237
 	test_suite_port_shift = 1237
 	rpc_user = None
 	rpc_user = None
 	rpc_password = None
 	rpc_password = None
 
 
-	_cd = namedtuple('coins_data',['daemon_ids'])
+	_cd = namedtuple('coins_data', ['daemon_ids'])
 	coins = {
 	coins = {
 		'BTC': _cd(['bitcoin_core']),
 		'BTC': _cd(['bitcoin_core']),
 		'BCH': _cd(['bitcoin_cash_node']),
 		'BCH': _cd(['bitcoin_cash_node']),
 		'LTC': _cd(['litecoin_core']),
 		'LTC': _cd(['litecoin_core']),
 		'XMR': _cd(['monero']),
 		'XMR': _cd(['monero']),
-		'ETH': _cd(['geth','erigon','openethereum']),
+		'ETH': _cd(['geth', 'erigon', 'openethereum']),
 		'ETC': _cd(['parity']),
 		'ETC': _cd(['parity']),
 	}
 	}
 
 
@@ -300,7 +300,7 @@ class CoinDaemon(Daemon):
 		message = 'blacklisted daemon: {!r}'
 		message = 'blacklisted daemon: {!r}'
 
 
 	@classmethod
 	@classmethod
-	def get_daemon_ids(cls,cfg,coin):
+	def get_daemon_ids(cls, cfg, coin):
 
 
 		ret = cls.coins[coin].daemon_ids
 		ret = cls.coins[coin].daemon_ids
 		if 'erigon' in ret and not cfg.enable_erigon:
 		if 'erigon' in ret and not cfg.enable_erigon:
@@ -310,14 +310,14 @@ class CoinDaemon(Daemon):
 			def gen():
 			def gen():
 				for daemon_id in ret:
 				for daemon_id in ret:
 					if daemon_id in blacklist:
 					if daemon_id in blacklist:
-						cls.warn_blacklisted(div=daemon_id,fmt_args=[daemon_id])
+						cls.warn_blacklisted(div=daemon_id, fmt_args=[daemon_id])
 					else:
 					else:
 						yield daemon_id
 						yield daemon_id
 			ret = list(gen())
 			ret = list(gen())
 		return ret
 		return ret
 
 
 	@classmethod
 	@classmethod
-	def get_daemon(cls,cfg,coin,daemon_id,proto=None):
+	def get_daemon(cls, cfg, coin, daemon_id, proto=None):
 		if proto:
 		if proto:
 			proto_cls = type(proto)
 			proto_cls = type(proto)
 		else:
 		else:
@@ -325,17 +325,17 @@ class CoinDaemon(Daemon):
 			proto_cls = init_proto(cfg, coin, return_cls=True)
 			proto_cls = init_proto(cfg, coin, return_cls=True)
 		return getattr(
 		return getattr(
 			importlib.import_module(f'mmgen.proto.{proto_cls.base_proto_coin.lower()}.daemon'),
 			importlib.import_module(f'mmgen.proto.{proto_cls.base_proto_coin.lower()}.daemon'),
-			daemon_id+'_daemon' )
+			daemon_id+'_daemon')
 
 
 	@classmethod
 	@classmethod
-	def get_network_ids(cls,cfg):
+	def get_network_ids(cls, cfg):
 		from .protocol import CoinProtocol
 		from .protocol import CoinProtocol
 		def gen():
 		def gen():
 			for coin in cls.coins:
 			for coin in cls.coins:
-				for daemon_id in cls.get_daemon_ids(cfg,coin):
-					for network in cls.get_daemon( cfg, coin, daemon_id ).networks:
-						yield CoinProtocol.Base.create_network_id(coin,network)
-		return remove_dups(list(gen()),quiet=True)
+				for daemon_id in cls.get_daemon_ids(cfg, coin):
+					for network in cls.get_daemon(cfg, coin, daemon_id).networks:
+						yield CoinProtocol.Base.create_network_id(coin, network)
+		return remove_dups(list(gen()), quiet=True)
 
 
 	def __new__(cls,
 	def __new__(cls,
 			cfg,
 			cfg,
@@ -347,7 +347,7 @@ class CoinDaemon(Daemon):
 			port_shift = None,
 			port_shift = None,
 			p2p_port   = None,
 			p2p_port   = None,
 			datadir    = None,
 			datadir    = None,
-			daemon_id  = None ):
+			daemon_id  = None):
 
 
 		assert network_id or proto,        'CoinDaemon_chk1'
 		assert network_id or proto,        'CoinDaemon_chk1'
 		assert not (network_id and proto), 'CoinDaemon_chk2'
 		assert not (network_id and proto), 'CoinDaemon_chk2'
@@ -358,20 +358,20 @@ class CoinDaemon(Daemon):
 			coin       = proto.coin
 			coin       = proto.coin
 		else:
 		else:
 			network_id = network_id.lower()
 			network_id = network_id.lower()
-			from .protocol import CoinProtocol,init_proto
-			proto = init_proto( cfg, network_id=network_id )
-			coin,network = CoinProtocol.Base.parse_network_id(network_id)
+			from .protocol import CoinProtocol, init_proto
+			proto = init_proto(cfg, network_id=network_id)
+			coin, network = CoinProtocol.Base.parse_network_id(network_id)
 			coin = coin.upper()
 			coin = coin.upper()
 
 
-		daemon_ids = cls.get_daemon_ids(cfg,coin)
+		daemon_ids = cls.get_daemon_ids(cfg, coin)
 		if not daemon_ids:
 		if not daemon_ids:
-			die(1,f'No configured daemons for coin {coin}!')
+			die(1, f'No configured daemons for coin {coin}!')
 		daemon_id = daemon_id or cfg.daemon_id or daemon_ids[0]
 		daemon_id = daemon_id or cfg.daemon_id or daemon_ids[0]
 
 
 		if daemon_id not in daemon_ids:
 		if daemon_id not in daemon_ids:
-			die(1,f'{daemon_id!r}: invalid daemon_id - valid choices: {fmt_list(daemon_ids)}')
+			die(1, f'{daemon_id!r}: invalid daemon_id - valid choices: {fmt_list(daemon_ids)}')
 
 
-		me = Daemon.__new__(cls.get_daemon( cfg, None, daemon_id, proto=proto ))
+		me = Daemon.__new__(cls.get_daemon(cfg, None, daemon_id, proto=proto))
 
 
 		assert network in me.networks, f'{network!r}: unsupported network for daemon {daemon_id}'
 		assert network in me.networks, f'{network!r}: unsupported network for daemon {daemon_id}'
 		me.network_id = network_id
 		me.network_id = network_id
@@ -392,30 +392,30 @@ class CoinDaemon(Daemon):
 			port_shift = None,
 			port_shift = None,
 			p2p_port   = None,
 			p2p_port   = None,
 			datadir    = None,
 			datadir    = None,
-			daemon_id  = None ):
+			daemon_id  = None):
 
 
 		self.test_suite = test_suite
 		self.test_suite = test_suite
 
 
-		super().__init__(cfg=cfg,opts=opts,flags=flags)
+		super().__init__(cfg=cfg, opts=opts, flags=flags)
 
 
-		self._set_ok += ('shared_args','usr_coind_args')
+		self._set_ok += ('shared_args', 'usr_coind_args')
 		self.shared_args = []
 		self.shared_args = []
 		self.usr_coind_args = []
 		self.usr_coind_args = []
 
 
-		for k,v in self.daemon_data._asdict().items():
-			setattr(self,k,v)
+		for k, v in self.daemon_data._asdict().items():
+			setattr(self, k, v)
 
 
 		self.desc = '{} {} {}daemon'.format(
 		self.desc = '{} {} {}daemon'.format(
 			self.coind_name,
 			self.coind_name,
-			getattr(self.proto.network_names,self.network),
-			'test suite ' if test_suite else '' )
+			getattr(self.proto.network_names, self.network),
+			'test suite ' if test_suite else '')
 
 
 		# user-set values take precedence
 		# user-set values take precedence
 		self.datadir = os.path.abspath(datadir or cfg.daemon_data_dir or self.init_datadir())
 		self.datadir = os.path.abspath(datadir or cfg.daemon_data_dir or self.init_datadir())
 		self.non_dfl_datadir = bool(datadir or cfg.daemon_data_dir or test_suite or self.network == 'regtest')
 		self.non_dfl_datadir = bool(datadir or cfg.daemon_data_dir or test_suite or self.network == 'regtest')
 
 
 		# init_datadir() may have already initialized logdir
 		# init_datadir() may have already initialized logdir
-		self.logdir = os.path.abspath(getattr(self,'logdir',self.datadir))
+		self.logdir = os.path.abspath(getattr(self, 'logdir', self.datadir))
 
 
 		ps_adj = (port_shift or 0) + (self.test_suite_port_shift if test_suite else 0)
 		ps_adj = (port_shift or 0) + (self.test_suite_port_shift if test_suite else 0)
 
 
@@ -424,10 +424,10 @@ class CoinDaemon(Daemon):
 		self.p2p_port = (
 		self.p2p_port = (
 			p2p_port or (
 			p2p_port or (
 				self.get_p2p_port() + ps_adj if self.get_p2p_port() and (test_suite or ps_adj) else None
 				self.get_p2p_port() + ps_adj if self.get_p2p_port() and (test_suite or ps_adj) else None
-			) if self.network != 'regtest' else None )
+			) if self.network != 'regtest' else None)
 
 
-		if hasattr(self,'private_ports'):
-			self.private_port = getattr(self.private_ports,self.network)
+		if hasattr(self, 'private_ports'):
+			self.private_port = getattr(self.private_ports, self.network)
 
 
 		# bind_port == self.private_port or self.rpc_port
 		# bind_port == self.private_port or self.rpc_port
 		self.pidfile = f'{self.logdir}/{self.id}-{self.network}-daemon-{self.bind_port}.pid'
 		self.pidfile = f'{self.logdir}/{self.id}-{self.network}-daemon-{self.bind_port}.pid'
@@ -437,7 +437,7 @@ class CoinDaemon(Daemon):
 
 
 	def init_datadir(self):
 	def init_datadir(self):
 		if self.test_suite:
 		if self.test_suite:
-			return os.path.join('test','daemons',self.network_id)
+			return os.path.join('test', 'daemons', self.network_id)
 		else:
 		else:
 			return os.path.join(*self.datadirs[self.platform])
 			return os.path.join(*self.datadirs[self.platform])
 
 
@@ -446,7 +446,7 @@ class CoinDaemon(Daemon):
 		return self.datadir
 		return self.datadir
 
 
 	def get_rpc_port(self):
 	def get_rpc_port(self):
-		return getattr(self.rpc_ports,self.network)
+		return getattr(self.rpc_ports, self.network)
 
 
 	def get_p2p_port(self):
 	def get_p2p_port(self):
 		return None
 		return None
@@ -456,27 +456,27 @@ class CoinDaemon(Daemon):
 		return ([self.exec_fn]
 		return ([self.exec_fn]
 				+ self.coind_args
 				+ self.coind_args
 				+ self.shared_args
 				+ self.shared_args
-				+ self.usr_coind_args )
+				+ self.usr_coind_args)
 
 
-	def cli_cmd(self,*cmds):
+	def cli_cmd(self, *cmds):
 		return ([self.cli_fn]
 		return ([self.cli_fn]
 				+ self.shared_args
 				+ self.shared_args
-				+ list(cmds) )
+				+ list(cmds))
 
 
-	def start(self,*args,**kwargs):
+	def start(self, *args, **kwargs):
 		assert self.test_suite or self.network == 'regtest', 'start() restricted to test suite and regtest'
 		assert self.test_suite or self.network == 'regtest', 'start() restricted to test suite and regtest'
-		return super().start(*args,**kwargs)
+		return super().start(*args, **kwargs)
 
 
-	def stop(self,*args,**kwargs):
+	def stop(self, *args, **kwargs):
 		assert self.test_suite or self.network == 'regtest', 'stop() restricted to test suite and regtest'
 		assert self.test_suite or self.network == 'regtest', 'stop() restricted to test suite and regtest'
-		return super().stop(*args,**kwargs)
+		return super().stop(*args, **kwargs)
 
 
 	def pre_start(self):
 	def pre_start(self):
-		os.makedirs(self.datadir,exist_ok=True)
+		os.makedirs(self.datadir, exist_ok=True)
 
 
 		if self.test_suite or self.network == 'regtest':
 		if self.test_suite or self.network == 'regtest':
 			if self.cfg_file and not self.flag.keep_cfg_file:
 			if self.cfg_file and not self.flag.keep_cfg_file:
-				with open(f'{self.datadir}/{self.cfg_file}','w') as fp:
+				with open(f'{self.datadir}/{self.cfg_file}', 'w') as fp:
 					fp.write(self.cfg_file_hdr)
 					fp.write(self.cfg_file_hdr)
 
 
 		if self.use_pidfile and os.path.exists(self.pidfile):
 		if self.use_pidfile and os.path.exists(self.pidfile):
@@ -491,7 +491,7 @@ class CoinDaemon(Daemon):
 			run([
 			run([
 				('rm' if self.platform == 'win32' else '/bin/rm'),
 				('rm' if self.platform == 'win32' else '/bin/rm'),
 				'-rf',
 				'-rf',
-				self.datadir ])
+				self.datadir])
 			set_vt100()
 			set_vt100()
 		else:
 		else:
 			msg(f'Cannot remove {self.network_datadir!r} - daemon is not stopped')
 			msg(f'Cannot remove {self.network_datadir!r} - daemon is not stopped')

+ 6 - 6
mmgen/derive.py

@@ -21,19 +21,19 @@ derive: coin private key secret derivation for the MMGen suite
 """
 """
 
 
 from collections import namedtuple
 from collections import namedtuple
-from hashlib import sha512,sha256
+from hashlib import sha512, sha256
 from .addrlist import AddrIdxList
 from .addrlist import AddrIdxList
 
 
-pk_bytes = namedtuple('coin_privkey_bytes',['idx','pos','data'])
+pk_bytes = namedtuple('coin_privkey_bytes', ['idx', 'pos', 'data'])
 
 
-def derive_coin_privkey_bytes(seed,idxs):
+def derive_coin_privkey_bytes(seed, idxs):
 
 
-	assert isinstance(idxs,AddrIdxList), f'{type(idxs)}: idx list not of type AddrIdxList'
+	assert isinstance(idxs, AddrIdxList), f'{type(idxs)}: idx list not of type AddrIdxList'
 
 
 	t_keys = len(idxs)
 	t_keys = len(idxs)
 	pos = 0
 	pos = 0
 
 
-	for idx in range( 1, AddrIdxList.max_len+1 ): # key/addr indexes begin from one
+	for idx in range(1, AddrIdxList.max_len + 1): # key/addr indexes begin from one
 
 
 		seed = sha512(seed).digest()
 		seed = sha512(seed).digest()
 
 
@@ -42,7 +42,7 @@ def derive_coin_privkey_bytes(seed,idxs):
 			pos += 1
 			pos += 1
 
 
 			# secret is double sha256 of seed hash round /idx/
 			# secret is double sha256 of seed hash round /idx/
-			yield pk_bytes( idx, pos, sha256(sha256(seed).digest()).digest() )
+			yield pk_bytes(idx, pos, sha256(sha256(seed).digest()).digest())
 
 
 			if pos == t_keys:
 			if pos == t_keys:
 				break
 				break

+ 27 - 27
mmgen/devinit.py

@@ -13,35 +13,35 @@ devinit: Developer tools initialization for the MMGen suite
 """
 """
 
 
 devtools_funcs = {
 devtools_funcs = {
-	'pfmt':              lambda *args,**kwargs: devtools_call('pfmt',*args,**kwargs),
-	'pmsg':              lambda *args,**kwargs: devtools_call('pmsg',*args,**kwargs),
-	'pmsg_r':            lambda *args,**kwargs: devtools_call('pmsg_r',*args,**kwargs),
-	'pdie':              lambda *args,**kwargs: devtools_call('pdie',*args,**kwargs),
-	'pexit':             lambda *args,**kwargs: devtools_call('pexit',*args,**kwargs),
-	'Pmsg':              lambda *args,**kwargs: devtools_call('Pmsg',*args,**kwargs),
-	'Pdie':              lambda *args,**kwargs: devtools_call('Pdie',*args,**kwargs),
-	'Pexit':             lambda *args,**kwargs: devtools_call('Pexit',*args,**kwargs),
-	'print_stack_trace': lambda *args,**kwargs: devtools_call('print_stack_trace',*args,**kwargs),
-	'get_diff':          lambda *args,**kwargs: devtools_call('get_diff',*args,**kwargs),
-	'print_diff':        lambda *args,**kwargs: devtools_call('print_diff',*args,**kwargs),
-	'get_ndiff':         lambda *args,**kwargs: devtools_call('get_ndiff',*args,**kwargs),
-	'print_ndiff':       lambda *args,**kwargs: devtools_call('print_ndiff',*args,**kwargs),
+	'pfmt':              lambda *args, **kwargs: devtools_call('pfmt', *args, **kwargs),
+	'pmsg':              lambda *args, **kwargs: devtools_call('pmsg', *args, **kwargs),
+	'pmsg_r':            lambda *args, **kwargs: devtools_call('pmsg_r', *args, **kwargs),
+	'pdie':              lambda *args, **kwargs: devtools_call('pdie', *args, **kwargs),
+	'pexit':             lambda *args, **kwargs: devtools_call('pexit', *args, **kwargs),
+	'Pmsg':              lambda *args, **kwargs: devtools_call('Pmsg', *args, **kwargs),
+	'Pdie':              lambda *args, **kwargs: devtools_call('Pdie', *args, **kwargs),
+	'Pexit':             lambda *args, **kwargs: devtools_call('Pexit', *args, **kwargs),
+	'print_stack_trace': lambda *args, **kwargs: devtools_call('print_stack_trace', *args, **kwargs),
+	'get_diff':          lambda *args, **kwargs: devtools_call('get_diff', *args, **kwargs),
+	'print_diff':        lambda *args, **kwargs: devtools_call('print_diff', *args, **kwargs),
+	'get_ndiff':         lambda *args, **kwargs: devtools_call('get_ndiff', *args, **kwargs),
+	'print_ndiff':       lambda *args, **kwargs: devtools_call('print_ndiff', *args, **kwargs),
 }
 }
 
 
-def devtools_call(funcname,*args,**kwargs):
+def devtools_call(funcname, *args, **kwargs):
 	from . import devtools
 	from . import devtools
-	return getattr(devtools,funcname)(*args,**kwargs)
+	return getattr(devtools, funcname)(*args, **kwargs)
 
 
-def MMGenObject_call(methodname,*args,**kwargs):
+def MMGenObject_call(methodname, *args, **kwargs):
 	from .devtools import MMGenObjectMethods
 	from .devtools import MMGenObjectMethods
-	return getattr(MMGenObjectMethods,methodname)(*args,**kwargs)
+	return getattr(MMGenObjectMethods, methodname)(*args, **kwargs)
 
 
 class MMGenObjectDevTools:
 class MMGenObjectDevTools:
 
 
-	pmsg  = lambda *args,**kwargs: MMGenObject_call('pmsg',*args,**kwargs)
-	pdie  = lambda *args,**kwargs: MMGenObject_call('pdie',*args,**kwargs)
-	pexit = lambda *args,**kwargs: MMGenObject_call('pexit',*args,**kwargs)
-	pfmt  = lambda *args,**kwargs: MMGenObject_call('pfmt',*args,**kwargs)
+	pmsg  = lambda *args, **kwargs: MMGenObject_call('pmsg', *args, **kwargs)
+	pdie  = lambda *args, **kwargs: MMGenObject_call('pdie', *args, **kwargs)
+	pexit = lambda *args, **kwargs: MMGenObject_call('pexit', *args, **kwargs)
+	pfmt  = lambda *args, **kwargs: MMGenObject_call('pfmt', *args, **kwargs)
 
 
 	# Check that all immutables have been initialized.  Expensive, so do only when testing.
 	# Check that all immutables have been initialized.  Expensive, so do only when testing.
 	def immutable_attr_init_check(self):
 	def immutable_attr_init_check(self):
@@ -50,22 +50,22 @@ class MMGenObjectDevTools:
 
 
 		for attrname in self.valid_attrs:
 		for attrname in self.valid_attrs:
 
 
-			for o in (cls,cls.__bases__[0]): # assume there's only one base class
+			for o in (cls, cls.__bases__[0]): # assume there's only one base class
 				if attrname in o.__dict__:
 				if attrname in o.__dict__:
 					attr = o.__dict__[attrname]
 					attr = o.__dict__[attrname]
 					break
 					break
 			else:
 			else:
 				from .util import die
 				from .util import die
-				die(4,f'unable to find descriptor {cls.__name__}.{attrname}')
+				die(4, f'unable to find descriptor {cls.__name__}.{attrname}')
 
 
 			if type(attr).__name__ == 'ImmutableAttr' and attrname not in self.__dict__:
 			if type(attr).__name__ == 'ImmutableAttr' and attrname not in self.__dict__:
 				from .util import die
 				from .util import die
-				die(4,f'attribute {attrname!r} of {cls.__name__} has not been initialized in constructor!')
+				die(4, f'attribute {attrname!r} of {cls.__name__} has not been initialized in constructor!')
 
 
 def init_dev():
 def init_dev():
 	import builtins
 	import builtins
 	# MMGenObject is added to the namespace by objmethods.py, so we must name the builtin differently
 	# MMGenObject is added to the namespace by objmethods.py, so we must name the builtin differently
 	# to avoid inadvertently adding MMGenObject to the global namespace here:
 	# to avoid inadvertently adding MMGenObject to the global namespace here:
-	setattr(builtins,'MMGenObjectDevTools',MMGenObjectDevTools)
-	for funcname,func in devtools_funcs.items():
-		setattr(builtins,funcname,func)
+	setattr(builtins, 'MMGenObjectDevTools', MMGenObjectDevTools)
+	for funcname, func in devtools_funcs.items():
+		setattr(builtins, funcname, func)

+ 66 - 66
mmgen/devtools.py

@@ -16,77 +16,77 @@ import sys
 
 
 color_funcs = {
 color_funcs = {
 	name: lambda s, n=n: f'\033[{n};1m{s}\033[0m'
 	name: lambda s, n=n: f'\033[{n};1m{s}\033[0m'
-		for name,n in (
-			('red',   31),
-			('green', 32),
-			('yellow',33),
-			('blue',  34),
-			('purple',35))
+		for name, n in (
+			('red',    31),
+			('green',  32),
+			('yellow', 33),
+			('blue',   34),
+			('purple', 35))
 }
 }
 
 
-def pfmt(*args,color=None):
+def pfmt(*args, color=None):
 	import pprint
 	import pprint
-	ret = pprint.PrettyPrinter(indent=4,width=116).pformat(
-		args if len(args) > 1 else '' if not args else args[0] )
+	ret = pprint.PrettyPrinter(indent=4, width=116).pformat(
+		args if len(args) > 1 else '' if not args else args[0])
 	return color_funcs[color](ret) if color else ret
 	return color_funcs[color](ret) if color else ret
 
 
-def pmsg(*args,color=None):
-	sys.stderr.write(pfmt(*args,color=color) + '\n')
+def pmsg(*args, color=None):
+	sys.stderr.write(pfmt(*args, color=color) + '\n')
 
 
-def pmsg_r(*args,color=None):
-	sys.stderr.write(pfmt(*args,color=color))
+def pmsg_r(*args, color=None):
+	sys.stderr.write(pfmt(*args, color=color))
 
 
-def pdie(*args,exit_val=1):
-	pmsg(*args,color='red' if exit_val else None)
+def pdie(*args, exit_val=1):
+	pmsg(*args, color='red' if exit_val else None)
 	sys.exit(exit_val)
 	sys.exit(exit_val)
 
 
 def pexit(*args):
 def pexit(*args):
-	pdie(*args,exit_val=0)
+	pdie(*args, exit_val=0)
 
 
-def Pmsg(*args,color=None):
-	sys.stdout.write(pfmt(*args,color=color) + '\n')
+def Pmsg(*args, color=None):
+	sys.stdout.write(pfmt(*args, color=color) + '\n')
 
 
-def Pdie(*args,exit_val=1):
-	Pmsg(*args,color=('yellow' if exit_val == 1 else 'red' if exit_val else None))
+def Pdie(*args, exit_val=1):
+	Pmsg(*args, color=('yellow' if exit_val == 1 else 'red' if exit_val else None))
 	sys.exit(exit_val)
 	sys.exit(exit_val)
 
 
 def Pexit(*args):
 def Pexit(*args):
-	Pdie(*args,exit_val=0)
+	Pdie(*args, exit_val=0)
 
 
-def print_stack_trace(message=None,fh_list=[],nl='\n',sep='\n  ',trim=4):
+def print_stack_trace(message=None, fh_list=[], nl='\n', sep='\n  ', trim=4):
 	if not fh_list:
 	if not fh_list:
 		import os
 		import os
-		fh_list.append(open(f'devtools.trace.{os.getpid()}','w'))
+		fh_list.append(open(f'devtools.trace.{os.getpid()}', 'w'))
 		nl = ''
 		nl = ''
-	res = get_stack_trace(message,nl,sep,trim)
+	res = get_stack_trace(message, nl, sep, trim)
 	sys.stderr.write(res)
 	sys.stderr.write(res)
 	fh_list[0].write(res)
 	fh_list[0].write(res)
 
 
-def get_stack_trace(message=None,nl='\n',sep='\n  ',trim=3):
+def get_stack_trace(message=None, nl='\n', sep='\n  ', trim=3):
 
 
-	import os,re,traceback
+	import os, re, traceback
 
 
 	tb = [t for t in traceback.extract_stack() if t.filename[:1] != '<']
 	tb = [t for t in traceback.extract_stack() if t.filename[:1] != '<']
 	fs = '{}:{}: in {}:\n    {}'
 	fs = '{}:{}: in {}:\n    {}'
 	out = [
 	out = [
 		fs.format(
 		fs.format(
-			re.sub(r'^\./','',os.path.relpath(t.filename)),
+			re.sub(r'^\./', '', os.path.relpath(t.filename)),
 			t.lineno,
 			t.lineno,
 			(t.name+'()' if t.name[-1] != '>' else t.name),
 			(t.name+'()' if t.name[-1] != '>' else t.name),
 			t.line or '(none)')
 			t.line or '(none)')
-		for t in (tb[:-trim] if trim else tb) ]
+		for t in (tb[:-trim] if trim else tb)]
 
 
 	return f'{nl}STACK TRACE {message or "[unnamed]"}:{sep}{sep.join(out)}\n'
 	return f'{nl}STACK TRACE {message or "[unnamed]"}:{sep}{sep.join(out)}\n'
 
 
-def print_diff(*args,**kwargs):
-	sys.stderr.write(get_diff(*args,**kwargs))
+def print_diff(*args, **kwargs):
+	sys.stderr.write(get_diff(*args, **kwargs))
 
 
-def get_diff(a,b,a_fn='',b_fn='',from_json=True):
+def get_diff(a, b, a_fn='', b_fn='', from_json=True):
 
 
 	if from_json:
 	if from_json:
 		import json
 		import json
-		a = json.dumps(json.loads(a),indent=4)
-		b = json.dumps(json.loads(b),indent=4)
+		a = json.dumps(json.loads(a), indent=4)
+		b = json.dumps(json.loads(b), indent=4)
 
 
 	from difflib import unified_diff
 	from difflib import unified_diff
 	# chunk headers have trailing newlines, hence the rstrip()
 	# chunk headers have trailing newlines, hence the rstrip()
@@ -95,70 +95,70 @@ def get_diff(a,b,a_fn='',b_fn='',from_json=True):
 			a.split('\n'),
 			a.split('\n'),
 			b.split('\n'),
 			b.split('\n'),
 			a_fn,
 			a_fn,
-			b_fn )))
+			b_fn)))
 
 
-def print_ndiff(*args,**kwargs):
-	sys.stderr.write(get_ndiff(*args,**kwargs))
+def print_ndiff(*args, **kwargs):
+	sys.stderr.write(get_ndiff(*args, **kwargs))
 
 
-def get_ndiff(a,b):
+def get_ndiff(a, b):
 	from difflib import ndiff
 	from difflib import ndiff
 	return list(ndiff(
 	return list(ndiff(
 		a.split('\n'),
 		a.split('\n'),
-		b.split('\n') ))
+		b.split('\n')))
 
 
 class MMGenObjectMethods: # mixin class for MMGenObject
 class MMGenObjectMethods: # mixin class for MMGenObject
 
 
 	# Pretty-print an MMGenObject instance, recursing into sub-objects - WIP
 	# Pretty-print an MMGenObject instance, recursing into sub-objects - WIP
-	def pmsg(self,color=None):
+	def pmsg(self, color=None):
 		sys.stdout.write('\n'+self.pfmt(color=color))
 		sys.stdout.write('\n'+self.pfmt(color=color))
 
 
-	def pdie(self,exit_val=1):
+	def pdie(self, exit_val=1):
 		self.pmsg(color='red' if exit_val else None)
 		self.pmsg(color='red' if exit_val else None)
 		sys.exit(exit_val)
 		sys.exit(exit_val)
 
 
 	def pexit(self):
 	def pexit(self):
 		self.pdie(exit_val=0)
 		self.pdie(exit_val=0)
 
 
-	def pfmt(self,lvl=0,id_list=[],color=None):
+	def pfmt(self, lvl=0, id_list=[], color=None):
 
 
 		from decimal import Decimal
 		from decimal import Decimal
-		scalars = (str,int,float,Decimal)
+		scalars = (str, int, float, Decimal)
 
 
 		def isDict(obj):
 		def isDict(obj):
-			return isinstance(obj,dict)
+			return isinstance(obj, dict)
 		def isList(obj):
 		def isList(obj):
-			return isinstance(obj,list)
+			return isinstance(obj, list)
 		def isScalar(obj):
 		def isScalar(obj):
-			return isinstance(obj,scalars)
+			return isinstance(obj, scalars)
 
 
-		def do_list(out,e,lvl=0,is_dict=False):
+		def do_list(out, e, lvl=0, is_dict=False):
 			out.append('\n')
 			out.append('\n')
 			for i in e:
 			for i in e:
 				el = i if not is_dict else e[i]
 				el = i if not is_dict else e[i]
 				if is_dict:
 				if is_dict:
-#					out.append('{s}{:<{l}}'.format(i,s=' '*(4*lvl+8),l=10,l2=8*(lvl+1)+8))
+#					out.append('{s}{:<{l}}'.format(i, s=' '*(4*lvl+8), l=10, l2=8*(lvl+1)+8))
 					out.append('{s1}{i}{s2}'.format(
 					out.append('{s1}{i}{s2}'.format(
 						i  = i,
 						i  = i,
 						s1 = ' ' * (4*lvl+8),
 						s1 = ' ' * (4*lvl+8),
-						s2 = ' ' * 10 ))
-				if hasattr(el,'pfmt'):
+						s2 = ' ' * 10))
+				if hasattr(el, 'pfmt'):
 					out.append('{:>{l}}{}'.format(
 					out.append('{:>{l}}{}'.format(
 						'',
 						'',
-						el.pfmt( lvl=lvl+1, id_list=id_list+[id(self)] ),
-						l = (lvl+1)*8 ))
-				elif isinstance(el,scalars):
+						el.pfmt(lvl=lvl+1, id_list=id_list+[id(self)]),
+						l = (lvl+1)*8))
+				elif isinstance(el, scalars):
 					if isList(e):
 					if isList(e):
-						out.append( '{:>{l}}{!r:16}\n'.format( '', el, l=lvl*8 ))
+						out.append('{:>{l}}{!r:16}\n'.format('', el, l=lvl*8))
 					else:
 					else:
 						out.append(f' {el!r}')
 						out.append(f' {el!r}')
 				elif isList(el) or isDict(el):
 				elif isList(el) or isDict(el):
 					indent = 1 if is_dict else lvl*8+4
 					indent = 1 if is_dict else lvl*8+4
-					out.append('{:>{l}}{:16}'.format( '', f'<{type(el).__name__}>', l=indent ))
-					if isList(el) and isinstance(el[0],scalars):
+					out.append('{:>{l}}{:16}'.format('', f'<{type(el).__name__}>', l=indent))
+					if isList(el) and isinstance(el[0], scalars):
 						out.append('\n')
 						out.append('\n')
-					do_list(out,el,lvl=lvl+1,is_dict=isDict(el))
+					do_list(out, el, lvl=lvl+1, is_dict=isDict(el))
 				else:
 				else:
-					out.append('{:>{l}}{:16} {!r}\n'.format( '', f'<{type(el).__name__}>', el, l=(lvl*8)+8 ))
+					out.append('{:>{l}}{:16} {!r}\n'.format('', f'<{type(el).__name__}>', el, l=(lvl*8)+8))
 				out.append('\n')
 				out.append('\n')
 
 
 			if not e:
 			if not e:
@@ -169,27 +169,27 @@ class MMGenObjectMethods: # mixin class for MMGenObject
 		if id(self) in id_list:
 		if id(self) in id_list:
 			return out[-1].rstrip() + ' [RECURSION]\n'
 			return out[-1].rstrip() + ' [RECURSION]\n'
 		if isList(self) or isDict(self):
 		if isList(self) or isDict(self):
-			do_list(out,self,lvl=lvl,is_dict=isDict(self))
+			do_list(out, self, lvl=lvl, is_dict=isDict(self))
 
 
 		for k in self.__dict__:
 		for k in self.__dict__:
-			e = getattr(self,k)
+			e = getattr(self, k)
 			if isList(e) or isDict(e):
 			if isList(e) or isDict(e):
-				out.append('{:>{l}}{:<10} {:16}'.format( '', k, f'<{type(e).__name__}>', l=(lvl*8)+4 ))
-				do_list(out,e,lvl=lvl,is_dict=isDict(e))
-			elif hasattr(e,'pfmt') and callable(e.pfmt) and not isinstance(e,type):
+				out.append('{:>{l}}{:<10} {:16}'.format('', k, f'<{type(e).__name__}>', l=(lvl*8)+4))
+				do_list(out, e, lvl=lvl, is_dict=isDict(e))
+			elif hasattr(e, 'pfmt') and callable(e.pfmt) and not isinstance(e, type):
 				out.append('{:>{l}}{:10} {}'.format(
 				out.append('{:>{l}}{:10} {}'.format(
 					'',
 					'',
 					k,
 					k,
-					e.pfmt( lvl=lvl+1, id_list=id_list+[id(self)] ),
-					l = (lvl*8)+4 ))
+					e.pfmt(lvl=lvl+1, id_list=id_list+[id(self)]),
+					l = (lvl*8)+4))
 			else:
 			else:
 				out.append('{:>{l}}{:<10} {:16} {}\n'.format(
 				out.append('{:>{l}}{:<10} {:16} {}\n'.format(
 					'',
 					'',
 					k,
 					k,
 					f'<{type(e).__name__}>',
 					f'<{type(e).__name__}>',
 					repr(e),
 					repr(e),
-					l=(lvl*8)+4 ))
+					l=(lvl*8)+4))
 
 
 		import re
 		import re
-		ret = re.sub('\n+','\n',''.join(out))
+		ret = re.sub('\n+', '\n', ''.join(out))
 		return color_funcs[color](ret) if color else ret
 		return color_funcs[color](ret) if color else ret

+ 1 - 1
mmgen/exception.py

@@ -22,7 +22,7 @@ exception: Exception classes for the MMGen suite
 
 
 class MMGenError(Exception):
 class MMGenError(Exception):
 
 
-	def __init__(self,errno,strerror,stdout):
+	def __init__(self, errno, strerror, stdout):
 		self.mmcode = errno
 		self.mmcode = errno
 		self.stdout = stdout
 		self.stdout = stdout
 		super().__init__(strerror)
 		super().__init__(strerror)

+ 28 - 28
mmgen/filename.py

@@ -20,12 +20,12 @@
 filename: File and MMGenFile classes and methods for the MMGen suite
 filename: File and MMGenFile classes and methods for the MMGen suite
 """
 """
 
 
-import sys,os
-from .util import die,get_extension
+import sys, os
+from .util import die, get_extension
 
 
 class File:
 class File:
 
 
-	def __init__(self,fn,write=False):
+	def __init__(self, fn, write=False):
 
 
 		self.name     = fn
 		self.name     = fn
 		self.dirname  = os.path.dirname(fn)
 		self.dirname  = os.path.dirname(fn)
@@ -38,18 +38,18 @@ class File:
 		try:
 		try:
 			st = os.stat(fn)
 			st = os.stat(fn)
 		except:
 		except:
-			die( 'FileNotFound', f'{fn!r}: file not found' )
+			die('FileNotFound', f'{fn!r}: file not found')
 
 
 		import stat
 		import stat
 		if stat.S_ISBLK(st.st_mode):
 		if stat.S_ISBLK(st.st_mode):
 			if sys.platform in ('win32',):
 			if sys.platform in ('win32',):
 				die(2, 'Access to raw block devices not supported on platform {sys.platform!r}')
 				die(2, 'Access to raw block devices not supported on platform {sys.platform!r}')
-			mode = (os.O_RDONLY,os.O_RDWR)[bool(write)]
+			mode = (os.O_RDONLY, os.O_RDWR)[bool(write)]
 			try:
 			try:
 				fd = os.open(fn, mode)
 				fd = os.open(fn, mode)
 			except OSError as e:
 			except OSError as e:
 				if e.errno == 13:
 				if e.errno == 13:
-					die(2,f'{fn!r}: permission denied')
+					die(2, f'{fn!r}: permission denied')
 #				if e.errno != 17: raise
 #				if e.errno != 17: raise
 			else:
 			else:
 				if sys.platform == 'linux':
 				if sys.platform == 'linux':
@@ -66,21 +66,21 @@ class File:
 
 
 class FileList(list):
 class FileList(list):
 
 
-	def __init__(self,fns,write=False):
+	def __init__(self, fns, write=False):
 		list.__init__(
 		list.__init__(
 			self,
 			self,
-			[File(fn,write) for fn in fns] )
+			[File(fn, write) for fn in fns])
 
 
 	def names(self):
 	def names(self):
 		return [f.name for f in self]
 		return [f.name for f in self]
 
 
-	def sort_by_age(self,key='mtime',reverse=False):
-		assert key in ('atime','ctime','mtime'), f'{key!r}: invalid sort key'
-		self.sort( key=lambda a: getattr(a,key), reverse=reverse )
+	def sort_by_age(self, key='mtime', reverse=False):
+		assert key in ('atime', 'ctime', 'mtime'), f'{key!r}: invalid sort key'
+		self.sort(key=lambda a: getattr(a, key), reverse=reverse)
 
 
 class MMGenFile(File):
 class MMGenFile(File):
 
 
-	def __init__(self,fn,base_class=None,subclass=None,proto=None,write=False):
+	def __init__(self, fn, base_class=None, subclass=None, proto=None, write=False):
 		"""
 		"""
 		'base_class' - a base class with an 'ext_to_cls' method
 		'base_class' - a base class with an 'ext_to_cls' method
 		'subclass'   - a subclass with an 'ext' attribute
 		'subclass'   - a subclass with an 'ext' attribute
@@ -91,45 +91,45 @@ class MMGenFile(File):
 		attribute to True.
 		attribute to True.
 		"""
 		"""
 
 
-		super().__init__(fn,write)
+		super().__init__(fn, write)
 
 
 		assert (subclass or base_class) and not (subclass and base_class), 'MMGenFile chk1'
 		assert (subclass or base_class) and not (subclass and base_class), 'MMGenFile chk1'
 
 
-		if not getattr(subclass or base_class,'filename_api',False):
-			die(3,f'Class {(subclass or base_class).__name__!r} does not support the MMGenFile API')
+		if not getattr(subclass or base_class, 'filename_api', False):
+			die(3, f'Class {(subclass or base_class).__name__!r} does not support the MMGenFile API')
 
 
 		if base_class:
 		if base_class:
-			subclass = base_class.ext_to_cls( self.ext, proto )
+			subclass = base_class.ext_to_cls(self.ext, proto)
 			if not subclass:
 			if not subclass:
-				die( 'BadFileExtension', f'{self.ext!r}: not a recognized file extension for {base_class}' )
+				die('BadFileExtension', f'{self.ext!r}: not a recognized file extension for {base_class}')
 
 
 		self.subclass = subclass
 		self.subclass = subclass
 
 
 class MMGenFileList(FileList):
 class MMGenFileList(FileList):
 
 
-	def __init__(self,fns,base_class,proto=None,write=False):
+	def __init__(self, fns, base_class, proto=None, write=False):
 		list.__init__(
 		list.__init__(
 			self,
 			self,
-			[MMGenFile( fn, base_class=base_class, proto=proto, write=write ) for fn in fns] )
+			[MMGenFile(fn, base_class=base_class, proto=proto, write=write) for fn in fns])
 
 
-def find_files_in_dir(subclass,fdir,no_dups=False):
+def find_files_in_dir(subclass, fdir, no_dups=False):
 
 
-	assert isinstance(subclass,type), f'{subclass}: not a class'
+	assert isinstance(subclass, type), f'{subclass}: not a class'
 
 
-	if not getattr(subclass,'filename_api',False):
-		die(3,f'Class {subclass.__name__!r} does not support the MMGenFile API')
+	if not getattr(subclass, 'filename_api', False):
+		die(3, f'Class {subclass.__name__!r} does not support the MMGenFile API')
 
 
 	matches = [l for l in os.listdir(fdir) if l.endswith('.'+subclass.ext)]
 	matches = [l for l in os.listdir(fdir) if l.endswith('.'+subclass.ext)]
 
 
 	if no_dups:
 	if no_dups:
 		if len(matches) == 1:
 		if len(matches) == 1:
-			return os.path.join(fdir,matches[0])
+			return os.path.join(fdir, matches[0])
 		elif matches:
 		elif matches:
-			die(1,f'ERROR: more than one {subclass.__name__} file in directory {fdir!r}')
+			die(1, f'ERROR: more than one {subclass.__name__} file in directory {fdir!r}')
 		else:
 		else:
 			return None
 			return None
 	else:
 	else:
-		return [os.path.join(fdir,m) for m in matches]
+		return [os.path.join(fdir, m) for m in matches]
 
 
-def find_file_in_dir(subclass,fdir):
-	return find_files_in_dir(subclass,fdir,no_dups=True)
+def find_file_in_dir(subclass, fdir):
+	return find_files_in_dir(subclass, fdir, no_dups=True)

+ 56 - 56
mmgen/fileutil.py

@@ -22,7 +22,7 @@ fileutil: Routines that read, write, execute or stat files
 
 
 # 09 Mar 2024: make all utils accept Path instances as arguments
 # 09 Mar 2024: make all utils accept Path instances as arguments
 
 
-import sys,os
+import sys, os
 
 
 from .color import set_vt100
 from .color import set_vt100
 from .util import (
 from .util import (
@@ -48,42 +48,42 @@ def check_or_create_dir(path):
 					str(path)])
 					str(path)])
 				set_vt100()
 				set_vt100()
 		try:
 		try:
-			os.makedirs(path,0o700)
+			os.makedirs(path, 0o700)
 		except:
 		except:
 			die(2, f'ERROR: unable to read or create path ‘{path}’')
 			die(2, f'ERROR: unable to read or create path ‘{path}’')
 
 
 def check_binary(args):
 def check_binary(args):
-	from subprocess import run,DEVNULL
+	from subprocess import run, DEVNULL
 	try:
 	try:
-		run(args,stdout=DEVNULL,stderr=DEVNULL,check=True)
+		run(args, stdout=DEVNULL, stderr=DEVNULL, check=True)
 	except:
 	except:
-		die(2,f'{args[0]!r} binary missing, not in path, or not executable')
+		die(2, f'{args[0]!r} binary missing, not in path, or not executable')
 	set_vt100()
 	set_vt100()
 
 
-def shred_file(fn,verbose=False):
-	check_binary(['shred','--version'])
+def shred_file(fn, verbose=False):
+	check_binary(['shred', '--version'])
 	from subprocess import run
 	from subprocess import run
 	run(
 	run(
-		['shred','--force','--iterations=30','--zero','--remove=wipesync']
+		['shred', '--force', '--iterations=30', '--zero', '--remove=wipesync']
 		+ (['--verbose'] if verbose else [])
 		+ (['--verbose'] if verbose else [])
 		+ [str(fn)],
 		+ [str(fn)],
-		check=True )
+		check=True)
 	set_vt100()
 	set_vt100()
 
 
-def _check_file_type_and_access(fname,ftype,blkdev_ok=False):
+def _check_file_type_and_access(fname, ftype, blkdev_ok=False):
 
 
 	import stat
 	import stat
 
 
-	access,op_desc = (
-		(os.W_OK,'writ') if ftype in ('output file','output directory') else
-		(os.R_OK,'read') )
+	access, op_desc = (
+		(os.W_OK, 'writ') if ftype in ('output file', 'output directory') else
+		(os.R_OK, 'read'))
 
 
 	if ftype == 'output directory':
 	if ftype == 'output directory':
 		ok_types = [(stat.S_ISDIR, 'output directory')]
 		ok_types = [(stat.S_ISDIR, 'output directory')]
 	else:
 	else:
 		ok_types = [
 		ok_types = [
-			(stat.S_ISREG,'regular file'),
-			(stat.S_ISLNK,'symbolic link')
+			(stat.S_ISREG, 'regular file'),
+			(stat.S_ISLNK, 'symbolic link')
 		]
 		]
 		if blkdev_ok:
 		if blkdev_ok:
 			if not sys.platform in ('win32',):
 			if not sys.platform in ('win32',):
@@ -98,31 +98,31 @@ def _check_file_type_and_access(fname,ftype,blkdev_ok=False):
 		if t[0](mode):
 		if t[0](mode):
 			break
 			break
 	else:
 	else:
-		ok_list = ' or '.join( t[1] for t in ok_types )
+		ok_list = ' or '.join(t[1] for t in ok_types)
 		die(1, f'Requested {ftype} ‘{fname}’ is not a {ok_list}')
 		die(1, f'Requested {ftype} ‘{fname}’ is not a {ok_list}')
 
 
-	if not os.access(fname,access):
+	if not os.access(fname, access):
 		die(1, f'Requested {ftype} ‘{fname}’ is not {op_desc}able by you')
 		die(1, f'Requested {ftype} ‘{fname}’ is not {op_desc}able by you')
 
 
 	return True
 	return True
 
 
-def check_infile(f,blkdev_ok=False):
-	return _check_file_type_and_access(f,'input file',blkdev_ok=blkdev_ok)
+def check_infile(f, blkdev_ok=False):
+	return _check_file_type_and_access(f, 'input file', blkdev_ok=blkdev_ok)
 
 
-def check_outfile(f,blkdev_ok=False):
-	return _check_file_type_and_access(f,'output file',blkdev_ok=blkdev_ok)
+def check_outfile(f, blkdev_ok=False):
+	return _check_file_type_and_access(f, 'output file', blkdev_ok=blkdev_ok)
 
 
 def check_outdir(f):
 def check_outdir(f):
-	return _check_file_type_and_access(f,'output directory')
+	return _check_file_type_and_access(f, 'output directory')
 
 
-def get_seed_file(cfg,nargs,wallets=None,invoked_as=None):
+def get_seed_file(cfg, nargs, wallets=None, invoked_as=None):
 
 
 	wallets = wallets or cfg._args
 	wallets = wallets or cfg._args
 
 
 	from .filename import find_file_in_dir
 	from .filename import find_file_in_dir
 	from .wallet.mmgen import wallet
 	from .wallet.mmgen import wallet
 
 
-	wf = find_file_in_dir(wallet,cfg.data_dir)
+	wf = find_file_in_dir(wallet, cfg.data_dir)
 
 
 	wd_from_opt = bool(cfg.hidden_incog_input_params or cfg.in_fmt) # have wallet data from opt?
 	wd_from_opt = bool(cfg.hidden_incog_input_params or cfg.in_fmt) # have wallet data from opt?
 
 
@@ -138,16 +138,16 @@ def get_seed_file(cfg,nargs,wallets=None,invoked_as=None):
 	if wallets or wf:
 	if wallets or wf:
 		check_infile(wallets[0] if wallets else wf)
 		check_infile(wallets[0] if wallets else wf)
 
 
-	return str(wallets[0]) if wallets else (wf,None)[wd_from_opt] # could be a Path instance
+	return str(wallets[0]) if wallets else (wf, None)[wd_from_opt] # could be a Path instance
 
 
-def _open_or_die(filename,mode,silent=False):
+def _open_or_die(filename, mode, silent=False):
 	try:
 	try:
-		return open(filename,mode)
+		return open(filename, mode)
 	except:
 	except:
 		if silent:
 		if silent:
-			die(2,'')
+			die(2, '')
 		else:
 		else:
-			fn = {0:'STDIN',1:'STDOUT',2:'STDERR'}[filename] if isinstance(filename,int) else f'‘{filename}’'
+			fn = {0:'STDIN', 1:'STDOUT', 2:'STDERR'}[filename] if isinstance(filename, int) else f'‘{filename}’'
 			desc = 'reading' if 'r' in mode else 'writing'
 			desc = 'reading' if 'r' in mode else 'writing'
 			die(2, f'Unable to open file {fn} for {desc}')
 			die(2, f'Unable to open file {fn} for {desc}')
 
 
@@ -184,13 +184,13 @@ def write_data_to_file(
 		cfg._util.qmsg('Output to STDOUT requested')
 		cfg._util.qmsg('Output to STDOUT requested')
 		if cfg.stdin_tty:
 		if cfg.stdin_tty:
 			if no_tty:
 			if no_tty:
-				die(2,f'Printing {desc} to screen is not allowed')
+				die(2, f'Printing {desc} to screen is not allowed')
 			if (ask_tty and not cfg.quiet) or binary:
 			if (ask_tty and not cfg.quiet) or binary:
 				from .ui import confirm_or_raise
 				from .ui import confirm_or_raise
 				confirm_or_raise(
 				confirm_or_raise(
 					cfg,
 					cfg,
 					message = '',
 					message = '',
-					action  = f'output {desc} to screen' )
+					action  = f'output {desc} to screen')
 		else:
 		else:
 			try:
 			try:
 				of = os.readlink(f'/proc/{os.getpid()}/fd/1') # Linux
 				of = os.readlink(f'/proc/{os.getpid()}/fd/1') # Linux
@@ -200,15 +200,15 @@ def write_data_to_file(
 			if of:
 			if of:
 				if of[:5] == 'pipe:':
 				if of[:5] == 'pipe:':
 					if no_tty:
 					if no_tty:
-						die(2,f'Writing {desc} to pipe is not allowed')
+						die(2, f'Writing {desc} to pipe is not allowed')
 					if ask_tty and not cfg.quiet:
 					if ask_tty and not cfg.quiet:
 						from .ui import confirm_or_raise
 						from .ui import confirm_or_raise
 						confirm_or_raise(
 						confirm_or_raise(
 							cfg,
 							cfg,
 							message = '',
 							message = '',
-							action  = f'output {desc} to pipe' )
+							action  = f'output {desc} to pipe')
 						msg('')
 						msg('')
-				of2,pd = os.path.relpath(of),os.path.pardir
+				of2, pd = os.path.relpath(of), os.path.pardir
 				msg('Redirecting output to file {!r}'.format(of if of2[:len(pd)] == pd else of2))
 				msg('Redirecting output to file {!r}'.format(of if of2[:len(pd)] == pd else of2))
 			else:
 			else:
 				msg('Redirecting output to file')
 				msg('Redirecting output to file')
@@ -216,15 +216,15 @@ def write_data_to_file(
 		if binary:
 		if binary:
 			if sys.platform == 'win32': # condition on separate line for pylint
 			if sys.platform == 'win32': # condition on separate line for pylint
 				import msvcrt
 				import msvcrt
-				msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
+				msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
 
 
 		# MSWin workaround. See msg_r()
 		# MSWin workaround. See msg_r()
 		try:
 		try:
-			sys.stdout.write(data.decode() if isinstance(data,bytes) else data)
+			sys.stdout.write(data.decode() if isinstance(data, bytes) else data)
 		except:
 		except:
-			os.write(1,data if isinstance(data,bytes) else data.encode())
+			os.write(1, data if isinstance(data, bytes) else data.encode())
 
 
-	def do_file(outfile,ask_write_prompt):
+	def do_file(outfile, ask_write_prompt):
 		if (outdir or (cfg.outdir and not ignore_opt_outdir)) and not os.path.isabs(outfile):
 		if (outdir or (cfg.outdir and not ignore_opt_outdir)) and not os.path.isabs(outfile):
 			outfile = make_full_path(str(outdir or cfg.outdir), outfile) # outdir could be Path instance
 			outfile = make_full_path(str(outdir or cfg.outdir), outfile) # outdir could be Path instance
 
 
@@ -235,8 +235,8 @@ def write_data_to_file(
 			if not keypress_confirm(
 			if not keypress_confirm(
 					cfg,
 					cfg,
 					ask_write_prompt,
 					ask_write_prompt,
-					default_yes = ask_write_default_yes ):
-				die(1,f'{capfirst(desc)} not saved')
+					default_yes = ask_write_default_yes):
+				die(1, f'{capfirst(desc)} not saved')
 
 
 		hush = False
 		hush = False
 		if os.path.lexists(outfile) and ask_overwrite:
 		if os.path.lexists(outfile) and ask_overwrite:
@@ -244,7 +244,7 @@ def write_data_to_file(
 			confirm_or_raise(
 			confirm_or_raise(
 				cfg,
 				cfg,
 				message = '',
 				message = '',
-				action  = f'File {outfile!r} already exists\nOverwrite?' )
+				action  = f'File {outfile!r} already exists\nOverwrite?')
 			msg(f'Overwriting file {outfile!r}')
 			msg(f'Overwriting file {outfile!r}')
 			hush = True
 			hush = True
 
 
@@ -253,34 +253,34 @@ def write_data_to_file(
 		if check_data:
 		if check_data:
 			d = ''
 			d = ''
 			try:
 			try:
-				with open(outfile,('r','rb')[bool(binary)]) as fp:
+				with open(outfile, ('r', 'rb')[bool(binary)]) as fp:
 					d = fp.read()
 					d = fp.read()
 			except:
 			except:
 				pass
 				pass
 			if d != cmp_data:
 			if d != cmp_data:
-				die(3,f'{desc} in file {outfile!r} has been altered by some other program! Aborting file write')
+				die(3, f'{desc} in file {outfile!r} has been altered by some other program! Aborting file write')
 
 
 		# To maintain portability, always open files in binary mode
 		# To maintain portability, always open files in binary mode
 		# If 'binary' option not set, encode/decode data before writing and after reading
 		# If 'binary' option not set, encode/decode data before writing and after reading
 		try:
 		try:
-			with _open_or_die(outfile,'wb') as fp:
+			with _open_or_die(outfile, 'wb') as fp:
 				fp.write(data if binary else data.encode())
 				fp.write(data if binary else data.encode())
 		except:
 		except:
-			die(2,f'Failed to write {desc} to file {outfile!r}')
+			die(2, f'Failed to write {desc} to file {outfile!r}')
 
 
 		if not (hush or quiet):
 		if not (hush or quiet):
 			msg(f'{capfirst(desc)} written to file {outfile!r}')
 			msg(f'{capfirst(desc)} written to file {outfile!r}')
 
 
 		return True
 		return True
 
 
-	if cfg.stdout or outfile in ('','-'):
+	if cfg.stdout or outfile in ('', '-'):
 		do_stdout()
 		do_stdout()
 	elif sys.stdin.isatty() and not sys.stdout.isatty():
 	elif sys.stdin.isatty() and not sys.stdout.isatty():
 		do_stdout()
 		do_stdout()
 	else:
 	else:
-		do_file(outfile,ask_write_prompt)
+		do_file(outfile, ask_write_prompt)
 
 
-def get_words_from_file(cfg,infile,desc,quiet=False):
+def get_words_from_file(cfg, infile, desc, quiet=False):
 
 
 	if not quiet:
 	if not quiet:
 		cfg._util.qmsg(f'Getting {desc} from file ‘{infile}’')
 		cfg._util.qmsg(f'Getting {desc} from file ‘{infile}’')
@@ -291,7 +291,7 @@ def get_words_from_file(cfg,infile,desc,quiet=False):
 	try:
 	try:
 		words = data.decode().split()
 		words = data.decode().split()
 	except:
 	except:
-		die(1,f'{capfirst(desc)} data must be UTF-8 encoded.')
+		die(1, f'{capfirst(desc)} data must be UTF-8 encoded.')
 
 
 	cfg._util.dmsg('Sanitized input: [{}]'.format(' '.join(words)))
 	cfg._util.dmsg('Sanitized input: [{}]'.format(' '.join(words)))
 
 
@@ -304,7 +304,7 @@ def get_data_from_file(
 		dash   = False,
 		dash   = False,
 		silent = False,
 		silent = False,
 		binary = False,
 		binary = False,
-		quiet  = False ):
+		quiet  = False):
 
 
 	if not (cfg.quiet or silent or quiet):
 	if not (cfg.quiet or silent or quiet):
 		cfg._util.qmsg(f'Getting {desc} from file ‘{infile}’')
 		cfg._util.qmsg(f'Getting {desc} from file ‘{infile}’')
@@ -319,8 +319,8 @@ def get_data_from_file(
 		data = data.decode()
 		data = data.decode()
 
 
 	if len(data) == cfg.max_input_size + 1:
 	if len(data) == cfg.max_input_size + 1:
-		die( 'MaxInputSizeExceeded',
-			f'Too much input data!  Max input data size: {cfg.max_input_size} bytes' )
+		die('MaxInputSizeExceeded',
+			f'Too much input data!  Max input data size: {cfg.max_input_size} bytes')
 
 
 	return data
 	return data
 
 
@@ -330,16 +330,16 @@ def get_lines_from_file(
 		desc          = 'data',
 		desc          = 'data',
 		trim_comments = False,
 		trim_comments = False,
 		quiet         = False,
 		quiet         = False,
-		silent        = False ):
+		silent        = False):
 
 
 	def decrypt_file_maybe():
 	def decrypt_file_maybe():
-		data = get_data_from_file( cfg, fn, desc=desc, binary=True, quiet=quiet, silent=silent )
+		data = get_data_from_file(cfg, fn, desc=desc, binary=True, quiet=quiet, silent=silent)
 		from .crypto import Crypto
 		from .crypto import Crypto
 		have_enc_ext = get_extension(fn) == Crypto.mmenc_ext
 		have_enc_ext = get_extension(fn) == Crypto.mmenc_ext
 		if have_enc_ext or not is_utf8(data):
 		if have_enc_ext or not is_utf8(data):
-			m = ('Attempting to decrypt','Decrypting')[have_enc_ext]
+			m = ('Attempting to decrypt', 'Decrypting')[have_enc_ext]
 			cfg._util.qmsg(f'{m} {desc} ‘{fn}’')
 			cfg._util.qmsg(f'{m} {desc} ‘{fn}’')
-			data = Crypto(cfg).mmgen_decrypt_retry(data,desc)
+			data = Crypto(cfg).mmgen_decrypt_retry(data, desc)
 		return data
 		return data
 
 
 	lines = decrypt_file_maybe().decode().splitlines()
 	lines = decrypt_file_maybe().decode().splitlines()

+ 19 - 19
mmgen/flags.py

@@ -20,27 +20,27 @@
 flags: Class flags and opts for the MMGen suite
 flags: Class flags and opts for the MMGen suite
 """
 """
 
 
-from .base_obj import AttrCtrl,Lockable
-from .util import fmt_list,die
+from .base_obj import AttrCtrl, Lockable
+from .util import fmt_list, die
 
 
 class ClassFlags(AttrCtrl):
 class ClassFlags(AttrCtrl):
 	_name = 'flags'
 	_name = 'flags'
 	_desc = 'flag'
 	_desc = 'flag'
 	reserved_attrs = ()
 	reserved_attrs = ()
 
 
-	def __init__(self,parent,arg):
+	def __init__(self, parent, arg):
 		self._parent = parent
 		self._parent = parent
-		self._available = getattr(self._parent,'avail_'+self._name)
+		self._available = getattr(self._parent, 'avail_'+self._name)
 
 
 		for a in self._available:
 		for a in self._available:
 			if a.startswith('_'):
 			if a.startswith('_'):
-				die( 'ClassFlagsError', f'{a!r}: {self._desc} cannot begin with an underscore' )
+				die('ClassFlagsError', f'{a!r}: {self._desc} cannot begin with an underscore')
 			for b in self.reserved_attrs:
 			for b in self.reserved_attrs:
 				if a == b:
 				if a == b:
-					die( 'ClassFlagsError', f'{a!r}: {b} is a reserved name for {self._desc}' )
+					die('ClassFlagsError', f'{a!r}: {b} is a reserved name for {self._desc}')
 
 
 		if arg:
 		if arg:
-			assert type(arg) in (list,tuple), f"{arg!r}: {self._name!r} must be list or tuple"
+			assert type(arg) in (list, tuple), f"{arg!r}: {self._name!r} must be list or tuple"
 		else:
 		else:
 			arg = []
 			arg = []
 
 
@@ -49,15 +49,15 @@ class ClassFlags(AttrCtrl):
 				self.not_available_error(e)
 				self.not_available_error(e)
 
 
 		for e in self._available:
 		for e in self._available:
-			setattr(self,e,e in arg)
+			setattr(self, e, e in arg)
 
 
 	def __dir__(self):
 	def __dir__(self):
 		return [k for k in self.__dict__ if not k.startswith('_') and not k in self.reserved_attrs]
 		return [k for k in self.__dict__ if not k.startswith('_') and not k in self.reserved_attrs]
 
 
 	def __str__(self):
 	def __str__(self):
-		return ' '.join(f'{k}={getattr(self,k)}' for k in dir(self))
+		return ' '.join(f'{k}={getattr(self, k)}' for k in dir(self))
 
 
-	def __setattr__(self,name,val):
+	def __setattr__(self, name, val):
 
 
 		if self._locked:
 		if self._locked:
 
 
@@ -65,23 +65,23 @@ class ClassFlags(AttrCtrl):
 				self.not_available_error(name)
 				self.not_available_error(name)
 
 
 			if self._name == 'flags':
 			if self._name == 'flags':
-				assert isinstance(val,bool), f'{val!r} not boolean'
-				old_val = getattr(self,name)
+				assert isinstance(val, bool), f'{val!r} not boolean'
+				old_val = getattr(self, name)
 				if val and old_val:
 				if val and old_val:
-					die( 'ClassFlagsError', f'{self._desc} {name!r} already set' )
+					die('ClassFlagsError', f'{self._desc} {name!r} already set')
 				if not val and not old_val:
 				if not val and not old_val:
-					die( 'ClassFlagsError', f'{self._desc} {name!r} not set, so cannot be unset' )
+					die('ClassFlagsError', f'{self._desc} {name!r} not set, so cannot be unset')
 
 
-		super().__setattr__(name,val)
+		super().__setattr__(name, val)
 
 
-	def not_available_error(self,name):
-		die( 'ClassFlagsError', '{!r}: unrecognized {} for {}: (available {}: {})'.format(
+	def not_available_error(self, name):
+		die('ClassFlagsError', '{!r}: unrecognized {} for {}: (available {}: {})'.format(
 			name,
 			name,
 			self._desc,
 			self._desc,
 			type(self._parent).__name__,
 			type(self._parent).__name__,
 			self._name,
 			self._name,
-			fmt_list(self._available,fmt='bare') ))
+			fmt_list(self._available, fmt='bare')))
 
 
-class ClassOpts(ClassFlags,Lockable):
+class ClassOpts(ClassFlags, Lockable):
 	_name = 'opts'
 	_name = 'opts'
 	_desc = 'opt'
 	_desc = 'opt'

+ 36 - 36
mmgen/key.py

@@ -20,41 +20,41 @@
 key: MMGen public and private key objects
 key: MMGen public and private key objects
 """
 """
 
 
-from .objmethods import HiliteStr,InitErrors,MMGenObject
-from .obj import ImmutableAttr,get_obj
+from .objmethods import HiliteStr, InitErrors, MMGenObject
+from .obj import ImmutableAttr, get_obj
 
 
-class WifKey(HiliteStr,InitErrors):
+class WifKey(HiliteStr, InitErrors):
 	"""
 	"""
 	Initialize a WIF key, checking its well-formedness.
 	Initialize a WIF key, checking its well-formedness.
 	The numeric validity of the private key it encodes is not checked.
 	The numeric validity of the private key it encodes is not checked.
 	"""
 	"""
 	width = 53
 	width = 53
 	color = 'blue'
 	color = 'blue'
-	def __new__(cls,proto,wif):
-		if isinstance(wif,cls):
+	def __new__(cls, proto, wif):
+		if isinstance(wif, cls):
 			return wif
 			return wif
 		try:
 		try:
 			assert wif.isascii() and wif.isalnum(), 'not an ASCII alphanumeric string'
 			assert wif.isascii() and wif.isalnum(), 'not an ASCII alphanumeric string'
 			proto.decode_wif(wif) # raises exception on error
 			proto.decode_wif(wif) # raises exception on error
-			return str.__new__(cls,wif)
+			return str.__new__(cls, wif)
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,wif)
+			return cls.init_fail(e, wif)
 
 
-def is_wif(proto,s):
-	return get_obj( WifKey, proto=proto, wif=s, silent=True, return_bool=True )
+def is_wif(proto, s):
+	return get_obj(WifKey, proto=proto, wif=s, silent=True, return_bool=True)
 
 
-class PubKey(bytes,InitErrors,MMGenObject): # TODO: add some real checks
+class PubKey(bytes, InitErrors, MMGenObject): # TODO: add some real checks
 
 
-	def __new__(cls,s,compressed):
+	def __new__(cls, s, compressed):
 		try:
 		try:
-			assert isinstance(s,bytes)
-			me = bytes.__new__(cls,s)
+			assert isinstance(s, bytes)
+			me = bytes.__new__(cls, s)
 			me.compressed = compressed
 			me.compressed = compressed
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,s)
+			return cls.init_fail(e, s)
 
 
-class PrivKey(bytes,InitErrors,MMGenObject):
+class PrivKey(bytes, InitErrors, MMGenObject):
 	"""
 	"""
 	Input:   a) raw, non-preprocessed bytes; or b) WIF key.
 	Input:   a) raw, non-preprocessed bytes; or b) WIF key.
 	Output:  preprocessed key bytes, plus WIF key in 'wif' attribute
 	Output:  preprocessed key bytes, plus WIF key in 'wif' attribute
@@ -65,49 +65,49 @@ class PrivKey(bytes,InitErrors,MMGenObject):
 	width = 32
 	width = 32
 	trunc_ok = False
 	trunc_ok = False
 
 
-	compressed = ImmutableAttr(bool,typeconv=False)
-	wif        = ImmutableAttr(WifKey,typeconv=False)
+	compressed = ImmutableAttr(bool, typeconv=False)
+	wif        = ImmutableAttr(WifKey, typeconv=False)
 
 
-	# initialize with (priv_bin,compressed), WIF or self
-	def __new__(cls,proto,s=None,compressed=None,wif=None,pubkey_type=None):
-		if isinstance(s,cls):
+	# initialize with (priv_bin, compressed), WIF or self
+	def __new__(cls, proto, s=None, compressed=None, wif=None, pubkey_type=None):
+		if isinstance(s, cls):
 			return s
 			return s
 		if wif:
 		if wif:
 			try:
 			try:
-				assert s is None,"'wif' and key hex args are mutually exclusive"
+				assert s is None, "'wif' and key hex args are mutually exclusive"
 				assert wif.isascii() and wif.isalnum(), 'not an ASCII alphanumeric string'
 				assert wif.isascii() and wif.isalnum(), 'not an ASCII alphanumeric string'
 				k = proto.decode_wif(wif) # raises exception on error
 				k = proto.decode_wif(wif) # raises exception on error
-				me = bytes.__new__(cls,k.sec)
+				me = bytes.__new__(cls, k.sec)
 				me.compressed = k.compressed
 				me.compressed = k.compressed
 				me.pubkey_type = k.pubkey_type
 				me.pubkey_type = k.pubkey_type
-				me.wif = str.__new__(WifKey,wif) # check has been done
+				me.wif = str.__new__(WifKey, wif) # check has been done
 				me.orig_bytes = None
 				me.orig_bytes = None
-				if k.sec != proto.preprocess_key(k.sec,k.pubkey_type):
+				if k.sec != proto.preprocess_key(k.sec, k.pubkey_type):
 					from .util import die
 					from .util import die
-					die( 'PrivateKeyError',
-						f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}' )
+					die('PrivateKeyError',
+						f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}')
 				me.proto = proto
 				me.proto = proto
 				return me
 				return me
 			except Exception as e:
 			except Exception as e:
-				return cls.init_fail(e,s,objname=f'{proto.coin} WIF key')
+				return cls.init_fail(e, s, objname=f'{proto.coin} WIF key')
 		else:
 		else:
 			try:
 			try:
-				assert s,'private key bytes data missing'
-				assert isinstance(s,bytes),'input is not bytes'
-				assert pubkey_type is not None,"'pubkey_type' arg missing"
+				assert s, 'private key bytes data missing'
+				assert isinstance(s, bytes), 'input is not bytes'
+				assert pubkey_type is not None, "'pubkey_type' arg missing"
 				assert len(s) == cls.width, f'key length must be {cls.width} bytes'
 				assert len(s) == cls.width, f'key length must be {cls.width} bytes'
 				if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
 				if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
-					me = bytes.__new__(cls,s)
+					me = bytes.__new__(cls, s)
 				else:
 				else:
 					assert compressed is not None, "'compressed' arg missing"
 					assert compressed is not None, "'compressed' arg missing"
-					assert type(compressed) is bool,(
-						f"'compressed' must be of type bool, not {type(compressed).__name__}" )
-					me = bytes.__new__( cls, proto.preprocess_key(s,pubkey_type) )
-					me.wif = WifKey( proto, proto.encode_wif(me,pubkey_type,compressed) )
+					assert type(compressed) is bool, (
+						f"'compressed' must be of type bool, not {type(compressed).__name__}")
+					me = bytes.__new__(cls, proto.preprocess_key(s, pubkey_type))
+					me.wif = WifKey(proto, proto.encode_wif(me, pubkey_type, compressed))
 					me.compressed = compressed
 					me.compressed = compressed
 				me.pubkey_type = pubkey_type
 				me.pubkey_type = pubkey_type
 				me.orig_bytes = s # save the non-preprocessed key
 				me.orig_bytes = s # save the non-preprocessed key
 				me.proto = proto
 				me.proto = proto
 				return me
 				return me
 			except Exception as e:
 			except Exception as e:
-				return cls.init_fail(e,s)
+				return cls.init_fail(e, s)

+ 21 - 25
mmgen/keygen.py

@@ -28,39 +28,39 @@ keygen_public_data = namedtuple(
 		'pubkey',
 		'pubkey',
 		'viewkey_bytes',
 		'viewkey_bytes',
 		'pubkey_type',
 		'pubkey_type',
-		'compressed' ])
+		'compressed'])
 
 
 class keygen_base:
 class keygen_base:
 
 
-	def __init__(self,cfg):
+	def __init__(self, cfg):
 		if not (self.production_safe or cfg.test_suite):
 		if not (self.production_safe or cfg.test_suite):
 			from .util import die
 			from .util import die
 			die(2,
 			die(2,
 				f'Public key generator {type(self).__name__!r} is not safe from timing attacks '
 				f'Public key generator {type(self).__name__!r} is not safe from timing attacks '
 				'and may only be used in a testing environment')
 				'and may only be used in a testing environment')
 
 
-	def gen_data(self,privkey):
-		assert isinstance(privkey,PrivKey)
+	def gen_data(self, privkey):
+		assert isinstance(privkey, PrivKey)
 		return keygen_public_data(
 		return keygen_public_data(
 			self.to_pubkey(privkey),
 			self.to_pubkey(privkey),
 			self.to_viewkey(privkey),
 			self.to_viewkey(privkey),
 			privkey.pubkey_type,
 			privkey.pubkey_type,
-			privkey.compressed )
+			privkey.compressed)
 
 
-	def to_viewkey(self,privkey):
+	def to_viewkey(self, privkey):
 		return None
 		return None
 
 
 	@classmethod
 	@classmethod
-	def get_clsname(cls,cfg,silent=False):
+	def get_clsname(cls, cfg, silent=False):
 		return cls.__name__
 		return cls.__name__
 
 
 backend_data = {
 backend_data = {
 	'std': {
 	'std': {
-		'backends': ('libsecp256k1','python-ecdsa'),
+		'backends': ('libsecp256k1', 'python-ecdsa'),
 		'package': 'secp256k1',
 		'package': 'secp256k1',
 	},
 	},
 	'monero': {
 	'monero': {
-		'backends': ('nacl','ed25519ll-djbec','ed25519'),
+		'backends': ('nacl', 'ed25519ll-djbec', 'ed25519'),
 		'package': 'xmr',
 		'package': 'xmr',
 	},
 	},
 	'zcash_z': {
 	'zcash_z': {
@@ -76,11 +76,11 @@ def get_pubkey_type_cls(pubkey_type):
 	import importlib
 	import importlib
 	return getattr(
 	return getattr(
 		importlib.import_module(f'mmgen.proto.{backend_data[pubkey_type]["package"]}.keygen'),
 		importlib.import_module(f'mmgen.proto.{backend_data[pubkey_type]["package"]}.keygen'),
-		'backend' )
+		'backend')
 
 
-def _check_backend(cfg,backend,pubkey_type,desc='keygen backend'):
+def _check_backend(cfg, backend, pubkey_type, desc='keygen backend'):
 
 
-	from .util import is_int,die
+	from .util import is_int, die
 
 
 	assert is_int(backend), f'illegal value for {desc} (must be an integer)'
 	assert is_int(backend), f'illegal value for {desc} (must be an integer)'
 
 
@@ -90,25 +90,21 @@ def _check_backend(cfg,backend,pubkey_type,desc='keygen backend'):
 		die(1,
 		die(1,
 			f'{backend}: {desc} out of range\n' +
 			f'{backend}: {desc} out of range\n' +
 			'Configured backends: ' +
 			'Configured backends: ' +
-			' '.join( f'{n}:{k}' for n,k in enumerate(backends,1) )
+			' '.join(f'{n}:{k}' for n, k in enumerate(backends, 1))
 		)
 		)
 
 
 	cfg._util.qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation')
 	cfg._util.qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation')
 
 
 	return True
 	return True
 
 
-def check_backend(cfg,proto,backend,addr_type):
+def check_backend(cfg, proto, backend, addr_type):
 
 
 	from .addr import MMGenAddrType
 	from .addr import MMGenAddrType
-	pubkey_type = MMGenAddrType(proto,addr_type or proto.dfl_mmtype).pubkey_type
+	pubkey_type = MMGenAddrType(proto, addr_type or proto.dfl_mmtype).pubkey_type
 
 
-	return  _check_backend(
-		cfg,
-		backend,
-		pubkey_type,
-		desc = '--keygen-backend parameter' )
+	return  _check_backend(cfg, backend, pubkey_type, desc='--keygen-backend parameter')
 
 
-def KeyGenerator(cfg,proto,pubkey_type,backend=None,silent=False):
+def KeyGenerator(cfg, proto, pubkey_type, backend=None, silent=False):
 	"""
 	"""
 	factory function returning a key generator backend for the specified pubkey type
 	factory function returning a key generator backend for the specified pubkey type
 	"""
 	"""
@@ -119,13 +115,13 @@ def KeyGenerator(cfg,proto,pubkey_type,backend=None,silent=False):
 	backend = backend or cfg.keygen_backend
 	backend = backend or cfg.keygen_backend
 
 
 	if backend:
 	if backend:
-		_check_backend( cfg, backend, pubkey_type )
+		_check_backend(cfg, backend, pubkey_type)
 
 
 	backend_id = backend_data[pubkey_type]['backends'][int(backend) - 1 if backend else 0]
 	backend_id = backend_data[pubkey_type]['backends'][int(backend) - 1 if backend else 0]
 
 
 	backend_clsname = getattr(
 	backend_clsname = getattr(
 		pubkey_type_cls,
 		pubkey_type_cls,
-		backend_id.replace('-','_')
-			).get_clsname(cfg,silent=silent)
+		backend_id.replace('-', '_')
+			).get_clsname(cfg, silent=silent)
 
 
-	return getattr(pubkey_type_cls,backend_clsname)(cfg)
+	return getattr(pubkey_type_cls, backend_clsname)(cfg)

+ 29 - 29
mmgen/led.py

@@ -29,31 +29,31 @@ from .color import blue, orange
 
 
 class LEDControl:
 class LEDControl:
 
 
-	binfo = namedtuple('board_info',['name','status','trigger','trigger_states'])
+	binfo = namedtuple('board_info', ['name', 'status', 'trigger', 'trigger_states'])
 	boards = {
 	boards = {
 		'raspi_pi': binfo(
 		'raspi_pi': binfo(
 			name    = 'Raspberry Pi',
 			name    = 'Raspberry Pi',
 			status  = '/sys/class/leds/led0/brightness',
 			status  = '/sys/class/leds/led0/brightness',
 			trigger = '/sys/class/leds/led0/trigger',
 			trigger = '/sys/class/leds/led0/trigger',
-			trigger_states = ('none','mmc0') ),
+			trigger_states = ('none', 'mmc0')),
 		'orange_pi': binfo(
 		'orange_pi': binfo(
 			name    = 'Orange Pi (Armbian)',
 			name    = 'Orange Pi (Armbian)',
 			status  = '/sys/class/leds/orangepi:red:status/brightness',
 			status  = '/sys/class/leds/orangepi:red:status/brightness',
 			trigger = None,
 			trigger = None,
-			trigger_states = None ),
+			trigger_states = None),
 		'rock_pi': binfo(
 		'rock_pi': binfo(
 			name    = 'Rock Pi (Armbian)',
 			name    = 'Rock Pi (Armbian)',
 			status  = '/sys/class/leds/status/brightness',
 			status  = '/sys/class/leds/status/brightness',
 			trigger = '/sys/class/leds/status/trigger',
 			trigger = '/sys/class/leds/status/trigger',
-			trigger_states = ('none','heartbeat') ),
+			trigger_states = ('none', 'heartbeat')),
 		'dummy': binfo(
 		'dummy': binfo(
 			name    = 'Fake',
 			name    = 'Fake',
 			status  = '/tmp/led_status',
 			status  = '/tmp/led_status',
 			trigger = '/tmp/led_trigger',
 			trigger = '/tmp/led_trigger',
-			trigger_states = ('none','original_value') ),
+			trigger_states = ('none', 'original_value')),
 	}
 	}
 
 
-	def __init__(self,enabled,simulate=False,debug=False):
+	def __init__(self, enabled, simulate=False, debug=False):
 
 
 		self.enabled = enabled
 		self.enabled = enabled
 		self.debug = debug or simulate
 		self.debug = debug or simulate
@@ -65,7 +65,7 @@ class LEDControl:
 		self.ev = threading.Event()
 		self.ev = threading.Event()
 		self.led_thread = None
 		self.led_thread = None
 
 
-		for board_id,board in self.boards.items():
+		for board_id, board in self.boards.items():
 			if board_id == 'dummy' and not simulate:
 			if board_id == 'dummy' and not simulate:
 				continue
 				continue
 			try:
 			try:
@@ -75,20 +75,20 @@ class LEDControl:
 			else:
 			else:
 				break
 				break
 		else:
 		else:
-			die( 'NoLEDSupport', 'Control files not found!  LED control not supported on this system' )
+			die('NoLEDSupport', 'Control files not found!  LED control not supported on this system')
 
 
 		msg(f'{board.name} board detected')
 		msg(f'{board.name} board detected')
 
 
 		if self.debug:
 		if self.debug:
 			msg(f'\n  Status file:  {board.status}\n  Trigger file: {board.trigger}')
 			msg(f'\n  Status file:  {board.status}\n  Trigger file: {board.trigger}')
 
 
-		def check_access(fn,desc,init_val=None):
+		def check_access(fn, desc, init_val=None):
 
 
 			def write_init_val(init_val):
 			def write_init_val(init_val):
 				if not init_val:
 				if not init_val:
 					with open(fn) as fp:
 					with open(fn) as fp:
 						init_val = fp.read().strip()
 						init_val = fp.read().strip()
-				with open(fn,'w') as fp:
+				with open(fn, 'w') as fp:
 					fp.write(f'{init_val}\n')
 					fp.write(f'{init_val}\n')
 
 
 			try:
 			try:
@@ -107,58 +107,58 @@ class LEDControl:
 					))
 					))
 					sys.exit(1)
 					sys.exit(1)
 
 
-		check_access(board.status,desc='status LED control')
+		check_access(board.status, desc='status LED control')
 
 
 		if board.trigger:
 		if board.trigger:
-			check_access(board.trigger,desc='LED trigger',init_val=board.trigger_states[0])
+			check_access(board.trigger, desc='LED trigger', init_val=board.trigger_states[0])
 
 
 		self.board = board
 		self.board = board
 
 
 	@classmethod
 	@classmethod
 	def create_dummy_control_files(cls):
 	def create_dummy_control_files(cls):
 		db = cls.boards['dummy']
 		db = cls.boards['dummy']
-		with open(db.status,'w') as fp:
+		with open(db.status, 'w') as fp:
 			fp.write('0\n')
 			fp.write('0\n')
-		with open(db.trigger,'w') as fp:
+		with open(db.trigger, 'w') as fp:
 			fp.write(db.trigger_states[1]+'\n')
 			fp.write(db.trigger_states[1]+'\n')
 
 
-	def noop(self,*args,**kwargs):
+	def noop(self, *args, **kwargs):
 		pass
 		pass
 
 
-	def ev_sleep(self,secs):
+	def ev_sleep(self, secs):
 		self.ev.wait(secs)
 		self.ev.wait(secs)
 		return self.ev.is_set()
 		return self.ev.is_set()
 
 
-	def led_loop(self,on_secs,off_secs):
+	def led_loop(self, on_secs, off_secs):
 
 
 		if self.debug:
 		if self.debug:
-			msg(f'led_loop({on_secs},{off_secs})')
+			msg(f'led_loop({on_secs}, {off_secs})')
 
 
 		if not on_secs:
 		if not on_secs:
-			with open(self.board.status,'w') as fp:
+			with open(self.board.status, 'w') as fp:
 				fp.write('0\n')
 				fp.write('0\n')
 			while True:
 			while True:
 				if self.ev_sleep(3600):
 				if self.ev_sleep(3600):
 					return
 					return
 
 
 		while True:
 		while True:
-			for s_time,val in ((on_secs,255),(off_secs,0)):
+			for s_time, val in ((on_secs, 255), (off_secs, 0)):
 				if self.debug:
 				if self.debug:
-					msg_r(('^','+')[bool(val)])
-				with open(self.board.status,'w') as fp:
+					msg_r(('^', '+')[bool(val)])
+				with open(self.board.status, 'w') as fp:
 					fp.write(f'{val}\n')
 					fp.write(f'{val}\n')
 				if self.ev_sleep(s_time):
 				if self.ev_sleep(s_time):
 					if self.debug:
 					if self.debug:
 						msg('\n')
 						msg('\n')
 					return
 					return
 
 
-	def set(self,state):
-		lt = namedtuple('led_timings',['on_secs','off_secs'])
+	def set(self, state):
+		lt = namedtuple('led_timings', ['on_secs', 'off_secs'])
 		timings = {
 		timings = {
-			'off':     lt( 0, 0 ),
-			'standby': lt( 2.2, 0.2 ),
-			'busy':    lt( 0.06, 0.06 ),
-			'error':   lt( 0.5, 0.5 ) }
+			'off':     lt(0,    0),
+			'standby': lt(2.2,  0.2),
+			'busy':    lt(0.06, 0.06),
+			'error':   lt(0.5,  0.5)}
 
 
 		if self.led_thread:
 		if self.led_thread:
 			self.ev.set()
 			self.ev.set()
@@ -186,5 +186,5 @@ class LEDControl:
 			msg('Stopping LED')
 			msg('Stopping LED')
 
 
 		if self.board.trigger:
 		if self.board.trigger:
-			with open(self.board.trigger,'w') as fp:
+			with open(self.board.trigger, 'w') as fp:
 				fp.write(self.board.trigger_states[1]+'\n')
 				fp.write(self.board.trigger_states[1]+'\n')

+ 8 - 8
mmgen/main.py

@@ -20,15 +20,15 @@
 main: Script launcher for the MMGen Project
 main: Script launcher for the MMGen Project
 """
 """
 
 
-import sys,os
+import sys, os
 
 
 def launch(*, mod=None, func=None, fqmod=None, package='mmgen'):
 def launch(*, mod=None, func=None, fqmod=None, package='mmgen'):
 
 
 	if sys.platform in ('linux', 'darwin') and sys.stdin.isatty():
 	if sys.platform in ('linux', 'darwin') and sys.stdin.isatty():
-		import termios,atexit
+		import termios, atexit
 		fd = sys.stdin.fileno()
 		fd = sys.stdin.fileno()
 		old = termios.tcgetattr(fd)
 		old = termios.tcgetattr(fd)
-		atexit.register(lambda: termios.tcsetattr(fd,termios.TCSADRAIN,old))
+		atexit.register(lambda: termios.tcsetattr(fd, termios.TCSADRAIN, old))
 
 
 	try:
 	try:
 		__import__(f'{package}.main_{mod}') if mod else func() if func else __import__(fqmod)
 		__import__(f'{package}.main_{mod}') if mod else func() if func else __import__(fqmod)
@@ -47,9 +47,9 @@ def launch(*, mod=None, func=None, fqmod=None, package='mmgen'):
 			errmsg = repr(e.args[0]) if e.args else repr(e)
 			errmsg = repr(e.args[0]) if e.args else repr(e)
 
 
 		from collections import namedtuple
 		from collections import namedtuple
-		from .color import nocolor,yellow,red
+		from .color import nocolor, yellow, red
 
 
-		_o = namedtuple('exit_data',['color','exit_val','fs'])
+		_o = namedtuple('exit_data', ['color', 'exit_val', 'fs'])
 		d = {
 		d = {
 			0:   _o(nocolor, 0, '{message}'),
 			0:   _o(nocolor, 0, '{message}'),
 			1:   _o(nocolor, 1, '{message}'),
 			1:   _o(nocolor, 1, '{message}'),
@@ -57,14 +57,14 @@ def launch(*, mod=None, func=None, fqmod=None, package='mmgen'):
 			3:   _o(yellow,  3, '\nMMGen Error ({name}):\n{message}'),
 			3:   _o(yellow,  3, '\nMMGen Error ({name}):\n{message}'),
 			4:   _o(red,     4, '\nMMGen Fatal Error ({name}):\n{message}'),
 			4:   _o(red,     4, '\nMMGen Fatal Error ({name}):\n{message}'),
 			'x': _o(yellow,  5, '\nMMGen Unhandled Exception ({name}): {e}'),
 			'x': _o(yellow,  5, '\nMMGen Unhandled Exception ({name}): {e}'),
-		}[getattr(e,'mmcode','x')]
+		}[getattr(e, 'mmcode', 'x')]
 
 
-		(sys.stdout if getattr(e,'stdout',None) else sys.stderr).write(
+		(sys.stdout if getattr(e, 'stdout', None) else sys.stderr).write(
 			d.color(d.fs.format(
 			d.color(d.fs.format(
 				name = type(e).__name__,
 				name = type(e).__name__,
 				message = errmsg.strip() or e,
 				message = errmsg.strip() or e,
 				e = e))
 				e = e))
-			+ '\n' )
+			+ '\n')
 
 
 		if os.getenv('MMGEN_EXEC_WRAPPER') or os.getenv('MMGEN_TRACEBACK'):
 		if os.getenv('MMGEN_EXEC_WRAPPER') or os.getenv('MMGEN_TRACEBACK'):
 			raise
 			raise

+ 55 - 55
mmgen/mn_entry.py

@@ -20,10 +20,10 @@
 mn_entry.py - Mnemonic user entry methods for the MMGen suite
 mn_entry.py - Mnemonic user entry methods for the MMGen suite
 """
 """
 
 
-import sys,time
+import sys, time
 
 
-from .util import msg,msg_r,fmt,fmt_list,capfirst,die,ascii_lowercase
-from .term import get_char,get_char_raw
+from .util import msg, msg_r, fmt, fmt_list, capfirst, die, ascii_lowercase
+from .term import get_char, get_char_raw
 from .color import cyan
 from .color import cyan
 
 
 _return_chars = '\n\r '
 _return_chars = '\n\r '
@@ -43,19 +43,19 @@ class MnEntryMode:
 		pad characters per word are permitted.
 		pad characters per word are permitted.
 	"""
 	"""
 
 
-	def __init__(self,mne):
+	def __init__(self, mne):
 		self.pad_max_info = '  ' + self.pad_max_info.lstrip() if self.pad_max else '\n'
 		self.pad_max_info = '  ' + self.pad_max_info.lstrip() if self.pad_max else '\n'
 		self.mne = mne
 		self.mne = mne
 
 
-	def get_char(self,s):
+	def get_char(self, s):
 		did_erase = False
 		did_erase = False
 		while True:
 		while True:
-			ch = get_char_raw('',num_bytes=1)
+			ch = get_char_raw('', num_bytes=1)
 			if s and ch in _erase_chars:
 			if s and ch in _erase_chars:
 				s = s[:-1]
 				s = s[:-1]
 				did_erase = True
 				did_erase = True
 			else:
 			else:
-				return (ch,s,did_erase)
+				return (ch, s, did_erase)
 
 
 class MnEntryModeFull(MnEntryMode):
 class MnEntryModeFull(MnEntryMode):
 	name = 'Full'
 	name = 'Full'
@@ -73,10 +73,10 @@ class MnEntryModeFull(MnEntryMode):
 	def ss_len(self):
 	def ss_len(self):
 		return self.mne.longest_word
 		return self.mne.longest_word
 
 
-	def get_word(self,mne):
-		s,pad = ('', 0)
+	def get_word(self, mne):
+		s, pad = ('', 0)
 		while True:
 		while True:
-			ch,s,_ = self.get_char(s)
+			ch, s, _ = self.get_char(s)
 			if ch in _return_chars:
 			if ch in _return_chars:
 				if s:
 				if s:
 					break
 					break
@@ -87,7 +87,7 @@ class MnEntryModeFull(MnEntryMode):
 				if pad + len(s) > self.ss_len:
 				if pad + len(s) > self.ss_len:
 					break
 					break
 
 
-		return mne.idx(s,'full')
+		return mne.idx(s, 'full')
 
 
 class MnEntryModeShort(MnEntryMode):
 class MnEntryModeShort(MnEntryMode):
 	name = 'Short'
 	name = 'Short'
@@ -104,7 +104,7 @@ class MnEntryModeShort(MnEntryMode):
 	"""
 	"""
 	pad_max = 16
 	pad_max = 16
 
 
-	def __init__(self,mne):
+	def __init__(self, mne):
 		if mne.wl_id == 'bip39':
 		if mne.wl_id == 'bip39':
 			self.prompt_info += '  ' + self.prompt_info_bip39_add.strip()
 			self.prompt_info += '  ' + self.prompt_info_bip39_add.strip()
 		super().__init__(mne)
 		super().__init__(mne)
@@ -113,10 +113,10 @@ class MnEntryModeShort(MnEntryMode):
 	def ss_len(self):
 	def ss_len(self):
 		return self.mne.uniq_ss_len
 		return self.mne.uniq_ss_len
 
 
-	def get_word(self,mne):
-		s,pad = ('', 0)
+	def get_word(self, mne):
+		s, pad = ('', 0)
 		while True:
 		while True:
-			ch,s,_ = self.get_char(s)
+			ch, s, _ = self.get_char(s)
 			if ch in _return_chars:
 			if ch in _return_chars:
 				if s:
 				if s:
 					break
 					break
@@ -129,7 +129,7 @@ class MnEntryModeShort(MnEntryMode):
 				if pad > self.pad_max:
 				if pad > self.pad_max:
 					break
 					break
 
 
-		return mne.idx(s,'short')
+		return mne.idx(s, 'short')
 
 
 class MnEntryModeFixed(MnEntryMode):
 class MnEntryModeFixed(MnEntryMode):
 	name = 'Fixed'
 	name = 'Fixed'
@@ -140,14 +140,14 @@ class MnEntryModeFixed(MnEntryMode):
 	prompt_info = """
 	prompt_info = """
 		Each word is entered automatically once exactly {ssl} characters are typed.
 		Each word is entered automatically once exactly {ssl} characters are typed.
 	"""
 	"""
-	prompt_info_add = ( """
+	prompt_info_add = ("""
 		Words shorter than {ssl} letters must be padded to fit.
 		Words shorter than {ssl} letters must be padded to fit.
 		""", """
 		""", """
 		{sw}-letter words must be padded with one pad character.
 		{sw}-letter words must be padded with one pad character.
-		""" )
+		""")
 	pad_max = None
 	pad_max = None
 
 
-	def __init__(self,mne):
+	def __init__(self, mne):
 		self.len_diff = mne.uniq_ss_len - mne.shortest_word
 		self.len_diff = mne.uniq_ss_len - mne.shortest_word
 		self.prompt_info += self.prompt_info_add[self.len_diff==1].lstrip()
 		self.prompt_info += self.prompt_info_add[self.len_diff==1].lstrip()
 		super().__init__(mne)
 		super().__init__(mne)
@@ -156,23 +156,23 @@ class MnEntryModeFixed(MnEntryMode):
 	def ss_len(self):
 	def ss_len(self):
 		return self.mne.uniq_ss_len
 		return self.mne.uniq_ss_len
 
 
-	def get_word(self,mne):
-		s,pad = ('', 0)
+	def get_word(self, mne):
+		s, pad = ('', 0)
 		while True:
 		while True:
-			ch,s,_ = self.get_char(s)
+			ch, s, _ = self.get_char(s)
 			if ch in _return_chars:
 			if ch in _return_chars:
 				if s:
 				if s:
 					break
 					break
 			elif ch in ascii_lowercase:
 			elif ch in ascii_lowercase:
 				s += ch
 				s += ch
 				if len(s) + pad == self.ss_len:
 				if len(s) + pad == self.ss_len:
-					return mne.idx(s,'short')
+					return mne.idx(s, 'short')
 			else:
 			else:
 				pad += 1
 				pad += 1
 				if pad > self.len_diff:
 				if pad > self.len_diff:
 					return None
 					return None
 				if len(s) + pad == self.ss_len:
 				if len(s) + pad == self.ss_len:
-					return mne.idx(s,'short')
+					return mne.idx(s, 'short')
 
 
 class MnEntryModeMinimal(MnEntryMode):
 class MnEntryModeMinimal(MnEntryMode):
 	name = 'Minimal'
 	name = 'Minimal'
@@ -191,26 +191,26 @@ class MnEntryModeMinimal(MnEntryMode):
 	pad_max = 16
 	pad_max = 16
 	ss_len = None
 	ss_len = None
 
 
-	def get_word(self,mne):
-		s,pad = ('', 0)
-		lo,hi = (0, len(mne.wl) - 1)
+	def get_word(self, mne):
+		s, pad = ('', 0)
+		lo, hi = (0, len(mne.wl) - 1)
 		while True:
 		while True:
-			ch,s,did_erase = self.get_char(s)
+			ch, s, did_erase = self.get_char(s)
 			if did_erase:
 			if did_erase:
-				lo,hi = (0, len(mne.wl) - 1)
+				lo, hi = (0, len(mne.wl) - 1)
 			if ch in _return_chars:
 			if ch in _return_chars:
 				if s:
 				if s:
-					return mne.idx(s,'full',lo_idx=lo,hi_idx=hi)
+					return mne.idx(s, 'full', lo_idx=lo, hi_idx=hi)
 			elif ch in ascii_lowercase:
 			elif ch in ascii_lowercase:
 				s += ch
 				s += ch
-				ret = mne.idx(s,'minimal',lo_idx=lo,hi_idx=hi)
-				if not isinstance(ret,tuple):
+				ret = mne.idx(s, 'minimal', lo_idx=lo, hi_idx=hi)
+				if not isinstance(ret, tuple):
 					return ret
 					return ret
-				lo,hi = ret
+				lo, hi = ret
 			else:
 			else:
 				pad += 1
 				pad += 1
 				if pad > self.pad_max:
 				if pad > self.pad_max:
-					return mne.idx(s,'full',lo_idx=lo,hi_idx=hi)
+					return mne.idx(s, 'full', lo_idx=lo, hi_idx=hi)
 
 
 class MnemonicEntry:
 class MnemonicEntry:
 
 
@@ -225,13 +225,13 @@ class MnemonicEntry:
 			acters may be typed before, after, or in the middle of words.
 			acters may be typed before, after, or in the middle of words.
 		""",
 		""",
 	}
 	}
-	word_prompt = ('Enter word #{}: ','Incorrect entry. Repeat word #{}: ')
+	word_prompt = ('Enter word #{}: ', 'Incorrect entry. Repeat word #{}: ')
 	usr_dfl_entry_mode = None
 	usr_dfl_entry_mode = None
 	_lw = None
 	_lw = None
 	_sw = None
 	_sw = None
 	_usl = None
 	_usl = None
 
 
-	def __init__(self,cfg):
+	def __init__(self, cfg):
 		self.cfg = cfg
 		self.cfg = cfg
 		self.set_dfl_entry_mode()
 		self.set_dfl_entry_mode()
 
 
@@ -252,7 +252,7 @@ class MnemonicEntry:
 		if not self._usl:
 		if not self._usl:
 			usl = 0
 			usl = 0
 			for i in range(len(self.wl)-1):
 			for i in range(len(self.wl)-1):
-				w1,w2 = self.wl[i],self.wl[i+1]
+				w1, w2 = self.wl[i], self.wl[i+1]
 				while True:
 				while True:
 					if w1[:usl] == w2[:usl]:
 					if w1[:usl] == w2[:usl]:
 						usl += 1
 						usl += 1
@@ -261,7 +261,7 @@ class MnemonicEntry:
 			self._usl = usl
 			self._usl = usl
 		return self._usl
 		return self._usl
 
 
-	def idx(self,w,entry_mode,lo_idx=None,hi_idx=None):
+	def idx(self, w, entry_mode, lo_idx=None, hi_idx=None):
 		"""
 		"""
 		Return values:
 		Return values:
 		  - all modes:
 		  - all modes:
@@ -285,9 +285,9 @@ class MnemonicEntry:
 			if cur_w == w:
 			if cur_w == w:
 				if entry_mode == 'minimal':
 				if entry_mode == 'minimal':
 					if idx > 0 and self.wl[idx-1][:len(w)] == w:
 					if idx > 0 and self.wl[idx-1][:len(w)] == w:
-						return (lo,hi)
+						return (lo, hi)
 					elif idx < last_idx and self.wl[idx+1][:len(w)] == w:
 					elif idx < last_idx and self.wl[idx+1][:len(w)] == w:
-						return (lo,hi)
+						return (lo, hi)
 				return idx
 				return idx
 			elif hi <= lo:
 			elif hi <= lo:
 				return None
 				return None
@@ -296,17 +296,17 @@ class MnemonicEntry:
 			else:
 			else:
 				lo = idx + 1
 				lo = idx + 1
 
 
-	def get_cls_by_entry_mode(self,entry_mode):
+	def get_cls_by_entry_mode(self, entry_mode):
 		return getattr(sys.modules[__name__], 'MnEntryMode' + capfirst(entry_mode))
 		return getattr(sys.modules[__name__], 'MnEntryMode' + capfirst(entry_mode))
 
 
 	def choose_entry_mode(self):
 	def choose_entry_mode(self):
 		msg('Choose an entry mode:\n')
 		msg('Choose an entry mode:\n')
 		em_objs = [self.get_cls_by_entry_mode(entry_mode)(self) for entry_mode in self.entry_modes]
 		em_objs = [self.get_cls_by_entry_mode(entry_mode)(self) for entry_mode in self.entry_modes]
-		for n,mode in enumerate(em_objs,1):
+		for n, mode in enumerate(em_objs, 1):
 			msg('  {}) {:8} {}'.format(
 			msg('  {}) {:8} {}'.format(
 				n,
 				n,
 				mode.name + ':',
 				mode.name + ':',
-				fmt(mode.choose_info,' '*14).lstrip().format(usl=self.uniq_ss_len),
+				fmt(mode.choose_info, ' '*14).lstrip().format(usl=self.uniq_ss_len),
 			))
 			))
 		prompt = f'Type a number, or hit ENTER for the default ({capfirst(self.dfl_entry_mode)}): '
 		prompt = f'Type a number, or hit ENTER for the default ({capfirst(self.dfl_entry_mode)}): '
 		erase = '\r' + ' ' * (len(prompt)+19) + '\r'
 		erase = '\r' + ' ' * (len(prompt)+19) + '\r'
@@ -315,7 +315,7 @@ class MnemonicEntry:
 			if uret == '':
 			if uret == '':
 				msg_r(erase)
 				msg_r(erase)
 				return self.get_cls_by_entry_mode(self.dfl_entry_mode)(self)
 				return self.get_cls_by_entry_mode(self.dfl_entry_mode)(self)
-			elif uret in [str(i) for i in range(1,len(em_objs)+1)]:
+			elif uret in [str(i) for i in range(1, len(em_objs)+1)]:
 				msg_r(erase)
 				msg_r(erase)
 				return em_objs[int(uret)-1]
 				return em_objs[int(uret)-1]
 			else:
 			else:
@@ -323,7 +323,7 @@ class MnemonicEntry:
 				time.sleep(self.cfg.err_disp_timeout)
 				time.sleep(self.cfg.err_disp_timeout)
 				msg_r(erase)
 				msg_r(erase)
 
 
-	def get_mnemonic_from_user(self,mn_len,validate=True):
+	def get_mnemonic_from_user(self, mn_len, validate=True):
 		mll = list(self.bconv.seedlen_map_rev)
 		mll = list(self.bconv.seedlen_map_rev)
 		assert mn_len in mll, f'{mn_len}: invalid mnemonic length (must be one of {mll})'
 		assert mn_len in mll, f'{mn_len}: invalid mnemonic length (must be one of {mll})'
 
 
@@ -349,8 +349,8 @@ class MnemonicEntry:
 					sw       = self.shortest_word,
 					sw       = self.shortest_word,
 			))
 			))
 
 
-		clear_line = '\n' if self.cfg.test_suite else '{r}{s}{r}'.format(r='\r',s=' '*40)
-		idx,idxs = 1,[] # initialize idx to a non-None value
+		clear_line = '\n' if self.cfg.test_suite else '{r}{s}{r}'.format(r='\r', s=' '*40)
+		idx, idxs = 1, [] # initialize idx to a non-None value
 
 
 		while len(idxs) < mn_len:
 		while len(idxs) < mn_len:
 			msg_r(self.word_prompt[idx is None].format(len(idxs)+1))
 			msg_r(self.word_prompt[idx is None].format(len(idxs)+1))
@@ -369,7 +369,7 @@ class MnemonicEntry:
 		return ' '.join(words)
 		return ' '.join(words)
 
 
 	@classmethod
 	@classmethod
-	def get_cls_by_wordlist(cls,wl):
+	def get_cls_by_wordlist(cls, wl):
 		d = {
 		d = {
 			'mmgen': MnemonicEntryMMGen,
 			'mmgen': MnemonicEntryMMGen,
 			'bip39': MnemonicEntryBIP39,
 			'bip39': MnemonicEntryBIP39,
@@ -385,7 +385,7 @@ class MnemonicEntry:
 		In addition to setting the default entry mode for the current wordlist, checks validity
 		In addition to setting the default entry mode for the current wordlist, checks validity
 		of all user-configured entry modes
 		of all user-configured entry modes
 		"""
 		"""
-		for k,v in self.cfg.mnemonic_entry_modes.items():
+		for k, v in self.cfg.mnemonic_entry_modes.items():
 			cls = self.get_cls_by_wordlist(k)
 			cls = self.get_cls_by_wordlist(k)
 			if v not in cls.entry_modes:
 			if v not in cls.entry_modes:
 				errmsg = f"""
 				errmsg = f"""
@@ -393,37 +393,37 @@ class MnemonicEntry:
 					Entry mode {v!r} not recognized for wordlist {k!r}:
 					Entry mode {v!r} not recognized for wordlist {k!r}:
 					Valid choices: {fmt_list(cls.entry_modes)}
 					Valid choices: {fmt_list(cls.entry_modes)}
 				"""
 				"""
-				die(2, '\n' + fmt(errmsg,indent='  '))
+				die(2, '\n' + fmt(errmsg, indent='  '))
 			if cls == type(self):
 			if cls == type(self):
 				self.usr_dfl_entry_mode = v
 				self.usr_dfl_entry_mode = v
 
 
 class MnemonicEntryMMGen(MnemonicEntry):
 class MnemonicEntryMMGen(MnemonicEntry):
 	wl_id = 'mmgen'
 	wl_id = 'mmgen'
 	modname = 'baseconv'
 	modname = 'baseconv'
-	entry_modes = ('full','minimal','fixed')
+	entry_modes = ('full', 'minimal', 'fixed')
 	dfl_entry_mode = 'minimal'
 	dfl_entry_mode = 'minimal'
 	has_chksum = False
 	has_chksum = False
 
 
 class MnemonicEntryBIP39(MnemonicEntry):
 class MnemonicEntryBIP39(MnemonicEntry):
 	wl_id = 'bip39'
 	wl_id = 'bip39'
 	modname = 'bip39'
 	modname = 'bip39'
-	entry_modes = ('full','short','fixed')
+	entry_modes = ('full', 'short', 'fixed')
 	dfl_entry_mode = 'fixed'
 	dfl_entry_mode = 'fixed'
 	has_chksum = True
 	has_chksum = True
 
 
 class MnemonicEntryMonero(MnemonicEntry):
 class MnemonicEntryMonero(MnemonicEntry):
 	wl_id = 'xmrseed'
 	wl_id = 'xmrseed'
 	modname = 'xmrseed'
 	modname = 'xmrseed'
-	entry_modes = ('full','short')
+	entry_modes = ('full', 'short')
 	dfl_entry_mode = 'short'
 	dfl_entry_mode = 'short'
 	has_chksum = True
 	has_chksum = True
 
 
-def mn_entry(cfg,wl_id,entry_mode=None):
+def mn_entry(cfg, wl_id, entry_mode=None):
 	if wl_id == 'words':
 	if wl_id == 'words':
 		wl_id = 'mmgen'
 		wl_id = 'mmgen'
 	me = MnemonicEntry.get_cls_by_wordlist(wl_id)(cfg)
 	me = MnemonicEntry.get_cls_by_wordlist(wl_id)(cfg)
 	import importlib
 	import importlib
-	me.bconv = getattr(importlib.import_module(f'mmgen.{me.modname}'),me.modname)(wl_id)
+	me.bconv = getattr(importlib.import_module(f'mmgen.{me.modname}'), me.modname)(wl_id)
 	me.wl = me.bconv.digits
 	me.wl = me.bconv.digits
 	if entry_mode:
 	if entry_mode:
 		me.em = getattr(sys.modules[__name__], 'MnEntryMode' + capfirst(entry_mode))(me)
 		me.em = getattr(sys.modules[__name__], 'MnEntryMode' + capfirst(entry_mode))(me)

+ 76 - 76
mmgen/msg.py

@@ -12,40 +12,40 @@
 msg: base message signing classes
 msg: base message signing classes
 """
 """
 
 
-import os,importlib,json
+import os, importlib, json
 from .cfg import gc
 from .cfg import gc
-from .objmethods import MMGenObject,HiliteStr,InitErrors
-from .util import msg,die,make_chksum_6,fmt_list,remove_dups
-from .color import red,orange,grnbg
+from .objmethods import MMGenObject, HiliteStr, InitErrors
+from .util import msg, die, make_chksum_6, fmt_list, remove_dups
+from .color import red, orange, grnbg
 from .protocol import init_proto
 from .protocol import init_proto
-from .fileutil import get_data_from_file,write_data_to_file
-from .addr import MMGenID,CoinAddr
+from .fileutil import get_data_from_file, write_data_to_file
+from .addr import MMGenID, CoinAddr
 
 
-class MMGenIDRange(HiliteStr,InitErrors,MMGenObject):
+class MMGenIDRange(HiliteStr, InitErrors, MMGenObject):
 	"""
 	"""
 	closely based on MMGenID
 	closely based on MMGenID
 	"""
 	"""
 	color = 'orange'
 	color = 'orange'
 	width = 0
 	width = 0
 	trunc_ok = False
 	trunc_ok = False
-	def __new__(cls,proto,id_str):
+	def __new__(cls, proto, id_str):
 		from .addrlist import AddrIdxList
 		from .addrlist import AddrIdxList
 		from .addr import AddrListID
 		from .addr import AddrListID
 		from .seed import SeedID
 		from .seed import SeedID
 		try:
 		try:
 			ss = str(id_str).split(':')
 			ss = str(id_str).split(':')
-			assert len(ss) in (2,3),'not 2 or 3 colon-separated items'
-			t = proto.addr_type((ss[1],proto.dfl_mmtype)[len(ss)==2])
-			me = str.__new__(cls,'{}:{}:{}'.format(ss[0],t,ss[-1]))
+			assert len(ss) in (2, 3), 'not 2 or 3 colon-separated items'
+			t = proto.addr_type((ss[1], proto.dfl_mmtype)[len(ss)==2])
+			me = str.__new__(cls, '{}:{}:{}'.format(ss[0], t, ss[-1]))
 			me.sid = SeedID(sid=ss[0])
 			me.sid = SeedID(sid=ss[0])
 			me.idxlist = AddrIdxList(ss[-1])
 			me.idxlist = AddrIdxList(ss[-1])
 			me.mmtype = t
 			me.mmtype = t
 			assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}'
 			assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}'
-			me.al_id = str.__new__(AddrListID,me.sid+':'+me.mmtype) # checks already done
+			me.al_id = str.__new__(AddrListID, me.sid+':'+me.mmtype) # checks already done
 			me.proto = proto
 			me.proto = proto
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,id_str)
+			return cls.init_fail(e, id_str)
 
 
 class coin_msg:
 class coin_msg:
 
 
@@ -53,7 +53,7 @@ class coin_msg:
 
 
 		ext = 'rawmsg.json'
 		ext = 'rawmsg.json'
 		signed = False
 		signed = False
-		chksum_keys = ('addrlists','message','msghash_type','network')
+		chksum_keys = ('addrlists', 'message', 'msghash_type', 'network')
 
 
 		@property
 		@property
 		def desc(self):
 		def desc(self):
@@ -70,11 +70,11 @@ class coin_msg:
 
 
 		@property
 		@property
 		def filename_stem(self):
 		def filename_stem(self):
-			coin,network = self.data['network'].split('_')
+			coin, network = self.data['network'].split('_')
 			return '{}[{}]{}'.format(
 			return '{}[{}]{}'.format(
 				self.chksum.upper(),
 				self.chksum.upper(),
 				coin.upper(),
 				coin.upper(),
-				('' if network == 'mainnet' else '.'+network) )
+				('' if network == 'mainnet' else '.'+network))
 
 
 		@property
 		@property
 		def filename(self):
 		def filename(self):
@@ -85,13 +85,13 @@ class coin_msg:
 			return f'{self.filename_stem}.{coin_msg.signed.ext}'
 			return f'{self.filename_stem}.{coin_msg.signed.ext}'
 
 
 		@staticmethod
 		@staticmethod
-		def get_proto_from_file(cfg,filename):
+		def get_proto_from_file(cfg, filename):
 			data = json.loads(get_data_from_file(cfg, filename))
 			data = json.loads(get_data_from_file(cfg, filename))
 			network_id = data['metadata']['network'] if 'metadata' in data else data['network'].lower()
 			network_id = data['metadata']['network'] if 'metadata' in data else data['network'].lower()
-			coin,network = network_id.split('_')
+			coin, network = network_id.split('_')
 			return init_proto(cfg=cfg, coin=coin, network=network)
 			return init_proto(cfg=cfg, coin=coin, network=network)
 
 
-		def write_to_file(self,outdir=None,ask_overwrite=False):
+		def write_to_file(self, outdir=None, ask_overwrite=False):
 			data = {
 			data = {
 				'id': f'{gc.proj_name} {self.desc}',
 				'id': f'{gc.proj_name} {self.desc}',
 				'metadata': self.data,
 				'metadata': self.data,
@@ -100,23 +100,23 @@ class coin_msg:
 
 
 			write_data_to_file(
 			write_data_to_file(
 				cfg           = self.cfg,
 				cfg           = self.cfg,
-				outfile       = os.path.join(outdir or '',self.filename),
-				data          = json.dumps(data,sort_keys=True,indent=4),
+				outfile       = os.path.join(outdir or '', self.filename),
+				data          = json.dumps(data, sort_keys=True, indent=4),
 				desc          = self.desc,
 				desc          = self.desc,
-				ask_overwrite = ask_overwrite )
+				ask_overwrite = ask_overwrite)
 
 
 	class new(base):
 	class new(base):
 
 
-		def __init__(self,message,addrlists,msghash_type,*args,**kwargs):
+		def __init__(self, message, addrlists, msghash_type, *args, **kwargs):
 
 
 			msghash_type = msghash_type or self.msg_cls.msghash_types[0]
 			msghash_type = msghash_type or self.msg_cls.msghash_types[0]
 
 
 			if msghash_type not in self.msg_cls.msghash_types:
 			if msghash_type not in self.msg_cls.msghash_types:
-				die(2,f'msghash_type {msghash_type!r} not supported for {self.proto.base_proto} protocol')
+				die(2, f'msghash_type {msghash_type!r} not supported for {self.proto.base_proto} protocol')
 
 
 			self.data = {
 			self.data = {
-				'network': '{}_{}'.format( self.proto.coin.lower(), self.proto.network ),
-				'addrlists': [MMGenIDRange(self.proto,i) for i in addrlists.split()],
+				'network': '{}_{}'.format(self.proto.coin.lower(), self.proto.network),
+				'addrlists': [MMGenIDRange(self.proto, i) for i in addrlists.split()],
 				'message': message,
 				'message': message,
 				'msghash_type': msghash_type,
 				'msghash_type': msghash_type,
 			}
 			}
@@ -124,7 +124,7 @@ class coin_msg:
 
 
 	class completed(base):
 	class completed(base):
 
 
-		def __init__(self,data,infile,*args,**kwargs):
+		def __init__(self, data, infile, *args, **kwargs):
 
 
 			if data:
 			if data:
 				self.__dict__ = data
 				self.__dict__ = data
@@ -133,14 +133,14 @@ class coin_msg:
 			self.data = get_data_from_file(
 			self.data = get_data_from_file(
 				cfg    = self.cfg,
 				cfg    = self.cfg,
 				infile = infile,
 				infile = infile,
-				desc   = self.desc )
+				desc   = self.desc)
 
 
 			d = json.loads(self.data)
 			d = json.loads(self.data)
 			self.data = d['metadata']
 			self.data = d['metadata']
 			self.sigs = d['signatures']
 			self.sigs = d['signatures']
-			self.addrlists = [MMGenIDRange(self.proto,i) for i in self.data['addrlists']]
+			self.addrlists = [MMGenIDRange(self.proto, i) for i in self.data['addrlists']]
 
 
-		def format(self,req_addr=None):
+		def format(self, req_addr=None):
 
 
 			labels = {
 			labels = {
 				'addr':       'address:',
 				'addr':       'address:',
@@ -152,37 +152,37 @@ class coin_msg:
 			def gen_entry(e):
 			def gen_entry(e):
 				for k in labels:
 				for k in labels:
 					if e.get(k):
 					if e.get(k):
-						yield fs_sig.format( labels[k], e[k] )
+						yield fs_sig.format(labels[k], e[k])
 
 
 			def gen_all():
 			def gen_all():
-				for k,v in hdr_data.items():
-					yield fs_hdr.format( v[0], v[1](self.data[k]) )
+				for k, v in hdr_data.items():
+					yield fs_hdr.format(v[0], v[1](self.data[k]))
 				if self.sigs:
 				if self.sigs:
 					yield ''
 					yield ''
 					yield 'Signatures:'
 					yield 'Signatures:'
-					for n,(k,v) in enumerate(self.sigs.items()):
+					for n, (k, v) in enumerate(self.sigs.items()):
 						yield ''
 						yield ''
 						yield f'{n+1:>3}) {k}'
 						yield f'{n+1:>3}) {k}'
 						yield from gen_entry(v)
 						yield from gen_entry(v)
 
 
 			def gen_single():
 			def gen_single():
-				for k,v in hdr_data.items():
-					yield fs_hdr.format( v[0], v[1](self.data[k]) )
+				for k, v in hdr_data.items():
+					yield fs_hdr.format(v[0], v[1](self.data[k]))
 				if self.sigs:
 				if self.sigs:
 					yield 'Signature data:'
 					yield 'Signature data:'
 					k = (
 					k = (
-						CoinAddr(self.proto,req_addr) if type(self).__name__ == 'exported_sigs' else
-						MMGenID(self.proto,req_addr) )
+						CoinAddr(self.proto, req_addr) if type(self).__name__ == 'exported_sigs' else
+						MMGenID(self.proto, req_addr))
 					if k not in self.sigs:
 					if k not in self.sigs:
-						die(1,f'{k}: address not found in signature data')
+						die(1, f'{k}: address not found in signature data')
 					yield from gen_entry(self.sigs[k])
 					yield from gen_entry(self.sigs[k])
 
 
 			hdr_data = {
 			hdr_data = {
-				'message':      ('Message:',           grnbg ),
-				'network':      ('Network:',           lambda v: v.replace('_',' ').upper() ),
-				'msghash_type': ('Message Hash Type:', lambda v: v ),
-				'addrlists':    ('Address Ranges:',    lambda v: fmt_list(v,fmt='bare') ),
-				'failed_sids':  ('Failed Seed IDs:',   lambda v: red(fmt_list(v,fmt='bare')) ),
+				'message':      ('Message:',           grnbg),
+				'network':      ('Network:',           lambda v: v.replace('_', ' ').upper()),
+				'msghash_type': ('Message Hash Type:', lambda v: v),
+				'addrlists':    ('Address Ranges:',    lambda v: fmt_list(v, fmt='bare')),
+				'failed_sids':  ('Failed Seed IDs:',   lambda v: red(fmt_list(v, fmt='bare'))),
 			}
 			}
 
 
 			if len(self.msg_cls.msghash_types) == 1:
 			if len(self.msg_cls.msghash_types) == 1:
@@ -212,7 +212,7 @@ class coin_msg:
 
 
 			from .addrlist import KeyAddrList
 			from .addrlist import KeyAddrList
 
 
-			async def sign_list(al_in,seed):
+			async def sign_list(al_in, seed):
 				al = KeyAddrList(
 				al = KeyAddrList(
 					cfg         = self.cfg,
 					cfg         = self.cfg,
 					proto       = self.proto,
 					proto       = self.proto,
@@ -220,13 +220,13 @@ class coin_msg:
 					addr_idxs   = al_in.idxlist,
 					addr_idxs   = al_in.idxlist,
 					mmtype      = al_in.mmtype,
 					mmtype      = al_in.mmtype,
 					skip_chksum = True,
 					skip_chksum = True,
-					add_p2pkh   = al_in.mmtype in ('S','B') )
+					add_p2pkh   = al_in.mmtype in ('S', 'B'))
 
 
 				for e in al.data:
 				for e in al.data:
 					sig = await self.do_sign(
 					sig = await self.do_sign(
 						wif     = e.sec.wif,
 						wif     = e.sec.wif,
 						message = self.data['message'],
 						message = self.data['message'],
-						msghash_type = self.data['msghash_type'] )
+						msghash_type = self.data['msghash_type'])
 
 
 					mmid = f'{al_in.sid}:{al_in.mmtype}:{e.idx}'
 					mmid = f'{al_in.sid}:{al_in.mmtype}:{e.idx}'
 					data = {
 					data = {
@@ -234,7 +234,7 @@ class coin_msg:
 						'sig': sig,
 						'sig': sig,
 					}
 					}
 					if self.msg_cls.include_pubhash:
 					if self.msg_cls.include_pubhash:
-						data.update({ 'pubhash': self.proto.decode_addr(e.addr_p2pkh or e.addr).bytes.hex() })
+						data.update({'pubhash': self.proto.decode_addr(e.addr_p2pkh or e.addr).bytes.hex()})
 
 
 					if e.addr_p2pkh:
 					if e.addr_p2pkh:
 						data.update({'addr_p2pkh': e.addr_p2pkh})
 						data.update({'addr_p2pkh': e.addr_p2pkh})
@@ -243,7 +243,7 @@ class coin_msg:
 
 
 			if self.proto.sign_mode == 'daemon':
 			if self.proto.sign_mode == 'daemon':
 				from .rpc import rpc_init
 				from .rpc import rpc_init
-				self.rpc = await rpc_init( self.cfg, self.proto, ignore_wallet=True )
+				self.rpc = await rpc_init(self.cfg, self.proto, ignore_wallet=True)
 
 
 			from .wallet import Wallet
 			from .wallet import Wallet
 			wallet_seeds = [Wallet(cfg=self.cfg, fn=fn, passwd_file=passwd_file).seed for fn in wallet_files]
 			wallet_seeds = [Wallet(cfg=self.cfg, fn=fn, passwd_file=passwd_file).seed for fn in wallet_files]
@@ -261,7 +261,7 @@ class coin_msg:
 			# Then subseeds:
 			# Then subseeds:
 			for sid in need_sids:
 			for sid in need_sids:
 				for seed in wallet_seeds:
 				for seed in wallet_seeds:
-					subseed = seed.subseeds.get_subseed_by_seed_id(sid,print_msg=True)
+					subseed = seed.subseeds.get_subseed_by_seed_id(sid, print_msg=True)
 					if subseed:
 					if subseed:
 						saved_seeds.append(subseed)
 						saved_seeds.append(subseed)
 						need_sids.remove(sid)
 						need_sids.remove(sid)
@@ -270,11 +270,11 @@ class coin_msg:
 			for al in self.addrlists:
 			for al in self.addrlists:
 				for seed in saved_seeds:
 				for seed in saved_seeds:
 					if al.sid == seed.sid:
 					if al.sid == seed.sid:
-						await sign_list(al,seed)
+						await sign_list(al, seed)
 						break
 						break
 
 
 			if need_sids:
 			if need_sids:
-				msg('Failed Seed IDs: {}'.format(orange(fmt_list(need_sids,fmt='bare'))))
+				msg('Failed Seed IDs: {}'.format(orange(fmt_list(need_sids, fmt='bare'))))
 
 
 			self.data['failed_sids'] = need_sids
 			self.data['failed_sids'] = need_sids
 
 
@@ -285,45 +285,45 @@ class coin_msg:
 
 
 	class signed_online(signed):
 	class signed_online(signed):
 
 
-		def get_sigs(self,addr):
+		def get_sigs(self, addr):
 
 
 			if addr:
 			if addr:
 				req_addr = (
 				req_addr = (
-					CoinAddr(self.proto,addr) if type(self).__name__ == 'exported_sigs' else
-					MMGenID(self.proto,addr) )
-				sigs = {k:v for k,v in self.sigs.items() if k == req_addr}
+					CoinAddr(self.proto, addr) if type(self).__name__ == 'exported_sigs' else
+					MMGenID(self.proto, addr))
+				sigs = {k:v for k, v in self.sigs.items() if k == req_addr}
 			else:
 			else:
 				sigs = self.sigs
 				sigs = self.sigs
 
 
 			if not sigs:
 			if not sigs:
-				die(1,'No signatures')
+				die(1, 'No signatures')
 
 
 			return sigs
 			return sigs
 
 
-		async def verify(self,addr=None):
+		async def verify(self, addr=None):
 
 
 			sigs = self.get_sigs(addr)
 			sigs = self.get_sigs(addr)
 
 
 			if self.proto.sign_mode == 'daemon':
 			if self.proto.sign_mode == 'daemon':
 				from .rpc import rpc_init
 				from .rpc import rpc_init
-				self.rpc = await rpc_init( self.cfg, self.proto, ignore_wallet=True )
+				self.rpc = await rpc_init(self.cfg, self.proto, ignore_wallet=True)
 
 
-			for k,v in sigs.items():
+			for k, v in sigs.items():
 				ret = await self.do_verify(
 				ret = await self.do_verify(
 					addr    = v.get('addr_p2pkh') or v['addr'],
 					addr    = v.get('addr_p2pkh') or v['addr'],
 					sig     = v['sig'],
 					sig     = v['sig'],
 					message = self.data['message'],
 					message = self.data['message'],
-					msghash_type = self.data['msghash_type'] )
+					msghash_type = self.data['msghash_type'])
 				if not ret:
 				if not ret:
-					die(3,f'Invalid signature for address {k} ({v["addr"]})')
+					die(3, f'Invalid signature for address {k} ({v["addr"]})')
 
 
 			return len(sigs)
 			return len(sigs)
 
 
-		def get_json_for_export(self,addr=None):
-			sigs = list( self.get_sigs(addr).values() )
+		def get_json_for_export(self, addr=None):
+			sigs = list(self.get_sigs(addr).values())
 			pfx = self.msg_cls.sigdata_pfx
 			pfx = self.msg_cls.sigdata_pfx
 			if pfx:
 			if pfx:
-				sigs = [{k:pfx+v for k,v in e.items()} for e in sigs]
+				sigs = [{k:pfx+v for k, v in e.items()} for e in sigs]
 			return json.dumps(
 			return json.dumps(
 				{
 				{
 					'message': self.data['message'],
 					'message': self.data['message'],
@@ -337,23 +337,23 @@ class coin_msg:
 
 
 	class exported_sigs(signed_online):
 	class exported_sigs(signed_online):
 
 
-		def __init__(self,infile,*args,**kwargs):
+		def __init__(self, infile, *args, **kwargs):
 
 
 			self.data = json.loads(
 			self.data = json.loads(
 				get_data_from_file(
 				get_data_from_file(
 					cfg    = self.cfg,
 					cfg    = self.cfg,
 					infile = infile,
 					infile = infile,
-					desc   = self.desc )
+					desc   = self.desc)
 				)
 				)
 
 
 			pfx = self.msg_cls.sigdata_pfx
 			pfx = self.msg_cls.sigdata_pfx
 			self.sigs = {sig_data['addr']:sig_data for sig_data in (
 			self.sigs = {sig_data['addr']:sig_data for sig_data in (
-				[{k:v[len(pfx):] for k,v in e.items()} for e in self.data['signatures']]
+				[{k:v[len(pfx):] for k, v in e.items()} for e in self.data['signatures']]
 					if pfx else
 					if pfx else
 				self.data['signatures']
 				self.data['signatures']
 			)}
 			)}
 
 
-def _get_obj(clsname,cfg,*args,coin=None,network='mainnet',infile=None,data=None,**kwargs):
+def _get_obj(clsname, cfg, *args, coin=None, network='mainnet', infile=None, data=None, **kwargs):
 
 
 	assert not args, 'msg:_get_obj(): only keyword args allowed'
 	assert not args, 'msg:_get_obj(): only keyword args allowed'
 
 
@@ -364,27 +364,27 @@ def _get_obj(clsname,cfg,*args,coin=None,network='mainnet',infile=None,data=None
 
 
 	proto = (
 	proto = (
 		data['proto'] if data else
 		data['proto'] if data else
-		init_proto( cfg=cfg, coin=coin, network=network ) if coin else
-		coin_msg.base.get_proto_from_file(cfg,infile) )
+		init_proto(cfg=cfg, coin=coin, network=network) if coin else
+		coin_msg.base.get_proto_from_file(cfg, infile))
 
 
 	try:
 	try:
 		msg_cls = getattr(
 		msg_cls = getattr(
 			importlib.import_module(f'mmgen.proto.{proto.base_proto_coin.lower()}.msg'),
 			importlib.import_module(f'mmgen.proto.{proto.base_proto_coin.lower()}.msg'),
-			'coin_msg' )
+			'coin_msg')
 	except:
 	except:
-		die(1,f'Message signing operations not supported for {proto.base_proto} protocol')
+		die(1, f'Message signing operations not supported for {proto.base_proto} protocol')
 
 
-	me = MMGenObject.__new__(getattr( msg_cls, clsname, getattr(coin_msg,clsname) ))
+	me = MMGenObject.__new__(getattr(msg_cls, clsname, getattr(coin_msg, clsname)))
 	me.msg_cls = msg_cls
 	me.msg_cls = msg_cls
 	me.cfg = cfg
 	me.cfg = cfg
 	me.proto = proto
 	me.proto = proto
 
 
-	me.__init__(infile=infile,data=data,*args,**kwargs)
+	me.__init__(infile=infile, data=data, *args, **kwargs)
 
 
 	return me
 	return me
 
 
 def _get(clsname):
 def _get(clsname):
-	return lambda *args,**kwargs: _get_obj(clsname,*args,**kwargs)
+	return lambda *args, **kwargs: _get_obj(clsname, *args, **kwargs)
 
 
 NewMsg          = _get('new')
 NewMsg          = _get('new')
 CompletedMsg    = _get('completed')
 CompletedMsg    = _get('completed')

+ 86 - 86
mmgen/obj.py

@@ -22,9 +22,9 @@ obj: MMGen native classes
 
 
 import unicodedata
 import unicodedata
 
 
-from .objmethods import MMGenObject,Hilite,HiliteStr,InitErrors
+from .objmethods import MMGenObject, Hilite, HiliteStr, InitErrors
 
 
-def get_obj(objname,*args,**kwargs):
+def get_obj(objname, *args, **kwargs):
 	"""
 	"""
 	Wrapper for data objects
 	Wrapper for data objects
 	- If the object throws an exception on instantiation, return False, otherwise return the object.
 	- If the object throws an exception on instantiation, return False, otherwise return the object.
@@ -34,7 +34,7 @@ def get_obj(objname,*args,**kwargs):
 	"""
 	"""
 	assert not args, 'get_obj_chk1'
 	assert not args, 'get_obj_chk1'
 
 
-	silent,return_bool = (False,False)
+	silent, return_bool = (False, False)
 	if 'silent' in kwargs:
 	if 'silent' in kwargs:
 		silent = kwargs['silent']
 		silent = kwargs['silent']
 		del kwargs['silent']
 		del kwargs['silent']
@@ -55,44 +55,44 @@ def get_obj(objname,*args,**kwargs):
 # dict that keeps a list of keys for efficient lookup by index
 # dict that keeps a list of keys for efficient lookup by index
 class IndexedDict(dict):
 class IndexedDict(dict):
 
 
-	def __init__(self,*args,**kwargs):
+	def __init__(self, *args, **kwargs):
 		if args or kwargs:
 		if args or kwargs:
 			self.die('initializing values via constructor')
 			self.die('initializing values via constructor')
 		self.__keylist = []
 		self.__keylist = []
-		dict.__init__(self,*args,**kwargs)
+		dict.__init__(self, *args, **kwargs)
 
 
-	def __setitem__(self,key,value):
+	def __setitem__(self, key, value):
 		if key in self:
 		if key in self:
 			self.die('reassignment to existing key')
 			self.die('reassignment to existing key')
 		self.__keylist.append(key)
 		self.__keylist.append(key)
-		return dict.__setitem__(self,key,value)
+		return dict.__setitem__(self, key, value)
 
 
 	@property
 	@property
 	def keys(self):
 	def keys(self):
 		return self.__keylist
 		return self.__keylist
 
 
-	def key(self,idx):
+	def key(self, idx):
 		return self.__keylist[idx]
 		return self.__keylist[idx]
 
 
-	def __delitem__(self,*args):
+	def __delitem__(self, *args):
 		self.die('item deletion')
 		self.die('item deletion')
 
 
-	def move_to_end(self,*args):
+	def move_to_end(self, *args):
 		self.die('item moving')
 		self.die('item moving')
 
 
-	def clear(self,*args):
+	def clear(self, *args):
 		self.die('clearing')
 		self.die('clearing')
 
 
-	def update(self,*args):
+	def update(self, *args):
 		self.die('updating')
 		self.die('updating')
 
 
-	def die(self,desc):
+	def die(self, desc):
 		raise NotImplementedError(f'{desc} not implemented for type {type(self).__name__}')
 		raise NotImplementedError(f'{desc} not implemented for type {type(self).__name__}')
 
 
-class MMGenList(list,MMGenObject):
+class MMGenList(list, MMGenObject):
 	pass
 	pass
 
 
-class MMGenDict(dict,MMGenObject):
+class MMGenDict(dict, MMGenObject):
 	pass
 	pass
 
 
 class ImmutableAttr: # Descriptor
 class ImmutableAttr: # Descriptor
@@ -100,53 +100,53 @@ class ImmutableAttr: # Descriptor
 	For attributes that are always present in the data instance
 	For attributes that are always present in the data instance
 	Reassignment and deletion forbidden
 	Reassignment and deletion forbidden
 	"""
 	"""
-	ok_dtypes = (type,type(None),type(lambda:0))
+	ok_dtypes = (type, type(None), type(lambda:0))
 
 
-	def __init__(self,dtype,typeconv=True,set_none_ok=False,include_proto=False):
+	def __init__(self, dtype, typeconv=True, set_none_ok=False, include_proto=False):
 		self.set_none_ok = set_none_ok
 		self.set_none_ok = set_none_ok
 		self.typeconv = typeconv
 		self.typeconv = typeconv
 
 
-		assert isinstance(dtype,self.ok_dtypes), 'ImmutableAttr_check1'
+		assert isinstance(dtype, self.ok_dtypes), 'ImmutableAttr_check1'
 		if include_proto:
 		if include_proto:
 			assert typeconv, 'ImmutableAttr_check2'
 			assert typeconv, 'ImmutableAttr_check2'
 		if set_none_ok:
 		if set_none_ok:
-			assert typeconv and not isinstance(dtype,str), 'ImmutableAttr_check3'
+			assert typeconv and not isinstance(dtype, str), 'ImmutableAttr_check3'
 
 
 		if typeconv:
 		if typeconv:
 			# convert this attribute's type
 			# convert this attribute's type
 			if set_none_ok:
 			if set_none_ok:
-				self.conv = lambda instance,value: None if value is None else dtype(value)
+				self.conv = lambda instance, value: None if value is None else dtype(value)
 			elif include_proto:
 			elif include_proto:
-				self.conv = lambda instance,value: dtype(instance.proto,value)
+				self.conv = lambda instance, value: dtype(instance.proto, value)
 			else:
 			else:
-				self.conv = lambda instance,value: dtype(value)
+				self.conv = lambda instance, value: dtype(value)
 		else:
 		else:
 			# check this attribute's type
 			# check this attribute's type
-			def assign_with_check(instance,value):
+			def assign_with_check(instance, value):
 				if type(value) is dtype:
 				if type(value) is dtype:
 					return value
 					return value
 				raise TypeError('Attribute {!r} of {} instance must of type {}'.format(
 				raise TypeError('Attribute {!r} of {} instance must of type {}'.format(
 					self.name,
 					self.name,
 					type(instance).__name__,
 					type(instance).__name__,
-					dtype ))
+					dtype))
 			self.conv = assign_with_check
 			self.conv = assign_with_check
 
 
-	def __set_name__(self,owner,name):
+	def __set_name__(self, owner, name):
 		self.name = name
 		self.name = name
 
 
-	def __get__(self,instance,owner):
+	def __get__(self, instance, owner):
 		return instance.__dict__[self.name]
 		return instance.__dict__[self.name]
 
 
-	def setattr_condition(self,instance):
+	def setattr_condition(self, instance):
 		'forbid all reassignment'
 		'forbid all reassignment'
 		return not self.name in instance.__dict__
 		return not self.name in instance.__dict__
 
 
-	def __set__(self,instance,value):
+	def __set__(self, instance, value):
 		if not self.setattr_condition(instance):
 		if not self.setattr_condition(instance):
 			raise AttributeError(f'Attribute {self.name!r} of {type(instance)} instance cannot be reassigned')
 			raise AttributeError(f'Attribute {self.name!r} of {type(instance)} instance cannot be reassigned')
-		instance.__dict__[self.name] = self.conv(instance,value)
+		instance.__dict__[self.name] = self.conv(instance, value)
 
 
-	def __delete__(self,instance):
+	def __delete__(self, instance):
 		raise AttributeError(
 		raise AttributeError(
 			f'Attribute {self.name!r} of {type(instance).__name__} instance cannot be deleted')
 			f'Attribute {self.name!r} of {type(instance).__name__} instance cannot be deleted')
 
 
@@ -155,27 +155,27 @@ class ListItemAttr(ImmutableAttr):
 	For attributes that might not be present in the data instance
 	For attributes that might not be present in the data instance
 	Reassignment or deletion allowed if specified
 	Reassignment or deletion allowed if specified
 	"""
 	"""
-	def __init__(self,dtype,typeconv=True,include_proto=False,reassign_ok=False,delete_ok=False):
+	def __init__(self, dtype, typeconv=True, include_proto=False, reassign_ok=False, delete_ok=False):
 		self.reassign_ok = reassign_ok
 		self.reassign_ok = reassign_ok
 		self.delete_ok = delete_ok
 		self.delete_ok = delete_ok
-		ImmutableAttr.__init__(self,dtype,typeconv=typeconv,include_proto=include_proto)
+		ImmutableAttr.__init__(self, dtype, typeconv=typeconv, include_proto=include_proto)
 
 
-	def __get__(self,instance,owner):
+	def __get__(self, instance, owner):
 		"return None if attribute doesn't exist"
 		"return None if attribute doesn't exist"
 		try:
 		try:
 			return instance.__dict__[self.name]
 			return instance.__dict__[self.name]
 		except:
 		except:
 			return None
 			return None
 
 
-	def setattr_condition(self,instance):
-		return getattr(instance,self.name) is None or self.reassign_ok
+	def setattr_condition(self, instance):
+		return getattr(instance, self.name) is None or self.reassign_ok
 
 
-	def __delete__(self,instance):
+	def __delete__(self, instance):
 		if self.delete_ok:
 		if self.delete_ok:
 			if self.name in instance.__dict__:
 			if self.name in instance.__dict__:
 				del instance.__dict__[self.name]
 				del instance.__dict__[self.name]
 		else:
 		else:
-			ImmutableAttr.__delete__(self,instance)
+			ImmutableAttr.__delete__(self, instance)
 
 
 class MMGenListItem(MMGenObject):
 class MMGenListItem(MMGenObject):
 	valid_attrs = set()
 	valid_attrs = set()
@@ -189,7 +189,7 @@ class MMGenListItem(MMGenObject):
 		'immutable_attr_init_check',
 		'immutable_attr_init_check',
 	}
 	}
 
 
-	def __init__(self,*args,**kwargs):
+	def __init__(self, *args, **kwargs):
 		# generate valid_attrs, or use the class valid_attrs if set
 		# generate valid_attrs, or use the class valid_attrs if set
 		self.__dict__['valid_attrs'] = self.valid_attrs or (
 		self.__dict__['valid_attrs'] = self.valid_attrs or (
 				{e for e in dir(self) if e[0] != '_'}
 				{e for e in dir(self) if e[0] != '_'}
@@ -200,49 +200,49 @@ class MMGenListItem(MMGenObject):
 		if args:
 		if args:
 			raise ValueError(f'Non-keyword args not allowed in {type(self).__name__!r} constructor')
 			raise ValueError(f'Non-keyword args not allowed in {type(self).__name__!r} constructor')
 
 
-		for k,v in kwargs.items():
+		for k, v in kwargs.items():
 			if v is not None:
 			if v is not None:
-				setattr(self,k,v)
+				setattr(self, k, v)
 
 
 		# Require all immutables to be initialized.  Check performed only when testing.
 		# Require all immutables to be initialized.  Check performed only when testing.
 		self.immutable_attr_init_check()
 		self.immutable_attr_init_check()
 
 
 	# allow only valid attributes to be set
 	# allow only valid attributes to be set
-	def __setattr__(self,name,value):
+	def __setattr__(self, name, value):
 		if name not in self.valid_attrs:
 		if name not in self.valid_attrs:
 			raise AttributeError(f'{name!r}: no such attribute in class {type(self)}')
 			raise AttributeError(f'{name!r}: no such attribute in class {type(self)}')
-		return object.__setattr__(self,name,value)
+		return object.__setattr__(self, name, value)
 
 
 	def _asdict(self):
 	def _asdict(self):
-		return dict((k,v) for k,v in self.__dict__.items() if k in self.valid_attrs)
+		return dict((k, v) for k, v in self.__dict__.items() if k in self.valid_attrs)
 
 
-class MMGenRange(tuple,InitErrors,MMGenObject):
+class MMGenRange(tuple, InitErrors, MMGenObject):
 
 
 	min_idx = None
 	min_idx = None
 	max_idx = None
 	max_idx = None
 
 
-	def __new__(cls,*args):
+	def __new__(cls, *args):
 		try:
 		try:
 			if len(args) == 1:
 			if len(args) == 1:
 				s = args[0]
 				s = args[0]
-				if isinstance(s,cls):
+				if isinstance(s, cls):
 					return s
 					return s
-				assert isinstance(s,str),'not a string or string subclass'
-				ss = s.split('-',1)
+				assert isinstance(s, str), 'not a string or string subclass'
+				ss = s.split('-', 1)
 				first = int(ss[0])
 				first = int(ss[0])
 				last = int(ss.pop())
 				last = int(ss.pop())
 			else:
 			else:
 				s = repr(args) # needed if exception occurs
 				s = repr(args) # needed if exception occurs
-				assert len(args) == 2,'one format string arg or two start,stop args required'
-				first,last = args
+				assert len(args) == 2, 'one format string arg or two start, stop args required'
+				first, last = args
 			assert first <= last, 'start of range greater than end of range'
 			assert first <= last, 'start of range greater than end of range'
 			if cls.min_idx is not None:
 			if cls.min_idx is not None:
 				assert first >= cls.min_idx, f'start of range < {cls.min_idx:,}'
 				assert first >= cls.min_idx, f'start of range < {cls.min_idx:,}'
 			if cls.max_idx is not None:
 			if cls.max_idx is not None:
 				assert last <= cls.max_idx, f'end of range > {cls.max_idx:,}'
 				assert last <= cls.max_idx, f'end of range > {cls.max_idx:,}'
-			return tuple.__new__(cls,(first,last))
+			return tuple.__new__(cls, (first, last))
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,s)
+			return cls.init_fail(e, s)
 
 
 	@property
 	@property
 	def first(self):
 	def first(self):
@@ -253,23 +253,23 @@ class MMGenRange(tuple,InitErrors,MMGenObject):
 		return self[1]
 		return self[1]
 
 
 	def iterate(self):
 	def iterate(self):
-		return range(self[0],self[1]+1)
+		return range(self[0], self[1]+1)
 
 
 	@property
 	@property
 	def items(self):
 	def items(self):
 		return list(self.iterate())
 		return list(self.iterate())
 
 
-class Int(int,Hilite,InitErrors):
+class Int(int, Hilite, InitErrors):
 	min_val = None
 	min_val = None
 	max_val = None
 	max_val = None
 	max_digits = None
 	max_digits = None
 	color = 'red'
 	color = 'red'
 
 
-	def __new__(cls,n,base=10):
-		if isinstance(n,cls):
+	def __new__(cls, n, base=10):
+		if isinstance(n, cls):
 			return n
 			return n
 		try:
 		try:
-			me = int.__new__(cls,str(n),base)
+			me = int.__new__(cls, str(n), base)
 			if cls.min_val is not None:
 			if cls.min_val is not None:
 				assert me >= cls.min_val, f'is less than cls.min_val ({cls.min_val})'
 				assert me >= cls.min_val, f'is less than cls.min_val ({cls.min_val})'
 			if cls.max_val is not None:
 			if cls.max_val is not None:
@@ -278,16 +278,16 @@ class Int(int,Hilite,InitErrors):
 				assert len(str(me)) <= cls.max_digits, f'has more than {cls.max_digits} digits'
 				assert len(str(me)) <= cls.max_digits, f'has more than {cls.max_digits} digits'
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,n)
+			return cls.init_fail(e, n)
 
 
 	@classmethod
 	@classmethod
-	def fmtc(cls,s,width,color=False):
+	def fmtc(cls, s, width, color=False):
 		return super().fmtc(str(s), width=width, color=color)
 		return super().fmtc(str(s), width=width, color=color)
 
 
-	def fmt(self,width,color=False):
+	def fmt(self, width, color=False):
 		return super().fmtc(str(self), width=width, color=color)
 		return super().fmtc(str(self), width=width, color=color)
 
 
-	def hl(self,**kwargs):
+	def hl(self, **kwargs):
 		return super().colorize(str(self), **kwargs)
 		return super().colorize(str(self), **kwargs)
 
 
 class NonNegativeInt(Int):
 class NonNegativeInt(Int):
@@ -302,44 +302,44 @@ class ETHNonce(Int):
 class Str(HiliteStr):
 class Str(HiliteStr):
 	pass
 	pass
 
 
-class HexStr(HiliteStr,InitErrors):
+class HexStr(HiliteStr, InitErrors):
 	color = 'red'
 	color = 'red'
 	width = None
 	width = None
 	hexcase = 'lower'
 	hexcase = 'lower'
 	trunc_ok = False
 	trunc_ok = False
-	def __new__(cls,s,case=None):
-		if isinstance(s,cls):
+	def __new__(cls, s, case=None):
+		if isinstance(s, cls):
 			return s
 			return s
 		if case is None:
 		if case is None:
 			case = cls.hexcase
 			case = cls.hexcase
-		from .util import hexdigits_lc,hexdigits_uc
+		from .util import hexdigits_lc, hexdigits_uc
 		try:
 		try:
-			assert isinstance(s,str),'not a string or string subclass'
-			assert case in ('upper','lower'), f'{case!r} incorrect case specifier'
+			assert isinstance(s, str), 'not a string or string subclass'
+			assert case in ('upper', 'lower'), f'{case!r} incorrect case specifier'
 			assert set(s) <= set(hexdigits_lc if case == 'lower' else hexdigits_uc), (
 			assert set(s) <= set(hexdigits_lc if case == 'lower' else hexdigits_uc), (
-				f'not {case}case hexadecimal symbols' )
-			assert not len(s) % 2,'odd-length string'
+				f'not {case}case hexadecimal symbols')
+			assert not len(s) % 2, 'odd-length string'
 			if cls.width:
 			if cls.width:
 				assert len(s) == cls.width, f'Value is not {cls.width} characters wide'
 				assert len(s) == cls.width, f'Value is not {cls.width} characters wide'
-			return str.__new__(cls,s)
+			return str.__new__(cls, s)
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,s)
+			return cls.init_fail(e, s)
 
 
-	def truncate(self,width,color=True):
+	def truncate(self, width, color=True):
 		return self.colorize(
 		return self.colorize(
 			self if width >= self.width else self[:width-2] + '..',
 			self if width >= self.width else self[:width-2] + '..',
-			color = color )
+			color = color)
 
 
 class CoinTxID(HexStr):
 class CoinTxID(HexStr):
-	color,width,hexcase = ('purple',64,'lower')
+	color, width, hexcase = ('purple', 64, 'lower')
 
 
 class WalletPassword(HexStr):
 class WalletPassword(HexStr):
-	color,width,hexcase = ('blue',32,'lower')
+	color, width, hexcase = ('blue', 32, 'lower')
 
 
 class MMGenTxID(HexStr):
 class MMGenTxID(HexStr):
-	color,width,hexcase = ('red',6,'upper')
+	color, width, hexcase = ('red', 6, 'upper')
 
 
-class MMGenLabel(HiliteStr,InitErrors):
+class MMGenLabel(HiliteStr, InitErrors):
 	color = 'pink'
 	color = 'pink'
 	allowed = []
 	allowed = []
 	forbidden = []
 	forbidden = []
@@ -348,26 +348,26 @@ class MMGenLabel(HiliteStr,InitErrors):
 	min_len = 0
 	min_len = 0
 	max_screen_width = 0 # if != 0, overrides max_len
 	max_screen_width = 0 # if != 0, overrides max_len
 	desc = 'label'
 	desc = 'label'
-	def __new__(cls,s):
-		if isinstance(s,cls):
+	def __new__(cls, s):
+		if isinstance(s, cls):
 			return s
 			return s
 		try:
 		try:
 			s = s.strip()
 			s = s.strip()
 			if not cls.allowed:
 			if not cls.allowed:
 				for ch in s:
 				for ch in s:
-					# Allow:    (L)etter,(N)umber,(P)unctuation,(S)ymbol,(Z)space
-					# Disallow: (C)ontrol,(M)combining
+					# Allow:    (L)etter, (N)umber, (P)unctuation, (S)ymbol, (Z)space
+					# Disallow: (C)ontrol, (M)combining
 					# Combining characters create width formatting issues, so disallow them for now
 					# Combining characters create width formatting issues, so disallow them for now
-					if unicodedata.category(ch)[0] in ('C','M'):
+					if unicodedata.category(ch)[0] in ('C', 'M'):
 						raise ValueError(
 						raise ValueError(
 							'{!a}: {} characters not allowed'.format(
 							'{!a}: {} characters not allowed'.format(
 								ch,
 								ch,
-								{ 'C':'control', 'M':'combining' }[unicodedata.category(ch)[0]] ))
+								{'C':'control', 'M':'combining'}[unicodedata.category(ch)[0]]))
 
 
-			me = str.__new__(cls,s)
+			me = str.__new__(cls, s)
 
 
 			if cls.max_screen_width:
 			if cls.max_screen_width:
-				me.screen_width = len(s) + len([1 for ch in s if unicodedata.east_asian_width(ch) in ('F','W')])
+				me.screen_width = len(s) + len([1 for ch in s if unicodedata.east_asian_width(ch) in ('F', 'W')])
 				assert me.screen_width <= cls.max_screen_width, f'too wide (>{cls.max_screen_width} screen width)'
 				assert me.screen_width <= cls.max_screen_width, f'too wide (>{cls.max_screen_width} screen width)'
 			else:
 			else:
 				assert len(s) <= cls.max_len, f'too long (>{cls.max_len} symbols)'
 				assert len(s) <= cls.max_len, f'too long (>{cls.max_len} symbols)'
@@ -385,7 +385,7 @@ class MMGenLabel(HiliteStr,InitErrors):
 
 
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,s)
+			return cls.init_fail(e, s)
 
 
 class MMGenWalletLabel(MMGenLabel):
 class MMGenWalletLabel(MMGenLabel):
 	max_len = 48
 	max_len = 48

+ 36 - 36
mmgen/objmethods.py

@@ -31,13 +31,13 @@ else:
 		def immutable_attr_init_check(self):
 		def immutable_attr_init_check(self):
 			pass
 			pass
 
 
-def truncate_str(s,width): # width = screen width
+def truncate_str(s, width): # width = screen width
 	wide_count = 0
 	wide_count = 0
-	for n,ch in enumerate(s,1):
-		wide_count += unicodedata.east_asian_width(ch) in ('F','W')
+	for n, ch in enumerate(s, 1):
+		wide_count += unicodedata.east_asian_width(ch) in ('F', 'W')
 		if n + wide_count > width:
 		if n + wide_count > width:
-			return s[:n-1] + ('',' ')[
-				unicodedata.east_asian_width(ch) in ('F','W')
+			return s[:n-1] + ('', ' ')[
+				unicodedata.east_asian_width(ch) in ('F', 'W')
 				and n + wide_count == width + 1]
 				and n + wide_count == width + 1]
 	raise ValueError('string requires no truncating')
 	raise ValueError('string requires no truncating')
 
 
@@ -49,34 +49,34 @@ class Hilite:
 
 
 	# class method equivalent of fmt()
 	# class method equivalent of fmt()
 	@classmethod
 	@classmethod
-	def fmtc( cls, s, width, color=False ):
+	def fmtc(cls, s, width, color=False):
 		if len(s) > width:
 		if len(s) > width:
 			assert cls.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= width of string"
 			assert cls.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= width of string"
-			return cls.colorize( s[:width].ljust(width), color=color )
+			return cls.colorize(s[:width].ljust(width), color=color)
 		else:
 		else:
-			return cls.colorize( s.ljust(width), color=color )
+			return cls.colorize(s.ljust(width), color=color)
 
 
 	@classmethod
 	@classmethod
-	def hlc(cls,s,color=True):
-		return getattr( color_mod, cls.color )(s) if color else s
+	def hlc(cls, s, color=True):
+		return getattr(color_mod, cls.color)(s) if color else s
 
 
 	@classmethod
 	@classmethod
-	def colorize(cls,s,color=True):
-		return getattr( color_mod, cls.color )(s) if color else s
+	def colorize(cls, s, color=True):
+		return getattr(color_mod, cls.color)(s) if color else s
 
 
 	@classmethod
 	@classmethod
-	def colorize2(cls,s,color=True,color_override=''):
-		return getattr( color_mod, color_override or cls.color )(s) if color else s
+	def colorize2(cls, s, color=True, color_override=''):
+		return getattr(color_mod, color_override or cls.color)(s) if color else s
 
 
-class HiliteStr(str,Hilite):
+class HiliteStr(str, Hilite):
 
 
 	# supports single-width characters only
 	# supports single-width characters only
-	def fmt( self, width, color=False ):
+	def fmt(self, width, color=False):
 		if len(self) > width:
 		if len(self) > width:
 			assert self.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= width of string"
 			assert self.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= width of string"
-			return self.colorize( self[:width].ljust(width), color=color )
+			return self.colorize(self[:width].ljust(width), color=color)
 		else:
 		else:
-			return self.colorize( self.ljust(width), color=color )
+			return self.colorize(self.ljust(width), color=color)
 
 
 	# an alternative to fmt(), with double-width char support and other features
 	# an alternative to fmt(), with double-width char support and other features
 	def fmt2(
 	def fmt2(
@@ -87,46 +87,46 @@ class HiliteStr(str,Hilite):
 			nullrepl       = '',
 			nullrepl       = '',
 			append_chars   = '',    # single-width chars only
 			append_chars   = '',    # single-width chars only
 			append_color   = False,
 			append_color   = False,
-			color_override = '' ):
+			color_override = ''):
 
 
 		if self == '':
 		if self == '':
-			return getattr( color_mod, self.color )(nullrepl.ljust(width)) if color else nullrepl.ljust(width)
+			return getattr(color_mod, self.color)(nullrepl.ljust(width)) if color else nullrepl.ljust(width)
 
 
-		s_wide_count = len(['' for ch in self if unicodedata.east_asian_width(ch) in ('F','W')])
+		s_wide_count = len(['' for ch in self if unicodedata.east_asian_width(ch) in ('F', 'W')])
 
 
-		a,b = encl or ('','')
+		a, b = encl or ('', '')
 		add_len = len(append_chars) + len(encl)
 		add_len = len(append_chars) + len(encl)
 
 
 		if len(self) + s_wide_count + add_len > width:
 		if len(self) + s_wide_count + add_len > width:
 			assert self.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= screen width of string"
 			assert self.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= screen width of string"
-			s = a + (truncate_str(self,width-add_len) if s_wide_count else self[:width-add_len]) + b
+			s = a + (truncate_str(self, width-add_len) if s_wide_count else self[:width-add_len]) + b
 		else:
 		else:
 			s = a + self + b
 			s = a + self + b
 
 
 		if append_chars:
 		if append_chars:
 			return (
 			return (
-				self.colorize(s,color=color)
+				self.colorize(s, color=color)
 				+ self.colorize2(
 				+ self.colorize2(
 					append_chars.ljust(width-len(s)-s_wide_count),
 					append_chars.ljust(width-len(s)-s_wide_count),
-					color_override = append_color ))
+					color_override = append_color))
 		else:
 		else:
-			return self.colorize2( s.ljust(width-s_wide_count), color=color, color_override=color_override )
+			return self.colorize2(s.ljust(width-s_wide_count), color=color, color_override=color_override)
 
 
-	def hl(self,color=True):
-		return getattr( color_mod, self.color )(self) if color else self
+	def hl(self, color=True):
+		return getattr(color_mod, self.color)(self) if color else self
 
 
 	# an alternative to hl(), with enclosure and color override
 	# an alternative to hl(), with enclosure and color override
 	# can be called as an unbound method with class as first argument
 	# can be called as an unbound method with class as first argument
-	def hl2(self,s=None,color=True,encl='',color_override=''):
+	def hl2(self, s=None, color=True, encl='', color_override=''):
 		if encl:
 		if encl:
-			return self.colorize2( encl[0]+(s or self)+encl[1], color=color, color_override=color_override )
+			return self.colorize2(encl[0]+(s or self)+encl[1], color=color, color_override=color_override)
 		else:
 		else:
-			return self.colorize2( (s or self), color=color, color_override=color_override )
+			return self.colorize2((s or self), color=color, color_override=color_override)
 
 
 class InitErrors:
 class InitErrors:
 
 
 	@classmethod
 	@classmethod
-	def init_fail(cls,e,m,e2=None,m2=None,objname=None,preformat=False):
+	def init_fail(cls, e, m, e2=None, m2=None, objname=None, preformat=False):
 
 
 		def get_errmsg():
 		def get_errmsg():
 			ret = m if preformat else (
 			ret = m if preformat else (
@@ -134,18 +134,18 @@ class InitErrors:
 					m,
 					m,
 					(objname or cls.__name__),
 					(objname or cls.__name__),
 					(f'({e2!s}) ' if e2 else ''),
 					(f'({e2!s}) ' if e2 else ''),
-					e ))
+					e))
 			return f'{m2!r}\n{ret}' if m2 else ret
 			return f'{m2!r}\n{ret}' if m2 else ret
 
 
-		if hasattr(cls,'passthru_excs') and type(e).__name__ in cls.passthru_excs:
+		if hasattr(cls, 'passthru_excs') and type(e).__name__ in cls.passthru_excs:
 			raise e
 			raise e
 
 
 		from .util import die
 		from .util import die
-		die(getattr(cls,'exc','ObjectInitError'), get_errmsg())
+		die(getattr(cls, 'exc', 'ObjectInitError'), get_errmsg())
 
 
 	@classmethod
 	@classmethod
 	def method_not_implemented(cls):
 	def method_not_implemented(cls):
 		import traceback
 		import traceback
 		raise NotImplementedError(
 		raise NotImplementedError(
 			'method {}() not implemented for class {!r}'.format(
 			'method {}() not implemented for class {!r}'.format(
-				traceback.extract_stack()[-2].name, cls.__name__) )
+				traceback.extract_stack()[-2].name, cls.__name__))

+ 5 - 5
mmgen/opts.py

@@ -134,7 +134,7 @@ def parse_opts(opts_data, opt_filter, global_opts_data, global_opts_filter):
 	return namedtuple('parsed_cmd_opts', ['user_opts', 'cmd_args', 'opts'])(
 	return namedtuple('parsed_cmd_opts', ['user_opts', 'cmd_args', 'opts'])(
 		uopts, # dict
 		uopts, # dict
 		uargs, # list, callers can pop
 		uargs, # list, callers can pop
-		tuple(v.name for k,v in opts if len(k) > 1)
+		tuple(v.name for k, v in opts if len(k) > 1)
 	)
 	)
 
 
 def opt_preproc_debug(po):
 def opt_preproc_debug(po):
@@ -145,10 +145,10 @@ def opt_preproc_debug(po):
 		('Cmd args',           po.cmd_args,        False),
 		('Cmd args',           po.cmd_args,        False),
 		('Opts',               po.opts,            True),
 		('Opts',               po.opts,            True),
 	)
 	)
-	from .util import Msg,fmt_list
+	from .util import Msg, fmt_list
 	Msg('\n=== opts.py debug ===')
 	Msg('\n=== opts.py debug ===')
-	for label,data,pretty in d:
-		Msg('    {:<20}: {}'.format(label,'\n' + fmt_list(data,fmt='col',indent=' '*8) if pretty else data))
+	for label, data, pretty in d:
+		Msg('    {:<20}: {}'.format(label, '\n' + fmt_list(data, fmt='col', indent=' '*8) if pretty else data))
 
 
 opts_data_dfl = {
 opts_data_dfl = {
 	'text': {
 	'text': {
@@ -202,7 +202,7 @@ class Opts:
 		cfg._uopts = uopts = po.user_opts
 		cfg._uopts = uopts = po.user_opts
 
 
 		if init_opts: # initialize user opts to given value
 		if init_opts: # initialize user opts to given value
-			for uopt,val in init_opts.items():
+			for uopt, val in init_opts.items():
 				if uopt not in uopts:
 				if uopt not in uopts:
 					uopts[uopt] = val
 					uopts[uopt] = val
 
 

+ 51 - 51
mmgen/passwdlist.py

@@ -22,10 +22,10 @@ passwdlist: Password list class for the MMGen suite
 
 
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .util import ymsg,is_int,die
-from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString,TwComment
+from .util import ymsg, is_int, die
+from .obj import ImmutableAttr, ListItemAttr, MMGenPWIDString, TwComment
 from .key import PrivKey
 from .key import PrivKey
-from .addr import MMGenPasswordType,AddrIdx,AddrListID
+from .addr import MMGenPasswordType, AddrIdx, AddrListID
 from .addrlist import (
 from .addrlist import (
 	AddrListChksum,
 	AddrListChksum,
 	AddrListIDStr,
 	AddrListIDStr,
@@ -34,10 +34,10 @@ from .addrlist import (
 )
 )
 
 
 class PasswordListEntry(AddrListEntryBase):
 class PasswordListEntry(AddrListEntryBase):
-	passwd  = ListItemAttr(str,typeconv=False) # TODO: create Password type
+	passwd  = ListItemAttr(str, typeconv=False) # TODO: create Password type
 	idx     = ImmutableAttr(AddrIdx)
 	idx     = ImmutableAttr(AddrIdx)
-	comment = ListItemAttr(TwComment,reassign_ok=True)
-	sec     = ListItemAttr(PrivKey,include_proto=True)
+	comment = ListItemAttr(TwComment, reassign_ok=True)
+	sec     = ListItemAttr(PrivKey, include_proto=True)
 
 
 class PasswordList(AddrList):
 class PasswordList(AddrList):
 	entry_type  = PasswordListEntry
 	entry_type  = PasswordListEntry
@@ -50,17 +50,17 @@ class PasswordList(AddrList):
 	gen_passwds = True
 	gen_passwds = True
 	pw_len      = None
 	pw_len      = None
 	dfl_pw_fmt  = 'b58'
 	dfl_pw_fmt  = 'b58'
-	pwinfo      = namedtuple('passwd_info',['min_len','max_len','dfl_len','valid_lens','desc','chk_func'])
+	pwinfo      = namedtuple('passwd_info', ['min_len', 'max_len', 'dfl_len', 'valid_lens', 'desc', 'chk_func'])
 	pw_info     = {
 	pw_info     = {
 		# 32**25 < 2**128 < 32**26
 		# 32**25 < 2**128 < 32**26
-		'b32':     pwinfo(10, 42 ,24, None,      'base32 password',          'baseconv.is_b32_str'),
+		'b32':     pwinfo(10, 42 , 24, None,         'base32 password',           'baseconv.is_b32_str'),
 		# 58**21 < 2**128 < 58**22
 		# 58**21 < 2**128 < 58**22
-		'b58':     pwinfo(8,  36 ,20, None,      'base58 password',          'baseconv.is_b58_str'),
-		'bip39':   pwinfo(12, 24 ,24, [12,18,24],'BIP39 mnemonic',           'bip39.is_bip39_mnemonic'),
-		'xmrseed': pwinfo(25, 25, 25, [25],      'Monero new-style mnemonic','xmrseed.is_xmrseed'),
-		'hex':     pwinfo(32, 64 ,64, [32,48,64],'hexadecimal password',     'util.is_hex_str'),
+		'b58':     pwinfo(8,  36 , 20, None,         'base58 password',           'baseconv.is_b58_str'),
+		'bip39':   pwinfo(12, 24 , 24, [12, 18, 24], 'BIP39 mnemonic',            'bip39.is_bip39_mnemonic'),
+		'xmrseed': pwinfo(25, 25,  25, [25],         'Monero new-style mnemonic', 'xmrseed.is_xmrseed'),
+		'hex':     pwinfo(32, 64 , 64, [32, 48, 64], 'hexadecimal password',      'util.is_hex_str'),
 	}
 	}
-	chksum_rec_f = lambda foo,e: (str(e.idx), e.passwd)
+	chksum_rec_f = lambda foo, e: (str(e.idx), e.passwd)
 
 
 	feature_warn_fs = 'WARNING: {!r} is a potentially dangerous feature.  Use at your own risk!'
 	feature_warn_fs = 'WARNING: {!r} is a potentially dangerous feature.  Use at your own risk!'
 	hex2bip39 = False
 	hex2bip39 = False
@@ -76,7 +76,7 @@ class PasswordList(AddrList):
 			pw_len          = None,
 			pw_len          = None,
 			pw_fmt          = None,
 			pw_fmt          = None,
 			chk_params_only = False,
 			chk_params_only = False,
-			skip_chksum_msg = False ):
+			skip_chksum_msg = False):
 
 
 		self.cfg = cfg
 		self.cfg = cfg
 		self.proto = proto # proto is ignored
 		self.proto = proto # proto is ignored
@@ -90,7 +90,7 @@ class PasswordList(AddrList):
 			self.data = self.file.parse_file(infile)
 			self.data = self.file.parse_file(infile)
 		else:
 		else:
 			if not chk_params_only:
 			if not chk_params_only:
-				for k in (seed,pw_idxs):
+				for k in (seed, pw_idxs):
 					assert k
 					assert k
 			self.pw_id_str = MMGenPWIDString(pw_id_str)
 			self.pw_id_str = MMGenPWIDString(pw_id_str)
 			self.set_pw_fmt(pw_fmt)
 			self.set_pw_fmt(pw_fmt)
@@ -100,20 +100,20 @@ class PasswordList(AddrList):
 			if self.hex2bip39:
 			if self.hex2bip39:
 				ymsg(self.feature_warn_fs.format(pw_fmt))
 				ymsg(self.feature_warn_fs.format(pw_fmt))
 			self.set_pw_len_vs_seed_len(seed) # sets self.bip39, self.xmrseed, self.xmrproto self.baseconv
 			self.set_pw_len_vs_seed_len(seed) # sets self.bip39, self.xmrseed, self.xmrproto self.baseconv
-			self.al_id = AddrListID( sid=seed.sid, mmtype=MMGenPasswordType(self.proto,'P') )
-			self.data = self.generate(seed,pw_idxs)
+			self.al_id = AddrListID(sid=seed.sid, mmtype=MMGenPasswordType(self.proto, 'P'))
+			self.data = self.generate(seed, pw_idxs)
 
 
 		self.num_addrs = len(self.data)
 		self.num_addrs = len(self.data)
 		self.fmt_data = ''
 		self.fmt_data = ''
 		self.chksum = AddrListChksum(self)
 		self.chksum = AddrListChksum(self)
 
 
 		fs = f'{self.al_id.sid}-{self.pw_id_str}-{self.pw_fmt_disp}-{self.pw_len}[{{}}]'
 		fs = f'{self.al_id.sid}-{self.pw_id_str}-{self.pw_fmt_disp}-{self.pw_len}[{{}}]'
-		self.id_str = AddrListIDStr(self,fs)
+		self.id_str = AddrListIDStr(self, fs)
 
 
 		if not skip_chksum_msg:
 		if not skip_chksum_msg:
 			self.do_chksum_msg(record=not infile)
 			self.do_chksum_msg(record=not infile)
 
 
-	def set_pw_fmt(self,pw_fmt):
+	def set_pw_fmt(self, pw_fmt):
 		if pw_fmt == 'hex2bip39':
 		if pw_fmt == 'hex2bip39':
 			self.hex2bip39 = True
 			self.hex2bip39 = True
 			self.pw_fmt = 'bip39'
 			self.pw_fmt = 'bip39'
@@ -122,12 +122,12 @@ class PasswordList(AddrList):
 			self.pw_fmt = pw_fmt
 			self.pw_fmt = pw_fmt
 			self.pw_fmt_disp = pw_fmt
 			self.pw_fmt_disp = pw_fmt
 		if self.pw_fmt not in self.pw_info:
 		if self.pw_fmt not in self.pw_info:
-			die( 'InvalidPasswdFormat',
-				f'{self.pw_fmt!r}: invalid password format.  Valid formats: {", ".join(self.pw_info)}' )
+			die('InvalidPasswdFormat',
+				f'{self.pw_fmt!r}: invalid password format.  Valid formats: {", ".join(self.pw_info)}')
 
 
-	def chk_pw_len(self,passwd=None):
+	def chk_pw_len(self, passwd=None):
 		if passwd is None:
 		if passwd is None:
-			assert self.pw_len,'either passwd or pw_len must be set'
+			assert self.pw_len, 'either passwd or pw_len must be set'
 			pw_len = self.pw_len
 			pw_len = self.pw_len
 			fs = '{l}: invalid user-requested length for {b} ({c}{m})'
 			fs = '{l}: invalid user-requested length for {b} ({c}{m})'
 		else:
 		else:
@@ -136,13 +136,13 @@ class PasswordList(AddrList):
 		d = self.pw_info[self.pw_fmt]
 		d = self.pw_info[self.pw_fmt]
 		if d.valid_lens:
 		if d.valid_lens:
 			if pw_len not in d.valid_lens:
 			if pw_len not in d.valid_lens:
-				die(2, fs.format( l=pw_len, b=d.desc, c='not one of ', m=d.valid_lens, pw=passwd ))
+				die(2, fs.format(l=pw_len, b=d.desc, c='not one of ', m=d.valid_lens, pw=passwd))
 		elif pw_len > d.max_len:
 		elif pw_len > d.max_len:
-			die(2, fs.format( l=pw_len, b=d.desc, c='>', m=d.max_len, pw=passwd ))
+			die(2, fs.format(l=pw_len, b=d.desc, c='>', m=d.max_len, pw=passwd))
 		elif pw_len < d.min_len:
 		elif pw_len < d.min_len:
-			die(2, fs.format( l=pw_len, b=d.desc, c='<', m=d.min_len, pw=passwd ))
+			die(2, fs.format(l=pw_len, b=d.desc, c='<', m=d.min_len, pw=passwd))
 
 
-	def set_pw_len(self,pw_len):
+	def set_pw_len(self, pw_len):
 		d = self.pw_info[self.pw_fmt]
 		d = self.pw_info[self.pw_fmt]
 
 
 		if pw_len is None:
 		if pw_len is None:
@@ -150,11 +150,11 @@ class PasswordList(AddrList):
 			return
 			return
 
 
 		if not is_int(pw_len):
 		if not is_int(pw_len):
-			die(2,f'{pw_len!r}: invalid user-requested password length (not an integer)')
+			die(2, f'{pw_len!r}: invalid user-requested password length (not an integer)')
 		self.pw_len = int(pw_len)
 		self.pw_len = int(pw_len)
 		self.chk_pw_len()
 		self.chk_pw_len()
 
 
-	def set_pw_len_vs_seed_len(self,seed):
+	def set_pw_len_vs_seed_len(self, seed):
 		pf = self.pw_fmt
 		pf = self.pw_fmt
 		if pf == 'hex':
 		if pf == 'hex':
 			pw_bytes = self.pw_len // 2
 			pw_bytes = self.pw_len // 2
@@ -162,83 +162,83 @@ class PasswordList(AddrList):
 		elif pf == 'bip39':
 		elif pf == 'bip39':
 			from .bip39 import bip39
 			from .bip39 import bip39
 			self.bip39 = bip39()
 			self.bip39 = bip39()
-			pw_bytes = bip39.nwords2seedlen(self.pw_len,in_bytes=True)
-			good_pw_len = bip39.seedlen2nwords(seed.byte_len,in_bytes=True)
+			pw_bytes = bip39.nwords2seedlen(self.pw_len, in_bytes=True)
+			good_pw_len = bip39.seedlen2nwords(seed.byte_len, in_bytes=True)
 		elif pf == 'xmrseed':
 		elif pf == 'xmrseed':
 			from .xmrseed import xmrseed
 			from .xmrseed import xmrseed
 			from .protocol import init_proto
 			from .protocol import init_proto
 			self.xmrseed = xmrseed()
 			self.xmrseed = xmrseed()
-			self.xmrproto = init_proto( self.cfg, 'xmr' )
+			self.xmrproto = init_proto(self.cfg, 'xmr')
 			pw_bytes = xmrseed().seedlen_map_rev[self.pw_len]
 			pw_bytes = xmrseed().seedlen_map_rev[self.pw_len]
 			try:
 			try:
 				good_pw_len = xmrseed().seedlen_map[seed.byte_len]
 				good_pw_len = xmrseed().seedlen_map[seed.byte_len]
 			except:
 			except:
-				die(1,f'{seed.byte_len*8}: unsupported seed length for Monero new-style mnemonic')
-		elif pf in ('b32','b58'):
+				die(1, f'{seed.byte_len*8}: unsupported seed length for Monero new-style mnemonic')
+		elif pf in ('b32', 'b58'):
 			pw_int = (32 if pf == 'b32' else 58) ** self.pw_len
 			pw_int = (32 if pf == 'b32' else 58) ** self.pw_len
 			pw_bytes = pw_int.bit_length() // 8
 			pw_bytes = pw_int.bit_length() // 8
 			from .baseconv import baseconv
 			from .baseconv import baseconv
 			self.baseconv = baseconv(self.pw_fmt)
 			self.baseconv = baseconv(self.pw_fmt)
-			good_pw_len = len( baseconv(pf).frombytes(b'\xff'*seed.byte_len) )
+			good_pw_len = len(baseconv(pf).frombytes(b'\xff'*seed.byte_len))
 		else:
 		else:
 			raise NotImplementedError(f'{pf!r}: unknown password format')
 			raise NotImplementedError(f'{pf!r}: unknown password format')
 
 
 		if pw_bytes > seed.byte_len:
 		if pw_bytes > seed.byte_len:
 			die(1,
 			die(1,
 				f'Cannot generate passwords with more entropy than underlying seed! ({len(seed.data)*8} bits)\n' +
 				f'Cannot generate passwords with more entropy than underlying seed! ({len(seed.data)*8} bits)\n' +
-				(f'Re-run the command with --passwd-len={good_pw_len}' if pf in ('bip39','hex') else
+				(f'Re-run the command with --passwd-len={good_pw_len}' if pf in ('bip39', 'hex') else
 				'Re-run the command, specifying a password length of {} or less')
 				'Re-run the command, specifying a password length of {} or less')
 			)
 			)
 
 
-		if pf in ('bip39','hex') and pw_bytes < seed.byte_len:
+		if pf in ('bip39', 'hex') and pw_bytes < seed.byte_len:
 			from .ui import keypress_confirm
 			from .ui import keypress_confirm
 			if not keypress_confirm(
 			if not keypress_confirm(
 					self.cfg,
 					self.cfg,
 					f'WARNING: requested {self.pw_info[pf].desc} length has less entropy ' +
 					f'WARNING: requested {self.pw_info[pf].desc} length has less entropy ' +
 					'than underlying seed!\nIs this what you want?',
 					'than underlying seed!\nIs this what you want?',
-					default_yes = True ):
-				die(1,'Exiting at user request')
+					default_yes = True):
+				die(1, 'Exiting at user request')
 
 
-	def gen_passwd(self,secbytes):
+	def gen_passwd(self, secbytes):
 		assert self.pw_fmt in self.pw_info
 		assert self.pw_fmt in self.pw_info
 		if self.pw_fmt == 'hex':
 		if self.pw_fmt == 'hex':
 			# take most significant part
 			# take most significant part
 			return secbytes.hex()[:self.pw_len]
 			return secbytes.hex()[:self.pw_len]
 		elif self.pw_fmt == 'bip39':
 		elif self.pw_fmt == 'bip39':
-			pw_len_bytes = self.bip39.nwords2seedlen( self.pw_len, in_bytes=True )
+			pw_len_bytes = self.bip39.nwords2seedlen(self.pw_len, in_bytes=True)
 			# take most significant part
 			# take most significant part
-			return ' '.join( self.bip39.fromhex(secbytes[:pw_len_bytes].hex()) )
+			return ' '.join(self.bip39.fromhex(secbytes[:pw_len_bytes].hex()))
 		elif self.pw_fmt == 'xmrseed':
 		elif self.pw_fmt == 'xmrseed':
 			pw_len_bytes = self.xmrseed.seedlen_map_rev[self.pw_len]
 			pw_len_bytes = self.xmrseed.seedlen_map_rev[self.pw_len]
 			bytes_preproc = self.xmrproto.preprocess_key(
 			bytes_preproc = self.xmrproto.preprocess_key(
 				secbytes[:pw_len_bytes], # take most significant part
 				secbytes[:pw_len_bytes], # take most significant part
-				None )
-			return ' '.join( self.xmrseed.frombytes(bytes_preproc) )
+				None)
+			return ' '.join(self.xmrseed.frombytes(bytes_preproc))
 		else:
 		else:
 			# take least significant part
 			# take least significant part
 			return self.baseconv.frombytes(
 			return self.baseconv.frombytes(
 				secbytes,
 				secbytes,
 				pad = self.pw_len,
 				pad = self.pw_len,
-				tostr = True )[-self.pw_len:]
+				tostr = True)[-self.pw_len:]
 
 
-	def check_format(self,pw):
+	def check_format(self, pw):
 		if not self.chk_func(pw):
 		if not self.chk_func(pw):
 			raise ValueError(f'Password is not valid {self.pw_info[self.pw_fmt].desc} data')
 			raise ValueError(f'Password is not valid {self.pw_info[self.pw_fmt].desc} data')
-		pwlen = len(pw.split()) if self.pw_fmt in ('bip39','xmrseed') else len(pw)
+		pwlen = len(pw.split()) if self.pw_fmt in ('bip39', 'xmrseed') else len(pw)
 		if pwlen != self.pw_len:
 		if pwlen != self.pw_len:
 			raise ValueError(f'Password has incorrect length ({pwlen} != {self.pw_len})')
 			raise ValueError(f'Password has incorrect length ({pwlen} != {self.pw_len})')
 		return True
 		return True
 
 
-	def scramble_seed(self,seed):
+	def scramble_seed(self, seed):
 		# Changing either pw_fmt or pw_len will cause a different, unrelated
 		# Changing either pw_fmt or pw_len will cause a different, unrelated
 		# set of passwords to be generated: this is what we want.
 		# set of passwords to be generated: this is what we want.
 		# NB: In original implementation, pw_id_str was 'baseN', not 'bN'
 		# NB: In original implementation, pw_id_str was 'baseN', not 'bN'
 		scramble_key = f'{self.pw_fmt}:{self.pw_len}:{self.pw_id_str}'
 		scramble_key = f'{self.pw_fmt}:{self.pw_len}:{self.pw_id_str}'
 
 
 		if self.hex2bip39:
 		if self.hex2bip39:
-			pwlen = self.bip39.nwords2seedlen(self.pw_len,in_hex=True)
+			pwlen = self.bip39.nwords2seedlen(self.pw_len, in_hex=True)
 			scramble_key = f'hex:{pwlen}:{self.pw_id_str}'
 			scramble_key = f'hex:{pwlen}:{self.pw_id_str}'
 
 
-		self.dmsg_sc('str',scramble_key)
+		self.dmsg_sc('str', scramble_key)
 		from .crypto import Crypto
 		from .crypto import Crypto
-		return Crypto(self.cfg).scramble_seed(seed,scramble_key.encode())
+		return Crypto(self.cfg).scramble_seed(seed, scramble_key.encode())

+ 64 - 64
mmgen/protocol.py

@@ -25,17 +25,17 @@ from collections import namedtuple
 from .cfg import gc
 from .cfg import gc
 from .objmethods import MMGenObject
 from .objmethods import MMGenObject
 
 
-decoded_wif = namedtuple('decoded_wif',['sec','pubkey_type','compressed'])
+decoded_wif = namedtuple('decoded_wif', ['sec', 'pubkey_type', 'compressed'])
 decoded_addr = namedtuple('decoded_addr', ['bytes', 'ver_bytes', 'fmt'])
 decoded_addr = namedtuple('decoded_addr', ['bytes', 'ver_bytes', 'fmt'])
 decoded_addr_multiview = namedtuple('mv_decoded_addr', ['bytes', 'ver_bytes', 'fmt', 'addr', 'views', 'view_pref'])
 decoded_addr_multiview = namedtuple('mv_decoded_addr', ['bytes', 'ver_bytes', 'fmt', 'addr', 'views', 'view_pref'])
-parsed_addr = namedtuple('parsed_addr',['ver_bytes','data'])
+parsed_addr = namedtuple('parsed_addr', ['ver_bytes', 'data'])
 
 
-_finfo = namedtuple('fork_info',['height','hash','name','replayable'])
-_nw = namedtuple('coin_networks',['mainnet','testnet','regtest'])
+_finfo = namedtuple('fork_info', ['height', 'hash', 'name', 'replayable'])
+_nw = namedtuple('coin_networks', ['mainnet', 'testnet', 'regtest'])
 
 
 class CoinProtocol(MMGenObject):
 class CoinProtocol(MMGenObject):
 
 
-	proto_info = namedtuple('proto_info',['name','trust_level']) # trust levels: see altcoin/params.py
+	proto_info = namedtuple('proto_info', ['name', 'trust_level']) # trust levels: see altcoin/params.py
 
 
 	# keys are mirrored in gc.core_coins:
 	# keys are mirrored in gc.core_coins:
 	coins = {
 	coins = {
@@ -54,10 +54,10 @@ class CoinProtocol(MMGenObject):
 		base_coin  = None
 		base_coin  = None
 		is_fork_of = None
 		is_fork_of = None
 		chain_names = None
 		chain_names = None
-		networks   = ('mainnet','testnet','regtest')
+		networks   = ('mainnet', 'testnet', 'regtest')
 		decimal_prec = 28
 		decimal_prec = 28
 
 
-		def __init__(self,cfg,coin,name,network,tokensym=None,need_amt=False):
+		def __init__(self, cfg, coin, name, network, tokensym=None, need_amt=False):
 			self.cfg        = cfg
 			self.cfg        = cfg
 			self.coin       = coin.upper()
 			self.coin       = coin.upper()
 			self.coin_id    = self.coin
 			self.coin_id    = self.coin
@@ -65,24 +65,24 @@ class CoinProtocol(MMGenObject):
 			self.network    = network
 			self.network    = network
 			self.tokensym   = tokensym
 			self.tokensym   = tokensym
 			self.cls_name   = type(self).__name__
 			self.cls_name   = type(self).__name__
-			self.testnet    = network in ('testnet','regtest')
+			self.testnet    = network in ('testnet', 'regtest')
 			self.regtest    = network == 'regtest'
 			self.regtest    = network == 'regtest'
-			self.networks   = tuple(k for k,v in self.network_names._asdict().items() if v)
+			self.networks   = tuple(k for k, v in self.network_names._asdict().items() if v)
 			self.network_id = coin.lower() + {
 			self.network_id = coin.lower() + {
 				'mainnet': '',
 				'mainnet': '',
 				'testnet': '_tn',
 				'testnet': '_tn',
 				'regtest': '_rt',
 				'regtest': '_rt',
 			}[network]
 			}[network]
 
 
-			if hasattr(self,'wif_ver_num'):
-				self.wif_ver_bytes = {k:bytes.fromhex(v) for k,v in self.wif_ver_num.items()}
-				self.wif_ver_bytes_to_pubkey_type = {v:k for k,v in self.wif_ver_bytes.items()}
+			if hasattr(self, 'wif_ver_num'):
+				self.wif_ver_bytes = {k:bytes.fromhex(v) for k, v in self.wif_ver_num.items()}
+				self.wif_ver_bytes_to_pubkey_type = {v:k for k, v in self.wif_ver_bytes.items()}
 				vbs = list(self.wif_ver_bytes.values())
 				vbs = list(self.wif_ver_bytes.values())
 				self.wif_ver_bytes_len = len(vbs[0]) if len(set(len(b) for b in vbs)) == 1 else None
 				self.wif_ver_bytes_len = len(vbs[0]) if len(set(len(b) for b in vbs)) == 1 else None
 
 
-			if hasattr(self,'addr_ver_info'):
-				self.addr_ver_bytes = {bytes.fromhex(k):v for k,v in self.addr_ver_info.items()}
-				self.addr_fmt_to_ver_bytes = {v:k for k,v in self.addr_ver_bytes.items()}
+			if hasattr(self, 'addr_ver_info'):
+				self.addr_ver_bytes = {bytes.fromhex(k):v for k, v in self.addr_ver_info.items()}
+				self.addr_fmt_to_ver_bytes = {v:k for k, v in self.addr_ver_bytes.items()}
 				self.addr_ver_bytes_len = len(list(self.addr_ver_bytes)[0])
 				self.addr_ver_bytes_len = len(list(self.addr_ver_bytes)[0])
 
 
 			if gc.cmd_caps:
 			if gc.cmd_caps:
@@ -100,15 +100,15 @@ class CoinProtocol(MMGenObject):
 			if self.tokensym:
 			if self.tokensym:
 				assert self.name.startswith('Ethereum'), 'CoinProtocol.Base_chk1'
 				assert self.name.startswith('Ethereum'), 'CoinProtocol.Base_chk1'
 
 
-			if self.base_coin in ('ETH','XMR'):
+			if self.base_coin in ('ETH', 'XMR'):
 				from .util2 import get_keccak
 				from .util2 import get_keccak
 				self.keccak_256 = get_keccak(cfg)
 				self.keccak_256 = get_keccak(cfg)
 
 
 			if need_amt:
 			if need_amt:
 				from . import amt
 				from . import amt
 				from decimal import getcontext
 				from decimal import getcontext
-				self.coin_amt = getattr(amt,self.coin_amt)
-				self.max_tx_fee = self.coin_amt(self.max_tx_fee) if hasattr(self,'max_tx_fee') else None
+				self.coin_amt = getattr(amt, self.coin_amt)
+				self.max_tx_fee = self.coin_amt(self.max_tx_fee) if hasattr(self, 'max_tx_fee') else None
 				getcontext().prec = self.decimal_prec
 				getcontext().prec = self.decimal_prec
 			else:
 			else:
 				self.coin_amt = None
 				self.coin_amt = None
@@ -119,7 +119,7 @@ class CoinProtocol(MMGenObject):
 			return self.coin
 			return self.coin
 
 
 		@classmethod
 		@classmethod
-		def chain_name_to_network(cls,cfg,coin,chain_name):
+		def chain_name_to_network(cls, cfg, coin, chain_name):
 			"""
 			"""
 			The generic networks 'mainnet', 'testnet' and 'regtest' are required for all coins
 			The generic networks 'mainnet', 'testnet' and 'regtest' are required for all coins
 			that support transaction operations.
 			that support transaction operations.
@@ -128,8 +128,8 @@ class CoinProtocol(MMGenObject):
 			the attribute 'chain_name' is used, while 'network' retains the generic name.
 			the attribute 'chain_name' is used, while 'network' retains the generic name.
 			For Bitcoin and Bitcoin forks, 'network' and 'chain_name' are equivalent.
 			For Bitcoin and Bitcoin forks, 'network' and 'chain_name' are equivalent.
 			"""
 			"""
-			for network in ('mainnet','testnet','regtest'):
-				proto = init_proto( cfg, coin, network=network )
+			for network in ('mainnet', 'testnet', 'regtest'):
+				proto = init_proto(cfg, coin, network=network)
 				for proto_chain_name in proto.chain_names:
 				for proto_chain_name in proto.chain_names:
 					if chain_name == proto_chain_name:
 					if chain_name == proto_chain_name:
 						return network
 						return network
@@ -137,22 +137,22 @@ class CoinProtocol(MMGenObject):
 
 
 		@staticmethod
 		@staticmethod
 		def parse_network_id(network_id):
 		def parse_network_id(network_id):
-			nid = namedtuple('parsed_network_id',['coin','network'])
+			nid = namedtuple('parsed_network_id', ['coin', 'network'])
 			if network_id.endswith('_tn'):
 			if network_id.endswith('_tn'):
-				return nid(network_id[:-3],'testnet')
+				return nid(network_id[:-3], 'testnet')
 			elif network_id.endswith('_rt'):
 			elif network_id.endswith('_rt'):
-				return nid(network_id[:-3],'regtest')
+				return nid(network_id[:-3], 'regtest')
 			else:
 			else:
-				return nid(network_id,'mainnet')
+				return nid(network_id, 'mainnet')
 
 
 		@staticmethod
 		@staticmethod
-		def create_network_id(coin,network):
-			return coin.lower() + { 'mainnet':'', 'testnet':'_tn', 'regtest':'_rt' }[network]
+		def create_network_id(coin, network):
+			return coin.lower() + {'mainnet':'', 'testnet':'_tn', 'regtest':'_rt'}[network]
 
 
-		def cap(self,s):
+		def cap(self, s):
 			return s in self.caps
 			return s in self.caps
 
 
-		def get_addr_len(self,addr_fmt):
+		def get_addr_len(self, addr_fmt):
 			return self.addr_len
 			return self.addr_len
 
 
 		def decode_addr_bytes(self, addr_bytes):
 		def decode_addr_bytes(self, addr_bytes):
@@ -162,18 +162,18 @@ class CoinProtocol(MMGenObject):
 				addr_bytes[:vlen],
 				addr_bytes[:vlen],
 				self.addr_ver_bytes[addr_bytes[:vlen]])
 				self.addr_ver_bytes[addr_bytes[:vlen]])
 
 
-		def coin_addr(self,addr):
+		def coin_addr(self, addr):
 			from .addr import CoinAddr
 			from .addr import CoinAddr
-			return CoinAddr( proto=self, addr=addr )
+			return CoinAddr(proto=self, addr=addr)
 
 
-		def addr_type(self,id_str):
+		def addr_type(self, id_str):
 			from .addr import MMGenAddrType
 			from .addr import MMGenAddrType
-			return MMGenAddrType( proto=self, id_str=id_str )
+			return MMGenAddrType(proto=self, id_str=id_str)
 
 
-		def viewkey(self,viewkey_str):
+		def viewkey(self, viewkey_str):
 			raise NotImplementedError(f'{self.name} protocol does not support view keys')
 			raise NotImplementedError(f'{self.name} protocol does not support view keys')
 
 
-		def base_proto_subclass(self,cls,modname,sub_clsname=None):
+		def base_proto_subclass(self, cls, modname, sub_clsname=None):
 			"""
 			"""
 			magic module loading and class selection
 			magic module loading and class selection
 			"""
 			"""
@@ -182,13 +182,13 @@ class CoinProtocol(MMGenObject):
 			clsname = (
 			clsname = (
 				self.mod_clsname
 				self.mod_clsname
 				+ ('Token' if self.tokensym else '')
 				+ ('Token' if self.tokensym else '')
-				+ cls.__name__ )
+				+ cls.__name__)
 
 
 			import importlib
 			import importlib
 			if sub_clsname:
 			if sub_clsname:
-				return getattr(getattr(importlib.import_module(modpath),clsname),sub_clsname)
+				return getattr(getattr(importlib.import_module(modpath), clsname), sub_clsname)
 			else:
 			else:
-				return getattr(importlib.import_module(modpath),clsname)
+				return getattr(importlib.import_module(modpath), clsname)
 
 
 
 
 	class Secp256k1(Base):
 	class Secp256k1(Base):
@@ -199,42 +199,42 @@ class CoinProtocol(MMGenObject):
 		privkey_len  = 32
 		privkey_len  = 32
 		pubkey_types = ('std',)
 		pubkey_types = ('std',)
 
 
-		def parse_addr(self,ver_bytes,addr_bytes,fmt):
+		def parse_addr(self, ver_bytes, addr_bytes, fmt):
 			return parsed_addr(
 			return parsed_addr(
 				ver_bytes  = ver_bytes,
 				ver_bytes  = ver_bytes,
 				data       = addr_bytes,
 				data       = addr_bytes,
 			)
 			)
 
 
-		def preprocess_key(self,sec,pubkey_type):
+		def preprocess_key(self, sec, pubkey_type):
 			# Key must be non-zero and less than group order of secp256k1 curve
 			# Key must be non-zero and less than group order of secp256k1 curve
-			if 0 < int.from_bytes(sec,'big') < self.secp256k1_group_order:
+			if 0 < int.from_bytes(sec, 'big') < self.secp256k1_group_order:
 				return sec
 				return sec
 			else: # chance of this is less than 1 in 2^127
 			else: # chance of this is less than 1 in 2^127
-				from .util import die,ymsg
-				pk = int.from_bytes(sec,'big')
+				from .util import die, ymsg
+				pk = int.from_bytes(sec, 'big')
 				if pk == 0: # chance of this is 1 in 2^256
 				if pk == 0: # chance of this is 1 in 2^256
-					die(4,'Private key is zero!')
+					die(4, 'Private key is zero!')
 				elif pk == self.secp256k1_group_order: # ditto
 				elif pk == self.secp256k1_group_order: # ditto
-					die(4,'Private key == secp256k1_group_order!')
+					die(4, 'Private key == secp256k1_group_order!')
 				else: # return key mod group order as the key
 				else: # return key mod group order as the key
 					if not self.cfg.test_suite:
 					if not self.cfg.test_suite:
 						ymsg(f'Warning: private key is greater than secp256k1 group order!:\n  {sec.hex()}')
 						ymsg(f'Warning: private key is greater than secp256k1 group order!:\n  {sec.hex()}')
-					return (pk % self.secp256k1_group_order).to_bytes(self.privkey_len,'big')
+					return (pk % self.secp256k1_group_order).to_bytes(self.privkey_len, 'big')
 
 
 	class DummyWIF:
 	class DummyWIF:
 		"""
 		"""
 		Ethereum and Monero protocols inherit from this class
 		Ethereum and Monero protocols inherit from this class
 		"""
 		"""
-		def encode_wif(self,privbytes,pubkey_type,compressed):
+		def encode_wif(self, privbytes, pubkey_type, compressed):
 			assert pubkey_type == self.pubkey_type, f'{pubkey_type}: invalid pubkey_type for {self.name} protocol!'
 			assert pubkey_type == self.pubkey_type, f'{pubkey_type}: invalid pubkey_type for {self.name} protocol!'
 			assert compressed is False, f'{self.name} protocol does not support compressed pubkeys!'
 			assert compressed is False, f'{self.name} protocol does not support compressed pubkeys!'
 			return privbytes.hex()
 			return privbytes.hex()
 
 
-		def decode_wif(self,wif):
+		def decode_wif(self, wif):
 			return decoded_wif(
 			return decoded_wif(
 				sec         = bytes.fromhex(wif),
 				sec         = bytes.fromhex(wif),
 				pubkey_type = self.pubkey_type,
 				pubkey_type = self.pubkey_type,
-				compressed  = False )
+				compressed  = False)
 
 
 def init_proto(
 def init_proto(
 		cfg,
 		cfg,
@@ -253,7 +253,7 @@ def init_proto(
 	assert not (coin and network_id), 'init_proto_chk4'
 	assert not (coin and network_id), 'init_proto_chk4'
 
 
 	if network_id:
 	if network_id:
-		coin,network = CoinProtocol.Base.parse_network_id(network_id)
+		coin, network = CoinProtocol.Base.parse_network_id(network_id)
 	elif network:
 	elif network:
 		assert network in CoinProtocol.Base.networks, f'init_proto_chk5 - {network!r}: invalid network'
 		assert network in CoinProtocol.Base.networks, f'init_proto_chk5 - {network!r}: invalid network'
 		assert testnet is False, 'init_proto_chk6'
 		assert testnet is False, 'init_proto_chk6'
@@ -265,37 +265,37 @@ def init_proto(
 
 
 	if coin not in CoinProtocol.coins:
 	if coin not in CoinProtocol.coins:
 		from .altcoin.params import init_genonly_altcoins
 		from .altcoin.params import init_genonly_altcoins
-		init_genonly_altcoins( coin, testnet=testnet ) # raises exception on failure
+		init_genonly_altcoins(coin, testnet=testnet) # raises exception on failure
 
 
 	name = CoinProtocol.coins[coin].name
 	name = CoinProtocol.coins[coin].name
 	proto_name = name + ('' if network == 'mainnet' else network.capitalize())
 	proto_name = name + ('' if network == 'mainnet' else network.capitalize())
 
 
-	if not hasattr(CoinProtocol,proto_name):
+	if not hasattr(CoinProtocol, proto_name):
 		import importlib
 		import importlib
 		setattr(
 		setattr(
 			CoinProtocol,
 			CoinProtocol,
 			proto_name,
 			proto_name,
-			getattr(importlib.import_module(f'mmgen.proto.{coin}.params'),network)
+			getattr(importlib.import_module(f'mmgen.proto.{coin}.params'), network)
 		)
 		)
 
 
 	if return_cls:
 	if return_cls:
-		return getattr(CoinProtocol,proto_name)
+		return getattr(CoinProtocol, proto_name)
 
 
-	return getattr(CoinProtocol,proto_name)(
+	return getattr(CoinProtocol, proto_name)(
 		cfg       = cfg,
 		cfg       = cfg,
 		coin      = coin,
 		coin      = coin,
 		name      = name,
 		name      = name,
 		network   = network,
 		network   = network,
 		tokensym  = tokensym,
 		tokensym  = tokensym,
-		need_amt  = need_amt )
+		need_amt  = need_amt)
 
 
-def init_proto_from_cfg(cfg,need_amt):
+def init_proto_from_cfg(cfg, need_amt):
 	return init_proto(
 	return init_proto(
 		cfg       = cfg,
 		cfg       = cfg,
 		coin      = cfg.coin,
 		coin      = cfg.coin,
 		network   = cfg.network,
 		network   = cfg.network,
 		tokensym  = cfg.token,
 		tokensym  = cfg.token,
-		need_amt  = need_amt )
+		need_amt  = need_amt)
 
 
 def warn_trustlevel(cfg):
 def warn_trustlevel(cfg):
 
 
@@ -305,11 +305,11 @@ def warn_trustlevel(cfg):
 		trust_level = CoinProtocol.coins[coinsym.lower()].trust_level
 		trust_level = CoinProtocol.coins[coinsym.lower()].trust_level
 	else:
 	else:
 		from .altcoin.params import CoinInfo
 		from .altcoin.params import CoinInfo
-		e = CoinInfo.get_entry(coinsym,'mainnet')
+		e = CoinInfo.get_entry(coinsym, 'mainnet')
 		trust_level = e.trust_level if e else None
 		trust_level = e.trust_level if e else None
-		if trust_level in (None,-1):
+		if trust_level in (None, -1):
 			from .util import die
 			from .util import die
-			die(1,f'Coin {coinsym} is not supported by {gc.proj_name}')
+			die(1, f'Coin {coinsym} is not supported by {gc.proj_name}')
 
 
 	if trust_level > 3:
 	if trust_level > 3:
 		return
 		return
@@ -322,7 +322,7 @@ def warn_trustlevel(cfg):
 	"""
 	"""
 
 
 	from .util import fmt
 	from .util import fmt
-	from .color import red,yellow,green
+	from .color import red, yellow, green
 
 
 	warning = fmt(m).strip().format(
 	warning = fmt(m).strip().format(
 		c = coinsym.upper(),
 		c = coinsym.upper(),
@@ -332,13 +332,13 @@ def warn_trustlevel(cfg):
 			2: yellow('MEDIUM'),
 			2: yellow('MEDIUM'),
 			3: green('OK'),
 			3: green('OK'),
 		}[trust_level],
 		}[trust_level],
-		p = gc.proj_name )
+		p = gc.proj_name)
 
 
 	if cfg.test_suite:
 	if cfg.test_suite:
 		cfg._util.qmsg(warning)
 		cfg._util.qmsg(warning)
 		return
 		return
 
 
 	from .ui import keypress_confirm
 	from .ui import keypress_confirm
-	if not keypress_confirm( cfg, warning, default_yes=True ):
+	if not keypress_confirm(cfg, warning, default_yes=True):
 		import sys
 		import sys
 		sys.exit(0)
 		sys.exit(0)

+ 11 - 11
mmgen/pyversion.py

@@ -17,40 +17,40 @@ class PythonVersion(str):
 	major = 0
 	major = 0
 	minor = 0
 	minor = 0
 
 
-	def __new__(cls,arg=None):
-		if isinstance(arg,PythonVersion):
+	def __new__(cls, arg=None):
+		if isinstance(arg, PythonVersion):
 			return arg
 			return arg
 		if arg:
 		if arg:
-			major,minor = arg.split('.')
+			major, minor = arg.split('.')
 		else:
 		else:
 			import platform
 			import platform
-			major,minor = platform.python_version_tuple()[:2]
-		me = str.__new__( cls, f'{major}.{minor}' )
+			major, minor = platform.python_version_tuple()[:2]
+		me = str.__new__(cls, f'{major}.{minor}')
 		me.major = int(major)
 		me.major = int(major)
 		me.minor = int(minor)
 		me.minor = int(minor)
 		return me
 		return me
 
 
-	def __lt__(self,other):
+	def __lt__(self, other):
 		other = type(self)(other)
 		other = type(self)(other)
 		return self.major < other.major or (self.major == other.major and self.minor < other.minor)
 		return self.major < other.major or (self.major == other.major and self.minor < other.minor)
 
 
-	def __le__(self,other):
+	def __le__(self, other):
 		other = type(self)(other)
 		other = type(self)(other)
 		return self.major < other.major or (self.major == other.major and self.minor <= other.minor)
 		return self.major < other.major or (self.major == other.major and self.minor <= other.minor)
 
 
-	def __eq__(self,other):
+	def __eq__(self, other):
 		other = type(self)(other)
 		other = type(self)(other)
 		return self.major == other.major and self.minor == other.minor
 		return self.major == other.major and self.minor == other.minor
 
 
-	def __ne__(self,other):
+	def __ne__(self, other):
 		other = type(self)(other)
 		other = type(self)(other)
 		return not (self.major == other.major and self.minor == other.minor)
 		return not (self.major == other.major and self.minor == other.minor)
 
 
-	def __gt__(self,other):
+	def __gt__(self, other):
 		other = type(self)(other)
 		other = type(self)(other)
 		return self.major > other.major or (self.major == other.major and self.minor > other.minor)
 		return self.major > other.major or (self.major == other.major and self.minor > other.minor)
 
 
-	def __ge__(self,other):
+	def __ge__(self, other):
 		other = type(self)(other)
 		other = type(self)(other)
 		return self.major > other.major or (self.major == other.major and self.minor >= other.minor)
 		return self.major > other.major or (self.major == other.major and self.minor >= other.minor)
 
 

+ 106 - 106
mmgen/rpc.py

@@ -20,43 +20,43 @@
 rpc: Cryptocoin RPC library for the MMGen suite
 rpc: Cryptocoin RPC library for the MMGen suite
 """
 """
 
 
-import sys,re,base64,json,asyncio,importlib
+import sys, re, base64, json, asyncio, importlib
 from decimal import Decimal
 from decimal import Decimal
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .util import msg,ymsg,die,fmt,fmt_list,pp_fmt,oneshot_warning
+from .util import msg, ymsg, die, fmt, fmt_list, pp_fmt, oneshot_warning
 from .base_obj import AsyncInit
 from .base_obj import AsyncInit
 from .obj import NonNegativeInt
 from .obj import NonNegativeInt
-from .objmethods import HiliteStr,InitErrors,MMGenObject
+from .objmethods import HiliteStr, InitErrors, MMGenObject
 
 
-auth_data = namedtuple('rpc_auth_data',['user','passwd'])
+auth_data = namedtuple('rpc_auth_data', ['user', 'passwd'])
 
 
-def dmsg_rpc(fs,data=None,is_json=False):
+def dmsg_rpc(fs, data=None, is_json=False):
 	msg(
 	msg(
 		fs if data is None else
 		fs if data is None else
 		fs.format(pp_fmt(json.loads(data) if is_json else data))
 		fs.format(pp_fmt(json.loads(data) if is_json else data))
 	)
 	)
 
 
-def dmsg_rpc_backend(host_url,host_path,payload):
+def dmsg_rpc_backend(host_url, host_path, payload):
 	msg(
 	msg(
 		f'\n    RPC URL: {host_url}{host_path}' +
 		f'\n    RPC URL: {host_url}{host_path}' +
 		'\n    RPC PAYLOAD data (httplib) ==>' +
 		'\n    RPC PAYLOAD data (httplib) ==>' +
-		f'\n{pp_fmt(payload)}\n' )
+		f'\n{pp_fmt(payload)}\n')
 
 
-def noop(*args,**kwargs):
+def noop(*args, **kwargs):
 	pass
 	pass
 
 
-class IPPort(HiliteStr,InitErrors):
+class IPPort(HiliteStr, InitErrors):
 	color = 'yellow'
 	color = 'yellow'
 	width = 0
 	width = 0
 	trunc_ok = False
 	trunc_ok = False
 	min_len = 9  # 0.0.0.0:0
 	min_len = 9  # 0.0.0.0:0
 	max_len = 21 # 255.255.255.255:65535
 	max_len = 21 # 255.255.255.255:65535
-	def __new__(cls,s):
-		if isinstance(s,cls):
+	def __new__(cls, s):
+		if isinstance(s, cls):
 			return s
 			return s
 		try:
 		try:
-			m = re.fullmatch(r'{q}\.{q}\.{q}\.{q}:(\d{{1,10}})'.format(q=r'([0-9]{1,3})'),s)
+			m = re.fullmatch(r'{q}\.{q}\.{q}\.{q}:(\d{{1,10}})'.format(q=r'([0-9]{1,3})'), s)
 			assert m is not None, f'{s!r}: invalid IP:HOST specifier'
 			assert m is not None, f'{s!r}: invalid IP:HOST specifier'
 			for e in m.groups():
 			for e in m.groups():
 				if len(e) != 1 and e[0] == '0':
 				if len(e) != 1 and e[0] == '0':
@@ -65,13 +65,13 @@ class IPPort(HiliteStr,InitErrors):
 			for e in res[:4]:
 			for e in res[:4]:
 				assert e <= 255, f'{e}: dotted decimal element > 255'
 				assert e <= 255, f'{e}: dotted decimal element > 255'
 			assert res[4] <= 65535, f'{res[4]}: port number > 65535'
 			assert res[4] <= 65535, f'{res[4]}: port number > 65535'
-			me = str.__new__(cls,s)
+			me = str.__new__(cls, s)
 			me.ip = '{}.{}.{}.{}'.format(*res)
 			me.ip = '{}.{}.{}.{}'.format(*res)
-			me.ip_num = sum( res[i] * ( 2 ** (-(i-3)*8) ) for i in range(4) )
+			me.ip_num = sum(res[i] * (2 ** (-(i-3)*8)) for i in range(4))
 			me.port = res[4]
 			me.port = res[4]
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,s)
+			return cls.init_fail(e, s)
 
 
 class json_encoder(json.JSONEncoder):
 class json_encoder(json.JSONEncoder):
 	def default(self, o):
 	def default(self, o):
@@ -84,7 +84,7 @@ class RPCBackends:
 
 
 	class base:
 	class base:
 
 
-		def __init__(self,caller):
+		def __init__(self, caller):
 			self.cfg            = caller.cfg
 			self.cfg            = caller.cfg
 			self.host           = caller.host
 			self.host           = caller.host
 			self.port           = caller.port
 			self.port           = caller.port
@@ -95,7 +95,7 @@ class RPCBackends:
 			self.name           = type(self).__name__
 			self.name           = type(self).__name__
 			self.caller         = caller
 			self.caller         = caller
 
 
-	class aiohttp(base,metaclass=AsyncInit):
+	class aiohttp(base, metaclass=AsyncInit):
 		"""
 		"""
 		Contrary to the requests library, aiohttp won’t read environment variables by
 		Contrary to the requests library, aiohttp won’t read environment variables by
 		default.  But you can do so by passing trust_env=True into aiohttp.ClientSession
 		default.  But you can do so by passing trust_env=True into aiohttp.ClientSession
@@ -108,61 +108,61 @@ class RPCBackends:
 			self.session.detach()
 			self.session.detach()
 			del self.session
 			del self.session
 
 
-		async def __init__(self,caller):
+		async def __init__(self, caller):
 			super().__init__(caller)
 			super().__init__(caller)
 			import aiohttp
 			import aiohttp
 			self.connector = aiohttp.TCPConnector(limit_per_host=self.cfg.aiohttp_rpc_queue_len)
 			self.connector = aiohttp.TCPConnector(limit_per_host=self.cfg.aiohttp_rpc_queue_len)
 			self.session = aiohttp.ClientSession(
 			self.session = aiohttp.ClientSession(
-				headers = { 'Content-Type': 'application/json' },
+				headers = {'Content-Type': 'application/json'},
 				connector = self.connector,
 				connector = self.connector,
 			)
 			)
 			if caller.auth_type == 'basic':
 			if caller.auth_type == 'basic':
-				self.auth = aiohttp.BasicAuth(*caller.auth,encoding='UTF-8')
+				self.auth = aiohttp.BasicAuth(*caller.auth, encoding='UTF-8')
 			else:
 			else:
 				self.auth = None
 				self.auth = None
 
 
-		async def run(self,payload,timeout,host_path):
-			dmsg_rpc_backend(self.host_url,host_path,payload)
+		async def run(self, payload, timeout, host_path):
+			dmsg_rpc_backend(self.host_url, host_path, payload)
 			async with self.session.post(
 			async with self.session.post(
 				url     = self.host_url + host_path,
 				url     = self.host_url + host_path,
 				auth    = self.auth,
 				auth    = self.auth,
-				data    = json.dumps(payload,cls=json_encoder),
+				data    = json.dumps(payload, cls=json_encoder),
 				timeout = timeout or self.timeout,
 				timeout = timeout or self.timeout,
 			) as res:
 			) as res:
-				return (await res.text(),res.status)
+				return (await res.text(), res.status)
 
 
 	class requests(base):
 	class requests(base):
 
 
 		def __del__(self):
 		def __del__(self):
 			self.session.close()
 			self.session.close()
 
 
-		def __init__(self,caller):
+		def __init__(self, caller):
 			super().__init__(caller)
 			super().__init__(caller)
-			import requests,urllib3
+			import requests, urllib3
 			urllib3.disable_warnings()
 			urllib3.disable_warnings()
 			self.session = requests.Session()
 			self.session = requests.Session()
 			self.session.trust_env = False # ignore *_PROXY environment vars
 			self.session.trust_env = False # ignore *_PROXY environment vars
 			self.session.headers = caller.http_hdrs
 			self.session.headers = caller.http_hdrs
 			if caller.auth_type:
 			if caller.auth_type:
 				auth = 'HTTP' + caller.auth_type.capitalize() + 'Auth'
 				auth = 'HTTP' + caller.auth_type.capitalize() + 'Auth'
-				self.session.auth = getattr(requests.auth,auth)(*caller.auth)
+				self.session.auth = getattr(requests.auth, auth)(*caller.auth)
 			if self.proxy: # used only by XMR for now: requires pysocks package
 			if self.proxy: # used only by XMR for now: requires pysocks package
 				self.session.proxies.update({
 				self.session.proxies.update({
 					'http':  f'socks5h://{self.proxy}',
 					'http':  f'socks5h://{self.proxy}',
 					'https': f'socks5h://{self.proxy}'
 					'https': f'socks5h://{self.proxy}'
 				})
 				})
 
 
-		async def run(self,*args,**kwargs):
-			return self.run_noasync(*args,**kwargs)
+		async def run(self, *args, **kwargs):
+			return self.run_noasync(*args, **kwargs)
 
 
-		def run_noasync(self,payload,timeout,host_path):
-			dmsg_rpc_backend(self.host_url,host_path,payload)
+		def run_noasync(self, payload, timeout, host_path):
+			dmsg_rpc_backend(self.host_url, host_path, payload)
 			res = self.session.post(
 			res = self.session.post(
 				url     = self.host_url + host_path,
 				url     = self.host_url + host_path,
-				data    = json.dumps(payload,cls=json_encoder),
+				data    = json.dumps(payload, cls=json_encoder),
 				timeout = timeout or self.timeout,
 				timeout = timeout or self.timeout,
-				verify  = False )
-			return (res.content,res.status_code)
+				verify  = False)
+			return (res.content, res.status_code)
 
 
 	class httplib(base):
 	class httplib(base):
 		"""
 		"""
@@ -171,22 +171,22 @@ class RPCBackends:
 		def __del__(self):
 		def __del__(self):
 			self.session.close()
 			self.session.close()
 
 
-		def __init__(self,caller):
+		def __init__(self, caller):
 			super().__init__(caller)
 			super().__init__(caller)
 			import http.client
 			import http.client
-			self.session = http.client.HTTPConnection(caller.host,caller.port,caller.timeout)
+			self.session = http.client.HTTPConnection(caller.host, caller.port, caller.timeout)
 			if caller.auth_type == 'basic':
 			if caller.auth_type == 'basic':
 				auth_str = f'{caller.auth.user}:{caller.auth.passwd}'
 				auth_str = f'{caller.auth.user}:{caller.auth.passwd}'
 				auth_str_b64 = 'Basic ' + base64.b64encode(auth_str.encode()).decode()
 				auth_str_b64 = 'Basic ' + base64.b64encode(auth_str.encode()).decode()
-				self.http_hdrs.update({ 'Host': self.host, 'Authorization': auth_str_b64 })
+				self.http_hdrs.update({'Host': self.host, 'Authorization': auth_str_b64})
 				dmsg_rpc(f'    RPC AUTHORIZATION data ==> raw: [{auth_str}]\n{"":>31}enc: [{auth_str_b64}]\n')
 				dmsg_rpc(f'    RPC AUTHORIZATION data ==> raw: [{auth_str}]\n{"":>31}enc: [{auth_str_b64}]\n')
 
 
-		async def run(self,payload,timeout,host_path):
-			dmsg_rpc_backend(self.host_url,host_path,payload)
+		async def run(self, payload, timeout, host_path):
+			dmsg_rpc_backend(self.host_url, host_path, payload)
 
 
 			if timeout:
 			if timeout:
 				import http.client
 				import http.client
-				s = http.client.HTTPConnection(self.host,self.port,timeout)
+				s = http.client.HTTPConnection(self.host, self.port, timeout)
 			else:
 			else:
 				s = self.session
 				s = self.session
 
 
@@ -194,25 +194,25 @@ class RPCBackends:
 				s.request(
 				s.request(
 					method  = 'POST',
 					method  = 'POST',
 					url     = host_path,
 					url     = host_path,
-					body    = json.dumps(payload,cls=json_encoder),
-					headers = self.http_hdrs )
+					body    = json.dumps(payload, cls=json_encoder),
+					headers = self.http_hdrs)
 				r = s.getresponse() # => http.client.HTTPResponse instance
 				r = s.getresponse() # => http.client.HTTPResponse instance
 			except Exception as e:
 			except Exception as e:
-				die( 'RPCFailure', str(e) )
+				die('RPCFailure', str(e))
 
 
 			if timeout:
 			if timeout:
-				ret = ( r.read(), r.status )
+				ret = (r.read(), r.status)
 				s.close()
 				s.close()
 				return ret
 				return ret
 			else:
 			else:
-				return ( r.read(), r.status )
+				return (r.read(), r.status)
 
 
 	class curl(base):
 	class curl(base):
 
 
-		def __init__(self,caller):
+		def __init__(self, caller):
 
 
 			def gen_opts():
 			def gen_opts():
-				for k,v in caller.http_hdrs.items():
+				for k, v in caller.http_hdrs.items():
 					yield from ('--header', f'{k}: {v}')
 					yield from ('--header', f'{k}: {v}')
 				if caller.auth_type:
 				if caller.auth_type:
 					# Authentication with curl is insecure, as it exposes the user's credentials
 					# Authentication with curl is insecure, as it exposes the user's credentials
@@ -227,12 +227,12 @@ class RPCBackends:
 			self.exec_opts = list(gen_opts()) + ['--silent']
 			self.exec_opts = list(gen_opts()) + ['--silent']
 			self.arg_max = 8192 # set way below system ARG_MAX, just to be safe
 			self.arg_max = 8192 # set way below system ARG_MAX, just to be safe
 
 
-		async def run(self,payload,timeout,host_path):
-			data = json.dumps(payload,cls=json_encoder)
+		async def run(self, payload, timeout, host_path):
+			data = json.dumps(payload, cls=json_encoder)
 			if len(data) > self.arg_max:
 			if len(data) > self.arg_max:
 				ymsg('Warning: Curl data payload length exceeded - falling back on httplib')
 				ymsg('Warning: Curl data payload length exceeded - falling back on httplib')
-				return RPCBackends.httplib(self.caller).run(payload,timeout,host_path)
-			dmsg_rpc_backend(self.host_url,host_path,payload)
+				return RPCBackends.httplib(self.caller).run(payload, timeout, host_path)
+			dmsg_rpc_backend(self.host_url, host_path, payload)
 			exec_cmd = [
 			exec_cmd = [
 				'curl',
 				'curl',
 				'--proxy', f'socks5h://{self.proxy}' if self.proxy else '',
 				'--proxy', f'socks5h://{self.proxy}' if self.proxy else '',
@@ -241,13 +241,13 @@ class RPCBackends:
 				'--data-binary', data
 				'--data-binary', data
 				] + self.exec_opts + [self.host_url + host_path]
 				] + self.exec_opts + [self.host_url + host_path]
 
 
-			dmsg_rpc('    RPC curl exec data ==>\n{}\n',exec_cmd)
+			dmsg_rpc('    RPC curl exec data ==>\n{}\n', exec_cmd)
 
 
-			from subprocess import run,PIPE
+			from subprocess import run, PIPE
 			from .color import set_vt100
 			from .color import set_vt100
-			res = run(exec_cmd,stdout=PIPE,check=True,text=True).stdout
+			res = run(exec_cmd, stdout=PIPE, check=True, text=True).stdout
 			set_vt100()
 			set_vt100()
-			return (res[:-3],int(res[-3:]))
+			return (res[:-3], int(res[-3:]))
 
 
 class RPCClient(MMGenObject):
 class RPCClient(MMGenObject):
 
 
@@ -256,7 +256,7 @@ class RPCClient(MMGenObject):
 	network_proto = 'http'
 	network_proto = 'http'
 	proxy = None
 	proxy = None
 
 
-	def __init__(self,cfg,host,port,test_connection=True):
+	def __init__(self, cfg, host, port, test_connection=True):
 
 
 		self.cfg = cfg
 		self.cfg = cfg
 		self.name = type(self).__name__
 		self.name = type(self).__name__
@@ -265,7 +265,7 @@ class RPCClient(MMGenObject):
 		if sys.platform == 'win32' and host == 'localhost':
 		if sys.platform == 'win32' and host == 'localhost':
 			host = '127.0.0.1'
 			host = '127.0.0.1'
 
 
-		global dmsg_rpc,dmsg_rpc_backend
+		global dmsg_rpc, dmsg_rpc_backend
 		if not self.cfg.debug_rpc:
 		if not self.cfg.debug_rpc:
 			dmsg_rpc = dmsg_rpc_backend = noop
 			dmsg_rpc = dmsg_rpc_backend = noop
 
 
@@ -275,18 +275,18 @@ class RPCClient(MMGenObject):
 		if test_connection:
 		if test_connection:
 			import socket
 			import socket
 			try:
 			try:
-				socket.create_connection((host,port),timeout=1).close()
+				socket.create_connection((host, port), timeout=1).close()
 			except:
 			except:
-				die( 'SocketError', f'Unable to connect to {host}:{port}' )
+				die('SocketError', f'Unable to connect to {host}:{port}')
 
 
-		self.http_hdrs = { 'Content-Type': 'application/json' }
+		self.http_hdrs = {'Content-Type': 'application/json'}
 		self.host_url = f'{self.network_proto}://{host}:{port}'
 		self.host_url = f'{self.network_proto}://{host}:{port}'
 		self.host = host
 		self.host = host
 		self.port = port
 		self.port = port
 		self.timeout = self.cfg.http_timeout
 		self.timeout = self.cfg.http_timeout
 		self.auth = None
 		self.auth = None
 
 
-	def _get_backend(self,backend):
+	def _get_backend(self, backend):
 		backend_id = backend or self.cfg.rpc_backend
 		backend_id = backend or self.cfg.rpc_backend
 		if backend_id == 'auto':
 		if backend_id == 'auto':
 			return {
 			return {
@@ -295,12 +295,12 @@ class RPCClient(MMGenObject):
 				'win32': RPCBackends.requests
 				'win32': RPCBackends.requests
 			}[sys.platform](self)
 			}[sys.platform](self)
 		else:
 		else:
-			return getattr(RPCBackends,backend_id)(self)
+			return getattr(RPCBackends, backend_id)(self)
 
 
-	def set_backend(self,backend=None):
+	def set_backend(self, backend=None):
 		self.backend = self._get_backend(backend)
 		self.backend = self._get_backend(backend)
 
 
-	async def set_backend_async(self,backend=None):
+	async def set_backend_async(self, backend=None):
 		ret = self._get_backend(backend)
 		ret = self._get_backend(backend)
 		self.backend = (await ret) if type(ret).__name__ == 'coroutine' else ret
 		self.backend = (await ret) if type(ret).__name__ == 'coroutine' else ret
 
 
@@ -308,17 +308,17 @@ class RPCClient(MMGenObject):
 	# - positional params are passed to the daemon, 'timeout' and 'wallet' kwargs to the backend
 	# - positional params are passed to the daemon, 'timeout' and 'wallet' kwargs to the backend
 	# - 'wallet' kwarg is used only by regtest
 	# - 'wallet' kwarg is used only by regtest
 
 
-	async def call(self,method,*params,timeout=None,wallet=None):
+	async def call(self, method, *params, timeout=None, wallet=None):
 		"""
 		"""
 		default call: call with param list unrolled, exactly as with cli
 		default call: call with param list unrolled, exactly as with cli
 		"""
 		"""
 		return self.process_http_resp(await self.backend.run(
 		return self.process_http_resp(await self.backend.run(
-			payload = {'id': 1, 'jsonrpc': '2.0', 'method': method, 'params': params },
+			payload = {'id': 1, 'jsonrpc': '2.0', 'method': method, 'params': params},
 			timeout = timeout,
 			timeout = timeout,
 			host_path = self.make_host_path(wallet)
 			host_path = self.make_host_path(wallet)
 		))
 		))
 
 
-	async def batch_call(self,method,param_list,timeout=None,wallet=None):
+	async def batch_call(self, method, param_list, timeout=None, wallet=None):
 		"""
 		"""
 		Make a single call with a list of tuples as first argument
 		Make a single call with a list of tuples as first argument
 		For RPC calls that return a list of results
 		For RPC calls that return a list of results
@@ -328,17 +328,17 @@ class RPCClient(MMGenObject):
 				'id': n,
 				'id': n,
 				'jsonrpc': '2.0',
 				'jsonrpc': '2.0',
 				'method': method,
 				'method': method,
-				'params': params } for n,params in enumerate(param_list,1) ],
+				'params': params} for n, params in enumerate(param_list, 1)],
 			timeout = timeout,
 			timeout = timeout,
 			host_path = self.make_host_path(wallet)
 			host_path = self.make_host_path(wallet)
-		),batch=True)
+		), batch=True)
 
 
-	async def gathered_call(self,method,args_list,timeout=None,wallet=None):
+	async def gathered_call(self, method, args_list, timeout=None, wallet=None):
 		"""
 		"""
 		Perform multiple RPC calls, returning results in a list
 		Perform multiple RPC calls, returning results in a list
 		Can be called two ways:
 		Can be called two ways:
 		  1) method = methodname, args_list = [args_tuple1, args_tuple2,...]
 		  1) method = methodname, args_list = [args_tuple1, args_tuple2,...]
-		  2) method = None, args_list = [(methodname1,args_tuple1), (methodname2,args_tuple2), ...]
+		  2) method = None, args_list = [(methodname1, args_tuple1), (methodname2, args_tuple2), ...]
 		"""
 		"""
 		cmd_list = args_list if method is None else tuple(zip([method] * len(args_list), args_list))
 		cmd_list = args_list if method is None else tuple(zip([method] * len(args_list), args_list))
 
 
@@ -348,10 +348,10 @@ class RPCClient(MMGenObject):
 
 
 		while cur_pos < len(cmd_list):
 		while cur_pos < len(cmd_list):
 			tasks = [self.backend.run(
 			tasks = [self.backend.run(
-						payload = {'id': n, 'jsonrpc': '2.0', 'method': method, 'params': params },
+						payload = {'id': n, 'jsonrpc': '2.0', 'method': method, 'params': params},
 						timeout = timeout,
 						timeout = timeout,
 						host_path = self.make_host_path(wallet)
 						host_path = self.make_host_path(wallet)
-					) for n,(method,params)  in enumerate(cmd_list[cur_pos:chunk_size+cur_pos],1)]
+					) for n, (method, params)  in enumerate(cmd_list[cur_pos:chunk_size+cur_pos], 1)]
 			ret.extend(await asyncio.gather(*tasks))
 			ret.extend(await asyncio.gather(*tasks))
 			cur_pos += chunk_size
 			cur_pos += chunk_size
 
 
@@ -362,22 +362,22 @@ class RPCClient(MMGenObject):
 	# - remaining kwargs are passed to CallSigs method
 	# - remaining kwargs are passed to CallSigs method
 	# - CallSigs method returns method and positional params for Call method
 	# - CallSigs method returns method and positional params for Call method
 
 
-	def icall(self,method,**kwargs):
-		timeout = kwargs.pop('timeout',None)
-		wallet = kwargs.pop('wallet',None)
+	def icall(self, method, **kwargs):
+		timeout = kwargs.pop('timeout', None)
+		wallet = kwargs.pop('wallet', None)
 		return self.call(
 		return self.call(
-			*getattr(self.call_sigs,method)(**kwargs),
+			*getattr(self.call_sigs, method)(**kwargs),
 			timeout = timeout,
 			timeout = timeout,
-			wallet = wallet )
+			wallet = wallet)
 
 
-	def gathered_icall(self,method,args_list,timeout=None,wallet=None):
+	def gathered_icall(self, method, args_list, timeout=None, wallet=None):
 		return self.gathered_call(
 		return self.gathered_call(
 			method,
 			method,
-			[getattr(self.call_sigs,method)(*a)[1:] for a in args_list],
+			[getattr(self.call_sigs, method)(*a)[1:] for a in args_list],
 			timeout = timeout,
 			timeout = timeout,
-			wallet = wallet )
+			wallet = wallet)
 
 
-	def process_http_resp(self,run_ret,batch=False,json_rpc=True):
+	def process_http_resp(self, run_ret, batch=False, json_rpc=True):
 
 
 		def float_parser(n):
 		def float_parser(n):
 			return n
 			return n
@@ -385,22 +385,22 @@ class RPCClient(MMGenObject):
 		text, status = run_ret
 		text, status = run_ret
 
 
 		if status == 200:
 		if status == 200:
-			dmsg_rpc('    RPC RESPONSE data ==>\n{}\n',text,is_json=True)
+			dmsg_rpc('    RPC RESPONSE data ==>\n{}\n', text, is_json=True)
 			m = None
 			m = None
 			if batch:
 			if batch:
-				return [r['result'] for r in json.loads(text,parse_float=float_parser)]
+				return [r['result'] for r in json.loads(text, parse_float=float_parser)]
 			else:
 			else:
 				try:
 				try:
 					if json_rpc:
 					if json_rpc:
-						ret = json.loads(text,parse_float=float_parser)['result']
-						if isinstance(ret,list) and ret and type(ret[0]) == dict and 'success' in ret[0]:
+						ret = json.loads(text, parse_float=float_parser)['result']
+						if isinstance(ret, list) and ret and type(ret[0]) == dict and 'success' in ret[0]:
 							for res in ret:
 							for res in ret:
 								if not res['success']:
 								if not res['success']:
 									m = str(res['error'])
 									m = str(res['error'])
 									assert False
 									assert False
 						return ret
 						return ret
 					else:
 					else:
-						return json.loads(text,parse_float=float_parser)
+						return json.loads(text, parse_float=float_parser)
 				except:
 				except:
 					if not m:
 					if not m:
 						t = json.loads(text)
 						t = json.loads(text)
@@ -414,7 +414,7 @@ class RPCClient(MMGenObject):
 					die('RPCFailure', m)
 					die('RPCFailure', m)
 		else:
 		else:
 			import http
 			import http
-			m,s = ( '', http.HTTPStatus(status) )
+			m, s = ('', http.HTTPStatus(status))
 			if text:
 			if text:
 				try:
 				try:
 					m = json.loads(text)['error']['message']
 					m = json.loads(text)['error']['message']
@@ -423,9 +423,9 @@ class RPCClient(MMGenObject):
 						m = text.decode()
 						m = text.decode()
 					except:
 					except:
 						m = text
 						m = text
-			die( 'RPCFailure', f'{s.value} {s.name}: {m}' )
+			die('RPCFailure', f'{s.value} {s.name}: {m}')
 
 
-	async def stop_daemon(self,quiet=False,silent=False):
+	async def stop_daemon(self, quiet=False, silent=False):
 		if self.daemon.state == 'ready':
 		if self.daemon.state == 'ready':
 			if not (quiet or silent):
 			if not (quiet or silent):
 				msg(f'Stopping {self.daemon.desc} on port {self.daemon.bind_port}')
 				msg(f'Stopping {self.daemon.desc} on port {self.daemon.bind_port}')
@@ -438,24 +438,24 @@ class RPCClient(MMGenObject):
 				msg(f'{self.daemon.desc} on port {self.daemon.bind_port} not running')
 				msg(f'{self.daemon.desc} on port {self.daemon.bind_port} not running')
 			return True
 			return True
 
 
-	def start_daemon(self,silent=False):
+	def start_daemon(self, silent=False):
 		return self.daemon.start(silent=silent)
 		return self.daemon.start(silent=silent)
 
 
-	async def restart_daemon(self,quiet=False,silent=False):
-		await self.stop_daemon(quiet=quiet,silent=silent)
+	async def restart_daemon(self, quiet=False, silent=False):
+		await self.stop_daemon(quiet=quiet, silent=silent)
 		return self.daemon.start(silent=silent)
 		return self.daemon.start(silent=silent)
 
 
-	def handle_unsupported_daemon_version(self,name,warn_only):
+	def handle_unsupported_daemon_version(self, name, warn_only):
 
 
 		class daemon_version_warning(oneshot_warning):
 		class daemon_version_warning(oneshot_warning):
 			color = 'yellow'
 			color = 'yellow'
 			message = 'ignoring unsupported {} daemon version at user request'
 			message = 'ignoring unsupported {} daemon version at user request'
 
 
 		if warn_only:
 		if warn_only:
-			daemon_version_warning(div=name,fmt_args=[self.daemon.coind_name])
+			daemon_version_warning(div=name, fmt_args=[self.daemon.coind_name])
 		else:
 		else:
 			name = self.daemon.coind_name
 			name = self.daemon.coind_name
-			die(2,'\n'+fmt(f"""
+			die(2, '\n'+fmt(f"""
 				The running {name} daemon has version {self.daemon_version_str}.
 				The running {name} daemon has version {self.daemon_version_str}.
 				This version of MMGen is tested only on {name} v{self.daemon.coind_version_str} and below.
 				This version of MMGen is tested only on {name} v{self.daemon.coind_version_str} and below.
 
 
@@ -463,7 +463,7 @@ class RPCClient(MMGenObject):
 
 
 				Alternatively, you may invoke the command with the --ignore-daemon-version
 				Alternatively, you may invoke the command with the --ignore-daemon-version
 				option, in which case you proceed at your own risk.
 				option, in which case you proceed at your own risk.
-				""",indent='    '))
+				""", indent='    '))
 
 
 async def rpc_init(
 async def rpc_init(
 		cfg,
 		cfg,
@@ -471,36 +471,36 @@ async def rpc_init(
 		backend               = None,
 		backend               = None,
 		daemon                = None,
 		daemon                = None,
 		ignore_daemon_version = False,
 		ignore_daemon_version = False,
-		ignore_wallet         = False ):
+		ignore_wallet         = False):
 
 
 	proto = proto or cfg._proto
 	proto = proto or cfg._proto
 
 
 	if not 'rpc_init' in proto.mmcaps:
 	if not 'rpc_init' in proto.mmcaps:
-		die(1,f'rpc_init() not supported for {proto.name} protocol!')
+		die(1, f'rpc_init() not supported for {proto.name} protocol!')
 
 
 	cls = getattr(
 	cls = getattr(
 		importlib.import_module(f'mmgen.proto.{proto.base_proto_coin.lower()}.rpc'),
 		importlib.import_module(f'mmgen.proto.{proto.base_proto_coin.lower()}.rpc'),
-			proto.base_proto + 'RPCClient' )
+			proto.base_proto + 'RPCClient')
 
 
 	from .daemon import CoinDaemon
 	from .daemon import CoinDaemon
 	rpc = await cls(
 	rpc = await cls(
 		cfg           = cfg,
 		cfg           = cfg,
 		proto         = proto,
 		proto         = proto,
-		daemon        = daemon or CoinDaemon(cfg,proto=proto,test_suite=cfg.test_suite),
+		daemon        = daemon or CoinDaemon(cfg, proto=proto, test_suite=cfg.test_suite),
 		backend       = backend or cfg.rpc_backend,
 		backend       = backend or cfg.rpc_backend,
-		ignore_wallet = ignore_wallet )
+		ignore_wallet = ignore_wallet)
 
 
 	if rpc.daemon_version > rpc.daemon.coind_version:
 	if rpc.daemon_version > rpc.daemon.coind_version:
 		rpc.handle_unsupported_daemon_version(
 		rpc.handle_unsupported_daemon_version(
 			proto.name,
 			proto.name,
-			ignore_daemon_version or proto.ignore_daemon_version or cfg.ignore_daemon_version )
+			ignore_daemon_version or proto.ignore_daemon_version or cfg.ignore_daemon_version)
 
 
 	if rpc.chain not in proto.chain_names:
 	if rpc.chain not in proto.chain_names:
-		die( 'RPCChainMismatch', '\n' + fmt(f"""
+		die('RPCChainMismatch', '\n' + fmt(f"""
 			Protocol:           {proto.cls_name}
 			Protocol:           {proto.cls_name}
-			Valid chain names:  {fmt_list(proto.chain_names,fmt='bare')}
+			Valid chain names:  {fmt_list(proto.chain_names, fmt='bare')}
 			RPC client chain:   {rpc.chain}
 			RPC client chain:   {rpc.chain}
-			""",indent='  ').rstrip() )
+			""", indent='  ').rstrip())
 
 
 	rpc.blockcount = NonNegativeInt(rpc.blockcount)
 	rpc.blockcount = NonNegativeInt(rpc.blockcount)
 
 

+ 26 - 26
mmgen/seed.py

@@ -20,41 +20,41 @@
 seed: Seed-related classes and methods for the MMGen suite
 seed: Seed-related classes and methods for the MMGen suite
 """
 """
 
 
-from .util import make_chksum_8,hexdigits_uc,die
-from .objmethods import HiliteStr,InitErrors,MMGenObject
-from .obj import ImmutableAttr,get_obj
+from .util import make_chksum_8, hexdigits_uc, die
+from .objmethods import HiliteStr, InitErrors, MMGenObject
+from .obj import ImmutableAttr, get_obj
 
 
-class SeedID(HiliteStr,InitErrors):
+class SeedID(HiliteStr, InitErrors):
 	color = 'blue'
 	color = 'blue'
 	width = 8
 	width = 8
 	trunc_ok = False
 	trunc_ok = False
-	def __new__(cls,seed=None,sid=None):
-		if isinstance(sid,cls):
+	def __new__(cls, seed=None, sid=None):
+		if isinstance(sid, cls):
 			return sid
 			return sid
 		try:
 		try:
 			if seed:
 			if seed:
-				assert isinstance(seed,SeedBase),'not a subclass of SeedBase'
-				return str.__new__(cls,make_chksum_8(seed.data))
+				assert isinstance(seed, SeedBase), 'not a subclass of SeedBase'
+				return str.__new__(cls, make_chksum_8(seed.data))
 			elif sid:
 			elif sid:
 				assert set(sid) <= set(hexdigits_uc), 'not uppercase hex digits'
 				assert set(sid) <= set(hexdigits_uc), 'not uppercase hex digits'
 				assert len(sid) == cls.width, f'not {cls.width} characters wide'
 				assert len(sid) == cls.width, f'not {cls.width} characters wide'
-				return str.__new__(cls,sid)
+				return str.__new__(cls, sid)
 			raise ValueError('no arguments provided')
 			raise ValueError('no arguments provided')
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,seed or sid)
+			return cls.init_fail(e, seed or sid)
 
 
 def is_seed_id(s):
 def is_seed_id(s):
-	return get_obj( SeedID, sid=s, silent=True, return_bool=True )
+	return get_obj(SeedID, sid=s, silent=True, return_bool=True)
 
 
 class SeedBase(MMGenObject):
 class SeedBase(MMGenObject):
 
 
-	lens = ( 128, 192, 256 )
+	lens = (128, 192, 256)
 	dfl_len = 256
 	dfl_len = 256
 
 
-	data = ImmutableAttr(bytes,typeconv=False)
-	sid  = ImmutableAttr(SeedID,typeconv=False)
+	data = ImmutableAttr(bytes, typeconv=False)
+	sid  = ImmutableAttr(SeedID, typeconv=False)
 
 
-	def __init__(self,cfg,seed_bin=None,nSubseeds=None):
+	def __init__(self, cfg, seed_bin=None, nSubseeds=None):
 
 
 		if not seed_bin:
 		if not seed_bin:
 			from .crypto import Crypto
 			from .crypto import Crypto
@@ -62,7 +62,7 @@ class SeedBase(MMGenObject):
 			# Truncate random data for smaller seed lengths
 			# Truncate random data for smaller seed lengths
 			seed_bin = sha256(Crypto(cfg).get_random(1033)).digest()[:(cfg.seed_len or self.dfl_len)//8]
 			seed_bin = sha256(Crypto(cfg).get_random(1033)).digest()[:(cfg.seed_len or self.dfl_len)//8]
 		elif len(seed_bin)*8 not in self.lens:
 		elif len(seed_bin)*8 not in self.lens:
-			die(3,f'{len(seed_bin)*8}: invalid seed bit length')
+			die(3, f'{len(seed_bin)*8}: invalid seed bit length')
 
 
 		self.cfg = cfg
 		self.cfg = cfg
 		self.data = seed_bin
 		self.data = seed_bin
@@ -85,24 +85,24 @@ class Seed(SeedBase):
 
 
 	@property
 	@property
 	def subseeds(self):
 	def subseeds(self):
-		if not hasattr(self,'_subseeds'):
+		if not hasattr(self, '_subseeds'):
 			from .subseed import SubSeedList
 			from .subseed import SubSeedList
 			self._subseeds = SubSeedList(
 			self._subseeds = SubSeedList(
 				self,
 				self,
-				length = self.nSubseeds or self.cfg.subseeds )
+				length = self.nSubseeds or self.cfg.subseeds)
 		return self._subseeds
 		return self._subseeds
 
 
-	def subseed(self,*args,**kwargs):
-		return self.subseeds.get_subseed_by_ss_idx(*args,**kwargs)
+	def subseed(self, *args, **kwargs):
+		return self.subseeds.get_subseed_by_ss_idx(*args, **kwargs)
 
 
-	def subseed_by_seed_id(self,*args,**kwargs):
-		return self.subseeds.get_subseed_by_seed_id(*args,**kwargs)
+	def subseed_by_seed_id(self, *args, **kwargs):
+		return self.subseeds.get_subseed_by_seed_id(*args, **kwargs)
 
 
-	def split(self,*args,**kwargs):
+	def split(self, *args, **kwargs):
 		from .seedsplit import SeedShareList
 		from .seedsplit import SeedShareList
-		return SeedShareList(self,*args,**kwargs)
+		return SeedShareList(self, *args, **kwargs)
 
 
 	@staticmethod
 	@staticmethod
-	def join_shares(*args,**kwargs):
+	def join_shares(*args, **kwargs):
 		from .seedsplit import join_shares
 		from .seedsplit import join_shares
-		return join_shares(*args,**kwargs)
+		return join_shares(*args, **kwargs)

+ 71 - 74
mmgen/seedsplit.py

@@ -21,11 +21,11 @@ seedsplit: Seed split classes and methods for the MMGen suite
 """
 """
 
 
 from .color import yellow
 from .color import yellow
-from .util import msg,die
-from .objmethods import MMGenObject,HiliteStr,InitErrors
-from .obj import ImmutableAttr,MMGenPWIDString,MMGenIdx,get_obj,IndexedDict
-from .seed import Seed,SeedBase
-from .subseed import SubSeedList,SubSeedIdx,SubSeed
+from .util import msg, die
+from .objmethods import MMGenObject, HiliteStr, InitErrors
+from .obj import ImmutableAttr, MMGenPWIDString, MMGenIdx, get_obj, IndexedDict
+from .seed import Seed, SeedBase
+from .subseed import SubSeedList, SubSeedIdx, SubSeed
 from .crypto import Crypto
 from .crypto import Crypto
 
 
 class SeedShareIdx(MMGenIdx):
 class SeedShareIdx(MMGenIdx):
@@ -37,26 +37,26 @@ class SeedShareCount(SeedShareIdx):
 class MasterShareIdx(MMGenIdx):
 class MasterShareIdx(MMGenIdx):
 	max_val = 1024
 	max_val = 1024
 
 
-class SeedSplitSpecifier(HiliteStr,InitErrors,MMGenObject):
+class SeedSplitSpecifier(HiliteStr, InitErrors, MMGenObject):
 	color = 'red'
 	color = 'red'
-	def __new__(cls,s):
-		if isinstance(s,cls):
+	def __new__(cls, s):
+		if isinstance(s, cls):
 			return s
 			return s
 		try:
 		try:
 			arr = s.split(':')
 			arr = s.split(':')
-			assert len(arr) in (2,3), 'cannot be parsed'
-			a,b,c = arr if len(arr) == 3 else ['default'] + arr
-			me = str.__new__(cls,s)
+			assert len(arr) in (2, 3), 'cannot be parsed'
+			a, b, c = arr if len(arr) == 3 else ['default'] + arr
+			me = str.__new__(cls, s)
 			me.id = SeedSplitIDString(a)
 			me.id = SeedSplitIDString(a)
 			me.idx = SeedShareIdx(b)
 			me.idx = SeedShareIdx(b)
 			me.count = SeedShareCount(c)
 			me.count = SeedShareCount(c)
 			assert me.idx <= me.count, 'share index greater than share count'
 			assert me.idx <= me.count, 'share index greater than share count'
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,s)
+			return cls.init_fail(e, s)
 
 
 def is_seed_split_specifier(s):
 def is_seed_split_specifier(s):
-	return get_obj( SeedSplitSpecifier, s=s, silent=True, return_bool=True )
+	return get_obj(SeedSplitSpecifier, s=s, silent=True, return_bool=True)
 
 
 class SeedSplitIDString(MMGenPWIDString):
 class SeedSplitIDString(MMGenPWIDString):
 	desc = 'seed split ID string'
 	desc = 'seed split ID string'
@@ -68,7 +68,7 @@ class SeedShareList(SubSeedList):
 	count  = ImmutableAttr(SeedShareCount)
 	count  = ImmutableAttr(SeedShareCount)
 	id_str = ImmutableAttr(SeedSplitIDString)
 	id_str = ImmutableAttr(SeedSplitIDString)
 
 
-	def __init__(self,parent_seed,count,id_str=None,master_idx=None,debug_last_share=False):
+	def __init__(self, parent_seed, count, id_str=None, master_idx=None, debug_last_share=False):
 		self.member_type = SeedShare
 		self.member_type = SeedShare
 		self.parent_seed = parent_seed
 		self.parent_seed = parent_seed
 		self.id_str = id_str or 'default'
 		self.id_str = id_str or 'default'
@@ -77,13 +77,13 @@ class SeedShareList(SubSeedList):
 
 
 		def make_master_share():
 		def make_master_share():
 			for nonce in range(SeedShare.max_nonce+1):
 			for nonce in range(SeedShare.max_nonce+1):
-				ms = SeedShareMaster(self,master_idx,nonce)
+				ms = SeedShareMaster(self, master_idx, nonce)
 				if ms.sid == parent_seed.sid:
 				if ms.sid == parent_seed.sid:
 					if parent_seed.cfg.debug_subseed:
 					if parent_seed.cfg.debug_subseed:
 						msg(f'master_share seed ID collision with parent seed, incrementing nonce to {nonce+1}')
 						msg(f'master_share seed ID collision with parent seed, incrementing nonce to {nonce+1}')
 				else:
 				else:
 					return ms
 					return ms
-			die( 'SubSeedNonceRangeExceeded', 'nonce range exceeded' )
+			die('SubSeedNonceRangeExceeded', 'nonce range exceeded')
 
 
 		def last_share_debug(last_share):
 		def last_share_debug(last_share):
 			if not debug_last_share:
 			if not debug_last_share:
@@ -98,29 +98,29 @@ class SeedShareList(SubSeedList):
 
 
 		for nonce in range(SeedShare.max_nonce+1):
 		for nonce in range(SeedShare.max_nonce+1):
 			self.nonce_start = nonce
 			self.nonce_start = nonce
-			self.data = { 'long': IndexedDict(), 'short': IndexedDict() } # 'short' is required as a placeholder
+			self.data = {'long': IndexedDict(), 'short': IndexedDict()} # 'short' is required as a placeholder
 			if self.master_share:
 			if self.master_share:
-				self.data['long'][self.master_share.sid] = (1,self.master_share.nonce)
+				self.data['long'][self.master_share.sid] = (1, self.master_share.nonce)
 			self._generate(count-1)
 			self._generate(count-1)
 			self.last_share = ls = SeedShareLast(self)
 			self.last_share = ls = SeedShareLast(self)
 			if last_share_debug(ls) or ls.sid in self.data['long'] or ls.sid == parent_seed.sid:
 			if last_share_debug(ls) or ls.sid in self.data['long'] or ls.sid == parent_seed.sid:
 				# collision: throw out entire split list and redo with new start nonce
 				# collision: throw out entire split list and redo with new start nonce
 				if parent_seed.cfg.debug_subseed:
 				if parent_seed.cfg.debug_subseed:
-					self._collision_debug_msg(ls.sid,count,nonce,'nonce_start',debug_last_share)
+					self._collision_debug_msg(ls.sid, count, nonce, 'nonce_start', debug_last_share)
 			else:
 			else:
-				self.data['long'][ls.sid] = (count,nonce)
+				self.data['long'][ls.sid] = (count, nonce)
 				break
 				break
 		else:
 		else:
-			die( 'SubSeedNonceRangeExceeded', 'nonce range exceeded' )
+			die('SubSeedNonceRangeExceeded', 'nonce range exceeded')
 
 
 		if parent_seed.cfg.debug_subseed:
 		if parent_seed.cfg.debug_subseed:
 			A = parent_seed.data
 			A = parent_seed.data
 			B = self.join().data
 			B = self.join().data
 			assert A == B, f'Data mismatch!\noriginal seed: {A!r}\nrejoined seed: {B!r}'
 			assert A == B, f'Data mismatch!\noriginal seed: {A!r}\nrejoined seed: {B!r}'
 
 
-	def get_share_by_idx(self,idx,base_seed=False):
+	def get_share_by_idx(self, idx, base_seed=False):
 		if idx < 1 or idx > self.count:
 		if idx < 1 or idx > self.count:
-			die( 'RangeError', f'{idx}: share index out of range' )
+			die('RangeError', f'{idx}: share index out of range')
 		elif idx == self.count:
 		elif idx == self.count:
 			return self.last_share
 			return self.last_share
 		elif self.master_share and idx == 1:
 		elif self.master_share and idx == 1:
@@ -129,7 +129,7 @@ class SeedShareList(SubSeedList):
 			ss_idx = SubSeedIdx(str(idx) + 'L')
 			ss_idx = SubSeedIdx(str(idx) + 'L')
 			return self.get_subseed_by_ss_idx(ss_idx)
 			return self.get_subseed_by_ss_idx(ss_idx)
 
 
-	def get_share_by_seed_id(self,sid,base_seed=False):
+	def get_share_by_seed_id(self, sid, base_seed=False):
 		if sid == self.data['long'].key(self.count-1):
 		if sid == self.data['long'].key(self.count-1):
 			return self.last_share
 			return self.last_share
 		elif self.master_share and sid == self.data['long'].key(0):
 		elif self.master_share and sid == self.data['long'].key(0):
@@ -140,26 +140,26 @@ class SeedShareList(SubSeedList):
 	def join(self):
 	def join(self):
 		return Seed.join_shares(
 		return Seed.join_shares(
 			self.parent_seed.cfg,
 			self.parent_seed.cfg,
-			[self.get_share_by_idx(i+1) for i in range(len(self))] )
+			[self.get_share_by_idx(i+1) for i in range(len(self))])
 
 
 	def format(self):
 	def format(self):
 		assert self.split_type == 'N-of-N'
 		assert self.split_type == 'N-of-N'
 		fs1 = '    {}\n'
 		fs1 = '    {}\n'
 		fs2 = '{i:>5}: {}\n'
 		fs2 = '{i:>5}: {}\n'
-		mfs1,mfs2,midx,msid = ('','','','')
+		mfs1, mfs2, midx, msid = ('', '', '', '')
 		if self.master_share:
 		if self.master_share:
-			mfs1,mfs2 = (' with master share #{} ({})',' (master share #{})')
-			midx,msid = (self.master_share.idx,self.master_share.sid)
+			mfs1, mfs2 = (' with master share #{} ({})', ' (master share #{})')
+			midx, msid = (self.master_share.idx, self.master_share.sid)
 
 
-		hdr  = '    {} {} ({} bits)\n'.format('Seed:',self.parent_seed.sid.hl(),self.parent_seed.bitlen)
-		hdr += '    {} {c}-of-{c} (XOR){m}\n'.format('Split Type:',c=self.count,m=mfs1.format(midx,msid))
-		hdr += '    {} {}\n\n'.format('ID String:',self.id_str.hl())
+		hdr  = '    {} {} ({} bits)\n'.format('Seed:', self.parent_seed.sid.hl(), self.parent_seed.bitlen)
+		hdr += '    {} {c}-of-{c} (XOR){m}\n'.format('Split Type:', c=self.count, m=mfs1.format(midx, msid))
+		hdr += '    {} {}\n\n'.format('ID String:', self.id_str.hl())
 		hdr += fs1.format('Shares')
 		hdr += fs1.format('Shares')
 		hdr += fs1.format('------')
 		hdr += fs1.format('------')
 
 
 		sl = self.data['long'].keys
 		sl = self.data['long'].keys
-		body1 = fs2.format(sl[0]+mfs2.format(midx),i=1)
-		body = (fs2.format(sl[n],i=n+1) for n in range(1,len(self)))
+		body1 = fs2.format(sl[0]+mfs2.format(midx), i=1)
+		body = (fs2.format(sl[n], i=n+1) for n in range(1, len(self)))
 
 
 		return hdr + body1 + ''.join(body)
 		return hdr + body1 + ''.join(body)
 
 
@@ -181,103 +181,100 @@ class SeedShareBase(MMGenObject):
 	def desc(self):
 	def desc(self):
 		return self.get_desc()
 		return self.get_desc()
 
 
-	def get_desc(self,ui=False):
+	def get_desc(self, ui=False):
 		pl = self.parent_list
 		pl = self.parent_list
 		mss = f', with master share #{pl.master_share.idx}' if pl.master_share else ''
 		mss = f', with master share #{pl.master_share.idx}' if pl.master_share else ''
 		if ui:
 		if ui:
-			m   = ( yellow("(share {} of {} of ")
+			m   = (yellow("(share {} of {} of ")
 					+ pl.parent_seed.sid.hl()
 					+ pl.parent_seed.sid.hl()
 					+ yellow(', split id ')
 					+ yellow(', split id ')
 					+ pl.id_str.hl2(encl='‘’')
 					+ pl.id_str.hl2(encl='‘’')
-					+ yellow('{})') )
+					+ yellow('{})'))
 		else:
 		else:
 			m = "share {} of {} of " + pl.parent_seed.sid + ", split id '" + pl.id_str + "'{}"
 			m = "share {} of {} of " + pl.parent_seed.sid + ", split id '" + pl.id_str + "'{}"
-		return m.format(self.idx,pl.count,mss)
+		return m.format(self.idx, pl.count, mss)
 
 
-class SeedShare(SeedShareBase,SubSeed):
+class SeedShare(SeedShareBase, SubSeed):
 
 
 	@staticmethod
 	@staticmethod
-	def make_subseed_bin(parent_list,idx:int,nonce:int,length:str):
+	def make_subseed_bin(parent_list, idx:int, nonce:int, length:str):
 		seed = parent_list.parent_seed
 		seed = parent_list.parent_seed
 		assert parent_list.have_short is False
 		assert parent_list.have_short is False
 		assert length == 'long'
 		assert length == 'long'
 		# field maximums: id_str: none (256 chars), count: 65535 (1024), idx: 65535 (1024), nonce: 65535 (1000)
 		# field maximums: id_str: none (256 chars), count: 65535 (1024), idx: 65535 (1024), nonce: 65535 (1000)
 		scramble_key = (
 		scramble_key = (
 			f'{parent_list.split_type}:{parent_list.id_str}:'.encode() +
 			f'{parent_list.split_type}:{parent_list.id_str}:'.encode() +
-			parent_list.count.to_bytes(2,'big') +
-			idx.to_bytes(2,'big') +
-			nonce.to_bytes(2,'big')
+			parent_list.count.to_bytes(2, 'big') +
+			idx.to_bytes(2, 'big') +
+			nonce.to_bytes(2, 'big')
 		)
 		)
 		if parent_list.master_share:
 		if parent_list.master_share:
 			scramble_key += (
 			scramble_key += (
 				b':master:' +
 				b':master:' +
-				parent_list.master_share.idx.to_bytes(2,'big')
+				parent_list.master_share.idx.to_bytes(2, 'big')
 			)
 			)
-		return Crypto(parent_list.parent_seed.cfg).scramble_seed(seed.data,scramble_key)[:seed.byte_len]
+		return Crypto(parent_list.parent_seed.cfg).scramble_seed(seed.data, scramble_key)[:seed.byte_len]
 
 
-class SeedShareLast(SeedShareBase,SeedBase):
+class SeedShareLast(SeedShareBase, SeedBase):
 
 
 	idx = ImmutableAttr(SeedShareIdx)
 	idx = ImmutableAttr(SeedShareIdx)
 	nonce = 0
 	nonce = 0
 
 
-	def __init__(self,parent_list):
+	def __init__(self, parent_list):
 		self.idx = parent_list.count
 		self.idx = parent_list.count
 		self.parent_list = parent_list
 		self.parent_list = parent_list
 		SeedBase.__init__(
 		SeedBase.__init__(
 			self,
 			self,
 			parent_list.parent_seed.cfg,
 			parent_list.parent_seed.cfg,
-			seed_bin=self.make_subseed_bin(parent_list) )
+			seed_bin=self.make_subseed_bin(parent_list))
 
 
 	@staticmethod
 	@staticmethod
 	def make_subseed_bin(parent_list):
 	def make_subseed_bin(parent_list):
 		seed_list = (parent_list.get_share_by_idx(i+1) for i in range(len(parent_list)))
 		seed_list = (parent_list.get_share_by_idx(i+1) for i in range(len(parent_list)))
 		seed = parent_list.parent_seed
 		seed = parent_list.parent_seed
 
 
-		ret = int(seed.data.hex(),16)
+		ret = int(seed.data.hex(), 16)
 		for ss in seed_list:
 		for ss in seed_list:
-			ret ^= int(ss.data.hex(),16)
+			ret ^= int(ss.data.hex(), 16)
 
 
-		return ret.to_bytes(seed.byte_len,'big')
+		return ret.to_bytes(seed.byte_len, 'big')
 
 
-class SeedShareMaster(SeedBase,SeedShareBase):
+class SeedShareMaster(SeedBase, SeedShareBase):
 
 
 	idx   = ImmutableAttr(MasterShareIdx)
 	idx   = ImmutableAttr(MasterShareIdx)
-	nonce = ImmutableAttr(int,typeconv=False)
+	nonce = ImmutableAttr(int, typeconv=False)
 
 
-	def __init__(self,parent_list,idx,nonce):
+	def __init__(self, parent_list, idx, nonce):
 		self.idx = idx
 		self.idx = idx
 		self.nonce = nonce
 		self.nonce = nonce
 		self.parent_list = parent_list
 		self.parent_list = parent_list
 		self.cfg = parent_list.parent_seed.cfg
 		self.cfg = parent_list.parent_seed.cfg
 
 
-		SeedBase.__init__( self, self.cfg, self.make_base_seed_bin() )
+		SeedBase.__init__(self, self.cfg, self.make_base_seed_bin())
 
 
 		self.derived_seed = SeedBase(
 		self.derived_seed = SeedBase(
 			self.cfg,
 			self.cfg,
-			self.make_derived_seed_bin( parent_list.id_str, parent_list.count )
+			self.make_derived_seed_bin(parent_list.id_str, parent_list.count)
 		)
 		)
 
 
 	@property
 	@property
 	def fn_stem(self):
 	def fn_stem(self):
-		return '{}-MASTER{}[{}]'.format(
-			self.parent_list.parent_seed.sid,
-			self.idx,
-			self.sid )
+		return '{}-MASTER{}[{}]'.format(self.parent_list.parent_seed.sid, self.idx, self.sid)
 
 
 	def make_base_seed_bin(self):
 	def make_base_seed_bin(self):
 		seed = self.parent_list.parent_seed
 		seed = self.parent_list.parent_seed
 		# field maximums: idx: 65535 (1024)
 		# field maximums: idx: 65535 (1024)
-		scramble_key = b'master_share:' + self.idx.to_bytes(2,'big') + self.nonce.to_bytes(2,'big')
-		return Crypto(self.cfg).scramble_seed(seed.data,scramble_key)[:seed.byte_len]
+		scramble_key = b'master_share:' + self.idx.to_bytes(2, 'big') + self.nonce.to_bytes(2, 'big')
+		return Crypto(self.cfg).scramble_seed(seed.data, scramble_key)[:seed.byte_len]
 
 
 	# Don't bother with avoiding seed ID collision here, as sid of derived seed is not used
 	# Don't bother with avoiding seed ID collision here, as sid of derived seed is not used
 	# by user as an identifier
 	# by user as an identifier
-	def make_derived_seed_bin(self,id_str,count):
+	def make_derived_seed_bin(self, id_str, count):
 		# field maximums: id_str: none (256 chars), count: 65535 (1024)
 		# field maximums: id_str: none (256 chars), count: 65535 (1024)
-		scramble_key = id_str.encode() + b':' + count.to_bytes(2,'big')
-		return Crypto(self.cfg).scramble_seed(self.data,scramble_key)[:self.byte_len]
+		scramble_key = id_str.encode() + b':' + count.to_bytes(2, 'big')
+		return Crypto(self.cfg).scramble_seed(self.data, scramble_key)[:self.byte_len]
 
 
-	def get_desc(self,ui=False):
+	def get_desc(self, ui=False):
 		psid = self.parent_list.parent_seed.sid
 		psid = self.parent_list.parent_seed.sid
 		mss = f'master share #{self.idx} of '
 		mss = f'master share #{self.idx} of '
 		return yellow('(' + mss) + psid.hl() + yellow(')') if ui else mss + psid
 		return yellow('(' + mss) + psid.hl() + yellow(')') if ui else mss + psid
@@ -287,33 +284,33 @@ class SeedShareMasterJoining(SeedShareMaster):
 	id_str = ImmutableAttr(SeedSplitIDString)
 	id_str = ImmutableAttr(SeedSplitIDString)
 	count  = ImmutableAttr(SeedShareCount)
 	count  = ImmutableAttr(SeedShareCount)
 
 
-	def __init__(self,cfg,idx,base_seed,id_str,count):
+	def __init__(self, cfg, idx, base_seed, id_str, count):
 
 
-		SeedBase.__init__( self, cfg, seed_bin=base_seed.data )
+		SeedBase.__init__(self, cfg, seed_bin=base_seed.data)
 
 
 		self.cfg = cfg
 		self.cfg = cfg
 		self.id_str = id_str or 'default'
 		self.id_str = id_str or 'default'
 		self.count = count
 		self.count = count
-		self.derived_seed = SeedBase( cfg, self.make_derived_seed_bin(self.id_str,self.count) )
+		self.derived_seed = SeedBase(cfg, self.make_derived_seed_bin(self.id_str, self.count))
 
 
 def join_shares(
 def join_shares(
 		cfg,
 		cfg,
 		seed_list,
 		seed_list,
 		master_idx = None,
 		master_idx = None,
-		id_str     = None ):
+		id_str     = None):
 
 
-	if not hasattr(seed_list,'__next__'): # seed_list can be iterator or iterable
+	if not hasattr(seed_list, '__next__'): # seed_list can be iterator or iterable
 		seed_list = iter(seed_list)
 		seed_list = iter(seed_list)
 
 
 	class d:
 	class d:
-		byte_len,ret,count = None,0,0
+		byte_len, ret, count = None, 0, 0
 
 
 	def add_share(ss):
 	def add_share(ss):
 		if d.byte_len:
 		if d.byte_len:
 			assert ss.byte_len == d.byte_len, f'Seed length mismatch! {ss.byte_len} != {d.byte_len}'
 			assert ss.byte_len == d.byte_len, f'Seed length mismatch! {ss.byte_len} != {d.byte_len}'
 		else:
 		else:
 			d.byte_len = ss.byte_len
 			d.byte_len = ss.byte_len
-		d.ret ^= int(ss.data.hex(),16)
+		d.ret ^= int(ss.data.hex(), 16)
 		d.count += 1
 		d.count += 1
 
 
 	if master_idx:
 	if master_idx:
@@ -323,8 +320,8 @@ def join_shares(
 		add_share(ss)
 		add_share(ss)
 
 
 	if master_idx:
 	if master_idx:
-		add_share(SeedShareMasterJoining( cfg, master_idx, master_share, id_str, d.count+1 ).derived_seed)
+		add_share(SeedShareMasterJoining(cfg, master_idx, master_share, id_str, d.count+1).derived_seed)
 
 
 	SeedShareCount(d.count) # check that d.count is in valid range
 	SeedShareCount(d.count) # check that d.count is in valid range
 
 
-	return Seed( cfg, seed_bin=d.ret.to_bytes(d.byte_len,'big') )
+	return Seed(cfg, seed_bin=d.ret.to_bytes(d.byte_len, 'big'))

+ 34 - 34
mmgen/sha2.py

@@ -27,7 +27,7 @@ sha2: A non-optimized but very compact implementation of the SHA2 hash
 # implementation must not be used for anything but testing and study.  Test with
 # implementation must not be used for anything but testing and study.  Test with
 # the test/hashfunc.py script in the MMGen repository.
 # the test/hashfunc.py script in the MMGen repository.
 
 
-from struct import pack,unpack
+from struct import pack, unpack
 
 
 class Sha2:
 class Sha2:
 	'Implementation based on the pseudocode at https://en.wikipedia.org/wiki/SHA-2'
 	'Implementation based on the pseudocode at https://en.wikipedia.org/wiki/SHA-2'
@@ -40,7 +40,7 @@ class Sha2:
 
 
 		def nextPrime(n=2):
 		def nextPrime(n=2):
 			while True:
 			while True:
-				for factor in range(2,int(sqrt(n))+1):
+				for factor in range(2, int(sqrt(n))+1):
 					if n % factor == 0:
 					if n % factor == 0:
 						break
 						break
 				else:
 				else:
@@ -56,9 +56,9 @@ class Sha2:
 			return int((n - int(n)) * fb_mul)
 			return int((n - int(n)) * fb_mul)
 
 
 		if cls.use_gmp:
 		if cls.use_gmp:
-			from gmpy2 import context,set_context,sqrt,cbrt
+			from gmpy2 import context, set_context, sqrt, cbrt
 			# context() parameters are platform-dependent!
 			# context() parameters are platform-dependent!
-			set_context(context(precision=75,round=1)) # OK for gmp 6.1.2 / gmpy 2.1.0
+			set_context(context(precision=75, round=1)) # OK for gmp 6.1.2 / gmpy 2.1.0
 		else:
 		else:
 			cbrt = lambda n: pow(n, 1 / 3)
 			cbrt = lambda n: pow(n, 1 / 3)
 
 
@@ -68,9 +68,9 @@ class Sha2:
 		# First wordBits bits of the fractional parts of the cube roots of the first nRounds primes
 		# First wordBits bits of the fractional parts of the cube roots of the first nRounds primes
 		cls.K = tuple(getFractionalBits(cbrt(n)) for n in primes)
 		cls.K = tuple(getFractionalBits(cbrt(n)) for n in primes)
 
 
-	def __init__(self,message,preprocess=True):
+	def __init__(self, message, preprocess=True):
 		'Use preprocess=False for Sha256Compress'
 		'Use preprocess=False for Sha256Compress'
-		assert isinstance(message,(bytes,bytearray,list)),'message must be of type bytes, bytearray or list'
+		assert isinstance(message, (bytes, bytearray, list)), 'message must be of type bytes, bytearray or list'
 		if not self.K:
 		if not self.K:
 			type(self).initConstants()
 			type(self).initConstants()
 		self.H = list(self.H_init)
 		self.H = list(self.H_init)
@@ -100,25 +100,25 @@ class Sha2:
 	def bytesToWords(self):
 	def bytesToWords(self):
 		ws = self.wordSize
 		ws = self.wordSize
 		assert len(self.M) % ws == 0
 		assert len(self.M) % ws == 0
-		self.M = tuple(unpack(self.word_fmt,self.M[i*ws:ws+(i*ws)])[0] for i in range(len(self.M) // ws))
+		self.M = tuple(unpack(self.word_fmt, self.M[i*ws:ws+(i*ws)])[0] for i in range(len(self.M) // ws))
 
 
 	def digest(self):
 	def digest(self):
-		return b''.join((pack(self.word_fmt,w) for w in self.H))
+		return b''.join((pack(self.word_fmt, w) for w in self.H))
 
 
 	def hexdigest(self):
 	def hexdigest(self):
 		return self.digest().hex()
 		return self.digest().hex()
 
 
 	def compute(self):
 	def compute(self):
-		for i in range(0,len(self.M),16):
+		for i in range(0, len(self.M), 16):
 			self.processBlock(i)
 			self.processBlock(i)
 
 
-	def processBlock(self,offset):
+	def processBlock(self, offset):
 		'Process a blkSize-byte chunk of the message'
 		'Process a blkSize-byte chunk of the message'
 
 
-		def rrotate(a,b):
+		def rrotate(a, b):
 			return ((a << self.wordBits-b) & self.wordMask) | (a >> b)
 			return ((a << self.wordBits-b) & self.wordMask) | (a >> b)
 
 
-		def addm(a,b):
+		def addm(a, b):
 			return (a + b) & self.wordMask
 			return (a + b) & self.wordMask
 
 
 		# Copy chunk into first 16 words of message schedule array
 		# Copy chunk into first 16 words of message schedule array
@@ -126,46 +126,46 @@ class Sha2:
 			self.W[i] = self.M[offset + i]
 			self.W[i] = self.M[offset + i]
 
 
 		# Extend the first 16 words into the remaining nRounds words of message schedule array
 		# Extend the first 16 words into the remaining nRounds words of message schedule array
-		for i in range(16,self.nRounds):
+		for i in range(16, self.nRounds):
 			g0 = self.W[i-15]
 			g0 = self.W[i-15]
-			gamma0 = rrotate(g0,self.g0r1) ^ rrotate(g0,self.g0r2) ^ (g0 >> self.g0r3)
+			gamma0 = rrotate(g0, self.g0r1) ^ rrotate(g0, self.g0r2) ^ (g0 >> self.g0r3)
 			g1 = self.W[i-2]
 			g1 = self.W[i-2]
-			gamma1 = rrotate(g1,self.g1r1) ^ rrotate(g1,self.g1r2) ^ (g1 >> self.g1r3)
-			self.W[i] = addm(addm(addm(gamma0,self.W[i-7]),gamma1),self.W[i-16])
+			gamma1 = rrotate(g1, self.g1r1) ^ rrotate(g1, self.g1r2) ^ (g1 >> self.g1r3)
+			self.W[i] = addm(addm(addm(gamma0, self.W[i-7]), gamma1), self.W[i-16])
 
 
 		# Initialize working variables from current hash state
 		# Initialize working variables from current hash state
-		a,b,c,d,e,f,g,h = self.H
+		a, b, c, d, e, f, g, h = self.H
 
 
 		# Compression function main loop
 		# Compression function main loop
 		for i in range(self.nRounds):
 		for i in range(self.nRounds):
 			ch = (e & f) ^ (~e & g)
 			ch = (e & f) ^ (~e & g)
 			maj = (a & b) ^ (a & c) ^ (b & c)
 			maj = (a & b) ^ (a & c) ^ (b & c)
 
 
-			sigma0 = rrotate(a,self.s0r1) ^ rrotate(a,self.s0r2) ^ rrotate(a,self.s0r3)
-			sigma1 = rrotate(e,self.s1r1) ^ rrotate(e,self.s1r2) ^ rrotate(e,self.s1r3)
+			sigma0 = rrotate(a, self.s0r1) ^ rrotate(a, self.s0r2) ^ rrotate(a, self.s0r3)
+			sigma1 = rrotate(e, self.s1r1) ^ rrotate(e, self.s1r2) ^ rrotate(e, self.s1r3)
 
 
-			t1 = addm(addm(addm(addm(h,sigma1),ch),self.K[i]),self.W[i])
-			t2 = addm(sigma0,maj)
+			t1 = addm(addm(addm(addm(h, sigma1), ch), self.K[i]), self.W[i])
+			t2 = addm(sigma0, maj)
 
 
 			h = g
 			h = g
 			g = f
 			g = f
 			f = e
 			f = e
-			e = addm(d,t1)
+			e = addm(d, t1)
 			d = c
 			d = c
 			c = b
 			c = b
 			b = a
 			b = a
-			a = addm(t1,t2)
+			a = addm(t1, t2)
 
 
 		# Save hash state
 		# Save hash state
-		for n,v in enumerate((a,b,c,d,e,f,g,h)):
-			self.H[n] = addm(self.H[n],v)
+		for n, v in enumerate((a, b, c, d, e, f, g, h)):
+			self.H[n] = addm(self.H[n], v)
 
 
 class Sha256(Sha2):
 class Sha256(Sha2):
 	use_gmp = False
 	use_gmp = False
-	g0r1,g0r2,g0r3 = (7,18,3)
-	g1r1,g1r2,g1r3 = (17,19,10)
-	s0r1,s0r2,s0r3 = (2,13,22)
-	s1r1,s1r2,s1r3 = (6,11,25)
+	g0r1, g0r2, g0r3 = (7, 18, 3)
+	g1r1, g1r2, g1r3 = (17, 19, 10)
+	s0r1, s0r2, s0r3 = (2, 13, 22)
+	s1r1, s1r2, s1r3 = (6, 11, 25)
 	blkSize = 64
 	blkSize = 64
 	nRounds = 64
 	nRounds = 64
 	wordSize = 4
 	wordSize = 4
@@ -174,7 +174,7 @@ class Sha256(Sha2):
 	word_fmt = '>I'
 	word_fmt = '>I'
 
 
 	def pack_msglen(self):
 	def pack_msglen(self):
-		return pack('>Q',len(self.M)*8)
+		return pack('>Q', len(self.M)*8)
 
 
 class Sha512(Sha2):
 class Sha512(Sha2):
 	"""
 	"""
@@ -190,10 +190,10 @@ class Sha512(Sha2):
 	- the shift and rotate amounts used are different.
 	- the shift and rotate amounts used are different.
 	"""
 	"""
 	use_gmp = True
 	use_gmp = True
-	g0r1,g0r2,g0r3 = (1,8,7)
-	g1r1,g1r2,g1r3 = (19,61,6)
-	s0r1,s0r2,s0r3 = (28,34,39)
-	s1r1,s1r2,s1r3 = (14,18,41)
+	g0r1, g0r2, g0r3 = (1, 8, 7)
+	g1r1, g1r2, g1r3 = (19, 61, 6)
+	s0r1, s0r2, s0r3 = (28, 34, 39)
+	s1r1, s1r2, s1r3 = (14, 18, 41)
 	blkSize = 128
 	blkSize = 128
 	nRounds = 80
 	nRounds = 80
 	wordSize = 8
 	wordSize = 8

+ 47 - 47
mmgen/subseed.py

@@ -21,64 +21,64 @@ subseed: Subseed classes and methods for the MMGen suite
 """
 """
 
 
 from .color import green
 from .color import green
-from .util import msg_r,msg,die,make_chksum_8
-from .objmethods import MMGenObject,HiliteStr,InitErrors
-from .obj import MMGenRange,IndexedDict,ImmutableAttr
-from .seed import SeedBase,SeedID
+from .util import msg_r, msg, die, make_chksum_8
+from .objmethods import MMGenObject, HiliteStr, InitErrors
+from .obj import MMGenRange, IndexedDict, ImmutableAttr
+from .seed import SeedBase, SeedID
 
 
 class SubSeedIdxRange(MMGenRange):
 class SubSeedIdxRange(MMGenRange):
 	min_idx = 1
 	min_idx = 1
 	max_idx = 1000000
 	max_idx = 1000000
 
 
-class SubSeedIdx(HiliteStr,InitErrors):
+class SubSeedIdx(HiliteStr, InitErrors):
 	color = 'red'
 	color = 'red'
 	trunc_ok = False
 	trunc_ok = False
-	def __new__(cls,s):
-		if isinstance(s,cls):
+	def __new__(cls, s):
+		if isinstance(s, cls):
 			return s
 			return s
 		try:
 		try:
-			assert isinstance(s,str),'not a string or string subclass'
+			assert isinstance(s, str), 'not a string or string subclass'
 			idx = s[:-1] if s[-1] in 'SsLl' else s
 			idx = s[:-1] if s[-1] in 'SsLl' else s
 			from .util import is_int
 			from .util import is_int
-			assert is_int(idx),"valid format: an integer, plus optional letter 'S','s','L' or 'l'"
+			assert is_int(idx), "valid format: an integer, plus optional letter 'S', 's', 'L' or 'l'"
 			idx = int(idx)
 			idx = int(idx)
 			assert idx >= SubSeedIdxRange.min_idx, f'subseed index < {SubSeedIdxRange.min_idx:,}'
 			assert idx >= SubSeedIdxRange.min_idx, f'subseed index < {SubSeedIdxRange.min_idx:,}'
 			assert idx <= SubSeedIdxRange.max_idx, f'subseed index > {SubSeedIdxRange.max_idx:,}'
 			assert idx <= SubSeedIdxRange.max_idx, f'subseed index > {SubSeedIdxRange.max_idx:,}'
 
 
-			sstype,ltr = ('short','S') if s[-1] in 'Ss' else ('long','L')
-			me = str.__new__(cls,str(idx)+ltr)
+			sstype, ltr = ('short', 'S') if s[-1] in 'Ss' else ('long', 'L')
+			me = str.__new__(cls, str(idx)+ltr)
 			me.idx = idx
 			me.idx = idx
 			me.type = sstype
 			me.type = sstype
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e,s)
+			return cls.init_fail(e, s)
 
 
 class SubSeed(SeedBase):
 class SubSeed(SeedBase):
 
 
-	idx    = ImmutableAttr(int,typeconv=False)
-	nonce  = ImmutableAttr(int,typeconv=False)
+	idx    = ImmutableAttr(int, typeconv=False)
+	nonce  = ImmutableAttr(int, typeconv=False)
 	ss_idx = ImmutableAttr(SubSeedIdx)
 	ss_idx = ImmutableAttr(SubSeedIdx)
 	max_nonce = 1000
 	max_nonce = 1000
 
 
-	def __init__(self,parent_list,idx,nonce,length):
+	def __init__(self, parent_list, idx, nonce, length):
 		self.idx = idx
 		self.idx = idx
 		self.nonce = nonce
 		self.nonce = nonce
-		self.ss_idx = str(idx) + { 'long': 'L', 'short': 'S' }[length]
+		self.ss_idx = str(idx) + {'long': 'L', 'short': 'S'}[length]
 		self.parent_list = parent_list
 		self.parent_list = parent_list
 		SeedBase.__init__(
 		SeedBase.__init__(
 			self,
 			self,
 			parent_list.parent_seed.cfg,
 			parent_list.parent_seed.cfg,
-			seed_bin=self.make_subseed_bin( parent_list, idx, nonce, length ))
+			seed_bin=self.make_subseed_bin(parent_list, idx, nonce, length))
 
 
 	@staticmethod
 	@staticmethod
-	def make_subseed_bin(parent_list,idx:int,nonce:int,length:str):
+	def make_subseed_bin(parent_list, idx:int, nonce:int, length:str):
 		seed = parent_list.parent_seed
 		seed = parent_list.parent_seed
-		short = { 'short': True, 'long': False }[length]
+		short = {'short': True, 'long': False}[length]
 		# field maximums: idx: 4294967295 (1000000), nonce: 65535 (1000), short: 255 (1)
 		# field maximums: idx: 4294967295 (1000000), nonce: 65535 (1000), short: 255 (1)
-		scramble_key  = idx.to_bytes(4,'big') + nonce.to_bytes(2,'big') + short.to_bytes(1,'big')
+		scramble_key  = idx.to_bytes(4, 'big') + nonce.to_bytes(2, 'big') + short.to_bytes(1, 'big')
 		from .crypto import Crypto
 		from .crypto import Crypto
 		return Crypto(parent_list.parent_seed.cfg).scramble_seed(
 		return Crypto(parent_list.parent_seed.cfg).scramble_seed(
-			seed.data,scramble_key)[:16 if short else seed.byte_len]
+			seed.data, scramble_key)[:16 if short else seed.byte_len]
 
 
 class SubSeedList(MMGenObject):
 class SubSeedList(MMGenObject):
 	have_short = True
 	have_short = True
@@ -86,16 +86,16 @@ class SubSeedList(MMGenObject):
 	debug_last_share_sid_len = 3
 	debug_last_share_sid_len = 3
 	dfl_len = 100
 	dfl_len = 100
 
 
-	def __init__(self,parent_seed,length=None):
+	def __init__(self, parent_seed, length=None):
 		self.member_type = SubSeed
 		self.member_type = SubSeed
 		self.parent_seed = parent_seed
 		self.parent_seed = parent_seed
-		self.data = { 'long': IndexedDict(), 'short': IndexedDict() }
+		self.data = {'long': IndexedDict(), 'short': IndexedDict()}
 		self.len = length or self.dfl_len
 		self.len = length or self.dfl_len
 
 
 	def __len__(self):
 	def __len__(self):
 		return len(self.data['long'])
 		return len(self.data['long'])
 
 
-	def get_subseed_by_ss_idx(self,ss_idx_in,print_msg=False):
+	def get_subseed_by_ss_idx(self, ss_idx_in, print_msg=False):
 		ss_idx = SubSeedIdx(ss_idx_in)
 		ss_idx = SubSeedIdx(ss_idx_in)
 		if print_msg:
 		if print_msg:
 			msg_r('{} {} of {}...'.format(
 			msg_r('{} {} of {}...'.format(
@@ -108,27 +108,27 @@ class SubSeedList(MMGenObject):
 			self._generate(ss_idx.idx)
 			self._generate(ss_idx.idx)
 
 
 		sid = self.data[ss_idx.type].key(ss_idx.idx-1)
 		sid = self.data[ss_idx.type].key(ss_idx.idx-1)
-		idx,nonce = self.data[ss_idx.type][sid]
+		idx, nonce = self.data[ss_idx.type][sid]
 		if idx != ss_idx.idx:
 		if idx != ss_idx.idx:
 			die(3, "{} != {}: self.data[{t!r}].key(i) does not match self.data[{t!r}][i]!".format(
 			die(3, "{} != {}: self.data[{t!r}].key(i) does not match self.data[{t!r}][i]!".format(
 				idx,
 				idx,
 				ss_idx.idx,
 				ss_idx.idx,
-				t = ss_idx.type ))
+				t = ss_idx.type))
 
 
 		if print_msg:
 		if print_msg:
 			msg(f'\b\b\b => {SeedID.hlc(sid)}')
 			msg(f'\b\b\b => {SeedID.hlc(sid)}')
 
 
-		seed = self.member_type(self,idx,nonce,length=ss_idx.type)
+		seed = self.member_type(self, idx, nonce, length=ss_idx.type)
 		assert seed.sid == sid, f'{seed.sid} != {sid}: Seed ID mismatch!'
 		assert seed.sid == sid, f'{seed.sid} != {sid}: Seed ID mismatch!'
 		return seed
 		return seed
 
 
-	def get_subseed_by_seed_id(self,sid,last_idx=None,print_msg=False):
+	def get_subseed_by_seed_id(self, sid, last_idx=None, print_msg=False):
 
 
 		def get_existing_subseed_by_seed_id(sid):
 		def get_existing_subseed_by_seed_id(sid):
-			for k in ('long','short') if self.have_short else ('long',):
+			for k in ('long', 'short') if self.have_short else ('long',):
 				if sid in self.data[k]:
 				if sid in self.data[k]:
-					idx,nonce = self.data[k][sid]
-					return self.member_type(self,idx,nonce,length=k)
+					idx, nonce = self.data[k][sid]
+					return self.member_type(self, idx, nonce, length=k)
 
 
 		def do_msg(subseed):
 		def do_msg(subseed):
 			if print_msg:
 			if print_msg:
@@ -150,14 +150,14 @@ class SubSeedList(MMGenObject):
 		if len(self) >= last_idx:
 		if len(self) >= last_idx:
 			return None
 			return None
 
 
-		self._generate(last_idx,last_sid=sid)
+		self._generate(last_idx, last_sid=sid)
 
 
 		subseed = get_existing_subseed_by_seed_id(sid)
 		subseed = get_existing_subseed_by_seed_id(sid)
 		if subseed:
 		if subseed:
 			do_msg(subseed)
 			do_msg(subseed)
 			return subseed
 			return subseed
 
 
-	def _collision_debug_msg(self,sid,idx,nonce,nonce_desc='nonce',debug_last_share=False):
+	def _collision_debug_msg(self, sid, idx, nonce, nonce_desc='nonce', debug_last_share=False):
 		slen = 'short' if sid in self.data['short'] else 'long'
 		slen = 'short' if sid in self.data['short'] else 'long'
 		m1 = f'add_subseed(idx={idx},{slen}):'
 		m1 = f'add_subseed(idx={idx},{slen}):'
 		if sid == self.parent_seed.sid:
 		if sid == self.parent_seed.sid:
@@ -172,7 +172,7 @@ class SubSeedList(MMGenObject):
 			m2 = f'collision with ID {sid} (idx={colliding_idx},{slen}),'
 			m2 = f'collision with ID {sid} (idx={colliding_idx},{slen}),'
 		msg(f'{m1:30} {m2:46} incrementing {nonce_desc} to {nonce+1}')
 		msg(f'{m1:30} {m2:46} incrementing {nonce_desc} to {nonce+1}')
 
 
-	def _generate(self,last_idx=None,last_sid=None):
+	def _generate(self, last_idx=None, last_sid=None):
 
 
 		if last_idx is None:
 		if last_idx is None:
 			last_idx = self.len
 			last_idx = self.len
@@ -185,27 +185,27 @@ class SubSeedList(MMGenObject):
 		if last_sid is not None:
 		if last_sid is not None:
 			last_sid = SeedID(sid=last_sid)
 			last_sid = SeedID(sid=last_sid)
 
 
-		def add_subseed(idx,length):
-			for nonce in range(self.nonce_start,self.member_type.max_nonce+1): # handle SeedID collisions
-				sid = make_chksum_8(self.member_type.make_subseed_bin(self,idx,nonce,length))
+		def add_subseed(idx, length):
+			for nonce in range(self.nonce_start, self.member_type.max_nonce+1): # handle SeedID collisions
+				sid = make_chksum_8(self.member_type.make_subseed_bin(self, idx, nonce, length))
 				if sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid:
 				if sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid:
 					if self.parent_seed.cfg.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds
 					if self.parent_seed.cfg.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds
-						self._collision_debug_msg(sid,idx,nonce)
+						self._collision_debug_msg(sid, idx, nonce)
 				else:
 				else:
-					self.data[length][sid] = (idx,nonce)
+					self.data[length][sid] = (idx, nonce)
 					return last_sid == sid
 					return last_sid == sid
 			# must exit here, as this could leave self.data in inconsistent state
 			# must exit here, as this could leave self.data in inconsistent state
 			die('SubSeedNonceRangeExceeded', 'add_subseed(): nonce range exceeded')
 			die('SubSeedNonceRangeExceeded', 'add_subseed(): nonce range exceeded')
 
 
-		for idx in SubSeedIdxRange(first_idx,last_idx).iterate():
-			match1 = add_subseed(idx,'long')
-			match2 = add_subseed(idx,'short') if self.have_short else False
+		for idx in SubSeedIdxRange(first_idx, last_idx).iterate():
+			match1 = add_subseed(idx, 'long')
+			match2 = add_subseed(idx, 'short') if self.have_short else False
 			if match1 or match2:
 			if match1 or match2:
 				break
 				break
 
 
-	def format(self,first_idx,last_idx):
+	def format(self, first_idx, last_idx):
 
 
-		r = SubSeedIdxRange(first_idx,last_idx)
+		r = SubSeedIdxRange(first_idx, last_idx)
 
 
 		if len(self) < last_idx:
 		if len(self) < last_idx:
 			self._generate(last_idx)
 			self._generate(last_idx)
@@ -214,11 +214,11 @@ class SubSeedList(MMGenObject):
 		fs2 = '{i:>7}L: {:8} {i:>7}S: {:8}\n'
 		fs2 = '{i:>7}L: {:8} {i:>7}S: {:8}\n'
 
 
 		hdr = f'    Parent Seed: {self.parent_seed.sid.hl()} ({self.parent_seed.bitlen} bits)\n\n'
 		hdr = f'    Parent Seed: {self.parent_seed.sid.hl()} ({self.parent_seed.bitlen} bits)\n\n'
-		hdr += fs1.format('Long Subseeds','Short Subseeds')
-		hdr += fs1.format('-------------','--------------')
+		hdr += fs1.format('Long Subseeds', 'Short Subseeds')
+		hdr += fs1.format('-------------', '--------------')
 
 
 		sl = self.data['long'].keys
 		sl = self.data['long'].keys
 		ss = self.data['short'].keys
 		ss = self.data['short'].keys
-		body = (fs2.format( sl[n-1], ss[n-1], i=n ) for n in r.iterate())
+		body = (fs2.format(sl[n-1], ss[n-1], i=n) for n in r.iterate())
 
 
 		return hdr + ''.join(body)
 		return hdr + ''.join(body)

+ 37 - 37
mmgen/term.py

@@ -22,26 +22,26 @@ term: Terminal classes for the MMGen suite
 
 
 # TODO: reimplement as instance instead of class
 # TODO: reimplement as instance instead of class
 
 
-import sys,os,time
+import sys, os, time
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .util import msg,msg_r,die
+from .util import msg, msg_r, die
 
 
 if sys.platform in ('linux', 'darwin'):
 if sys.platform in ('linux', 'darwin'):
-	import tty,termios
+	import tty, termios
 	from select import select
 	from select import select
 	hold_protect_timeout = 2 if sys.platform == 'darwin' else 0.3
 	hold_protect_timeout = 2 if sys.platform == 'darwin' else 0.3
 elif sys.platform == 'win32':
 elif sys.platform == 'win32':
 	try:
 	try:
 		import msvcrt
 		import msvcrt
 	except:
 	except:
-		die(2,'Unable to set terminal mode')
+		die(2, 'Unable to set terminal mode')
 	if not sys.stdin.isatty():
 	if not sys.stdin.isatty():
-		msvcrt.setmode(sys.stdin.fileno(),os.O_BINARY)
+		msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
 else:
 else:
-	die(2,f'{sys.platform!r}: unsupported platform')
+	die(2, f'{sys.platform!r}: unsupported platform')
 
 
-_term_dimensions = namedtuple('terminal_dimensions',['width','height'])
+_term_dimensions = namedtuple('terminal_dimensions', ['width', 'height'])
 
 
 class MMGenTerm:
 class MMGenTerm:
 
 
@@ -50,11 +50,11 @@ class MMGenTerm:
 		pass
 		pass
 
 
 	@classmethod
 	@classmethod
-	def init(cls,noecho=False):
+	def init(cls, noecho=False):
 		pass
 		pass
 
 
 	@classmethod
 	@classmethod
-	def set(cls,*args,**kwargs):
+	def set(cls, *args, **kwargs):
 		pass
 		pass
 
 
 	@classmethod
 	@classmethod
@@ -69,34 +69,34 @@ class MMGenTermLinux(MMGenTerm):
 
 
 	@classmethod
 	@classmethod
 	def register_cleanup(cls):
 	def register_cleanup(cls):
-		if not hasattr(cls,'cleanup_registered'):
+		if not hasattr(cls, 'cleanup_registered'):
 			import atexit
 			import atexit
 			atexit.register(
 			atexit.register(
 				lambda: termios.tcsetattr(
 				lambda: termios.tcsetattr(
 					cls.stdin_fd,
 					cls.stdin_fd,
 					termios.TCSADRAIN,
 					termios.TCSADRAIN,
-					cls.orig_term) )
+					cls.orig_term))
 			cls.cleanup_registered = True
 			cls.cleanup_registered = True
 
 
 	@classmethod
 	@classmethod
 	def reset(cls):
 	def reset(cls):
-		termios.tcsetattr( cls.stdin_fd, termios.TCSANOW, cls.orig_term )
+		termios.tcsetattr(cls.stdin_fd, termios.TCSANOW, cls.orig_term)
 		cls.cur_term = cls.orig_term
 		cls.cur_term = cls.orig_term
 
 
 	@classmethod
 	@classmethod
-	def set(cls,setting):
+	def set(cls, setting):
 		d = {
 		d = {
 			'echo':   lambda t: t[:3] + [t[3] |  (termios.ECHO | termios.ECHONL)] + t[4:], # echo input chars
 			'echo':   lambda t: t[:3] + [t[3] |  (termios.ECHO | termios.ECHONL)] + t[4:], # echo input chars
 			'noecho': lambda t: t[:3] + [t[3] & ~(termios.ECHO | termios.ECHONL)] + t[4:], # don’t echo input chars
 			'noecho': lambda t: t[:3] + [t[3] & ~(termios.ECHO | termios.ECHONL)] + t[4:], # don’t echo input chars
 		}
 		}
-		termios.tcsetattr( cls.stdin_fd, termios.TCSANOW, d[setting](cls.cur_term) )
+		termios.tcsetattr(cls.stdin_fd, termios.TCSANOW, d[setting](cls.cur_term))
 		cls.cur_term = termios.tcgetattr(cls.stdin_fd)
 		cls.cur_term = termios.tcgetattr(cls.stdin_fd)
 
 
 	@classmethod
 	@classmethod
-	def init(cls,noecho=False):
+	def init(cls, noecho=False):
 		cls.stdin_fd = sys.stdin.fileno()
 		cls.stdin_fd = sys.stdin.fileno()
 		cls.cur_term = termios.tcgetattr(cls.stdin_fd)
 		cls.cur_term = termios.tcgetattr(cls.stdin_fd)
-		if not hasattr(cls,'orig_term'):
+		if not hasattr(cls, 'orig_term'):
 			cls.orig_term = cls.cur_term
 			cls.orig_term = cls.cur_term
 		if noecho:
 		if noecho:
 			cls.set('noecho')
 			cls.set('noecho')
@@ -109,9 +109,9 @@ class MMGenTermLinux(MMGenTerm):
 			try:
 			try:
 				ret = (
 				ret = (
 					int(os.environ['COLUMNS']),
 					int(os.environ['COLUMNS']),
-					int(os.environ['LINES']) )
+					int(os.environ['LINES']))
 			except:
 			except:
-				ret = (80,25)
+				ret = (80, 25)
 		return _term_dimensions(*ret)
 		return _term_dimensions(*ret)
 
 
 	@classmethod
 	@classmethod
@@ -128,7 +128,7 @@ class MMGenTermLinux(MMGenTerm):
 				break
 				break
 
 
 	@classmethod
 	@classmethod
-	def get_char(cls,prompt='',immed_chars='',prehold_protect=True,num_bytes=5):
+	def get_char(cls, prompt='', immed_chars='', prehold_protect=True, num_bytes=5):
 		"""
 		"""
 		Use os.read(), not file.read(), to get a variable number of bytes without blocking.
 		Use os.read(), not file.read(), to get a variable number of bytes without blocking.
 		Request 5 bytes to cover escape sequences generated by F1, F2, .. Fn keys (5 bytes)
 		Request 5 bytes to cover escape sequences generated by F1, F2, .. Fn keys (5 bytes)
@@ -142,7 +142,7 @@ class MMGenTermLinux(MMGenTerm):
 		while True:
 		while True:
 			# Protect against held-down key before read()
 			# Protect against held-down key before read()
 			key = select([sys.stdin], [], [], timeout)[0]
 			key = select([sys.stdin], [], [], timeout)[0]
-			s = os.read(cls.stdin_fd,num_bytes).decode()
+			s = os.read(cls.stdin_fd, num_bytes).decode()
 			if prehold_protect and key:
 			if prehold_protect and key:
 				continue
 				continue
 			if s in immed_chars:
 			if s in immed_chars:
@@ -155,10 +155,10 @@ class MMGenTermLinux(MMGenTerm):
 		return s
 		return s
 
 
 	@classmethod
 	@classmethod
-	def get_char_raw(cls,prompt='',num_bytes=5,**kwargs):
+	def get_char_raw(cls, prompt='', num_bytes=5, **kwargs):
 		tty.setcbreak(cls.stdin_fd)
 		tty.setcbreak(cls.stdin_fd)
 		msg_r(prompt)
 		msg_r(prompt)
-		s = os.read(cls.stdin_fd,num_bytes).decode()
+		s = os.read(cls.stdin_fd, num_bytes).decode()
 		termios.tcsetattr(cls.stdin_fd, termios.TCSADRAIN, cls.cur_term)
 		termios.tcsetattr(cls.stdin_fd, termios.TCSADRAIN, cls.cur_term)
 		return s
 		return s
 
 
@@ -169,11 +169,11 @@ class MMGenTermLinuxStub(MMGenTermLinux):
 		pass
 		pass
 
 
 	@classmethod
 	@classmethod
-	def init(cls,noecho=False):
+	def init(cls, noecho=False):
 		cls.stdin_fd = sys.stdin.fileno()
 		cls.stdin_fd = sys.stdin.fileno()
 
 
 	@classmethod
 	@classmethod
-	def set(cls,*args,**kwargs):
+	def set(cls, *args, **kwargs):
 		pass
 		pass
 
 
 	@classmethod
 	@classmethod
@@ -181,9 +181,9 @@ class MMGenTermLinuxStub(MMGenTermLinux):
 		pass
 		pass
 
 
 	@classmethod
 	@classmethod
-	def get_char(cls,prompt='',immed_chars='',prehold_protect=None,num_bytes=5):
+	def get_char(cls, prompt='', immed_chars='', prehold_protect=None, num_bytes=5):
 		msg_r(prompt)
 		msg_r(prompt)
-		return os.read(0,num_bytes).decode()
+		return os.read(0, num_bytes).decode()
 
 
 	get_char_raw = get_char
 	get_char_raw = get_char
 
 
@@ -196,26 +196,26 @@ class MMGenTermMSWin(MMGenTerm):
 	@classmethod
 	@classmethod
 	def get_terminal_size(cls):
 	def get_terminal_size(cls):
 		import struct
 		import struct
-		x,y = 0,0
+		x, y = 0, 0
 		try:
 		try:
-			from ctypes import windll,create_string_buffer
+			from ctypes import windll, create_string_buffer
 			# handles - stdin: -10, stdout: -11, stderr: -12
 			# handles - stdin: -10, stdout: -11, stderr: -12
 			csbi = create_string_buffer(22)
 			csbi = create_string_buffer(22)
 			h = windll.kernel32.GetStdHandle(-12)
 			h = windll.kernel32.GetStdHandle(-12)
-			res = windll.kernel32.GetConsoleScreenBufferInfo(h,csbi)
+			res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
 			assert res, 'failed to get console screen buffer info'
 			assert res, 'failed to get console screen buffer info'
-			left,top,right,bottom = struct.unpack('hhhhHhhhhhh', csbi.raw)[5:9]
+			left, top, right, bottom = struct.unpack('hhhhHhhhhhh', csbi.raw)[5:9]
 			x = right - left + 1
 			x = right - left + 1
 			y = bottom - top + 1
 			y = bottom - top + 1
 		except:
 		except:
 			pass
 			pass
 
 
 		if x and y:
 		if x and y:
-			return _term_dimensions(x,y)
+			return _term_dimensions(x, y)
 		else:
 		else:
 			from .color import yellow
 			from .color import yellow
 			msg(yellow('Warning: could not get terminal size. Using fallback dimensions.'))
 			msg(yellow('Warning: could not get terminal size. Using fallback dimensions.'))
-			return _term_dimensions(80,25)
+			return _term_dimensions(80, 25)
 
 
 	@classmethod
 	@classmethod
 	def kb_hold_protect(cls):
 	def kb_hold_protect(cls):
@@ -230,7 +230,7 @@ class MMGenTermMSWin(MMGenTerm):
 					return
 					return
 
 
 	@classmethod
 	@classmethod
-	def get_char(cls,prompt='',immed_chars='',prehold_protect=True,num_bytes=None):
+	def get_char(cls, prompt='', immed_chars='', prehold_protect=True, num_bytes=None):
 		"""
 		"""
 		always return a single character, ignore num_bytes
 		always return a single character, ignore num_bytes
 		first character of 2-character sequence returned by F1-F12 keys is discarded
 		first character of 2-character sequence returned by F1-F12 keys is discarded
@@ -253,13 +253,13 @@ class MMGenTermMSWin(MMGenTerm):
 						return ch
 						return ch
 
 
 	@classmethod
 	@classmethod
-	def get_char_raw(cls,prompt='',num_bytes=None,**kwargs):
+	def get_char_raw(cls, prompt='', num_bytes=None, **kwargs):
 		"""
 		"""
 		return single ASCII char or 2-char escape sequence, ignoring num_bytes
 		return single ASCII char or 2-char escape sequence, ignoring num_bytes
 		"""
 		"""
 		msg_r(prompt)
 		msg_r(prompt)
 		ret = msvcrt.getch()
 		ret = msvcrt.getch()
-		if ret in (b'\x00',b'\xe0'): # first byte of 2-byte escape sequence
+		if ret in (b'\x00', b'\xe0'): # first byte of 2-byte escape sequence
 			return chr(ret[0]) + chr(msvcrt.getch()[0])
 			return chr(ret[0]) + chr(msvcrt.getch()[0])
 		if ret == b'\x03':
 		if ret == b'\x03':
 			raise KeyboardInterrupt
 			raise KeyboardInterrupt
@@ -268,7 +268,7 @@ class MMGenTermMSWin(MMGenTerm):
 class MMGenTermMSWinStub(MMGenTermMSWin):
 class MMGenTermMSWinStub(MMGenTermMSWin):
 
 
 	@classmethod
 	@classmethod
-	def get_char(cls,prompt='',immed_chars='',prehold_protect=None,num_bytes=None):
+	def get_char(cls, prompt='', immed_chars='', prehold_protect=None, num_bytes=None):
 		"""
 		"""
 		Use stdin to allow UTF-8 and emulate the one-character behavior of MMGenTermMSWin
 		Use stdin to allow UTF-8 and emulate the one-character behavior of MMGenTermMSWin
 		"""
 		"""
@@ -289,7 +289,7 @@ def get_term():
 		'win32': (MMGenTermMSWin if sys.stdin.isatty() else MMGenTermMSWinStub),
 		'win32': (MMGenTermMSWin if sys.stdin.isatty() else MMGenTermMSWinStub),
 	}[sys.platform]
 	}[sys.platform]
 
 
-def init_term(cfg,noecho=False):
+def init_term(cfg, noecho=False):
 
 
 	term = get_term()
 	term = get_term()
 
 

+ 18 - 18
mmgen/ui.py

@@ -12,11 +12,11 @@
 ui: Interactive user interface functions for the MMGen suite
 ui: Interactive user interface functions for the MMGen suite
 """
 """
 
 
-import sys,os
+import sys, os
 
 
-from .util import msg,msg_r,Msg,die
+from .util import msg, msg_r, Msg, die
 
 
-def confirm_or_raise(cfg,message,action,expect='YES',exit_msg='Exiting at user request'):
+def confirm_or_raise(cfg, message, action, expect='YES', exit_msg='Exiting at user request'):
 	if message:
 	if message:
 		msg(message)
 		msg(message)
 	if line_input(
 	if line_input(
@@ -24,27 +24,27 @@ def confirm_or_raise(cfg,message,action,expect='YES',exit_msg='Exiting at user r
 			(f'{action}  ' if action[0].isupper() else f'Are you sure you want to {action}?\n') +
 			(f'{action}  ' if action[0].isupper() else f'Are you sure you want to {action}?\n') +
 			f'Type uppercase {expect!r} to confirm: '
 			f'Type uppercase {expect!r} to confirm: '
 		).strip() != expect:
 		).strip() != expect:
-		die( 'UserNonConfirmation', exit_msg )
+		die('UserNonConfirmation', exit_msg)
 
 
-def get_words_from_user(cfg,prompt):
-	words = line_input( cfg, prompt, echo=cfg.echo_passphrase ).split()
+def get_words_from_user(cfg, prompt):
+	words = line_input(cfg, prompt, echo=cfg.echo_passphrase).split()
 	if cfg.debug:
 	if cfg.debug:
 		msg('Sanitized input: [{}]'.format(' '.join(words)))
 		msg('Sanitized input: [{}]'.format(' '.join(words)))
 	return words
 	return words
 
 
-def get_data_from_user(cfg,desc='data'): # user input MUST be UTF-8
-	data = line_input( cfg, f'Enter {desc}: ', echo=cfg.echo_passphrase )
+def get_data_from_user(cfg, desc='data'): # user input MUST be UTF-8
+	data = line_input(cfg, f'Enter {desc}: ', echo=cfg.echo_passphrase)
 	if cfg.debug:
 	if cfg.debug:
 		msg(f'User input: [{data}]')
 		msg(f'User input: [{data}]')
 	return data
 	return data
 
 
-def line_input(cfg,prompt,echo=True,insert_txt='',hold_protect=True):
+def line_input(cfg, prompt, echo=True, insert_txt='', hold_protect=True):
 	"""
 	"""
 	multi-line prompts OK
 	multi-line prompts OK
 	one-line prompts must begin at beginning of line
 	one-line prompts must begin at beginning of line
 	empty prompts forbidden due to interactions with readline
 	empty prompts forbidden due to interactions with readline
 	"""
 	"""
-	assert prompt,'calling line_input() with an empty prompt forbidden'
+	assert prompt, 'calling line_input() with an empty prompt forbidden'
 
 
 	def get_readline():
 	def get_readline():
 		try:
 		try:
@@ -60,7 +60,7 @@ def line_input(cfg,prompt,echo=True,insert_txt='',hold_protect=True):
 	if cfg.test_suite_popen_spawn:
 	if cfg.test_suite_popen_spawn:
 		msg(prompt)
 		msg(prompt)
 		sys.stderr.flush() # required by older Pythons (e.g. v3.7)
 		sys.stderr.flush() # required by older Pythons (e.g. v3.7)
-		reply = os.read(0,4096).decode().rstrip('\n') # strip NL to mimic behavior of input()
+		reply = os.read(0, 4096).decode().rstrip('\n') # strip NL to mimic behavior of input()
 	elif not sys.stdin.isatty():
 	elif not sys.stdin.isatty():
 		msg_r(prompt)
 		msg_r(prompt)
 		reply = input('')
 		reply = input('')
@@ -86,10 +86,10 @@ def keypress_confirm(
 	default_yes     = False,
 	default_yes     = False,
 	verbose         = False,
 	verbose         = False,
 	no_nl           = False,
 	no_nl           = False,
-	complete_prompt = False ):
+	complete_prompt = False):
 
 
 	if not complete_prompt:
 	if not complete_prompt:
-		prompt = '{} {}: '.format( prompt, '(Y/n)' if default_yes else '(y/N)' )
+		prompt = '{} {}: '.format(prompt, '(Y/n)' if default_yes else '(y/N)')
 
 
 	nl = f'\r{" "*len(prompt)}\r' if no_nl else '\n'
 	nl = f'\r{" "*len(prompt)}\r' if no_nl else '\n'
 
 
@@ -99,7 +99,7 @@ def keypress_confirm(
 
 
 	from .term import get_char
 	from .term import get_char
 	while True:
 	while True:
-		reply = get_char(prompt,immed_chars='yYnN').strip('\n\r')
+		reply = get_char(prompt, immed_chars='yYnN').strip('\n\r')
 		if not reply:
 		if not reply:
 			msg_r(nl)
 			msg_r(nl)
 			return default_yes
 			return default_yes
@@ -111,7 +111,7 @@ def keypress_confirm(
 
 
 def do_pager(text):
 def do_pager(text):
 
 
-	pagers = ['less','more']
+	pagers = ['less', 'more']
 	end_msg = '\n(end of text)\n\n'
 	end_msg = '\n(end of text)\n\n'
 	os.environ['LESS'] = '--jump-target=2 --shift=4 --tabs=4 --RAW-CONTROL-CHARS --chop-long-lines'
 	os.environ['LESS'] = '--jump-target=2 --shift=4 --tabs=4 --RAW-CONTROL-CHARS --chop-long-lines'
 
 
@@ -123,7 +123,7 @@ def do_pager(text):
 	for pager in pagers:
 	for pager in pagers:
 		try:
 		try:
 			m = text + ('' if pager == 'less' else end_msg)
 			m = text + ('' if pager == 'less' else end_msg)
-			run([pager],input=m.encode(),check=True)
+			run([pager], input=m.encode(), check=True)
 			msg_r('\r')
 			msg_r('\r')
 		except:
 		except:
 			pass
 			pass
@@ -133,7 +133,7 @@ def do_pager(text):
 		Msg(text+end_msg)
 		Msg(text+end_msg)
 	set_vt100()
 	set_vt100()
 
 
-def do_license_msg(cfg,immed=False):
+def do_license_msg(cfg, immed=False):
 
 
 	if cfg.quiet or cfg.no_license or cfg.yes or not cfg.stdin_tty:
 	if cfg.quiet or cfg.no_license or cfg.yes or not cfg.stdin_tty:
 		return
 		return
@@ -145,7 +145,7 @@ def do_license_msg(cfg,immed=False):
 	from .term import get_char
 	from .term import get_char
 	prompt = "Press 'w' for conditions and warranty info, or 'c' to continue: "
 	prompt = "Press 'w' for conditions and warranty info, or 'c' to continue: "
 	while True:
 	while True:
-		reply = get_char(prompt, immed_chars=('','wc')[bool(immed)])
+		reply = get_char(prompt, immed_chars=('', 'wc')[bool(immed)])
 		if reply == 'w':
 		if reply == 'w':
 			do_pager(gpl.conditions)
 			do_pager(gpl.conditions)
 		elif reply == 'c':
 		elif reply == 'c':

+ 101 - 101
mmgen/util.py

@@ -20,9 +20,9 @@
 util: Frequently-used variables, classes and utility functions for the MMGen suite
 util: Frequently-used variables, classes and utility functions for the MMGen suite
 """
 """
 
 
-import sys,os,time,re
+import sys, os, time, re
 
 
-from .color import red,yellow,green,blue,purple
+from .color import red, yellow, green, blue, purple
 from .cfg import gv
 from .cfg import gv
 
 
 ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
 ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
@@ -32,12 +32,12 @@ hexdigits = '0123456789abcdefABCDEF'
 hexdigits_uc = '0123456789ABCDEF'
 hexdigits_uc = '0123456789ABCDEF'
 hexdigits_lc = '0123456789abcdef'
 hexdigits_lc = '0123456789abcdef'
 
 
-def noop(*args,**kwargs):
+def noop(*args, **kwargs):
 	pass
 	pass
 
 
 class Util:
 class Util:
 
 
-	def __init__(self,cfg):
+	def __init__(self, cfg):
 
 
 		self.cfg = cfg
 		self.cfg = cfg
 
 
@@ -75,9 +75,9 @@ class Util:
 
 
 		if not chk1 == chk2:
 		if not chk1 == chk2:
 			fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})"
 			fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})"
-			m = fs.format((hdr+':\n   ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
+			m = fs.format((hdr+':\n   ' if hdr else 'CHECKSUM'), desc2, chk2, desc1, chk1)
 			if die_on_fail:
 			if die_on_fail:
-				die(3,m)
+				die(3, m)
 			else:
 			else:
 				if verbose or self.cfg.verbose:
 				if verbose or self.cfg.verbose:
 					msg(m)
 					msg(m)
@@ -90,7 +90,7 @@ class Util:
 
 
 	def compare_or_die(self, val1, desc1, val2, desc2, e='Error'):
 	def compare_or_die(self, val1, desc1, val2, desc2, e='Error'):
 		if val1 != val2:
 		if val1 != val2:
-			die(3,f"{e}: {desc2} ({val2}) doesn't match {desc1} ({val1})")
+			die(3, f"{e}: {desc2} ({val2}) doesn't match {desc1} ({val1})")
 		if self.cfg.debug:
 		if self.cfg.debug:
 			msg(f'{capfirst(desc2)} OK ({val2})')
 			msg(f'{capfirst(desc2)} OK ({val2})')
 		return True
 		return True
@@ -101,7 +101,7 @@ if sys.platform == 'win32':
 			gv.stderr.write(s)
 			gv.stderr.write(s)
 			gv.stderr.flush()
 			gv.stderr.flush()
 		except:
 		except:
-			os.write(2,s.encode())
+			os.write(2, s.encode())
 
 
 	def msg(s):
 	def msg(s):
 		msg_r(s + '\n')
 		msg_r(s + '\n')
@@ -111,7 +111,7 @@ if sys.platform == 'win32':
 			gv.stdout.write(s)
 			gv.stdout.write(s)
 			gv.stdout.flush()
 			gv.stdout.flush()
 		except:
 		except:
-			os.write(1,s.encode())
+			os.write(1, s.encode())
 
 
 	def Msg(s):
 	def Msg(s):
 		Msg_r(s + '\n')
 		Msg_r(s + '\n')
@@ -156,73 +156,73 @@ def mdie(*args):
 	mmsg(*args)
 	mmsg(*args)
 	sys.exit(0)
 	sys.exit(0)
 
 
-def die(ev,s='',stdout=False):
-	if isinstance(ev,int):
-		from .exception import MMGenSystemExit,MMGenError
+def die(ev, s='', stdout=False):
+	if isinstance(ev, int):
+		from .exception import MMGenSystemExit, MMGenError
 		if ev <= 2:
 		if ev <= 2:
-			raise MMGenSystemExit(ev,s,stdout)
+			raise MMGenSystemExit(ev, s, stdout)
 		else:
 		else:
-			raise MMGenError(ev,s,stdout)
-	elif isinstance(ev,str):
+			raise MMGenError(ev, s, stdout)
+	elif isinstance(ev, str):
 		from . import exception
 		from . import exception
-		raise getattr(exception,ev)(s)
+		raise getattr(exception, ev)(s)
 	else:
 	else:
 		raise ValueError(f'{ev}: exit value must be string or int instance')
 		raise ValueError(f'{ev}: exit value must be string or int instance')
 
 
-def Die(ev=0,s=''):
-	die(ev=ev,s=s,stdout=True)
+def Die(ev=0, s=''):
+	die(ev=ev, s=s, stdout=True)
 
 
 def pp_fmt(d):
 def pp_fmt(d):
 	import pprint
 	import pprint
-	return pprint.PrettyPrinter(indent=4,compact=False).pformat(d)
+	return pprint.PrettyPrinter(indent=4, compact=False).pformat(d)
 
 
 def pp_msg(d):
 def pp_msg(d):
 	msg(pp_fmt(d))
 	msg(pp_fmt(d))
 
 
-def indent(s,indent='    ',append='\n'):
+def indent(s, indent='    ', append='\n'):
 	"indent multiple lines of text with specified string"
 	"indent multiple lines of text with specified string"
 	return indent + ('\n'+indent).join(s.strip().splitlines()) + append
 	return indent + ('\n'+indent).join(s.strip().splitlines()) + append
 
 
-def fmt(s,indent='',strip_char=None,append='\n'):
+def fmt(s, indent='', strip_char=None, append='\n'):
 	"de-indent multiple lines of text, or indent with specified string"
 	"de-indent multiple lines of text, or indent with specified string"
 	return indent + ('\n'+indent).join([l.lstrip(strip_char) for l in s.strip().splitlines()]) + append
 	return indent + ('\n'+indent).join([l.lstrip(strip_char) for l in s.strip().splitlines()]) + append
 
 
-def fmt_list(iterable,fmt='dfl',indent='',conv=None):
+def fmt_list(iterable, fmt='dfl', indent='', conv=None):
 	"pretty-format a list"
 	"pretty-format a list"
-	_conv,sep,lq,rq = {
-		'dfl':       ( str,  ", ", "'",  "'"),
-		'utf8':      ( str,  ", ", "“",  "”"),
-		'bare':      ( repr, " ",  "",   ""),
-		'barest':    ( str,  " ",  "",   ""),
-		'fancy':     ( str,  " ",  "‘",  "’"),
-		'no_quotes': ( str,  ", ", "",   ""),
-		'compact':   ( str,  ",",  "",   ""),
-		'no_spc':    ( str,  ",",  "'",  "'"),
-		'min':       ( str,  ",",  "",   ""),
-		'repr':      ( repr, ", ", "",   ""),
-		'csv':       ( repr, ",",  "",   ""),
-		'col':       ( str,  "\n", "",   ""),
+	_conv, sep, lq, rq = {
+		'dfl':       (str,  ", ", "'",  "'"),
+		'utf8':      (str,  ", ", "“",  "”"),
+		'bare':      (repr, " ",  "",   ""),
+		'barest':    (str,  " ",  "",   ""),
+		'fancy':     (str,  " ",  "‘",  "’"),
+		'no_quotes': (str,  ", ", "",   ""),
+		'compact':   (str,  ",",  "",   ""),
+		'no_spc':    (str,  ",",  "'",  "'"),
+		'min':       (str,  ",",  "",   ""),
+		'repr':      (repr, ", ", "",   ""),
+		'csv':       (repr, ",",  "",   ""),
+		'col':       (str,  "\n", "",   ""),
 	}[fmt]
 	}[fmt]
 	conv = conv or _conv
 	conv = conv or _conv
 	return indent + (sep+indent).join(lq+conv(e)+rq for e in iterable)
 	return indent + (sep+indent).join(lq+conv(e)+rq for e in iterable)
 
 
-def fmt_dict(mapping,fmt='dfl',kconv=None,vconv=None):
+def fmt_dict(mapping, fmt='dfl', kconv=None, vconv=None):
 	"pretty-format a dict"
 	"pretty-format a dict"
-	kc,vc,sep,fs = {
-		'dfl':           ( str, str,  ", ",  "'{}' ({})" ),
-		'dfl_compact':   ( str, str,  " ",   "{} ({})" ),
-		'square':        ( str, str,  ", ",  "'{}' [{}]" ),
-		'square_compact':( str, str,  " ",   "{} [{}]" ),
-		'equal':         ( str, str,  ", ",  "'{}'={}" ),
-		'equal_spaced':  ( str, str,  ", ",  "'{}' = {}" ),
-		'equal_compact': ( str, str,  " ",   "{}={}" ),
-		'kwargs':        ( str, repr, ", ",  "{}={}" ),
-		'colon':         ( str, repr, ", ",  "{}:{}" ),
-		'colon_compact': ( str, str,  " ",   "{}:{}" ),
+	kc, vc, sep, fs = {
+		'dfl':           (str, str,  ", ",  "'{}' ({})"),
+		'dfl_compact':   (str, str,  " ",   "{} ({})"),
+		'square':        (str, str,  ", ",  "'{}' [{}]"),
+		'square_compact':(str, str,  " ",   "{} [{}]"),
+		'equal':         (str, str,  ", ",  "'{}'={}"),
+		'equal_spaced':  (str, str,  ", ",  "'{}' = {}"),
+		'equal_compact': (str, str,  " ",   "{}={}"),
+		'kwargs':        (str, repr, ", ",  "{}={}"),
+		'colon':         (str, repr, ", ",  "{}:{}"),
+		'colon_compact': (str, str,  " ",   "{}:{}"),
 	}[fmt]
 	}[fmt]
 	kconv = kconv or kc
 	kconv = kconv or kc
 	vconv = vconv or vc
 	vconv = vconv or vc
-	return sep.join(fs.format(kconv(k),vconv(v)) for k,v in mapping.items())
+	return sep.join(fs.format(kconv(k), vconv(v)) for k, v in mapping.items())
 
 
 def list_gen(*data):
 def list_gen(*data):
 	"""
 	"""
@@ -231,10 +231,10 @@ def list_gen(*data):
 	  elements of the sublist are included in the result.  Otherwise the sublist is skipped.
 	  elements of the sublist are included in the result.  Otherwise the sublist is skipped.
 	- If a sublist contains only one element, the condition defaults to true.
 	- If a sublist contains only one element, the condition defaults to true.
 	"""
 	"""
-	assert type(data) in (list,tuple), f'{type(data).__name__} not in (list,tuple)'
+	assert type(data) in (list, tuple), f'{type(data).__name__} not in (list, tuple)'
 	def gen():
 	def gen():
 		for d in data:
 		for d in data:
-			assert isinstance(d,list), f'{type(d).__name__} != list'
+			assert isinstance(d, list), f'{type(d).__name__} != list'
 			if len(d) == 1:
 			if len(d) == 1:
 				yield d[0]
 				yield d[0]
 			elif d[-1]:
 			elif d[-1]:
@@ -242,7 +242,7 @@ def list_gen(*data):
 					yield d[idx]
 					yield d[idx]
 	return list(gen())
 	return list(gen())
 
 
-def remove_dups(iterable,edesc='element',desc='list',quiet=False,hide=False):
+def remove_dups(iterable, edesc='element', desc='list', quiet=False, hide=False):
 	"""
 	"""
 	Remove duplicate occurrences of iterable elements, preserving first occurrence
 	Remove duplicate occurrences of iterable elements, preserving first occurrence
 	If iterable is a generator, return a list, else type(iterable)
 	If iterable is a generator, return a list, else type(iterable)
@@ -256,44 +256,44 @@ def remove_dups(iterable,edesc='element',desc='list',quiet=False,hide=False):
 			ret.append(e)
 			ret.append(e)
 	return ret if type(iterable).__name__ == 'generator' else type(iterable)(ret)
 	return ret if type(iterable).__name__ == 'generator' else type(iterable)(ret)
 
 
-def contains_any(target_list,source_list):
-	return any(map(target_list.count,source_list))
+def contains_any(target_list, source_list):
+	return any(map(target_list.count, source_list))
 
 
-def suf(arg,suf_type='s',verb='none'):
+def suf(arg, suf_type='s', verb='none'):
 	suf_types = {
 	suf_types = {
 		'none': {
 		'none': {
-			's':   ('s',  ''),
-			'es':  ('es', ''),
-			'ies': ('ies','y'),
+			's':   ('s',   ''),
+			'es':  ('es',  ''),
+			'ies': ('ies', 'y'),
 		},
 		},
 		'is': {
 		'is': {
-			's':   ('s are',  ' is'),
-			'es':  ('es are', ' is'),
-			'ies': ('ies are','y is'),
+			's':   ('s are',   ' is'),
+			'es':  ('es are',  ' is'),
+			'ies': ('ies are', 'y is'),
 		},
 		},
 		'has': {
 		'has': {
-			's':   ('s have',  ' has'),
-			'es':  ('es have', ' has'),
-			'ies': ('ies have','y has'),
+			's':   ('s have',   ' has'),
+			'es':  ('es have',  ' has'),
+			'ies': ('ies have', 'y has'),
 		},
 		},
 	}
 	}
-	if isinstance(arg,int):
+	if isinstance(arg, int):
 		n = arg
 		n = arg
-	elif isinstance(arg,(list,tuple,set,dict)):
+	elif isinstance(arg, (list, tuple, set, dict)):
 		n = len(arg)
 		n = len(arg)
 	else:
 	else:
-		die(2,f'{arg}: invalid parameter for suf()')
+		die(2, f'{arg}: invalid parameter for suf()')
 	return suf_types[verb][suf_type][n == 1]
 	return suf_types[verb][suf_type][n == 1]
 
 
 def get_extension(fn):
 def get_extension(fn):
 	return os.path.splitext(fn)[1][1:]
 	return os.path.splitext(fn)[1][1:]
 
 
-def remove_extension(fn,ext):
-	a,b = os.path.splitext(fn)
+def remove_extension(fn, ext):
+	a, b = os.path.splitext(fn)
 	return a if b[1:] == ext else fn
 	return a if b[1:] == ext else fn
 
 
-def make_chksum_N(s,nchars,sep=False,rounds=2,upper=True):
-	if isinstance(s,str):
+def make_chksum_N(s, nchars, sep=False, rounds=2, upper=True):
+	if isinstance(s, str):
 		s = s.encode()
 		s = s.encode()
 	from hashlib import sha256
 	from hashlib import sha256
 	for i in range(rounds):
 	for i in range(rounds):
@@ -301,28 +301,28 @@ def make_chksum_N(s,nchars,sep=False,rounds=2,upper=True):
 	ret = s.hex()[:nchars]
 	ret = s.hex()[:nchars]
 	if sep:
 	if sep:
 		assert 4 <= nchars <= 64 and (not nchars % 4), 'illegal ‘nchars’ value'
 		assert 4 <= nchars <= 64 and (not nchars % 4), 'illegal ‘nchars’ value'
-		ret = ' '.join( ret[i:i+4] for i in range(0,nchars,4) )
+		ret = ' '.join(ret[i:i+4] for i in range(0, nchars, 4))
 	else:
 	else:
 		assert 4 <= nchars <= 64, 'illegal ‘nchars’ value'
 		assert 4 <= nchars <= 64, 'illegal ‘nchars’ value'
 	return ret.upper() if upper else ret
 	return ret.upper() if upper else ret
 
 
-def make_chksum_8(s,sep=False):
+def make_chksum_8(s, sep=False):
 	from .obj import HexStr
 	from .obj import HexStr
 	from hashlib import sha256
 	from hashlib import sha256
-	s = HexStr(sha256(sha256(s).digest()).hexdigest()[:8].upper(),case='upper')
-	return '{} {}'.format(s[:4],s[4:]) if sep else s
+	s = HexStr(sha256(sha256(s).digest()).hexdigest()[:8].upper(), case='upper')
+	return '{} {}'.format(s[:4], s[4:]) if sep else s
 
 
 def make_chksum_6(s):
 def make_chksum_6(s):
 	from .obj import HexStr
 	from .obj import HexStr
 	from hashlib import sha256
 	from hashlib import sha256
-	if isinstance(s,str):
+	if isinstance(s, str):
 		s = s.encode()
 		s = s.encode()
 	return HexStr(sha256(s).hexdigest()[:6])
 	return HexStr(sha256(s).hexdigest()[:6])
 
 
 def is_chksum_6(s):
 def is_chksum_6(s):
 	return len(s) == 6 and set(s) <= set(hexdigits_lc)
 	return len(s) == 6 and set(s) <= set(hexdigits_lc)
 
 
-def split_into_cols(col_wid,s):
+def split_into_cols(col_wid, s):
 	return ' '.join([s[col_wid*i:col_wid*(i+1)] for i in range(len(s)//col_wid+1)]).rstrip()
 	return ' '.join([s[col_wid*i:col_wid*(i+1)] for i in range(len(s)//col_wid+1)]).rstrip()
 
 
 def capfirst(s): # different from str.capitalize() - doesn't downcase any uc in string
 def capfirst(s): # different from str.capitalize() - doesn't downcase any uc in string
@@ -332,20 +332,20 @@ def decode_timestamp(s):
 #	tz_save = open('/etc/timezone').read().rstrip()
 #	tz_save = open('/etc/timezone').read().rstrip()
 	os.environ['TZ'] = 'UTC'
 	os.environ['TZ'] = 'UTC'
 #	os.environ['TZ'] = tz_save
 #	os.environ['TZ'] = tz_save
-	return int(time.mktime( time.strptime(s,'%Y%m%d_%H%M%S') ))
+	return int(time.mktime(time.strptime(s, '%Y%m%d_%H%M%S')))
 
 
 def make_timestamp(secs=None):
 def make_timestamp(secs=None):
 	return '{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}'.format(*time.gmtime(
 	return '{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}'.format(*time.gmtime(
-		int(secs) if secs is not None else time.time() )[:6])
+		int(secs) if secs is not None else time.time())[:6])
 
 
 def make_timestr(secs=None):
 def make_timestr(secs=None):
 	return '{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format(*time.gmtime(
 	return '{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format(*time.gmtime(
-		int(secs) if secs is not None else time.time() )[:6])
+		int(secs) if secs is not None else time.time())[:6])
 
 
 def secs_to_dhms(secs):
 def secs_to_dhms(secs):
 	hrs = secs // 3600
 	hrs = secs // 3600
 	return '{}{:02d}:{:02d}:{:02d} h/m/s'.format(
 	return '{}{:02d}:{:02d}:{:02d} h/m/s'.format(
-		('{} day{}, '.format(hrs//24,suf(hrs//24)) if hrs > 24 else ''),
+		('{} day{}, '.format(hrs//24, suf(hrs//24)) if hrs > 24 else ''),
 		hrs % 24,
 		hrs % 24,
 		(secs // 60) % 60,
 		(secs // 60) % 60,
 		secs % 60
 		secs % 60
@@ -360,9 +360,9 @@ def secs_to_ms(secs):
 def is_int(s): # actually is_nonnegative_int()
 def is_int(s): # actually is_nonnegative_int()
 	return set(str(s)) <= set(digits)
 	return set(str(s)) <= set(digits)
 
 
-def check_int_between(val,imin,imax,desc):
+def check_int_between(val, imin, imax, desc):
 	if not imin <= int(val) <= imax:
 	if not imin <= int(val) <= imax:
-		die(1,f'{val}: invalid value for {desc} (must be between {imin} and {imax})')
+		die(1, f'{val}: invalid value for {desc} (must be between {imin} and {imax})')
 	return int(val)
 	return int(val)
 
 
 def is_hex_str(s):
 def is_hex_str(s):
@@ -379,36 +379,36 @@ def is_utf8(s):
 	else:
 	else:
 		return True
 		return True
 
 
-def remove_whitespace(s,ws='\t\r\n '):
-	return s.translate(dict((ord(e),None) for e in ws))
+def remove_whitespace(s, ws='\t\r\n '):
+	return s.translate(dict((ord(e), None) for e in ws))
 
 
 def strip_comment(line):
 def strip_comment(line):
-	return re.sub('#.*','',line).rstrip()
+	return re.sub('#.*', '', line).rstrip()
 
 
 def strip_comments(lines):
 def strip_comments(lines):
 	pat = re.compile('#.*')
 	pat = re.compile('#.*')
-	return [m for m in [pat.sub('',l).rstrip() for l in lines] if m != '']
+	return [m for m in [pat.sub('', l).rstrip() for l in lines] if m != '']
 
 
-def make_full_path(outdir,outfile):
+def make_full_path(outdir, outfile):
 	return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
 	return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
 
 
 class oneshot_warning:
 class oneshot_warning:
 
 
 	color = 'nocolor'
 	color = 'nocolor'
 
 
-	def __init__(self,div=None,fmt_args=[],reverse=False):
-		self.do(type(self),div,fmt_args,reverse)
+	def __init__(self, div=None, fmt_args=[], reverse=False):
+		self.do(type(self), div, fmt_args, reverse)
 
 
-	def do(self,wcls,div,fmt_args,reverse):
+	def do(self, wcls, div, fmt_args, reverse):
 
 
 		def do_warning():
 		def do_warning():
 			from . import color
 			from . import color
-			msg(getattr(color, getattr(wcls,'color'))('WARNING: ' + getattr(wcls,'message').format(*fmt_args)))
+			msg(getattr(color, getattr(wcls, 'color'))('WARNING: ' + getattr(wcls, 'message').format(*fmt_args)))
 
 
-		if not hasattr(wcls,'data'):
-			setattr(wcls,'data',[])
+		if not hasattr(wcls, 'data'):
+			setattr(wcls, 'data', [])
 
 
-		data = getattr(wcls,'data')
+		data = getattr(wcls, 'data')
 		condition = (div in data) if reverse else (not div in data)
 		condition = (div in data) if reverse else (not div in data)
 
 
 		if not div in data:
 		if not div in data:
@@ -422,10 +422,10 @@ class oneshot_warning:
 
 
 class oneshot_warning_group(oneshot_warning):
 class oneshot_warning_group(oneshot_warning):
 
 
-	def __init__(self,wcls,div=None,fmt_args=[],reverse=False):
-		self.do(getattr(self,wcls),div,fmt_args,reverse)
+	def __init__(self, wcls, div=None, fmt_args=[], reverse=False):
+		self.do(getattr(self, wcls), div, fmt_args, reverse)
 
 
-def get_subclasses(cls,names=False):
+def get_subclasses(cls, names=False):
 	def gen(cls):
 	def gen(cls):
 		for i in cls.__subclasses__():
 		for i in cls.__subclasses__():
 			yield i
 			yield i
@@ -442,11 +442,11 @@ def wrap_ripemd160(called=[]):
 			import hashlib
 			import hashlib
 			hashlib.new('ripemd160')
 			hashlib.new('ripemd160')
 		except ValueError:
 		except ValueError:
-			def hashlib_new_wrapper(name,*args,**kwargs):
+			def hashlib_new_wrapper(name, *args, **kwargs):
 				if name == 'ripemd160':
 				if name == 'ripemd160':
-					return ripemd160(*args,**kwargs)
+					return ripemd160(*args, **kwargs)
 				else:
 				else:
-					return hashlib_new(name,*args,**kwargs)
+					return hashlib_new(name, *args, **kwargs)
 			from .contrib.ripemd160 import ripemd160
 			from .contrib.ripemd160 import ripemd160
 			hashlib_new = hashlib.new
 			hashlib_new = hashlib.new
 			hashlib.new = hashlib_new_wrapper
 			hashlib.new = hashlib_new_wrapper
@@ -454,7 +454,7 @@ def wrap_ripemd160(called=[]):
 
 
 def exit_if_mswin(feature):
 def exit_if_mswin(feature):
 	if sys.platform == 'win32':
 	if sys.platform == 'win32':
-		die(2, capfirst(feature) + ' not supported on the MSWin / MSYS2 platform' )
+		die(2, capfirst(feature) + ' not supported on the MSWin / MSYS2 platform')
 
 
 def have_sudo(silent=False):
 def have_sudo(silent=False):
 	from subprocess import run, DEVNULL
 	from subprocess import run, DEVNULL

+ 25 - 25
mmgen/util2.py

@@ -12,19 +12,19 @@
 util2: Less frequently-used variables, classes and utility functions for the MMGen suite
 util2: Less frequently-used variables, classes and utility functions for the MMGen suite
 """
 """
 
 
-import sys,re,time
-from .util import msg,suf,hexdigits,die
+import sys, re, time
+from .util import msg, suf, hexdigits, die
 
 
-def die_wait(delay,ev=0,s=''):
-	assert isinstance(delay,int)
-	assert isinstance(ev,int)
+def die_wait(delay, ev=0, s=''):
+	assert isinstance(delay, int)
+	assert isinstance(ev, int)
 	if s:
 	if s:
 		msg(s)
 		msg(s)
 	time.sleep(delay)
 	time.sleep(delay)
 	sys.exit(ev)
 	sys.exit(ev)
 
 
-def die_pause(ev=0,s=''):
-	assert isinstance(ev,int)
+def die_pause(ev=0, s=''):
+	assert isinstance(ev, int)
 	if s:
 	if s:
 		msg(s)
 		msg(s)
 	input('Press ENTER to exit')
 	input('Press ENTER to exit')
@@ -39,12 +39,12 @@ def load_cryptodomex():
 		try:
 		try:
 			import Crypto # cryptodome
 			import Crypto # cryptodome
 		except ImportError:
 		except ImportError:
-			die(2,'Unable to import either the ‘pycryptodomex’ or ‘pycryptodome’ package')
+			die(2, 'Unable to import either the ‘pycryptodomex’ or ‘pycryptodome’ package')
 		else:
 		else:
 			sys.modules['Cryptodome'] = Crypto
 			sys.modules['Cryptodome'] = Crypto
 
 
 # called with no arguments by pyethereum.utils:
 # called with no arguments by pyethereum.utils:
-def get_keccak(cfg=None,cached_ret=[]):
+def get_keccak(cfg=None, cached_ret=[]):
 
 
 	if not cached_ret:
 	if not cached_ret:
 		if cfg and cfg.use_internal_keccak_module:
 		if cfg and cfg.use_internal_keccak_module:
@@ -61,7 +61,7 @@ def get_keccak(cfg=None,cached_ret=[]):
 					die('MMGenImportError',
 					die('MMGenImportError',
 						'Please install the ‘pycryptodome’ or ‘pycryptodomex’ package on your system')
 						'Please install the ‘pycryptodome’ or ‘pycryptodomex’ package on your system')
 			def keccak_256(data):
 			def keccak_256(data):
-				return keccak.new(data=data,digest_bytes=32)
+				return keccak.new(data=data, digest_bytes=32)
 		cached_ret.append(keccak_256)
 		cached_ret.append(keccak_256)
 
 
 	return cached_ret[0]
 	return cached_ret[0]
@@ -87,13 +87,13 @@ bytespec_map = (
 	('E',  1152921504606846976),
 	('E',  1152921504606846976),
 )
 )
 
 
-def int2bytespec(n,spec,fmt,print_sym=True,strip=False,add_space=False):
+def int2bytespec(n, spec, fmt, print_sym=True, strip=False, add_space=False):
 
 
 	def spec2int(spec):
 	def spec2int(spec):
-		for k,v in bytespec_map:
+		for k, v in bytespec_map:
 			if k == spec:
 			if k == spec:
 				return v
 				return v
-		die(1,f'{spec!r}: unrecognized bytespec')
+		die(1, f'{spec!r}: unrecognized bytespec')
 
 
 	ret = f'{n/spec2int(spec):{fmt}f}'
 	ret = f'{n/spec2int(spec):{fmt}f}'
 	if strip:
 	if strip:
@@ -101,17 +101,17 @@ def int2bytespec(n,spec,fmt,print_sym=True,strip=False,add_space=False):
 		return (
 		return (
 			ret
 			ret
 			+ ('0' if ret.endswith('.') else '')
 			+ ('0' if ret.endswith('.') else '')
-			+ ((' ' if add_space else '') + spec if print_sym else '') )
+			+ ((' ' if add_space else '') + spec if print_sym else ''))
 	else:
 	else:
 		return (
 		return (
 			ret
 			ret
-			+ ((' ' if add_space else '') + spec if print_sym else '') )
+			+ ((' ' if add_space else '') + spec if print_sym else ''))
 
 
 def parse_bytespec(nbytes):
 def parse_bytespec(nbytes):
-	m = re.match(r'([0123456789.]+)(.*)',nbytes)
+	m = re.match(r'([0123456789.]+)(.*)', nbytes)
 	if m:
 	if m:
 		if m.group(2):
 		if m.group(2):
-			for k,v in bytespec_map:
+			for k, v in bytespec_map:
 				if k == m.group(2):
 				if k == m.group(2):
 					from decimal import Decimal
 					from decimal import Decimal
 					return int(Decimal(m.group(1)) * v)
 					return int(Decimal(m.group(1)) * v)
@@ -121,9 +121,9 @@ def parse_bytespec(nbytes):
 		else:
 		else:
 			return int(nbytes)
 			return int(nbytes)
 
 
-	die(1,f'{nbytes!r}: invalid byte specifier')
+	die(1, f'{nbytes!r}: invalid byte specifier')
 
 
-def format_elapsed_days_hr(t,now=None,cached={}):
+def format_elapsed_days_hr(t, now=None, cached={}):
 	e = int((now or time.time()) - t)
 	e = int((now or time.time()) - t)
 	if not e in cached:
 	if not e in cached:
 		days = abs(e) // 86400
 		days = abs(e) // 86400
@@ -155,7 +155,7 @@ def format_elapsed_hr(t, now=None, cached={}, rel_now=True, show_secs=False):
 		cached[key] = ' '.join(f'{n} {desc}{suf(n)}' for desc, n in data if n) + add_suffix()
 		cached[key] = ' '.join(f'{n} {desc}{suf(n)}' for desc, n in data if n) + add_suffix()
 	return cached[key]
 	return cached[key]
 
 
-def pretty_format(s,width=80,pfx=''):
+def pretty_format(s, width=80, pfx=''):
 	out = []
 	out = []
 	while s:
 	while s:
 		if len(s) <= width:
 		if len(s) <= width:
@@ -166,8 +166,8 @@ def pretty_format(s,width=80,pfx=''):
 		s = s[i+1:]
 		s = s[i+1:]
 	return pfx + ('\n'+pfx).join(out)
 	return pfx + ('\n'+pfx).join(out)
 
 
-def block_format(data,gw=2,cols=8,line_nums=None,data_is_hex=False):
-	assert line_nums in (None,'hex','dec'),"'line_nums' must be one of None, 'hex' or 'dec'"
+def block_format(data, gw=2, cols=8, line_nums=None, data_is_hex=False):
+	assert line_nums in (None, 'hex', 'dec'), "'line_nums' must be one of None, 'hex' or 'dec'"
 	ln_fs = '{:06x}: ' if line_nums == 'hex' else '{:06}: '
 	ln_fs = '{:06x}: ' if line_nums == 'hex' else '{:06}: '
 	bytes_per_chunk = gw
 	bytes_per_chunk = gw
 	if data_is_hex:
 	if data_is_hex:
@@ -180,12 +180,12 @@ def block_format(data,gw=2,cols=8,line_nums=None,data_is_hex=False):
 			for i in range(nchunks)
 			for i in range(nchunks)
 	).rstrip() + '\n'
 	).rstrip() + '\n'
 
 
-def pretty_hexdump(data,gw=2,cols=8,line_nums=None):
-	return block_format(data.hex(),gw,cols,line_nums,data_is_hex=True)
+def pretty_hexdump(data, gw=2, cols=8, line_nums=None):
+	return block_format(data.hex(), gw, cols, line_nums, data_is_hex=True)
 
 
 def decode_pretty_hexdump(data):
 def decode_pretty_hexdump(data):
 	pat = re.compile(fr'^[{hexdigits}]+:\s+')
 	pat = re.compile(fr'^[{hexdigits}]+:\s+')
-	lines = [pat.sub('',line) for line in data.splitlines()]
+	lines = [pat.sub('', line) for line in data.splitlines()]
 	try:
 	try:
 		return bytes.fromhex(''.join((''.join(lines).split())))
 		return bytes.fromhex(''.join((''.join(lines).split())))
 	except:
 	except:

+ 16 - 16
mmgen/xmrseed.py

@@ -24,17 +24,17 @@ from .baseconv import baseconv
 from .util import die
 from .util import die
 
 
 def is_xmrseed(s):
 def is_xmrseed(s):
-	return bool( xmrseed().tobytes(s.split()) )
+	return bool(xmrseed().tobytes(s.split()))
 
 
 # implements a subset of the baseconv API
 # implements a subset of the baseconv API
 class xmrseed(baseconv):
 class xmrseed(baseconv):
 
 
 	desc            = baseconv.dt('Monero mnemonic', 'Monero new-style mnemonic seed phrase')
 	desc            = baseconv.dt('Monero mnemonic', 'Monero new-style mnemonic seed phrase')
 	wl_chksum       = '3c381ebb'
 	wl_chksum       = '3c381ebb'
-	seedlen_map     = { 32:25 }
-	seedlen_map_rev = { 25:32 }
+	seedlen_map     = {32: 25}
+	seedlen_map_rev = {25: 32}
 
 
-	def __init__(self,wl_id='xmrseed'):
+	def __init__(self, wl_id='xmrseed'):
 		assert wl_id == 'xmrseed', "initialize with 'xmrseed' for compatibility with baseconv API"
 		assert wl_id == 'xmrseed', "initialize with 'xmrseed' for compatibility with baseconv API"
 		from .wordlist.monero import words
 		from .wordlist.monero import words
 		self.digits = words
 		self.digits = words
@@ -46,9 +46,9 @@ class xmrseed(baseconv):
 		wstr = ''.join(word[:3] for word in words)
 		wstr = ''.join(word[:3] for word in words)
 		return words[crc32(wstr.encode()) % len(words)]
 		return words[crc32(wstr.encode()) % len(words)]
 
 
-	def tobytes(self,words_arg,pad=None):
+	def tobytes(self, words_arg, pad=None):
 
 
-		assert isinstance(words_arg,(list,tuple)),'words must be list or tuple'
+		assert isinstance(words_arg, (list, tuple)), 'words must be list or tuple'
 		assert pad is None, f"{pad}: invalid 'pad' argument (must be None)"
 		assert pad is None, f"{pad}: invalid 'pad' argument (must be None)"
 
 
 		words = words_arg
 		words = words_arg
@@ -58,26 +58,26 @@ class xmrseed(baseconv):
 		base = len(wl)
 		base = len(wl)
 
 
 		if not set(words) <= set(wl):
 		if not set(words) <= set(wl):
-			die( 'MnemonicError',  f'{words!r}: not in {desc} format'  )
+			die('MnemonicError',  f'{words!r}: not in {desc} format')
 
 
 		if len(words) not in self.seedlen_map_rev:
 		if len(words) not in self.seedlen_map_rev:
-			die( 'MnemonicError',  f'{len(words)}: invalid seed phrase length for {desc}' )
+			die('MnemonicError',  f'{len(words)}: invalid seed phrase length for {desc}')
 
 
 		z = self.monero_mn_checksum(words[:-1])
 		z = self.monero_mn_checksum(words[:-1])
 		if z != words[-1]:
 		if z != words[-1]:
-			die( 'MnemonicError', f'invalid {desc} checksum' )
+			die('MnemonicError', f'invalid {desc} checksum')
 
 
 		words = tuple(words[:-1])
 		words = tuple(words[:-1])
 
 
 		def gen():
 		def gen():
 			for i in range(len(words)//3):
 			for i in range(len(words)//3):
-				w1,w2,w3 = [wl.index(w) for w in words[3*i:3*i+3]]
+				w1, w2, w3 = [wl.index(w) for w in words[3*i:3*i+3]]
 				x = w1 + base*((w2-w1)%base) + base*base*((w3-w2)%base)
 				x = w1 + base*((w2-w1)%base) + base*base*((w3-w2)%base)
-				yield x.to_bytes(4,'big')[::-1]
+				yield x.to_bytes(4, 'big')[::-1]
 
 
 		return b''.join(gen())
 		return b''.join(gen())
 
 
-	def frombytes(self,bytestr,pad=None,tostr=False):
+	def frombytes(self, bytestr, pad=None, tostr=False):
 		assert pad is None, f"{pad}: invalid 'pad' argument (must be None)"
 		assert pad is None, f"{pad}: invalid 'pad' argument (must be None)"
 
 
 		desc = self.desc.short
 		desc = self.desc.short
@@ -85,19 +85,19 @@ class xmrseed(baseconv):
 		base = len(wl)
 		base = len(wl)
 
 
 		if len(bytestr) not in self.seedlen_map:
 		if len(bytestr) not in self.seedlen_map:
-			die( 'SeedLengthError', f'{len(bytestr)}: invalid seed byte length for {desc}' )
+			die('SeedLengthError', f'{len(bytestr)}: invalid seed byte length for {desc}')
 
 
 		def num2base_monero(num):
 		def num2base_monero(num):
 			w1 = num % base
 			w1 = num % base
 			w2 = (num//base + w1) % base
 			w2 = (num//base + w1) % base
 			w3 = (num//base//base + w2) % base
 			w3 = (num//base//base + w2) % base
-			return ( wl[w1], wl[w2], wl[w3] )
+			return (wl[w1], wl[w2], wl[w3])
 
 
 		def gen():
 		def gen():
 			for i in range(len(bytestr)//4):
 			for i in range(len(bytestr)//4):
-				yield from num2base_monero(int.from_bytes( bytestr[i*4:i*4+4][::-1], 'big' ))
+				yield from num2base_monero(int.from_bytes(bytestr[i*4:i*4+4][::-1], 'big'))
 
 
 		o = list(gen())
 		o = list(gen())
-		o.append( self.monero_mn_checksum(o) )
+		o.append(self.monero_mn_checksum(o))
 
 
 		return ' '.join(o) if tostr else tuple(o)
 		return ' '.join(o) if tostr else tuple(o)