Browse Source

f-strings, whitespace (program files) [43 files patched]

The MMGen Project 3 years ago
parent
commit
2872d4b683

+ 64 - 54
mmgen/addr.py

@@ -347,7 +347,11 @@ class AddrListIDStr(str,Hilite):
 		else:
 		else:
 			bc = (addrlist.proto.base_coin,addrlist.proto.coin)[addrlist.proto.base_coin=='ETH']
 			bc = (addrlist.proto.base_coin,addrlist.proto.coin)[addrlist.proto.base_coin=='ETH']
 			mt = addrlist.al_id.mmtype
 			mt = addrlist.al_id.mmtype
-			ret = '{}{}{}[{}]'.format(addrlist.al_id.sid,('-'+bc,'')[bc=='BTC'],('-'+mt,'')[mt in ('L','E')],s)
+			ret = '{}{}{}[{}]'.format(
+				addrlist.al_id.sid,
+				('-'+bc,'')[bc == 'BTC'],
+				('-'+mt,'')[mt in ('L','E')],
+				s )
 
 
 		dmsg_sc('id_str',ret[8:].split('[')[0])
 		dmsg_sc('id_str',ret[8:].split('[')[0])
 
 
@@ -355,7 +359,7 @@ class AddrListIDStr(str,Hilite):
 
 
 class AddrList(MMGenObject): # Address info for a single seed ID
 class AddrList(MMGenObject): # Address info for a single seed ID
 	msgs = {
 	msgs = {
-	'file_header': """
+		'file_header': """
 # {pnm} address file
 # {pnm} address file
 #
 #
 # This file is editable.
 # This file is editable.
@@ -364,13 +368,13 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 # address, and it will be appended to the tracking wallet label upon import.
 # address, and it will be appended to the tracking wallet label upon import.
 # The label may contain any printable ASCII symbol.
 # The label may contain any printable ASCII symbol.
 """.strip().format(n=TwComment.max_screen_width,pnm=pnm),
 """.strip().format(n=TwComment.max_screen_width,pnm=pnm),
-	'record_chksum': """
+		'record_chksum': """
 Record this checksum: it will be used to verify the address file in the future
 Record this checksum: it will be used to verify the address file in the future
 """.strip(),
 """.strip(),
-	'check_chksum': 'Check this value against your records',
-	'removed_dup_keys': """
+		'check_chksum': 'Check this value against your records',
+		'removed_dup_keys': f"""
 Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
-""".strip().format(pnm=pnm)
+""".strip(),
 	}
 	}
 	entry_type = AddrListEntry
 	entry_type = AddrListEntry
 	main_attr = 'addr'
 	main_attr = 'addr'
@@ -431,7 +435,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 		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,'Incorrect arguments for {}'.format(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
@@ -446,8 +450,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 
 
 		if do_chksum:
 		if do_chksum:
 			self.chksum = AddrListChksum(self)
 			self.chksum = AddrListChksum(self)
-			qmsg('Checksum for {} data {}: {}'.format(
-					self.data_desc,self.id_str.hl(),self.chksum.hl()))
+			qmsg(f'Checksum for {self.data_desc} data {self.id_str.hl()}: {self.chksum.hl()}')
 			qmsg(self.msgs[('check_chksum','record_chksum')[src=='gen']])
 			qmsg(self.msgs[('check_chksum','record_chksum')[src=='gen']])
 
 
 	def update_msgs(self):
 	def update_msgs(self):
@@ -482,7 +485,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 			pos += 1
 			pos += 1
 
 
 			if not g.debug:
 			if not g.debug:
-				qmsg_r('\rGenerating {} #{} ({} of {})'.format(self.gen_desc,num,pos,t_addrs))
+				qmsg_r(f'\rGenerating {self.gen_desc} #{num} ({pos} of {t_addrs})')
 
 
 			e = le(proto=self.proto,idx=num)
 			e = le(proto=self.proto,idx=num)
 
 
@@ -503,14 +506,19 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 
 
 			if type(self) == PasswordList:
 			if type(self) == PasswordList:
 				e.passwd = str(self.make_passwd(e.sec)) # TODO - own type
 				e.passwd = str(self.make_passwd(e.sec)) # TODO - own type
-				dmsg('Key {:>03}: {}'.format(pos,e.passwd))
+				dmsg(f'Key {pos:>03}: {e.passwd}')
 
 
 			out.append(e)
 			out.append(e)
 			if g.debug_addrlist:
 			if g.debug_addrlist:
-				Msg('generate():\n{}'.format(e.pfmt()))
+				Msg(f'generate():\n{e.pfmt()}')
 
 
 		qmsg('\r{}: {} {}{} generated{}'.format(
 		qmsg('\r{}: {} {}{} generated{}'.format(
-				self.al_id.hl(),t_addrs,self.gen_desc,suf(t_addrs,self.gen_desc_pl),' '*15))
+			self.al_id.hl(),
+			t_addrs,
+			self.gen_desc,
+			suf(t_addrs,self.gen_desc_pl),
+			' ' * 15 ))
+
 		return out
 		return out
 
 
 	def check_format(self,addr):
 	def check_format(self,addr):
@@ -546,7 +554,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 		return [e.idx for e in self.data]
 		return [e.idx for e in self.data]
 
 
 	def addrs(self):
 	def addrs(self):
-		return ['{}:{}'.format(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]
@@ -612,11 +620,11 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 		ag = AddrGenerator(self.proto,at)
 		ag = AddrGenerator(self.proto,at)
 		d = self.data
 		d = self.data
 		for n,e in enumerate(d,1):
 		for n,e in enumerate(d,1):
-			qmsg_r('\rGenerating addresses from keylist: {}/{}'.format(n,len(d)))
+			qmsg_r(f'\rGenerating addresses from keylist: {n}/{len(d)}')
 			e.addr = ag.to_addr(kg.to_pubhex(e.sec))
 			e.addr = ag.to_addr(kg.to_pubhex(e.sec))
 			if g.debug_addrlist:
 			if g.debug_addrlist:
-				Msg('generate_addrs_from_keys():\n{}'.format(e.pfmt()))
-		qmsg('\rGenerated addresses from keylist: {}/{} '.format(n,len(d)))
+				Msg(f'generate_addrs_from_keys():\n{e.pfmt()}')
+		qmsg(f'\rGenerated addresses from keylist: {n}/{len(d)} ')
 
 
 	def make_label(self):
 	def make_label(self):
 		bc,mt = self.proto.base_coin,self.al_id.mmtype
 		bc,mt = self.proto.base_coin,self.al_id.mmtype
@@ -630,30 +638,29 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 
 
 		out = [self.msgs['file_header']+'\n']
 		out = [self.msgs['file_header']+'\n']
 		if self.chksum:
 		if self.chksum:
-			out.append('# {} data checksum for {}: {}'.format(
-						capfirst(self.data_desc),self.id_str,self.chksum))
+			out.append(f'# {capfirst(self.data_desc)} data checksum for {self.id_str}: {self.chksum}')
 			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()
 		dmsg_sc('lbl',lbl[9:])
 		dmsg_sc('lbl',lbl[9:])
-		out.append('{} {{'.format(lbl))
+		out.append(f'{lbl} {{')
 
 
 		fs = '  {:<%s}  {:<34}{}' % len(str(self.data[-1].idx))
 		fs = '  {:<%s}  {:<34}{}' % len(str(self.data[-1].idx))
 		for e in self.data:
 		for e in self.data:
 			c = ' '+e.label if add_comments and e.label else ''
 			c = ' '+e.label if add_comments and e.label else ''
 			if type(self) == KeyList:
 			if type(self) == KeyList:
-				out.append(fs.format(e.idx,'{}: {}'.format(self.al_id.mmtype.wif_label,e.sec.wif),c))
+				out.append(fs.format( e.idx, f'{self.al_id.mmtype.wif_label}: {e.sec.wif}', c ))
 			elif type(self) == PasswordList:
 			elif type(self) == 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,c))
 				out.append(fs.format(e.idx,e.addr,c))
 				if self.has_keys:
 				if self.has_keys:
 					if opt.b16:
 					if opt.b16:
-						out.append(fs.format('', 'orig_hex: '+e.sec.orig_hex,c))
-					out.append(fs.format('','{}: {}'.format(self.al_id.mmtype.wif_label,e.sec.wif),c))
+						out.append(fs.format( '', f'orig_hex: {e.sec.orig_hex}', c ))
+					out.append(fs.format( '', f'{self.al_id.mmtype.wif_label}: {e.sec.wif}', c ))
 					for k in ('viewkey','wallet_passwd'):
 					for k in ('viewkey','wallet_passwd'):
 						v = getattr(e,k)
 						v = getattr(e,k)
-						if v: out.append(fs.format('','{}: {}'.format(k,v),c))
+						if v: 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'
@@ -682,14 +689,14 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 
 
 			if self.has_keys: # order: wif,(orig_hex),viewkey,wallet_passwd
 			if self.has_keys: # order: wif,(orig_hex),viewkey,wallet_passwd
 				d = self.get_line(lines)
 				d = self.get_line(lines)
-				assert d[0] == self.al_id.mmtype.wif_label+':',iifs.format(d[0],self.al_id.mmtype.wif_label)
+				assert d[0] == self.al_id.mmtype.wif_label+':', iifs.format(d[0],self.al_id.mmtype.wif_label)
 				a.sec = PrivKey(proto=self.proto,wif=d[1])
 				a.sec = PrivKey(proto=self.proto,wif=d[1])
 				for k,dtype,add_proto in (
 				for k,dtype,add_proto in (
 					('viewkey',ViewKey,True),
 					('viewkey',ViewKey,True),
 					('wallet_passwd',WalletPassword,False) ):
 					('wallet_passwd',WalletPassword,False) ):
 					if k in self.al_id.mmtype.extra_attrs:
 					if k in self.al_id.mmtype.extra_attrs:
 						d = self.get_line(lines)
 						d = self.get_line(lines)
-						assert d[0] == k+':',iifs.format(d[0],k)
+						assert d[0] == k+':', iifs.format(d[0],k)
 						setattr(a,k,dtype( *((self.proto,d[1]) if add_proto else (d[1],)) ) )
 						setattr(a,k,dtype( *((self.proto,d[1]) if add_proto else (d[1],)) ) )
 
 
 			ret.append(a)
 			ret.append(a)
@@ -700,9 +707,9 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 				ag = AddrGenerator(self.proto,self.al_id.mmtype)
 				ag = AddrGenerator(self.proto,self.al_id.mmtype)
 				llen = len(ret)
 				llen = len(ret)
 				for n,e in enumerate(ret):
 				for n,e in enumerate(ret):
-					qmsg_r('\rVerifying keys {}/{}'.format(n+1,llen))
+					qmsg_r(f'\rVerifying keys {n+1}/{llen}')
 					assert e.addr == ag.to_addr(kg.to_pubhex(e.sec)),(
 					assert e.addr == ag.to_addr(kg.to_pubhex(e.sec)),(
-						"Key doesn't match address!\n  {}\n  {}".format(e.sec.wif,e.addr))
+						f'Key doesn’t match address!\n  {e.sec.wif}\n  {e.addr}')
 				qmsg(' - done')
 				qmsg(' - done')
 
 
 		return ret
 		return ret
@@ -934,9 +941,9 @@ Record this checksum: it will be used to verify the password file in the future
 		self.fmt_data = ''
 		self.fmt_data = ''
 		self.chksum = AddrListChksum(self)
 		self.chksum = AddrListChksum(self)
 
 
-		fs = '{}-{}-{}-{}[{{}}]'.format(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)
-		qmsg('Checksum for {} data {}: {}'.format(self.data_desc,self.id_str.hl(),self.chksum.hl()))
+		qmsg(f'Checksum for {self.data_desc} data {self.id_str.hl()}: {self.chksum.hl()}')
 		qmsg(self.msgs[('record_chksum','check_chksum')[bool(infile)]])
 		qmsg(self.msgs[('record_chksum','check_chksum')[bool(infile)]])
 
 
 	def set_pw_fmt(self,pw_fmt):
 	def set_pw_fmt(self,pw_fmt):
@@ -964,11 +971,11 @@ Record this checksum: it will be used to verify the password file in the future
 		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]
@@ -978,7 +985,7 @@ Record this checksum: it will be used to verify the password file in the future
 			return
 			return
 
 
 		if not is_int(pw_len):
 		if not is_int(pw_len):
-			die(2,"'{}': invalid user-requested password length (not an integer)".format(pw_len,d.desc))
+			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()
 
 
@@ -996,24 +1003,27 @@ Record this checksum: it will be used to verify the password file in the future
 			try:
 			try:
 				good_pw_len = baseconv.seedlen_map['xmrseed'][seed.byte_len]
 				good_pw_len = baseconv.seedlen_map['xmrseed'][seed.byte_len]
 			except:
 			except:
-				die(1,'{}: unsupported seed length for Monero new-style mnemonic'.format(seed.byte_len*8))
+				die(1,f'{seed.byte_len*8}: unsupported seed length for Monero new-style mnemonic')
 		elif pf in ('b32','b58'):
 		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
 			good_pw_len = len(baseconv.frombytes(b'\xff'*seed.byte_len,wl_id=pf))
 			good_pw_len = len(baseconv.frombytes(b'\xff'*seed.byte_len,wl_id=pf))
 		else:
 		else:
-			raise NotImplementedError('{!r}: unknown password format'.format(pf))
+			raise NotImplementedError(f'{pf!r}: unknown password format')
 
 
 		if pw_bytes > seed.byte_len:
 		if pw_bytes > seed.byte_len:
-			m1 = 'Cannot generate passwords with more entropy than underlying seed! ({} bits)'
-			m2  = ( 'Re-run the command with --passwd-len={}' if pf in ('bip39','hex') else
-					'Re-run the command, specifying a password length of {} or less' )
-			die(1,(m1+'\n'+m2).format(len(seed.data) * 8,good_pw_len))
+			die(1,
+				'Cannot generate passwords with more entropy than underlying seed! ({} bits)\n'.format(
+					len(seed.data) * 8 ) + (
+					'Re-run the command with --passwd-len={}' if pf in ('bip39','hex') else
+					'Re-run the command, specifying a password length of {} or less'
+				).format(good_pw_len) )
 
 
 		if pf in ('bip39','hex') and pw_bytes < seed.byte_len:
 		if pf in ('bip39','hex') and pw_bytes < seed.byte_len:
-			m1 = 'WARNING: requested {} length has less entropy than underlying seed!'
-			m2 = 'Is this what you want?'
-			if not keypress_confirm((m1+'\n'+m2).format(self.pw_info[pf].desc),default_yes=True):
+			if not keypress_confirm(
+					f'WARNING: requested {self.pw_info[pf].desc} length has less entropy ' +
+					'than underlying seed!\nIs this what you want?',
+					default_yes = True ):
 				die(1,'Exiting at user request')
 				die(1,'Exiting at user request')
 
 
 	def make_passwd(self,hex_sec):
 	def make_passwd(self,hex_sec):
@@ -1038,22 +1048,22 @@ Record this checksum: it will be used to verify the password file in the future
 
 
 	def check_format(self,pw):
 	def check_format(self,pw):
 		if not self.pw_info[self.pw_fmt].chk_func(pw):
 		if not self.pw_info[self.pw_fmt].chk_func(pw):
-			raise ValueError('Password is not valid {} data'.format(self.pw_info[self.pw_fmt].desc))
+			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('Password has incorrect length ({} != {})'.format(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 = '{}:{}:{}'.format(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:
 			from .bip39 import bip39
 			from .bip39 import bip39
 			pwlen = bip39.nwords2seedlen(self.pw_len,in_hex=True)
 			pwlen = bip39.nwords2seedlen(self.pw_len,in_hex=True)
-			scramble_key = '{}:{}:{}'.format('hex',pwlen,self.pw_id_str)
+			scramble_key = f'hex:{pwlen}:{self.pw_id_str}'
 
 
 		from .crypto import scramble_seed
 		from .crypto import scramble_seed
 		dmsg_sc('str',scramble_key)
 		dmsg_sc('str',scramble_key)
@@ -1064,11 +1074,11 @@ Record this checksum: it will be used to verify the password file in the future
 		if self.pw_fmt in ('bip39','xmrseed'):
 		if self.pw_fmt in ('bip39','xmrseed'):
 			ret = lines.pop(0).split(None,self.pw_len+1)
 			ret = lines.pop(0).split(None,self.pw_len+1)
 			if len(ret) > self.pw_len+1:
 			if len(ret) > self.pw_len+1:
-				m1 = 'extraneous text {!r} found after password'.format(ret[self.pw_len+1])
+				m1 = f'extraneous text {ret[self.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]'
 				m = m1+' '+m2
 				m = m1+' '+m2
 			elif len(ret) < self.pw_len+1:
 			elif len(ret) < self.pw_len+1:
-				m = 'invalid password length {}'.format(len(ret)-1)
+				m = f'invalid password length {len(ret)-1}'
 			else:
 			else:
 				return (ret[0],' '.join(ret[1:self.pw_len+1]),'')
 				return (ret[0],' '.join(ret[1:self.pw_len+1]),'')
 			raise ValueError(m)
 			raise ValueError(m)
@@ -1077,16 +1087,16 @@ Record this checksum: it will be used to verify the password file in the future
 			return ret if len(ret) == 3 else ret + ['']
 			return ret if len(ret) == 3 else ret + ['']
 
 
 	def make_label(self):
 	def make_label(self):
-		return '{} {} {}:{}'.format(self.al_id.sid,self.pw_id_str,self.pw_fmt_disp,self.pw_len)
+		return f'{self.al_id.sid} {self.pw_id_str} {self.pw_fmt_disp}:{self.pw_len}'
 
 
 class AddrData(MMGenObject):
 class AddrData(MMGenObject):
 	msgs = {
 	msgs = {
-	'too_many_acct_addresses': """
+	'too_many_acct_addresses': f"""
 ERROR: More than one address found for account: '{{}}'.
 ERROR: More than one address found for account: '{{}}'.
 Your 'wallet.dat' file appears to have been altered by a non-{pnm} program.
 Your 'wallet.dat' file appears to have been altered by a non-{pnm} program.
 Please restore your tracking wallet from a backup or create a new one and
 Please restore your tracking wallet from a backup or create a new one and
 re-import your addresses.
 re-import your addresses.
-""".strip().format(pnm=pnm)
+""".strip()
 	}
 	}
 
 
 	def __new__(cls,proto,*args,**kwargs):
 	def __new__(cls,proto,*args,**kwargs):
@@ -1121,7 +1131,7 @@ re-import your addresses.
 			self.al_ids[addrlist.al_id] = addrlist
 			self.al_ids[addrlist.al_id] = addrlist
 			return True
 			return True
 		else:
 		else:
-			raise TypeError('Error: object {!r} is not of type AddrList'.format(addrlist))
+			raise TypeError(f'Error: object {addrlist!r} is not of type AddrList')
 
 
 	def make_reverse_dict(self,coinaddrs):
 	def make_reverse_dict(self,coinaddrs):
 		d = MMGenDict()
 		d = MMGenDict()
@@ -1169,6 +1179,6 @@ class TwAddrData(AddrData,metaclass=AsyncInit):
 				out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],label=l.comment))
 				out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],label=l.comment))
 				i += 1
 				i += 1
 
 
-		vmsg('{n} {pnm} addresses found, {m} accounts total'.format(n=i,pnm=pnm,m=len(twd)))
+		vmsg(f'{i} {pnm} addresses found, {len(twd)} accounts total')
 		for al_id in out:
 		for al_id in out:
 			self.add(AddrList(self.proto,al_id=al_id,adata=AddrListData(sorted(out[al_id],key=lambda a: a.idx))))
 			self.add(AddrList(self.proto,al_id=al_id,adata=AddrListData(sorted(out[al_id],key=lambda a: a.idx))))

+ 28 - 25
mmgen/altcoin.py

@@ -36,7 +36,9 @@ altcoin.py - Coin constants for Bitcoin-derived altcoins
 #   NBT:  150/191 c/u,  25/('B'),  26/('B')
 #   NBT:  150/191 c/u,  25/('B'),  26/('B')
 
 
 import sys
 import sys
-def msg(s): sys.stderr.write(s+'\n')
+
+def msg(s):
+	sys.stderr.write(s+'\n')
 
 
 def test_equal(desc,a,b,*cdata):
 def test_equal(desc,a,b,*cdata):
 	if type(a) == int:
 	if type(a) == int:
@@ -44,11 +46,16 @@ def test_equal(desc,a,b,*cdata):
 		b = hex(b)
 		b = hex(b)
 	(network,coin,e,b_desc,verbose) = cdata
 	(network,coin,e,b_desc,verbose) = cdata
 	if verbose:
 	if verbose:
-		m = '  {:20}: {!r}'
-		msg(m.format(desc,a))
+		msg(f'  {desc:20}: {a!r}')
 	if a != b:
 	if a != b:
-		m = '{}s for {} {} do not match:\n  CoinInfo: {}\n  {}: {}'
-		raise ValueError(m.format(desc.capitalize(),coin.upper(),network,a,b_desc,b))
+		raise ValueError(
+			'{}s for {} {} do not match:\n  CoinInfo: {}\n  {}: {}'.format(
+				desc.capitalize(),
+				coin.upper(),
+				network,
+				a,
+				b_desc,
+				b ))
 
 
 from collections import namedtuple
 from collections import namedtuple
 ce = namedtuple('CoinInfoEntry',
 ce = namedtuple('CoinInfoEntry',
@@ -415,7 +422,7 @@ class CoinInfo(object):
 				cdata = (network,coin,e,'Computed value',verbose)
 				cdata = (network,coin,e,'Computed value',verbose)
 
 
 				if not quiet:
 				if not quiet:
-					msg('{} {}'.format(coin,network))
+					msg(f'{coin} {network}')
 
 
 				vn_info = e.p2pkh_info
 				vn_info = e.p2pkh_info
 				ret = cls.find_addr_leading_symbol(vn_info[0])
 				ret = cls.find_addr_leading_symbol(vn_info[0])
@@ -437,7 +444,7 @@ class CoinInfo(object):
 					proto = init_proto(coin,testnet=network=='testnet')
 					proto = init_proto(coin,testnet=network=='testnet')
 					cdata = (network,coin,e,type(proto).__name__,verbose)
 					cdata = (network,coin,e,type(proto).__name__,verbose)
 					if not quiet:
 					if not quiet:
-						msg('Verifying {} {}'.format(coin.upper(),network))
+						msg(f'Verifying {coin.upper()} {network}')
 
 
 					if coin != 'bch': # TODO
 					if coin != 'bch': # TODO
 						test_equal('coin name',e.name,proto.name,*cdata)
 						test_equal('coin name',e.name,proto.name,*cdata)
@@ -490,9 +497,9 @@ class CoinInfo(object):
 			e[k] = list(e[k])
 			e[k] = list(e[k])
 			e[k][0] = myhex(e[k][0])
 			e[k][0] = myhex(e[k][0])
 			s1 = cls.find_addr_leading_symbol(int(e[k][0][2:],16))
 			s1 = cls.find_addr_leading_symbol(int(e[k][0][2:],16))
-			m = 'Fixing leading address letter for coin {} ({!r} --> {})'.format(e['symbol'],e[k][1],s1)
+			m = f'Fixing leading address letter for coin {e["symbol"]} ({e[k][1]!r} --> {s1})'
 			if e[k][1] != '?':
 			if e[k][1] != '?':
-				assert s1 == e[k][1],'First letters do not match! {}'.format(m)
+				assert s1 == e[k][1], f'First letters do not match! {m}'
 			else:
 			else:
 				msg(m)
 				msg(m)
 				e[k][1] = s1
 				e[k][1] = s1
@@ -501,7 +508,7 @@ class CoinInfo(object):
 		old_sym = None
 		old_sym = None
 		for sym in sorted([e.symbol for e in data]):
 		for sym in sorted([e.symbol for e in data]):
 			if sym == old_sym:
 			if sym == old_sym:
-				msg("'{}': duplicate coin symbol in data!".format(sym))
+				msg(f'{sym!r}: duplicate coin symbol in data!')
 				sys.exit(2)
 				sys.exit(2)
 			old_sym = sym
 			old_sym = sym
 
 
@@ -528,20 +535,20 @@ class CoinInfo(object):
 				if sym in tt:
 				if sym in tt:
 					src = tt[sym]
 					src = tt[sym]
 					if src != trust:
 					if src != trust:
-						msg("Updating trust for coin '{}': {} -> {}".format(sym,trust,src))
+						msg(f'Updating trust for coin {sym!r}: {trust} -> {src}')
 						e['trust_level'] = src
 						e['trust_level'] = src
 				else:
 				else:
 					if trust != 0:
 					if trust != 0:
-						msg("Downgrading trust for coin '{}': {} -> {}".format(sym,trust,0))
+						msg(f'Downgrading trust for coin {sym!r}: {trust} -> 0')
 						e['trust_level'] = 0
 						e['trust_level'] = 0
 
 
 				if sym in cls.cross_checks:
 				if sym in cls.cross_checks:
 					if int(e['trust_level']) == 0 and len(cls.cross_checks[sym]) > 1:
 					if int(e['trust_level']) == 0 and len(cls.cross_checks[sym]) > 1:
-						msg("Upgrading trust for coin '{}': {} -> {}".format(sym,e['trust_level'],1))
+						msg(f'Upgrading trust for coin {sym!r}: {e["trust_level"]} -> 1')
 						e['trust_level'] = 1
 						e['trust_level'] = 1
 
 
 			print(fs.format(*e.values()))
 			print(fs.format(*e.values()))
-		msg('Processed {} entries'.format(len(data)))
+		msg(f'Processed {len(data)} entries')
 
 
 	@classmethod
 	@classmethod
 	def find_addr_leading_symbol(cls,ver_num,verbose=False):
 	def find_addr_leading_symbol(cls,ver_num,verbose=False):
@@ -569,17 +576,17 @@ class CoinInfo(object):
 	def print_symbols(cls,include_names=False,reverse=False):
 	def print_symbols(cls,include_names=False,reverse=False):
 		for e in cls.coin_constants['mainnet']:
 		for e in cls.coin_constants['mainnet']:
 			if reverse:
 			if reverse:
-				print('{:6} {}'.format(e.symbol,e.name))
+				print(f'{e.symbol:6} {e.name}')
 			else:
 			else:
 				name_w = max(len(e.name) for e in cls.coin_constants['mainnet'])
 				name_w = max(len(e.name) for e in cls.coin_constants['mainnet'])
-				print(('{:{}} '.format(e.name,name_w) if include_names else '') + e.symbol)
+				print((f'{e.name:{name_w}} ' if include_names else '') + e.symbol)
 
 
 	@classmethod
 	@classmethod
 	def create_trust_table(cls):
 	def create_trust_table(cls):
 		tt = {}
 		tt = {}
 		mn = cls.external_tests['mainnet']
 		mn = cls.external_tests['mainnet']
 		for ext_prog in mn:
 		for ext_prog in mn:
-			assert len(set(mn[ext_prog])) == len(mn[ext_prog]),"Duplicate entry in '{}'!".format(ext_prog)
+			assert len(set(mn[ext_prog])) == len(mn[ext_prog]), f'Duplicate entry in {ext_prog!r}!'
 			for coin in mn[ext_prog]:
 			for coin in mn[ext_prog]:
 				if coin in tt:
 				if coin in tt:
 					tt[coin] += 1
 					tt[coin] += 1
@@ -609,8 +616,7 @@ class CoinInfo(object):
 			if verbose:
 			if verbose:
 				m1 = 'Requested tool {t!r} does not support coin {c} on network {n}'
 				m1 = 'Requested tool {t!r} does not support coin {c} on network {n}'
 				m2 = 'No test tool found for coin {c} on network {n}'
 				m2 = 'No test tool found for coin {c} on network {n}'
-				m = m1 if tool_arg else m2
-				msg(m.format(t=tool,c=coin,n=network))
+				msg((m1 if tool_arg else m2).format(t=tool,c=coin,n=network))
 			return None
 			return None
 
 
 		if addr_type == 'zcash_z':
 		if addr_type == 'zcash_z':
@@ -618,8 +624,7 @@ class CoinInfo(object):
 				return 'zcash-mini'
 				return 'zcash-mini'
 			else:
 			else:
 				if verbose:
 				if verbose:
-					m = "Address type {a!r} supported only by tool 'zcash-mini'"
-					msg(m.format(a=addr_type))
+					msg(f"Address type {addr_type!r} supported only by tool 'zcash-mini'")
 				return None
 				return None
 
 
 		try:
 		try:
@@ -630,8 +635,7 @@ class CoinInfo(object):
 			pass
 			pass
 		else:
 		else:
 			if verbose:
 			if verbose:
-				m = 'Tool {t!r} blacklisted for coin {c}, addr_type {a!r}'
-				msg(m.format(t=tool,c=coin,a=addr_type))
+				msg(f'Tool {tool!r} blacklisted for coin {coin}, addr_type {addr_type!r}')
 			return None
 			return None
 
 
 		if tool_arg: # skip whitelists
 		if tool_arg: # skip whitelists
@@ -645,8 +649,7 @@ class CoinInfo(object):
 				if verbose:
 				if verbose:
 					m1 = 'Requested tool {t!r} does not support coin {c}, addr_type {a!r}, on network {n}'
 					m1 = 'Requested tool {t!r} does not support coin {c}, addr_type {a!r}, on network {n}'
 					m2 = 'No test tool found supporting coin {c}, addr_type {a!r}, on network {n}'
 					m2 = 'No test tool found supporting coin {c}, addr_type {a!r}, on network {n}'
-					m = m1 if tool_arg else m2
-					msg(m.format(t=tool,c=coin,n=network,a=addr_type))
+					msg((m1 if tool_arg else m2).format(t=tool,c=coin,n=network,a=addr_type))
 				return None
 				return None
 
 
 		return tool
 		return tool

+ 13 - 10
mmgen/altcoins/eth/contract.py

@@ -51,7 +51,9 @@ class TokenBase(MMGenObject): # ERC20
 	async def do_call(self,method_sig,method_args='',toUnit=False):
 	async def do_call(self,method_sig,method_args='',toUnit=False):
 		data = create_method_id(method_sig) + method_args
 		data = create_method_id(method_sig) + method_args
 		if g.debug:
 		if g.debug:
-			msg('ETH_CALL {}:  {}'.format(method_sig,'\n  '.join(parse_abi(data))))
+			msg('ETH_CALL {}:  {}'.format(
+				method_sig,
+				'\n  '.join(parse_abi(data)) ))
 		ret = await self.rpc.call('eth_call',{ 'to': '0x'+self.addr, 'data': '0x'+data },'pending')
 		ret = await self.rpc.call('eth_call',{ 'to': '0x'+self.addr, 'data': '0x'+data },'pending')
 		if self.proto.network == 'regtest' and g.daemon_id == 'erigon': # ERIGON
 		if self.proto.network == 'regtest' and g.daemon_id == 'erigon': # ERIGON
 			import asyncio
 			import asyncio
@@ -79,19 +81,19 @@ class TokenBase(MMGenObject): # ERC20
 			assert ret[:2] == '0x'
 			assert ret[:2] == '0x'
 			return int(ret,16)
 			return int(ret,16)
 		except:
 		except:
-			msg("RPC call to decimals() failed (returned '{}')".format(ret))
+			msg(f'RPC call to decimals() failed (returned {ret!r})')
 			return None
 			return None
 
 
 	async def get_total_supply(self):
 	async def get_total_supply(self):
 		return await self.do_call('totalSupply()',toUnit=True)
 		return await self.do_call('totalSupply()',toUnit=True)
 
 
 	async def info(self):
 	async def info(self):
-		fs = '{:15}{}\n' * 5
-		return fs.format('token address:', self.addr,
-						'token symbol:',   await self.get_symbol(),
-						'token name:',     await self.get_name(),
-						'decimals:',       self.decimals,
-						'total supply:',   await self.get_total_supply())
+		return ('{:15}{}\n' * 5).format(
+			'token address:', self.addr,
+			'token symbol:',  await self.get_symbol(),
+			'token name:',    await self.get_name(),
+			'decimals:',      self.decimals,
+			'total supply:',  await self.get_total_supply() )
 
 
 	async def code(self):
 	async def code(self):
 		return (await self.rpc.call('eth_getCode','0x'+self.addr))[2:]
 		return (await self.rpc.call('eth_getCode','0x'+self.addr))[2:]
@@ -99,7 +101,7 @@ class TokenBase(MMGenObject): # ERC20
 	def create_data(self,to_addr,amt,method_sig='transfer(address,uint256)',from_addr=None):
 	def create_data(self,to_addr,amt,method_sig='transfer(address,uint256)',from_addr=None):
 		from_arg = from_addr.rjust(64,'0') if from_addr else ''
 		from_arg = from_addr.rjust(64,'0') if from_addr else ''
 		to_arg = to_addr.rjust(64,'0')
 		to_arg = to_addr.rjust(64,'0')
-		amt_arg = '{:064x}'.format(int(amt / self.base_unit))
+		amt_arg = '{:064x}'.format( int(amt / self.base_unit) )
 		return create_method_id(method_sig) + from_arg + to_arg + amt_arg
 		return create_method_id(method_sig) + from_arg + to_arg + amt_arg
 
 
 	def make_tx_in( self,from_addr,to_addr,amt,start_gas,gasPrice,nonce,
 	def make_tx_in( self,from_addr,to_addr,amt,start_gas,gasPrice,nonce,
@@ -128,7 +130,8 @@ class TokenBase(MMGenObject): # ERC20
 		if g.debug:
 		if g.debug:
 			msg('TOKEN DATA:')
 			msg('TOKEN DATA:')
 			pp_msg(tx.to_dict())
 			pp_msg(tx.to_dict())
-			msg('PARSED ABI DATA:\n  {}'.format('\n  '.join(parse_abi(tx.data.hex()))))
+			msg('PARSED ABI DATA:\n  {}'.format(
+				'\n  '.join(parse_abi(tx.data.hex())) ))
 		return hex_tx,coin_txid
 		return hex_tx,coin_txid
 
 
 # The following are used for token deployment only:
 # The following are used for token deployment only:

+ 3 - 3
mmgen/altcoins/eth/tw.py

@@ -43,7 +43,7 @@ class EthereumTrackingWallet(TrackingWallet):
 		upgraded = False
 		upgraded = False
 
 
 		if not 'accounts' in self.data or not 'coin' in self.data:
 		if not 'accounts' in self.data or not 'coin' in self.data:
-			ymsg('Upgrading {} (v1->v2: accounts field added)'.format(self.desc))
+			ymsg(f'Upgrading {self.desc} (v1->v2: accounts field added)')
 			if not 'accounts' in self.data:
 			if not 'accounts' in self.data:
 				self.data = {}
 				self.data = {}
 				import json
 				import json
@@ -66,13 +66,13 @@ class EthereumTrackingWallet(TrackingWallet):
 			upgraded = True
 			upgraded = True
 
 
 		if self.data['tokens'] and not have_token_params_fields():
 		if self.data['tokens'] and not have_token_params_fields():
-			ymsg('Upgrading {} (v2->v3: token params fields added)'.format(self.desc))
+			ymsg(f'Upgrading {self.desc} (v2->v3: token params fields added)')
 			add_token_params_fields()
 			add_token_params_fields()
 			upgraded = True
 			upgraded = True
 
 
 		if upgraded:
 		if upgraded:
 			self.force_write()
 			self.force_write()
-			msg('{} upgraded successfully!'.format(self.desc))
+			msg(f'{self.desc} upgraded successfully!')
 
 
 	async def rpc_get_balance(self,addr):
 	async def rpc_get_balance(self,addr):
 		return ETHAmt(int(await self.rpc.call('eth_getBalance','0x'+addr,'latest'),16),'wei')
 		return ETHAmt(int(await self.rpc.call('eth_getBalance','0x'+addr,'latest'),16),'wei')

+ 19 - 16
mmgen/altcoins/eth/tx.py

@@ -46,7 +46,7 @@ class EthereumMMGenTX:
 		# given absolute fee in ETH, return gas price in Gwei using tx_gas
 		# given absolute fee in ETH, return gas price in Gwei using tx_gas
 		def fee_abs2rel(self,abs_fee,to_unit='Gwei'):
 		def fee_abs2rel(self,abs_fee,to_unit='Gwei'):
 			ret = ETHAmt(int(abs_fee.toWei() // self.tx_gas.toWei()),'wei')
 			ret = ETHAmt(int(abs_fee.toWei() // self.tx_gas.toWei()),'wei')
-			dmsg('fee_abs2rel() ==> {} ETH'.format(ret))
+			dmsg(f'fee_abs2rel() ==> {ret} ETH')
 			return ret if to_unit == 'eth' else ret.to_unit(to_unit,show_decimal=True)
 			return ret if to_unit == 'eth' else ret.to_unit(to_unit,show_decimal=True)
 
 
 		def get_hex_locktime(self):
 		def get_hex_locktime(self):
@@ -54,7 +54,7 @@ class EthereumMMGenTX:
 
 
 		# given rel fee (gasPrice) in wei, return absolute fee using tx_gas (not in MMGenTX)
 		# given rel fee (gasPrice) in wei, return absolute fee using tx_gas (not in MMGenTX)
 		def fee_gasPrice2abs(self,rel_fee):
 		def fee_gasPrice2abs(self,rel_fee):
-			assert isinstance(rel_fee,int),"'{}': incorrect type for fee estimate (not an integer)".format(rel_fee)
+			assert isinstance(rel_fee,int), f'{rel_fee!r}: incorrect type for fee estimate (not an integer)'
 			return ETHAmt(rel_fee * self.tx_gas.toWei(),'wei')
 			return ETHAmt(rel_fee * self.tx_gas.toWei(),'wei')
 
 
 		def is_replaceable(self):
 		def is_replaceable(self):
@@ -114,7 +114,7 @@ class EthereumMMGenTX:
 			assert len(self.inputs) == 1,'Transaction has more than one input!'
 			assert len(self.inputs) == 1,'Transaction has more than one input!'
 			o_num = len(self.outputs)
 			o_num = len(self.outputs)
 			o_ok = 0 if self.usr_contract_data else 1
 			o_ok = 0 if self.usr_contract_data else 1
-			assert o_num == o_ok,'Transaction has {} output{} (should have {})'.format(o_num,suf(o_num),o_ok)
+			assert o_num == o_ok, f'Transaction has {o_num} output{suf(o_num)} (should have {o_ok})'
 			await self.make_txobj()
 			await self.make_txobj()
 			odict = { k: str(v) for k,v in self.txobj.items() if k != 'token_to' }
 			odict = { k: str(v) for k,v in self.txobj.items() if k != 'token_to' }
 			self.hex = json.dumps(odict)
 			self.hex = json.dumps(odict)
@@ -147,7 +147,7 @@ class EthereumMMGenTX:
 					elif int(reply) < 1:
 					elif int(reply) < 1:
 						msg('Account number must be >= 1')
 						msg('Account number must be >= 1')
 					elif int(reply) > len(unspent):
 					elif int(reply) > len(unspent):
-						msg('Account number must be <= {}'.format(len(unspent)))
+						msg(f'Account number must be <= {len(unspent)}')
 					else:
 					else:
 						return [int(reply)]
 						return [int(reply)]
 
 
@@ -175,7 +175,7 @@ class EthereumMMGenTX:
 		def fee_est2abs(self,rel_fee,fe_type=None):
 		def fee_est2abs(self,rel_fee,fe_type=None):
 			ret = self.fee_gasPrice2abs(rel_fee) * opt.tx_fee_adj
 			ret = self.fee_gasPrice2abs(rel_fee) * opt.tx_fee_adj
 			if opt.verbose:
 			if opt.verbose:
-				msg('Estimated fee: {} ETH'.format(ret))
+				msg(f'Estimated fee: {ret} ETH')
 			return ret
 			return ret
 
 
 		def convert_and_check_fee(self,tx_fee,desc='Missing description'):
 		def convert_and_check_fee(self,tx_fee,desc='Missing description'):
@@ -183,8 +183,11 @@ class EthereumMMGenTX:
 			if abs_fee == False:
 			if abs_fee == False:
 				return False
 				return False
 			elif not self.disable_fee_check and (abs_fee > self.proto.max_tx_fee):
 			elif not self.disable_fee_check and (abs_fee > self.proto.max_tx_fee):
-				m = '{} {c}: {} fee too large (maximum fee: {} {c})'
-				msg(m.format(abs_fee.hl(),desc,self.proto.max_tx_fee.hl(),c=self.proto.coin))
+				msg('{} {c}: {} fee too large (maximum fee: {} {c})'.format(
+					abs_fee.hl(),
+					desc,
+					self.proto.max_tx_fee.hl(),
+					c = self.proto.coin ))
 				return False
 				return False
 			else:
 			else:
 				return abs_fee
 				return abs_fee
@@ -211,12 +214,12 @@ class EthereumMMGenTX:
 							raise UserAddressNotInWallet(m.format(i))
 							raise UserAddressNotInWallet(m.format(i))
 						ret.append(i)
 						ret.append(i)
 					else:
 					else:
-						die(1,"'{}': not an MMGen ID or coin address".format(i))
+						die(1,f'{i!r}: not an MMGen ID or coin address')
 			return ret
 			return ret
 
 
 		def final_inputs_ok_msg(self,funds_left):
 		def final_inputs_ok_msg(self,funds_left):
 			chg = '0' if (self.outputs and self.outputs[0].is_chg) else funds_left
 			chg = '0' if (self.outputs and self.outputs[0].is_chg) else funds_left
-			return "Transaction leaves {} {} in the sender's account".format(
+			return 'Transaction leaves {} {} in the sender’s account'.format(
 				ETHAmt(chg).hl(),
 				ETHAmt(chg).hl(),
 				self.proto.coin
 				self.proto.coin
 			)
 			)
@@ -426,10 +429,10 @@ class EthereumMMGenTX:
 					if self.txobj['data']:
 					if self.txobj['data']:
 						cd = capfirst(self.contract_desc)
 						cd = capfirst(self.contract_desc)
 						if r.exec_status == 0:
 						if r.exec_status == 0:
-							msg('{} failed to execute!'.format(cd))
+							msg(f'{cd} failed to execute!')
 						else:
 						else:
-							msg('{} successfully executed with status {}'.format(cd,r.exec_status))
-					die(0,'Transaction has {} confirmation{}'.format(r.confs,suf(r.confs)))
+							msg(f'{cd} successfully executed with status {r.exec_status}')
+					die(0,f'Transaction has {r.confs} confirmation{suf(r.confs)}')
 				die(1,'Transaction is neither in mempool nor blockchain!')
 				die(1,'Transaction is neither in mempool nor blockchain!')
 
 
 		async def send(self,prompt_user=True,exit_on_fail=False):
 		async def send(self,prompt_user=True,exit_on_fail=False):
@@ -458,7 +461,7 @@ class EthereumMMGenTX:
 					ret = False
 					ret = False
 
 
 			if ret == False:
 			if ret == False:
-				msg(red('Send of MMGen transaction {} failed'.format(self.txid)))
+				msg(red(f'Send of MMGen transaction {self.txid} failed'))
 				if exit_on_fail:
 				if exit_on_fail:
 					sys.exit(1)
 					sys.exit(1)
 				return False
 				return False
@@ -553,9 +556,9 @@ class EthereumTokenMMGenTX:
 
 
 		def format_view_body(self,*args,**kwargs):
 		def format_view_body(self,*args,**kwargs):
 			return 'Token:     {d} {c}\n{r}'.format(
 			return 'Token:     {d} {c}\n{r}'.format(
-				d=self.txobj['token_addr'].hl(),
-				c=blue('(' + self.proto.dcoin + ')'),
-				r=super().format_view_body(*args,**kwargs))
+				d = self.txobj['token_addr'].hl(),
+				c = blue('(' + self.proto.dcoin + ')'),
+				r = super().format_view_body(*args,**kwargs ))
 
 
 	class Unsigned(Completed,EthereumMMGenTX.Unsigned):
 	class Unsigned(Completed,EthereumMMGenTX.Unsigned):
 		desc = 'unsigned transaction'
 		desc = 'unsigned transaction'

+ 19 - 18
mmgen/baseconv.py

@@ -85,7 +85,7 @@ class baseconv(object):
 			from .mn_tirosh import words
 			from .mn_tirosh import words
 			cls.digits[mn_id] = words[:cls.mn_base]
 			cls.digits[mn_id] = words[:cls.mn_base]
 		else:
 		else:
-			raise ValueError('{}: unrecognized mnemonic ID'.format(mn_id))
+			raise ValueError(f'{mn_id}: unrecognized mnemonic ID')
 
 
 	@classmethod
 	@classmethod
 	def get_wordlist(cls,wl_id):
 	def get_wordlist(cls,wl_id):
@@ -101,7 +101,7 @@ class baseconv(object):
 	def check_wordlists(cls):
 	def check_wordlists(cls):
 		for k,v in list(cls.wl_chksums.items()):
 		for k,v in list(cls.wl_chksums.items()):
 			res = cls.get_wordlist_chksum(k)
 			res = cls.get_wordlist_chksum(k)
-			assert res == v,'{}: checksum mismatch for {} (should be {})'.format(res,k,v)
+			assert res == v,f'{res}: checksum mismatch for {k} (should be {v})'
 		return True
 		return True
 
 
 	@classmethod
 	@classmethod
@@ -110,7 +110,7 @@ class baseconv(object):
 
 
 		wl = cls.digits[wl_id]
 		wl = cls.digits[wl_id]
 		from .util import qmsg,compare_chksums
 		from .util import qmsg,compare_chksums
-		ret = 'Wordlist: {}\nLength: {} words'.format(wl_id,len(wl))
+		ret = f'Wordlist: {wl_id}\nLength: {len(wl)} words'
 		new_chksum = cls.get_wordlist_chksum(wl_id)
 		new_chksum = cls.get_wordlist_chksum(wl_id)
 
 
 		a,b = 'generated','saved'
 		a,b = 'generated','saved'
@@ -136,8 +136,7 @@ class baseconv(object):
 		elif pad == 'seed':
 		elif pad == 'seed':
 			return seed_pad_func()
 			return seed_pad_func()
 		else:
 		else:
-			m = "{!r}: illegal value for 'pad' (must be None,'seed' or int)"
-			raise BaseConversionPadError(m.format(pad))
+			raise BaseConversionPadError(f"{pad!r}: illegal value for 'pad' (must be None,'seed' or int)")
 
 
 	@staticmethod
 	@staticmethod
 	def monero_mn_checksum(words):
 	def monero_mn_checksum(words):
@@ -161,14 +160,14 @@ class baseconv(object):
 		desc = cls.desc[wl_id][0]
 		desc = cls.desc[wl_id][0]
 
 
 		if len(words) == 0:
 		if len(words) == 0:
-			raise BaseConversionError('empty {} data'.format(desc))
+			raise BaseConversionError(f'empty {desc} data')
 
 
 		def get_seed_pad():
 		def get_seed_pad():
-			assert wl_id in cls.seedlen_map_rev,'seed padding not supported for base {!r}'.format(wl_id)
+			assert wl_id in cls.seedlen_map_rev,f'seed padding not supported for base {wl_id!r}'
 			d = cls.seedlen_map_rev[wl_id]
 			d = cls.seedlen_map_rev[wl_id]
 			if not len(words) in d:
 			if not len(words) in d:
-				m = '{}: invalid length for seed-padded {} data in base conversion'
-				raise BaseConversionError(m.format(len(words),desc))
+				raise 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(cls.get_pad(pad,get_seed_pad),1)
 		pad_val = max(cls.get_pad(pad,get_seed_pad),1)
@@ -176,12 +175,13 @@ class baseconv(object):
 		base = len(wl)
 		base = len(wl)
 
 
 		if not set(words) <= set(wl):
 		if not set(words) <= set(wl):
-			m = ('{w!r}:','seed data')[pad=='seed'] + ' not in {d} format'
-			raise BaseConversionError(m.format(w=words_arg,d=desc))
+			raise BaseConversionError(
+				( 'seed data' if pad == 'seed' else f'{words_arg!r}:' ) +
+				f' not in {desc} format' )
 
 
 		if wl_id == 'xmrseed':
 		if wl_id == 'xmrseed':
 			if len(words) not in cls.seedlen_map_rev['xmrseed']:
 			if len(words) not in cls.seedlen_map_rev['xmrseed']:
-				die(2,'{}: invalid length for Monero mnemonic'.format(len(words)))
+				die(2,f'{len(words)}: invalid length for Monero mnemonic')
 
 
 			z = cls.monero_mn_checksum(words[:-1])
 			z = cls.monero_mn_checksum(words[:-1])
 			assert z == words[-1],'invalid Monero mnemonic checksum'
 			assert z == words[-1],'invalid Monero mnemonic checksum'
@@ -204,8 +204,9 @@ class baseconv(object):
 
 
 		from .util import is_hex_str
 		from .util import is_hex_str
 		if not is_hex_str(hexstr):
 		if not is_hex_str(hexstr):
-			m = ('{h!r}:','seed data')[pad=='seed'] + ' not a hexadecimal string'
-			raise HexadecimalStringError(m.format(h=hexstr))
+			raise HexadecimalStringError(
+				( 'seed data' if pad == 'seed' else f'{hexstr!r}:' ) +
+				' not a hexadecimal string' )
 
 
 		return cls.frombytes(bytes.fromhex(hexstr),wl_id,pad,tostr)
 		return cls.frombytes(bytes.fromhex(hexstr),wl_id,pad,tostr)
 
 
@@ -220,11 +221,11 @@ class baseconv(object):
 			raise BaseConversionError('empty data not allowed in base conversion')
 			raise BaseConversionError('empty data not allowed in base conversion')
 
 
 		def get_seed_pad():
 		def get_seed_pad():
-			assert wl_id in cls.seedlen_map,'seed padding not supported for base {!r}'.format(wl_id)
+			assert wl_id in cls.seedlen_map, f'seed padding not supported for base {wl_id!r}'
 			d = cls.seedlen_map[wl_id]
 			d = cls.seedlen_map[wl_id]
 			if not len(bytestr) in d:
 			if not len(bytestr) in d:
-				m = '{}: invalid byte length for seed data in seed-padded base conversion'
-				raise SeedLengthError(m.format(len(bytestr)))
+				raise 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(cls.get_pad(pad,get_seed_pad),1)
 		pad = max(cls.get_pad(pad,get_seed_pad),1)
@@ -233,7 +234,7 @@ class baseconv(object):
 
 
 		if wl_id == 'xmrseed':
 		if wl_id == 'xmrseed':
 			if len(bytestr) not in cls.seedlen_map['xmrseed']:
 			if len(bytestr) not in cls.seedlen_map['xmrseed']:
-				die(2,'{}: invalid seed byte length for Monero mnemonic'.format(len(bytestr)))
+				die(2, f'{len(bytestr)}: invalid seed byte length for Monero mnemonic')
 
 
 			def num2base_monero(num):
 			def num2base_monero(num):
 				w1 = num % base
 				w1 = num % base

+ 7 - 7
mmgen/bip39.py

@@ -2099,7 +2099,7 @@ zoo
 		for k,v in cls.constants.items():
 		for k,v in cls.constants.items():
 			if v[1] == nwords:
 			if v[1] == nwords:
 				return int(k)//8 if in_bytes else int(k)//4 if in_hex else int(k)
 				return int(k)//8 if in_bytes else int(k)//4 if in_hex else int(k)
-		raise MnemonicError('{!r}: invalid word length for BIP39 mnemonic'.format(nwords))
+		raise 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):
@@ -2107,7 +2107,7 @@ zoo
 		try:
 		try:
 			return cls.constants[str(seed_bits)][1]
 			return cls.constants[str(seed_bits)][1]
 		except:
 		except:
-			raise ValueError('{!r}: invalid seed length for BIP39 mnemonic'.format(seed_bits))
+			raise ValueError(f'{seed_bits!r}: invalid seed length for BIP39 mnemonic')
 
 
 	@classmethod
 	@classmethod
 	def tohex(cls,words,wl_id,pad=None):
 	def tohex(cls,words,wl_id,pad=None):
@@ -2118,7 +2118,7 @@ zoo
 
 
 		for n,w in enumerate(words):
 		for n,w in enumerate(words):
 			if w not in wl:
 			if w not in wl:
-				raise MnemonicError('word #{} is not in the BIP39 word list'.format(n+1))
+				raise MnemonicError(f'word #{n+1} is not in the BIP39 word list')
 
 
 		res = ''.join(['{:011b}'.format(wl.index(w)) for w in words])
 		res = ''.join(['{:011b}'.format(wl.index(w)) for w in words])
 
 
@@ -2127,10 +2127,10 @@ zoo
 				bitlen = int(k)
 				bitlen = int(k)
 				break
 				break
 		else:
 		else:
-			raise MnemonicError('{}: invalid BIP39 seed phrase length'.format(len(words)))
+			raise MnemonicError(f'{len(words)}: invalid BIP39 seed phrase length')
 
 
 		if pad != None:
 		if pad != None:
-			assert pad * 4 == bitlen, '{}: invalid pad length'.format(pad)
+			assert pad * 4 == bitlen, f'{pad}: invalid pad length'
 
 
 		seed_bin = res[:bitlen]
 		seed_bin = res[:bitlen]
 		chk_bin = res[bitlen:]
 		chk_bin = res[bitlen:]
@@ -2157,11 +2157,11 @@ zoo
 		seed_bytes = bytes.fromhex(seed_hex)
 		seed_bytes = bytes.fromhex(seed_hex)
 		bitlen = len(seed_bytes) * 8
 		bitlen = len(seed_bytes) * 8
 
 
-		assert str(bitlen) in cls.constants,'{}: invalid seed bit length'.format(bitlen)
+		assert str(bitlen) in cls.constants, f'{bitlen}: invalid seed bit length'
 		chk_len,mn_len = cls.constants[str(bitlen)]
 		chk_len,mn_len = cls.constants[str(bitlen)]
 
 
 		if pad != None:
 		if pad != None:
-			assert mn_len == pad, '{}: invalid pad length'.format(pad)
+			assert mn_len == pad, f'{pad}: invalid pad length'
 
 
 		chk_hex = sha256(seed_bytes).hexdigest()
 		chk_hex = sha256(seed_bytes).hexdigest()
 
 

+ 16 - 18
mmgen/cfg.py

@@ -56,7 +56,7 @@ class CfgFile(object):
 			self.data = ''
 			self.data = ''
 
 
 	def copy_data(self):
 	def copy_data(self):
-		assert self.write_ok, 'writing to file {!r} not allowed!'.format(self.fn)
+		assert self.write_ok, f'writing to file {self.fn!r} not allowed!'
 		src = cfg_file('sys')
 		src = cfg_file('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
@@ -64,7 +64,7 @@ class CfgFile(object):
 				open(self.fn,'w').write('\n'.join(data)+'\n')
 				open(self.fn,'w').write('\n'.join(data)+'\n')
 				os.chmod(self.fn,0o600)
 				os.chmod(self.fn,0o600)
 			except:
 			except:
-				die(2,'ERROR: unable to write to {!r}'.format(self.fn))
+				die(2,f'ERROR: unable to write to {self.fn!r}')
 
 
 	def parse_value(self,value,refval):
 	def parse_value(self,value,refval):
 		if isinstance(refval,dict):
 		if isinstance(refval,dict):
@@ -89,7 +89,7 @@ class CfgFile(object):
 				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:
-					raise CfgFileParseError('Parse error in file {!r}, line {}'.format(self.fn,lineno))
+					raise CfgFileParseError(f'Parse error in file {self.fn!r}, line {lineno}')
 		return gen_lines()
 		return gen_lines()
 
 
 	@classmethod
 	@classmethod
@@ -105,7 +105,7 @@ class CfgFileSample(CfgFile):
 
 
 	@classmethod
 	@classmethod
 	def cls_make_metadata(cls,data):
 	def cls_make_metadata(cls,data):
-		return ['# Version {} {}'.format(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):
@@ -131,7 +131,7 @@ class CfgFileSample(CfgFile):
 			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:
-				raise CfgFileParseError('Parse error in file {!r}, line {}'.format(self.fn,lineno))
+				raise CfgFileParseError(f'Parse error in file {self.fn!r}, line {lineno}')
 
 
 		def gen_chunks(lines):
 		def gen_chunks(lines):
 			hdr = True
 			hdr = True
@@ -159,7 +159,7 @@ class CfgFileSample(CfgFile):
 						chunk.append(line)
 						chunk.append(line)
 					last_nonblank = lineno
 					last_nonblank = lineno
 				else:
 				else:
-					raise CfgFileParseError('Parse error in file {!r}, line {}'.format(self.fn,lineno))
+					raise 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)
@@ -201,7 +201,7 @@ class CfgFileSampleSys(CfgFileSample):
 			self.data = files('mmgen').joinpath('data',self.fn_base).read_text().splitlines()
 			self.data = files('mmgen').joinpath('data',self.fn_base).read_text().splitlines()
 
 
 	def make_metadata(self):
 	def make_metadata(self):
-		return ['# Version {} {}'.format(self.cur_ver,self.computed_chksum)]
+		return [f'# Version {self.cur_ver} {self.computed_chksum}']
 
 
 class CfgFileSampleUsr(CfgFileSample):
 class CfgFileSampleUsr(CfgFileSample):
 	desc = 'sample configuration file'
 	desc = 'sample configuration file'
@@ -260,25 +260,23 @@ class CfgFileSampleUsr(CfgFileSample):
 
 
 	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')
-		m1 = '  The following option{} been {}:\n    {}\n'
-		m2 = """
-			The following removed option{} set in {!r}
-			and must be deleted or commented out:
-			{}
-		"""
 		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')
 				opts = fmt_list([i.name for i in data],fmt='bare')
-				msg(m1.format(suf(data,verb='has'),desc,opts))
+				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 = cfg_file('usr')
 					uc = cfg_file('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:
-						ymsg(fmt(m2,'  ').format(suf(bad,verb='is'),uc.fn,'  '+fmt_list(bad,fmt='bare')))
-
+						m = f"""
+							The following removed option{suf(bad,verb='is')} set in {uc.fn!r}
+							and must be deleted or commented out:
+							{'  ' + fmt_list(bad,fmt='bare')}
+						"""
+						ymsg(fmt(m,indent='  ',strip_char='\t'))
 		while True:
 		while True:
 			if not keypress_confirm(self.details_confirm_prompt,no_nl=True):
 			if not keypress_confirm(self.details_confirm_prompt,no_nl=True):
 				return
 				return
@@ -288,9 +286,9 @@ class CfgFileSampleUsr(CfgFileSample):
 					sep,sep2 = ('\n  ','\n\n  ')
 					sep,sep2 = ('\n  ','\n\n  ')
 					if data:
 					if data:
 						yield (
 						yield (
-							'{} section{}:'.format(capfirst(desc),suf(data))
+							f'{capfirst(desc)} section{suf(data)}:'
 							+ sep2
 							+ sep2
-							+ sep2.join(['{}'.format(sep.join(v.chunk)) for v in data])
+							+ sep2.join([f'{sep.join(v.chunk)}' for v in data])
 						)
 						)
 
 
 			do_pager(
 			do_pager(

+ 20 - 23
mmgen/crypto.py

@@ -34,8 +34,7 @@ def scramble_seed(seed,scramble_key):
 	import hmac
 	import hmac
 	step1 = hmac.new(seed,scramble_key,sha256).digest()
 	step1 = hmac.new(seed,scramble_key,sha256).digest()
 	if g.debug:
 	if g.debug:
-		fs = 'Seed:  {!r}\nScramble key: {}\nScrambled seed: {}\n'
-		msg(fs.format(seed.hex(),scramble_key,step1.hex()))
+		msg(f'Seed:  {seed.hex()!r}\nScramble key: {scramble_key}\nScrambled seed: {step1.hex()}\n')
 	return sha256_rounds(step1,g.scramble_hash_rounds)
 	return sha256_rounds(step1,g.scramble_hash_rounds)
 
 
 def encrypt_seed(seed,key):
 def encrypt_seed(seed,key):
@@ -64,30 +63,30 @@ def decrypt_seed(enc_seed,key,seed_id,key_id):
 			vmsg('')
 			vmsg('')
 			return False
 			return False
 #	else:
 #	else:
-#		qmsg('Generated IDs (Seed/Key): {}/{}'.format(chk2,chk1))
+#		qmsg(f'Generated IDs (Seed/Key): {chk2}/{chk1}')
 
 
-	dmsg('Decrypted seed: {}'.format(dec_seed.hex()))
+	dmsg(f'Decrypted seed: {dec_seed.hex()}')
 	return dec_seed
 	return dec_seed
 
 
 def encrypt_data(data,key,iv=g.aesctr_dfl_iv,desc='data',verify=True):
 def encrypt_data(data,key,iv=g.aesctr_dfl_iv,desc='data',verify=True):
-	vmsg('Encrypting {}'.format(desc))
+	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:
-		vmsg_r('Performing a test decryption of the {}...'.format(desc))
+		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,"ERROR.\nDecrypted {s} doesn't match original {s}".format(s=desc))
+			die(2,f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
 		vmsg('done')
 		vmsg('done')
 
 
 	return enc_data
 	return enc_data
 
 
 def decrypt_data(enc_data,key,iv=g.aesctr_dfl_iv,desc='data'):
 def decrypt_data(enc_data,key,iv=g.aesctr_dfl_iv,desc='data'):
-	vmsg_r('Decrypting {} with key...'.format(desc))
+	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()
@@ -136,12 +135,11 @@ def scrypt_hash_passphrase(passwd,salt,hash_preset,buflen=32):
 	return ret
 	return ret
 
 
 def make_key(passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False):
 def make_key(passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False):
-	if from_what: desc += ' from '
 	if opt.verbose or verbose:
 	if opt.verbose or verbose:
-		msg_r('Generating {}{}...'.format(desc,from_what))
+		msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...")
 	key = scrypt_hash_passphrase(passwd,salt,hash_preset)
 	key = scrypt_hash_passphrase(passwd,salt,hash_preset)
 	if opt.verbose or verbose: msg('done')
 	if opt.verbose or verbose: msg('done')
-	dmsg('Key: {}'.format(key.hex()))
+	dmsg(f'Key: {key.hex()}')
 	return key
 	return key
 
 
 def _get_random_data_from_user(uchars,desc):
 def _get_random_data_from_user(uchars,desc):
@@ -166,9 +164,8 @@ def _get_random_data_from_user(uchars,desc):
 	"""
 	"""
 
 
 	msg(f'Enter {uchars} random symbols' if opt.quiet else
 	msg(f'Enter {uchars} random symbols' if opt.quiet else
-		'\n{}\n{}'.format( fmt(info1,indent='  '), fmt(info2) ))
-
-	prompt = 'You may begin typing.  {} symbols left: '
+		'\n' + fmt(info1,indent='  ') +
+		'\n' + fmt(info2) )
 
 
 	import time
 	import time
 	from .term import get_char_raw
 	from .term import get_char_raw
@@ -176,12 +173,12 @@ def _get_random_data_from_user(uchars,desc):
 	time_data = []
 	time_data = []
 
 
 	for i in range(uchars):
 	for i in range(uchars):
-		key_data += get_char_raw('\r'+prompt.format(uchars-i))
+		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 opt.quiet else f"\rThank you.  That's enough.{' '*18}\n\n" )
+	msg_r( '\r' if opt.quiet else f'\rThank you.  That’s enough.{" "*18}\n\n' )
 
 
-	time_data = ['{:.22f}'.format(t).rstrip('0') for t in time_data]
+	time_data = [f'{t:.22f}'.rstrip('0') for t in time_data]
 
 
 	avg_prec = sum(len(t.split('.')[1]) for t in time_data) // len(time_data)
 	avg_prec = sum(len(t.split('.')[1]) for t in time_data) // len(time_data)
 
 
@@ -217,21 +214,21 @@ def add_user_random(rand_bytes,desc):
 		return rand_bytes
 		return rand_bytes
 
 
 def get_hash_preset_from_user(hp=g.dfl_hash_preset,desc='data'):
 def get_hash_preset_from_user(hp=g.dfl_hash_preset,desc='data'):
-	prompt = f'Enter hash preset for {desc},\nor hit ENTER to accept the default value ({hp!r}): '
 	while True:
 	while True:
-		ret = my_raw_input(prompt)
+		ret = my_raw_input(
+			f'Enter hash preset for {desc},\n' +
+			f'or hit ENTER to accept the default value ({hp!r}): ' )
 		if ret:
 		if ret:
 			if ret in g.hash_presets:
 			if ret in g.hash_presets:
 				return ret
 				return ret
 			else:
 			else:
-				msg('Invalid input.  Valid choices are {}'.format(', '.join(g.hash_presets)))
+				msg(f'Invalid input.  Valid choices are {", ".join(g.hash_presets)}')
 				continue
 				continue
 		else:
 		else:
 			return hp
 			return hp
 
 
 def get_new_passphrase(desc,passchg=False):
 def get_new_passphrase(desc,passchg=False):
-
-	pw_desc = '{}passphrase for {}'.format(('','new ')[bool(passchg)], desc)
+	pw_desc = f"{'new ' if passchg else ''}passphrase for {desc}"
 	if opt.passwd_file:
 	if opt.passwd_file:
 		pw = ' '.join(get_words_from_file(opt.passwd_file,pw_desc))
 		pw = ' '.join(get_words_from_file(opt.passwd_file,pw_desc))
 	elif opt.echo_passphrase:
 	elif opt.echo_passphrase:
@@ -253,7 +250,7 @@ def get_new_passphrase(desc,passchg=False):
 	return pw
 	return pw
 
 
 def get_passphrase(desc,passchg=False):
 def get_passphrase(desc,passchg=False):
-	pw_desc ='{}passphrase for {}'.format(('','old ')[bool(passchg)],desc)
+	pw_desc = f"{'old ' if passchg else ''}passphrase for {desc}"
 	if opt.passwd_file:
 	if opt.passwd_file:
 		pwfile_reuse_warning(opt.passwd_file)
 		pwfile_reuse_warning(opt.passwd_file)
 		return ' '.join(get_words_from_file(opt.passwd_file,pw_desc))
 		return ' '.join(get_words_from_file(opt.passwd_file,pw_desc))

+ 2 - 2
mmgen/daemon.py

@@ -88,7 +88,7 @@ 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('\nExecuting: {}'.format(' '.join(cmd)))
+			msg(f'\nExecuting: {" ".join(cmd)}')
 
 
 		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)
@@ -445,7 +445,7 @@ class CoinDaemon(Daemon):
 		os.makedirs(self.datadir,exist_ok=True)
 		os.makedirs(self.datadir,exist_ok=True)
 
 
 		if self.cfg_file and not self.flag.keep_cfg_file:
 		if self.cfg_file and not self.flag.keep_cfg_file:
-			open('{}/{}'.format(self.datadir,self.cfg_file),'w').write(self.cfg_file_hdr)
+			open(f'{self.datadir}/{self.cfg_file}','w').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):
 			# Parity overwrites the data in the existing pidfile without zeroing it first, leading
 			# Parity overwrites the data in the existing pidfile without zeroing it first, leading

+ 28 - 16
mmgen/devtools.py

@@ -64,24 +64,27 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
 					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))
 					if hasattr(el,'pfmt'):
 					if hasattr(el,'pfmt'):
-						out.append('{:>{l}}{}'.format('',el.pfmt(
-							lvl=lvl+1,id_list=id_list+[id(self)]),l=(lvl+1)*8))
+						out.append('{:>{l}}{}'.format(
+							'',
+							el.pfmt( lvl=lvl+1, id_list=id_list+[id(self)] ),
+							l = (lvl+1)*8 ))
 					elif isinstance(el,scalars):
 					elif isinstance(el,scalars):
 						if isList(e):
 						if isList(e):
-							out.append('{:>{l}}{:16}\n'.format('',repr(el),l=lvl*8))
+							out.append( '{:>{l}}{!r:16}\n'.format( '', el, l=lvl*8 ))
 						else:
 						else:
-							out.append(' {}'.format(repr(el)))
+							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('','<'+type(el).__name__+'>',l=indent))
+						out.append('{:>{l}}{:16}'.format( '', f'<{type(el).__name__}>', l=indent ))
 						if isList(el) and isinstance(el[0],scalars):
 						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} {}\n'.format(
-							'','<'+type(el).__name__+'>',repr(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: out.append('{}\n'.format(repr(e)))
+
+				if not e:
+					out.append(f'{e!r}\n')
 
 
 			def isDict(obj):
 			def isDict(obj):
 				return isinstance(obj,dict)
 				return isinstance(obj,dict)
@@ -90,7 +93,8 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
 			def isScalar(obj):
 			def isScalar(obj):
 				return isinstance(obj,scalars)
 				return isinstance(obj,scalars)
 
 
-			out = ['<{}>{}\n'.format(type(self).__name__,' '+repr(self) if isScalar(self) else '')]
+			out = [f'<{type(self).__name__}>{" "+repr(self) if isScalar(self) else ""}\n']
+
 			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):
@@ -99,14 +103,21 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
 			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,'<'+type(e).__name__+'>',l=(lvl*8)+4))
+					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))
 					do_list(out,e,lvl=lvl,is_dict=isDict(e))
 				elif hasattr(e,'pfmt') and type(e) != type:
 				elif hasattr(e,'pfmt') and type(e) != type:
 					out.append('{:>{l}}{:10} {}'.format(
 					out.append('{:>{l}}{:10} {}'.format(
-						'',k,e.pfmt(lvl=lvl+1,id_list=id_list+[id(self)]),l=(lvl*8)+4))
+						'',
+						k,
+						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,'<'+type(e).__name__+'>',repr(e),l=(lvl*8)+4))
+						'',
+						k,
+						f'<{type(e).__name__}>',
+						repr(e),
+						l=(lvl*8)+4 ))
 
 
 			import re
 			import re
 			return re.sub('\n+','\n',''.join(out))
 			return re.sub('\n+','\n',''.join(out))
@@ -123,11 +134,11 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
 							attr = o.__dict__[attrname]
 							attr = o.__dict__[attrname]
 							break
 							break
 					else:
 					else:
-						rdie(3,'unable to find descriptor {}.{}'.format(cls.__name__,attrname))
+						rdie(3,f'unable to find descriptor {cls.__name__}.{attrname}')
 					if type(attr).__name__ == 'ImmutableAttr':
 					if type(attr).__name__ == 'ImmutableAttr':
 						if attrname not in self.__dict__:
 						if attrname not in self.__dict__:
-							fs = 'attribute {!r} of {} has not been initialized in constructor!'
-							rdie(3,fs.format(attrname,cls.__name__))
+							rdie(3,
+						f'attribute {attrname!r} of {cls.__name__} has not been initialized in constructor!')
 
 
 	def print_diff(a,b,from_file='',to_file='',from_json=True):
 	def print_diff(a,b,from_file='',to_file='',from_json=True):
 		if from_json:
 		if from_json:
@@ -136,7 +147,8 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
 		else:
 		else:
 			a = a.split('\n')
 			a = a.split('\n')
 			b = b.split('\n')
 			b = b.split('\n')
-		sys.stderr.write('  DIFF:\n    {}\n'.format('\n    '.join(unified_diff(a,b,from_file,to_file))))
+		sys.stderr.write('  DIFF:\n    {}\n'.format(
+			'\n    '.join(unified_diff(a,b,from_file,to_file)) ))
 
 
 	def get_ndiff(a,b):
 	def get_ndiff(a,b):
 		a = a.split('\n')
 		a = a.split('\n')

+ 13 - 12
mmgen/filename.py

@@ -47,20 +47,19 @@ class Filename(MMGenObject):
 					self.ftype = ftype
 					self.ftype = ftype
 				# elif: # other MMGen file types
 				# elif: # other MMGen file types
 				else:
 				else:
-					die(3,"'{}': not a recognized file type for Wallet".format(ftype))
+					die(3,f'{ftype!r}: not a recognized file type for Wallet')
 			else:
 			else:
-				die(3,"'{}': not a class".format(ftype))
+				die(3,f'{ftype!r}: not a class')
 		else:
 		else:
 			# TODO: other file types
 			# TODO: other file types
 			self.ftype = Wallet.ext_to_type(self.ext)
 			self.ftype = Wallet.ext_to_type(self.ext)
 			if not self.ftype:
 			if not self.ftype:
-				m = "'{}': not a recognized Wallet file extension".format(self.ext)
-				raise BadFileExtension(m)
+				raise BadFileExtension(f'{self.ext!r}: not a recognized Wallet file extension')
 
 
 		try:
 		try:
 			st = os.stat(fn)
 			st = os.stat(fn)
 		except:
 		except:
-			raise FileNotFound('{!r}: file not found'.format(fn))
+			raise 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):
@@ -70,7 +69,7 @@ class Filename(MMGenObject):
 				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,"'{}': permission denied".format(fn))
+					die(2,f'{fn!r}: permission denied')
 #				if e.errno != 17: raise
 #				if e.errno != 17: raise
 			else:
 			else:
 				self.size = os.lseek(fd, 0, os.SEEK_END)
 				self.size = os.lseek(fd, 0, os.SEEK_END)
@@ -92,25 +91,27 @@ class MMGenFileList(list,MMGenObject):
 
 
 	def sort_by_age(self,key='mtime',reverse=False):
 	def sort_by_age(self,key='mtime',reverse=False):
 		if key not in ('atime','ctime','mtime'):
 		if key not in ('atime','ctime','mtime'):
-			die(1,"'{}': illegal sort key".format(key))
+			die(1,f'{key!r}: illegal sort key')
 		self.sort(key=lambda a: getattr(a,key),reverse=reverse)
 		self.sort(key=lambda a: getattr(a,key),reverse=reverse)
 
 
 def find_files_in_dir(ftype,fdir,no_dups=False):
 def find_files_in_dir(ftype,fdir,no_dups=False):
 	if not isinstance(ftype,type):
 	if not isinstance(ftype,type):
-		die(3,"'{}': is of type {} (not a subclass of type 'type')".format(ftype,type(ftype)))
+		die(3,f"{ftype!r}: is of type {type(ftype)} (not a subclass of type 'type')")
 
 
 	from .wallet import Wallet
 	from .wallet import Wallet
 	if not issubclass(ftype,Wallet):
 	if not issubclass(ftype,Wallet):
-		die(3,"'{}': not a recognized file type".format(ftype))
+		die(3,f'{ftype!r}: not a recognized file type')
 
 
-	try: dirlist = os.listdir(fdir)
-	except: die(3,"ERROR: unable to read directory '{}'".format(fdir))
+	try:
+		dirlist = os.listdir(fdir)
+	except:
+		die(3,f'ERROR: unable to read directory {fdir!r}')
 
 
 	matches = [l for l in dirlist if l[-len(ftype.ext)-1:]=='.'+ftype.ext]
 	matches = [l for l in dirlist if l[-len(ftype.ext)-1:]=='.'+ftype.ext]
 
 
 	if no_dups:
 	if no_dups:
 		if len(matches) > 1:
 		if len(matches) > 1:
-			die(1,"ERROR: more than one {} file in directory '{}'".format(ftype.__name__,fdir))
+			die(1,f'ERROR: more than one {ftype.__name__} file in directory {fdir!r}')
 		return os.path.join(fdir,matches[0]) if len(matches) else None
 		return os.path.join(fdir,matches[0]) if len(matches) else None
 	else:
 	else:
 		return [os.path.join(fdir,m) for m in matches]
 		return [os.path.join(fdir,m) for m in matches]

+ 3 - 3
mmgen/license.py

@@ -22,11 +22,11 @@ license.py:  Copyright notice and text of GPLv3
 
 
 from .globalvars import g
 from .globalvars import g
 
 
-warning = """
-  {pnm} Copyright (C) {g.Cdates} by {g.author} {g.email}.  This
+warning = f"""
+  {g.proj_name} Copyright (C) {g.Cdates} by {g.author} {g.email}.  This
   program comes with ABSOLUTELY NO WARRANTY.  This is free software, and
   program comes with ABSOLUTELY NO WARRANTY.  This is free software, and
   you are welcome to redistribute it under certain conditions.
   you are welcome to redistribute it under certain conditions.
-""".format(g=g,pnm=g.proj_name)
+"""
 
 
 conditions = """
 conditions = """
                        TERMS AND CONDITIONS
                        TERMS AND CONDITIONS

+ 1 - 1
mmgen/main_addrgen.py

@@ -127,7 +127,7 @@ FMT CODES:
 
 
 cmd_args = opts.init(opts_data,add_opts=['b16'],opt_filter=opt_filter)
 cmd_args = opts.init(opts_data,add_opts=['b16'],opt_filter=opt_filter)
 
 
-errmsg = "'{}': invalid parameter for --type option".format(opt.type)
+errmsg = f'{opt.type!r}: invalid parameter for --type option'
 
 
 from .protocol import init_proto_from_opts
 from .protocol import init_proto_from_opts
 proto = init_proto_from_opts()
 proto = init_proto_from_opts()

+ 14 - 12
mmgen/main_addrimport.py

@@ -38,10 +38,10 @@ WARNING: If any of the addresses you're importing is already in the blockchain,
 has a balance and is not in your tracking wallet, you must exit the program now
 has a balance and is not in your tracking wallet, you must exit the program now
 and rerun it using the '--rescan' option.
 and rerun it using the '--rescan' option.
 """.strip(),
 """.strip(),
-	'bad_args': """
-You must specify an {pnm} address file, a single address with the '--address'
-option, or a list of non-{pnm} addresses with the '--addrlist' option
-""".strip().format(pnm=g.proj_name)
+	'bad_args': f"""
+You must specify an {g.proj_name} address file, a single address with the '--address'
+option, or a list of non-{g.proj_name} addresses with the '--addrlist' option
+""".strip()
 }[k]
 }[k]
 
 
 # In batch mode, daemon just rescans each address separately anyway, so make
 # In batch mode, daemon just rescans each address separately anyway, so make
@@ -49,7 +49,7 @@ option, or a list of non-{pnm} addresses with the '--addrlist' option
 
 
 opts_data = {
 opts_data = {
 	'text': {
 	'text': {
-		'desc': """Import addresses into an {} tracking wallet""".format(g.proj_name),
+		'desc': f'Import addresses into an {g.proj_name} tracking wallet',
 		'usage':'[opts] [mmgen address file]',
 		'usage':'[opts] [mmgen address file]',
 		'options': """
 		'options': """
 -h, --help         Print this help message
 -h, --help         Print this help message
@@ -87,7 +87,7 @@ def parse_cmd_args(rpc,cmd_args):
 		if opt.addrlist:
 		if opt.addrlist:
 			al = AddrList(
 			al = AddrList(
 				proto = proto,
 				proto = proto,
-				addrlist = get_lines_from_file(infile,'non-{pnm} addresses'.format(pnm=g.proj_name),
+				addrlist = get_lines_from_file(infile,f'non-{g.proj_name} addresses',
 				trim_comments = True) )
 				trim_comments = True) )
 		else:
 		else:
 			al = import_mmgen_list(infile)
 			al = import_mmgen_list(infile)
@@ -104,14 +104,14 @@ def check_opts(tw):
 	rescan = bool(opt.rescan)
 	rescan = bool(opt.rescan)
 
 
 	if rescan and not 'rescan' in tw.caps:
 	if rescan and not 'rescan' in tw.caps:
-		msg("'--rescan' ignored: not supported by {}".format(type(tw).__name__))
+		msg(f"'--rescan' ignored: not supported by {type(tw).__name__}")
 		rescan = False
 		rescan = False
 
 
 	if rescan and not opt.quiet:
 	if rescan and not opt.quiet:
 		confirm_or_raise(ai_msgs('rescan'),'continue',expect='YES')
 		confirm_or_raise(ai_msgs('rescan'),'continue',expect='YES')
 
 
 	if batch and not 'batch' in tw.caps:
 	if batch and not 'batch' in tw.caps:
-		msg("'--batch' ignored: not supported by {}".format(type(tw).__name__))
+		msg(f"'--batch' ignored: not supported by {type(tw).__name__}")
 		batch = False
 		batch = False
 
 
 	return batch,rescan
 	return batch,rescan
@@ -124,7 +124,9 @@ async def import_addr(tw,addr,label,rescan,msg_fmt,msg_args):
 			while True:
 			while True:
 				if task.done():
 				if task.done():
 					break
 					break
-				msg_r(('\r{} '+msg_fmt).format(secs_to_hms(int(time.time()-start)),*msg_args))
+				msg_r(('\r{} '+msg_fmt).format(
+					secs_to_hms(int(time.time()-start)),
+					*msg_args ))
 				await asyncio.sleep(0.5)
 				await asyncio.sleep(0.5)
 			await task
 			await task
 			msg('\nOK')
 			msg('\nOK')
@@ -132,7 +134,7 @@ async def import_addr(tw,addr,label,rescan,msg_fmt,msg_args):
 			await task
 			await task
 			qmsg(msg_fmt.format(*msg_args) + ' - OK')
 			qmsg(msg_fmt.format(*msg_args) + ' - OK')
 	except Exception as e:
 	except Exception as e:
-		die(2,'\nImport of address {!r} failed: {!r}'.format(addr,e.args[0]))
+		die(2,f'\nImport of address {addr!r} failed: {e.args[0]!r}')
 
 
 def make_args_list(tw,al,batch,rescan):
 def make_args_list(tw,al,batch,rescan):
 
 
@@ -142,10 +144,10 @@ def make_args_list(tw,al,batch,rescan):
 
 
 	for num,e in enumerate(al.data,1):
 	for num,e in enumerate(al.data,1):
 		if e.idx:
 		if e.idx:
-			label = '{}:{}'.format(al.al_id,e.idx) + (' ' + e.label if e.label else '')
+			label = f'{al.al_id}:{e.idx}' + (' ' + e.label if e.label else '')
 			add_msg = label
 			add_msg = label
 		else:
 		else:
-			label = '{}:{}'.format(proto.base_coin.lower(),e.addr)
+			label = f'{proto.base_coin.lower()}:{e.addr}'
 			add_msg = 'non-'+g.proj_name
 			add_msg = 'non-'+g.proj_name
 
 
 		if batch:
 		if batch:

+ 10 - 14
mmgen/main_autosign.py

@@ -44,15 +44,15 @@ opts_data = {
 	'text': {
 	'text': {
 		'desc': 'Auto-sign MMGen transactions',
 		'desc': 'Auto-sign MMGen transactions',
 		'usage':'[opts] [command]',
 		'usage':'[opts] [command]',
-		'options': """
+		'options': f"""
 -h, --help            Print this help message
 -h, --help            Print this help message
 --, --longhelp        Print help message for long options (common options)
 --, --longhelp        Print help message for long options (common options)
 -c, --coins=c         Coins to sign for (comma-separated list)
 -c, --coins=c         Coins to sign for (comma-separated list)
 -I, --no-insert-check Don’t check for device insertion
 -I, --no-insert-check Don’t check for device insertion
 -l, --led             Use status LED to signal standby, busy and error
 -l, --led             Use status LED to signal standby, busy and error
--m, --mountpoint=M    Specify an alternate mountpoint 'M' (default: '{mp}')
+-m, --mountpoint=M    Specify an alternate mountpoint 'M' (default: '{mountpoint}')
 -M, --mnemonic-fmt=F  During setup, prompt for mnemonic seed phrase of format
 -M, --mnemonic-fmt=F  During setup, prompt for mnemonic seed phrase of format
-                      'F' (choices: {mc}; default: '{md}')
+                      'F' (choices: {fmt_list(mn_fmts,fmt='no_spc')}; default: {mn_fmt_dfl!r})
 -n, --no-summary      Don’t print a transaction summary
 -n, --no-summary      Don’t print a transaction summary
 -s, --stealth-led     Stealth LED mode - signal busy and error only, and only
 -s, --stealth-led     Stealth LED mode - signal busy and error only, and only
                       after successful authorization.
                       after successful authorization.
@@ -61,16 +61,12 @@ opts_data = {
                       will not be printed.
                       will not be printed.
 -q, --quiet           Produce quieter output
 -q, --quiet           Produce quieter output
 -v, --verbose         Produce more verbose output
 -v, --verbose         Produce more verbose output
-""".format(
-		md = mn_fmt_dfl,
-		mc = fmt_list(mn_fmts,fmt='no_spc'),
-		mp = mountpoint
-	),
-	'notes': """
+""",
+	'notes': f"""
 
 
                               COMMANDS
                               COMMANDS
 
 
-gen_key - generate the wallet encryption key and copy it to '{td}'
+gen_key - generate the wallet encryption key and copy it to '{tx_dir}'
 setup   - generate the wallet encryption key and wallet
 setup   - generate the wallet encryption key and wallet
 wait    - start in loop mode: wait-mount-sign-unmount-wait
 wait    - start in loop mode: wait-mount-sign-unmount-wait
 
 
@@ -91,13 +87,13 @@ ready for device insertion or removal.
 The removable device must have a partition labeled MMGEN_TX and a user-
 The removable device must have a partition labeled MMGEN_TX and a user-
 writable directory '/tx', where unsigned MMGen transactions are placed.
 writable directory '/tx', where unsigned MMGen transactions are placed.
 
 
-On the signing machine the mount point '{mp}' must exist and /etc/fstab
+On the signing machine the mount point '{mountpoint}' must exist and /etc/fstab
 must contain the following entry:
 must contain the following entry:
 
 
     LABEL='MMGEN_TX' /mnt/tx auto noauto,user 0 0
     LABEL='MMGEN_TX' /mnt/tx auto noauto,user 0 0
 
 
 Transactions are signed with a wallet on the signing machine (in the directory
 Transactions are signed with a wallet on the signing machine (in the directory
-'{wd}') encrypted with a 64-character hexadecimal password on the
+'{wallet_dir}') encrypted with a 64-character hexadecimal password on the
 removable device.
 removable device.
 
 
 The password and wallet can be created in one operation by invoking the
 The password and wallet can be created in one operation by invoking the
@@ -108,7 +104,7 @@ Alternatively, the password and wallet can be created separately by first
 invoking the command with 'gen_key' and then creating and encrypting the
 invoking the command with 'gen_key' and then creating and encrypting the
 wallet using the -P (--passwd-file) option:
 wallet using the -P (--passwd-file) option:
 
 
-    $ mmgen-walletconv -r0 -q -iwords -d{wd} -p1 -P{td}/{kf} -Llabel
+    $ mmgen-walletconv -r0 -q -iwords -d{wallet_dir} -p1 -P{tx_dir}/{key_fn} -Llabel
 
 
 Note that the hash preset must be '1'.  Multiple wallets are permissible.
 Note that the hash preset must be '1'.  Multiple wallets are permissible.
 
 
@@ -116,7 +112,7 @@ For good security, it's advisable to re-generate a new wallet and key for
 each signing session.
 each signing session.
 
 
 This command is currently available only on Linux-based platforms.
 This command is currently available only on Linux-based platforms.
-""".format(pnm=prog_name,wd=wallet_dir,td=tx_dir,kf=key_fn,mp=mountpoint)
+"""
 	}
 	}
 }
 }
 
 

+ 4 - 4
mmgen/main_passgen.py

@@ -33,10 +33,10 @@ pwi_fs = '{:8} {:1} {:26} {:<7}  {:<7}  {}'
 opts_data = {
 opts_data = {
 	'sets': [('print_checksum',True,'quiet',True)],
 	'sets': [('print_checksum',True,'quiet',True)],
 	'text': {
 	'text': {
-		'desc': """
-                 Generate a range or list of passwords from an {pnm} wallet,
+		'desc': f"""
+                 Generate a range or list of passwords from an {g.proj_name} wallet,
                  mnemonic, seed or brainwallet for the given ID string
                  mnemonic, seed or brainwallet for the given ID string
-		 """.format(pnm=g.proj_name),
+		 """,
 		'usage':'[opts] [seed source] <ID string> <index list or range(s)>',
 		'usage':'[opts] [seed source] <ID string> <index list or range(s)>',
 		'options': """
 		'options': """
 -h, --help            Print this help message
 -h, --help            Print this help message
@@ -118,7 +118,7 @@ FMT CODES:
 			seed_lens=', '.join(map(str,g.seed_lens)),
 			seed_lens=', '.join(map(str,g.seed_lens)),
 			g=g,pnm=g.proj_name,
 			g=g,pnm=g.proj_name,
 			dpf=PasswordList.dfl_pw_fmt,
 			dpf=PasswordList.dfl_pw_fmt,
-			kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)])
+			kgs=' '.join([f'{n}:{k}' for n,k in enumerate(g.key_generators,1)])
 		),
 		),
 		'notes': lambda help_notes,s: s.format(
 		'notes': lambda help_notes,s: s.format(
 				o=opts,g=g,i58=pwi['b58'],i32=pwi['b32'],i39=pwi['bip39'],
 				o=opts,g=g,i58=pwi['b58'],i32=pwi['b32'],i39=pwi['bip39'],

+ 2 - 2
mmgen/main_regtest.py

@@ -26,7 +26,7 @@ from .common import *
 opts_data = {
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc': 'Coin daemon regression test mode setup and operations for the {} suite'.format(g.proj_name),
+		'desc': f'Coin daemon regression test mode setup and operations for the {g.proj_name} suite',
 		'usage':   '[opts] <command>',
 		'usage':   '[opts] <command>',
 		'options': """
 		'options': """
 -h, --help          Print this help message
 -h, --help          Print this help message
@@ -77,7 +77,7 @@ def check_num_args():
 if not cmd_args:
 if not cmd_args:
 	opts.usage()
 	opts.usage()
 elif cmd_args[0] not in MMGenRegtest.usr_cmds:
 elif cmd_args[0] not in MMGenRegtest.usr_cmds:
-	die(1,'{!r}: invalid command'.format(cmd_args[0]))
+	die(1,f'{cmd_args[0]!r}: invalid command')
 elif cmd_args[0] not in ('cli','balances'):
 elif cmd_args[0] not in ('cli','balances'):
 	check_num_args()
 	check_num_args()
 
 

+ 2 - 2
mmgen/main_seedjoin.py

@@ -102,7 +102,7 @@ def print_shares_info():
 				len(shares) )
 				len(shares) )
 		si = 1
 		si = 1
 	for n,s in enumerate(shares[si:],si+1):
 	for n,s in enumerate(shares[si:],si+1):
-		out += '{:3}: {}\n'.format(n,s.sid)
+		out += f'{n:3}: {s.sid}\n'
 	qmsg(out)
 	qmsg(out)
 
 
 cmd_args = opts.init(opts_data)
 cmd_args = opts.init(opts_data)
@@ -139,6 +139,6 @@ msg_r('Joining {n}-of-{n} XOR split...'.format(n=len(shares)))
 
 
 seed_out = Seed.join_shares([share1]+shares[1:])
 seed_out = Seed.join_shares([share1]+shares[1:])
 
 
-msg('OK\nJoined Seed ID: {}'.format(seed_out.sid.hl()))
+msg(f'OK\nJoined Seed ID: {seed_out.sid.hl()}')
 
 
 Wallet(seed=seed_out).write_to_file()
 Wallet(seed=seed_out).write_to_file()

+ 11 - 12
mmgen/main_split.py

@@ -29,10 +29,10 @@ from .common import *
 
 
 opts_data = {
 opts_data = {
 	'text': {
 	'text': {
-		'desc': """
-               Split funds in a {pnm} wallet after a chain fork using a
+		'desc': f"""
+               Split funds in an {g.proj_name} wallet after a chain fork using a
                timelocked transaction
                timelocked transaction
-		 """.format(pnm=g.proj_name),
+		 """,
 		'usage':'[opts] [output addr1] [output addr2]',
 		'usage':'[opts] [output addr1] [output addr2]',
 		'options': """
 		'options': """
 -h, --help           Print this help message
 -h, --help           Print this help message
@@ -51,10 +51,10 @@ opts_data = {
 -L, --locktime=    t Lock time (block height or unix seconds)
 -L, --locktime=    t Lock time (block height or unix seconds)
                      (default: {bh})
                      (default: {bh})
 """,
 """,
-	'notes': """\n
+	'notes': f"""\n
 This command creates two transactions: one (with the timelock) to be broadcast
 This command creates two transactions: one (with the timelock) to be broadcast
 on the long chain and one on the short chain after a replayable chain fork.
 on the long chain and one on the short chain after a replayable chain fork.
-Only {pnm} addresses may be spent to.
+Only {g.proj_name} addresses may be spent to.
 
 
 The command must be run on the longest chain.  The user is reponsible for
 The command must be run on the longest chain.  The user is reponsible for
 ensuring that the current chain is the longest.  The other chain is specified
 ensuring that the current chain is the longest.  The other chain is specified
@@ -77,7 +77,7 @@ attacks on the majority chain or reorg attacks on the minority chain if the
 minority chain is ahead of the timelock.  If the reorg'd minority chain is
 minority chain is ahead of the timelock.  If the reorg'd minority chain is
 behind the timelock, protection is contingent on getting the non-timelocked
 behind the timelock, protection is contingent on getting the non-timelocked
 transaction reconfirmed before the timelock expires. Use at your own risk.
 transaction reconfirmed before the timelock expires. Use at your own risk.
-""".format(pnm=g.proj_name)
+"""
 	},
 	},
 	'code': {
 	'code': {
 		'options': lambda proto,s: s.format(
 		'options': lambda proto,s: s.format(
@@ -96,11 +96,10 @@ die(1,'This command is disabled')
 # the following code is broken:
 # the following code is broken:
 opt.other_coin = opt.other_coin.upper() if opt.other_coin else proto.forks[-1][2].upper()
 opt.other_coin = opt.other_coin.upper() if opt.other_coin else proto.forks[-1][2].upper()
 if opt.other_coin.lower() not in [e[2] for e in proto.forks if e[3] == True]:
 if opt.other_coin.lower() not in [e[2] for e in proto.forks if e[3] == True]:
-	die(1,"'{}': not a replayable fork of {} chain".format(opt.other_coin,proto.coin))
+	die(1,f'{opt.other_coin!r}: not a replayable fork of {proto.coin} chain')
 
 
 if len(cmd_args) != 2:
 if len(cmd_args) != 2:
-	fs = 'This command requires exactly two {} addresses as arguments'
-	die(1,fs.format(g.proj_name))
+	die(1,f'This command requires exactly two {g.proj_name} addresses as arguments')
 
 
 from .obj import MMGenID
 from .obj import MMGenID
 try:
 try:
@@ -109,7 +108,7 @@ except:
 	die(1,'Command line arguments must be valid MMGen IDs')
 	die(1,'Command line arguments must be valid MMGen IDs')
 
 
 if mmids[0] == mmids[1]:
 if mmids[0] == mmids[1]:
-	die(2,'Both transactions have the same output! ({})'.format(mmids[0]))
+	die(2,f'Both transactions have the same output! ({mmids[0]})')
 
 
 from .tx import MMGenSplitTX
 from .tx import MMGenSplitTX
 from .protocol import init_proto
 from .protocol import init_proto
@@ -124,7 +123,7 @@ tx1 = MMGenSplitTX()
 opt.no_blank = True
 opt.no_blank = True
 
 
 async def main():
 async def main():
-	gmsg("Creating timelocked transaction for long chain ({})".format(proto.coin))
+	gmsg(f'Creating timelocked transaction for long chain ({proto.coin})')
 	locktime = int(opt.locktime)
 	locktime = int(opt.locktime)
 	if not locktime:
 	if not locktime:
 		rpc = rpc_init(proto)
 		rpc = rpc_init(proto)
@@ -134,7 +133,7 @@ async def main():
 	tx1.format()
 	tx1.format()
 	tx1.create_fn()
 	tx1.create_fn()
 
 
-	gmsg("\nCreating transaction for short chain ({})".format(opt.other_coin))
+	gmsg(f'\nCreating transaction for short chain ({opt.other_coin})')
 
 
 	proto = init_proto(opt.other_coin)
 	proto = init_proto(opt.other_coin)
 
 

+ 9 - 8
mmgen/main_tool.py

@@ -25,7 +25,7 @@ from .common import *
 
 
 def make_cmd_help():
 def make_cmd_help():
 	import mmgen.tool
 	import mmgen.tool
-	def make_help():
+	def do():
 		for bc in mmgen.tool.MMGenToolCmds.classes.values():
 		for bc in mmgen.tool.MMGenToolCmds.classes.values():
 			cls_doc = bc.__doc__.strip().split('\n')
 			cls_doc = bc.__doc__.strip().split('\n')
 			for l in cls_doc:
 			for l in cls_doc:
@@ -40,22 +40,23 @@ def make_cmd_help():
 			yield ''
 			yield ''
 
 
 			max_w = max(map(len,bc.user_commands))
 			max_w = max(map(len,bc.user_commands))
-			fs = '  {{:{}}} - {{}}'.format(max_w)
 			for name,code in sorted(bc.user_commands.items()):
 			for name,code in sorted(bc.user_commands.items()):
 				if code.__doc__:
 				if code.__doc__:
-					yield fs.format(name,
+					yield '  {:{}} - {}'.format(
+						name,
+						max_w,
 						pretty_format(
 						pretty_format(
 							code.__doc__.strip().replace('\n\t\t',' '),
 							code.__doc__.strip().replace('\n\t\t',' '),
-							width=79-(max_w+7),
-							pfx=' '*(max_w+5)).lstrip()
+							width = 79-(max_w+7),
+							pfx   = ' '*(max_w+5)).lstrip()
 					)
 					)
 			yield ''
 			yield ''
 
 
-	return '\n'.join(make_help())
+	return '\n'.join(do())
 
 
 opts_data = {
 opts_data = {
 	'text': {
 	'text': {
-		'desc':    'Perform various {pnm}- and cryptocoin-related operations'.format(pnm=g.proj_name),
+		'desc':    f'Perform various {g.proj_name}- and cryptocoin-related operations',
 		'usage':   '[opts] <command> <command args>',
 		'usage':   '[opts] <command> <command args>',
 		'options': """
 		'options': """
 -d, --outdir=       d Specify an alternate directory 'd' for output
 -d, --outdir=       d Specify an alternate directory 'd' for output
@@ -103,7 +104,7 @@ if cmd in ('help','usage') and cmd_args:
 	cmd_args[0] = 'command_name=' + cmd_args[0]
 	cmd_args[0] = 'command_name=' + cmd_args[0]
 
 
 if cmd not in tool.MMGenToolCmds:
 if cmd not in tool.MMGenToolCmds:
-	die(1,"'{}': no such command".format(cmd))
+	die(1,f'{cmd!r}: no such command')
 
 
 args,kwargs = tool._process_args(cmd,cmd_args)
 args,kwargs = tool._process_args(cmd,cmd_args)
 
 

+ 5 - 5
mmgen/main_txbump.py

@@ -27,12 +27,12 @@ from .wallet import Wallet
 opts_data = {
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc': """
+		'desc': f"""
                 Increase the fee on a replaceable (RBF) {g.proj_name} transaction,
                 Increase the fee on a replaceable (RBF) {g.proj_name} transaction,
                 creating a new transaction, and optionally sign and send the
                 creating a new transaction, and optionally sign and send the
                 new transaction
                 new transaction
-		 """.format(g=g),
-		'usage':   '[opts] <{g.proj_name} TX file> [seed source] ...'.format(g=g),
+		 """,
+		'usage':   f'[opts] <{g.proj_name} TX file> [seed source] ...',
 		'options': """
 		'options': """
 -h, --help             Print this help message
 -h, --help             Print this help message
 --, --longhelp         Print help message for long options (common options)
 --, --longhelp         Print help message for long options (common options)
@@ -87,7 +87,7 @@ column below:
 			pnl=g.proj_name.lower(),
 			pnl=g.proj_name.lower(),
 			fu=help_notes('rel_fee_desc'),
 			fu=help_notes('rel_fee_desc'),
 			fl=help_notes('fee_spec_letters'),
 			fl=help_notes('fee_spec_letters'),
-			kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]),
+			kgs=' '.join([f'{n}:{k}' for n,k in enumerate(g.key_generators,1)]),
 			kg=g.key_generator,
 			kg=g.key_generator,
 			cu=proto.coin),
 			cu=proto.coin),
 		'notes': lambda help_notes,s: s.format(
 		'notes': lambda help_notes,s: s.format(
@@ -147,7 +147,7 @@ async def main():
 	output_idx = tx.choose_output()
 	output_idx = tx.choose_output()
 
 
 	if not silent:
 	if not silent:
-		msg('Minimum fee for new transaction: {} {}'.format(tx.min_fee.hl(),tx.proto.coin))
+		msg(f'Minimum fee for new transaction: {tx.min_fee.hl()} {tx.proto.coin}')
 
 
 	tx.usr_fee = tx.get_usr_fee_interactive(tx_fee=opt.tx_fee,desc='User-selected')
 	tx.usr_fee = tx.get_usr_fee_interactive(tx_fee=opt.tx_fee,desc='User-selected')
 
 

+ 1 - 1
mmgen/main_txcreate.py

@@ -26,7 +26,7 @@ from .common import *
 opts_data = {
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc': 'Create a transaction with outputs to specified coin or {g.proj_name} addresses'.format(g=g),
+		'desc': f'Create a transaction with outputs to specified coin or {g.proj_name} addresses',
 		'usage':   '[opts]  <addr,amt> ... [change addr] [addr file] ...',
 		'usage':   '[opts]  <addr,amt> ... [change addr] [addr file] ...',
 		'options': """
 		'options': """
 -h, --help            Print this help message
 -h, --help            Print this help message

+ 2 - 2
mmgen/main_txdo.py

@@ -27,7 +27,7 @@ from .obj import SubSeedIdxRange
 opts_data = {
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc': 'Create, sign and send an {g.proj_name} transaction'.format(g=g),
+		'desc': f'Create, sign and send an {g.proj_name} transaction',
 		'usage':   '[opts]  <addr,amt> ... [change addr] [addr file] ... [seed source] ...',
 		'usage':   '[opts]  <addr,amt> ... [change addr] [addr file] ... [seed source] ...',
 		'options': """
 		'options': """
 -h, --help             Print this help message
 -h, --help             Print this help message
@@ -95,7 +95,7 @@ column below:
 	'code': {
 	'code': {
 		'options': lambda proto,help_notes,s: s.format(
 		'options': lambda proto,help_notes,s: s.format(
 			g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),
 			g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),
-			kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]),
+			kgs=' '.join([f'{n}:{k}' for n,k in enumerate(g.key_generators,1)]),
 			fu=help_notes('rel_fee_desc'),
 			fu=help_notes('rel_fee_desc'),
 			fl=help_notes('fee_spec_letters'),
 			fl=help_notes('fee_spec_letters'),
 			ss=g.subseeds,
 			ss=g.subseeds,

+ 1 - 1
mmgen/main_txsend.py

@@ -25,7 +25,7 @@ from .common import *
 opts_data = {
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc':    'Send a signed {pnm} cryptocoin transaction'.format(pnm=g.proj_name),
+		'desc':    f'Send a signed {g.proj_name} cryptocoin transaction',
 		'usage':   '[opts] <signed transaction file>',
 		'usage':   '[opts] <signed transaction file>',
 		'options': """
 		'options': """
 -h, --help      Print this help message
 -h, --help      Print this help message

+ 2 - 2
mmgen/main_txsign.py

@@ -28,7 +28,7 @@ from .wallet import Wallet
 opts_data = {
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc':    'Sign cryptocoin transactions generated by {pnl}-txcreate'.format(pnl=g.proj_name.lower()),
+		'desc':    f'Sign cryptocoin transactions generated by {g.proj_name.lower()}-txcreate',
 		'usage':   '[opts] <transaction file>... [seed source]...',
 		'usage':   '[opts] <transaction file>... [seed source]...',
 		'options': """
 		'options': """
 -h, --help            Print this help message
 -h, --help            Print this help message
@@ -81,7 +81,7 @@ column below:
 			g=g,
 			g=g,
 			pnm=g.proj_name,
 			pnm=g.proj_name,
 			pnl=g.proj_name.lower(),
 			pnl=g.proj_name.lower(),
-			kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]),
+			kgs=' '.join([f'{n}:{k}' for n,k in enumerate(g.key_generators,1)]),
 			kg=g.key_generator,
 			kg=g.key_generator,
 			ss=g.subseeds,
 			ss=g.subseeds,
 			ss_max=SubSeedIdxRange.max_idx,
 			ss_max=SubSeedIdxRange.max_idx,

+ 4 - 4
mmgen/main_wallet.py

@@ -43,11 +43,11 @@ invoked_as = {
 	'mmgen-seedsplit':    'seedsplit',
 	'mmgen-seedsplit':    'seedsplit',
 }[g.prog_name]
 }[g.prog_name]
 
 
-dsw = 'the default or specified {pnm} wallet'
+dsw = f'the default or specified {g.proj_name} wallet'
 
 
 # full: defhHiJkKlLmoOpPqrSvz-
 # full: defhHiJkKlLmoOpPqrSvz-
 if invoked_as == 'gen':
 if invoked_as == 'gen':
-	desc = 'Generate an {pnm} wallet from a random seed'
+	desc = f'Generate an {g.proj_name} wallet from a random seed'
 	opt_filter = 'ehdoJlLpPqrSvz-'
 	opt_filter = 'ehdoJlLpPqrSvz-'
 	usage = '[opts]'
 	usage = '[opts]'
 	oaction = 'output'
 	oaction = 'output'
@@ -81,7 +81,7 @@ elif invoked_as == 'seedsplit':
 
 
 opts_data = {
 opts_data = {
 	'text': {
 	'text': {
-		'desc': desc.format(pnm=g.proj_name),
+		'desc': desc,
 		'usage': usage,
 		'usage': usage,
 		'options': """
 		'options': """
 -h, --help            Print this help message
 -h, --help            Print this help message
@@ -187,7 +187,7 @@ else:
 
 
 if invoked_as == 'chk':
 if invoked_as == 'chk':
 	lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE'
 	lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE'
-	vmsg('Wallet label: {}'.format(lbl))
+	vmsg(f'Wallet label: {lbl}')
 	# TODO: display creation date
 	# TODO: display creation date
 	sys.exit(0)
 	sys.exit(0)
 
 

+ 17 - 19
mmgen/mn_entry.py

@@ -219,7 +219,7 @@ def mn_entry(wl_id,entry_mode=None):
 		wl_id = 'mmgen'
 		wl_id = 'mmgen'
 	me = MnemonicEntry.get_cls_by_wordlist(wl_id)
 	me = MnemonicEntry.get_cls_by_wordlist(wl_id)
 	import importlib
 	import importlib
-	me.conv_cls = getattr(importlib.import_module('mmgen.{}'.format(me.modname)),me.modname)
+	me.conv_cls = getattr(importlib.import_module(f'mmgen.{me.modname}'),me.modname)
 	me.conv_cls.init_mn(wl_id)
 	me.conv_cls.init_mn(wl_id)
 	me.wl = me.conv_cls.digits[wl_id]
 	me.wl = me.conv_cls.digits[wl_id]
 	obj = me()
 	obj = me()
@@ -336,7 +336,7 @@ class MnemonicEntry(object):
 
 
 	def get_mnemonic_from_user(self,mn_len,validate=True):
 	def get_mnemonic_from_user(self,mn_len,validate=True):
 		mll = list(self.conv_cls.seedlen_map_rev[self.wl_id])
 		mll = list(self.conv_cls.seedlen_map_rev[self.wl_id])
-		assert mn_len in mll, '{}: invalid mnemonic length (must be one of {})'.format(mn_len,mll)
+		assert mn_len in mll, f'{mn_len}: invalid mnemonic length (must be one of {mll})'
 
 
 		if self.usr_dfl_entry_mode:
 		if self.usr_dfl_entry_mode:
 			em = self.get_cls_by_entry_mode(self.usr_dfl_entry_mode)(self)
 			em = self.get_cls_by_entry_mode(self.usr_dfl_entry_mode)(self)
@@ -345,20 +345,19 @@ class MnemonicEntry(object):
 			em = self.choose_entry_mode()
 			em = self.choose_entry_mode()
 			i_add = '.'
 			i_add = '.'
 
 
-		msg('\r' + 'Using {} entry mode{}'.format(cyan(em.name.upper()),i_add))
+		msg('\r' + f'Using {cyan(em.name.upper())} entry mode{i_add}')
 		self.em = em
 		self.em = em
 
 
 		if not self.usr_dfl_entry_mode:
 		if not self.usr_dfl_entry_mode:
-			m = (
-				fmt(self.prompt_info['intro'])
-				+ '\n'
-				+ fmt(self.prompt_info['pad_info'].rstrip() + em.pad_max_info + em.prompt_info, indent='  ')
-			)
-			msg('\n' + m.format(
-				ml       = mn_len,
-				ssl      = em.ss_len,
-				pad_max  = em.pad_max,
-				sw       = self.shortest_word,
+			msg('\n' + (
+					fmt(self.prompt_info['intro'])
+					+ '\n'
+					+ fmt(self.prompt_info['pad_info'].rstrip() + em.pad_max_info + em.prompt_info, indent='  ')
+				).format(
+					ml       = mn_len,
+					ssl      = em.ss_len,
+					pad_max  = em.pad_max,
+					sw       = self.shortest_word,
 			))
 			))
 
 
 		clear_line = '\n' if g.test_suite else '{r}{s}{r}'.format(r='\r',s=' '*40)
 		clear_line = '\n' if g.test_suite else '{r}{s}{r}'.format(r='\r',s=' '*40)
@@ -393,8 +392,7 @@ class MnemonicEntry(object):
 		}
 		}
 		wl = wl.lower()
 		wl = wl.lower()
 		if wl not in d:
 		if wl not in d:
-			m = 'wordlist {!r} not recognized (valid options: {})'
-			raise ValueError(m.format(wl,fmt_list(list(d))))
+			raise ValueError(f'wordlist {wl!r} not recognized (valid options: {fmt_list(list(d))})')
 		return d[wl]
 		return d[wl]
 
 
 	@classmethod
 	@classmethod
@@ -402,8 +400,9 @@ class MnemonicEntry(object):
 		for k,v in g.mnemonic_entry_modes.items():
 		for k,v in g.mnemonic_entry_modes.items():
 			tcls = cls.get_cls_by_wordlist(k)
 			tcls = cls.get_cls_by_wordlist(k)
 			if v not in tcls.entry_modes:
 			if v not in tcls.entry_modes:
-				m = 'entry mode {!r} not recognized for wordlist {!r}:\n    (valid options: {})'
-				raise ValueError(m.format(v,k,fmt_list(tcls.entry_modes)))
+				raise ValueError(
+					f'entry mode {v!r} not recognized for wordlist {k!r}:' +
+					f'\n    (valid options: {fmt_list(tcls.entry_modes)})' )
 			tcls.usr_dfl_entry_mode = v
 			tcls.usr_dfl_entry_mode = v
 
 
 class MnemonicEntryMMGen(MnemonicEntry):
 class MnemonicEntryMMGen(MnemonicEntry):
@@ -430,5 +429,4 @@ class MnemonicEntryMonero(MnemonicEntry):
 try:
 try:
 	MnemonicEntry.get_cfg_vars()
 	MnemonicEntry.get_cfg_vars()
 except Exception as e:
 except Exception as e:
-	m = "Error in cfg file option 'mnemonic_entry_modes':\n  {}"
-	die(2,m.format(e.args[0]))
+	die(2, f"Error in cfg file option 'mnemonic_entry_modes':\n  {e.args[0]}")

+ 3 - 3
mmgen/obj.py

@@ -494,7 +494,7 @@ class CoinAmt(Decimal,Hilite,InitErrors): # abstract class
 	def to_unit(self,unit,show_decimal=False):
 	def to_unit(self,unit,show_decimal=False):
 		ret = Decimal(self) // getattr(self,unit)
 		ret = Decimal(self) // getattr(self,unit)
 		if show_decimal and ret < 1:
 		if show_decimal and ret < 1:
-			return '{:.8f}'.format(ret).rstrip('0')
+			return f'{ret:.8f}'.rstrip('0')
 		return int(ret)
 		return int(ret)
 
 
 	@classmethod
 	@classmethod
@@ -675,8 +675,8 @@ class SubSeedIdx(str,Hilite,InitErrors):
 			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, 'subseed index < {:,}'.format(SubSeedIdxRange.min_idx)
-			assert idx <= SubSeedIdxRange.max_idx, 'subseed index > {:,}'.format(SubSeedIdxRange.max_idx)
+			assert idx >= SubSeedIdxRange.min_idx, f'subseed index < {SubSeedIdxRange.min_idx:,}'
+			assert idx <= SubSeedIdxRange.max_idx, f'subseed index > {SubSeedIdxRange.max_idx:,}'
 
 
 			sstype,ltr = ('short','S') if s[-1] in 'Ss' else ('long','L')
 			sstype,ltr = ('short','S') if s[-1] in 'Ss' else ('long','L')
 			me = str.__new__(cls,str(idx)+ltr)
 			me = str.__new__(cls,str(idx)+ltr)

+ 49 - 39
mmgen/opts.py

@@ -40,11 +40,11 @@ def usage():
 	Die(1,Opts.make_usage_str(g.prog_name,'user',usage_data))
 	Die(1,Opts.make_usage_str(g.prog_name,'user',usage_data))
 
 
 def version():
 def version():
-	Die(0,fmt("""
-		{pn} version {g.version}
+	Die(0,fmt(f"""
+		{g.prog_name.upper()} version {g.version}
 		Part of the {g.proj_name} suite, an online/offline cryptocurrency wallet for the
 		Part of the {g.proj_name} suite, an online/offline cryptocurrency wallet for the
 		command line.  Copyright (C){g.Cdates} {g.author} {g.email}
 		command line.  Copyright (C){g.Cdates} {g.author} {g.email}
-	""".format(g=g,pn=g.prog_name.upper()),indent='    ').rstrip())
+	""",indent='    ').rstrip())
 
 
 def print_help(po,opts_data,opt_filter):
 def print_help(po,opts_data,opt_filter):
 	if not 'code' in opts_data:
 	if not 'code' in opts_data:
@@ -167,14 +167,9 @@ def show_common_opts_diff():
 	a = set(g.common_opts)
 	a = set(g.common_opts)
 	b = set(common_opts_data_to_list())
 	b = set(common_opts_data_to_list())
 
 
-	m1 = 'g.common_opts - common_opts_data:\n   {}\n'
-	msg(m1.format(do_fmt(a-b) if a-b else 'None'))
-
-	m2 = 'common_opts_data - g.common_opts (these do not set global var):\n{}\n'
-	msg(m2.format(do_fmt(b-a)))
-
-	m3 = 'common_opts_data ^ g.common_opts (these set global var):\n{}\n'
-	msg(m3.format(do_fmt(b.intersection(a))))
+	msg(f'g.common_opts - common_opts_data:\n   {do_fmt(a-b) if a-b else "None"}\n')
+	msg(f'common_opts_data - g.common_opts (these do not set global var):\n{do_fmt(b-a)}\n')
+	msg(f'common_opts_data ^ g.common_opts (these set global var):\n{do_fmt(b.intersection(a))}\n')
 
 
 	sys.exit(0)
 	sys.exit(0)
 
 
@@ -407,11 +402,18 @@ def opt_is_tx_fee(key,val,desc): # 'key' must remain a placeholder
 
 
 	if ret == False:
 	if ret == False:
 		raise UserOptError('{!r}: invalid {}\n(not a {} amount or {} specification)'.format(
 		raise UserOptError('{!r}: invalid {}\n(not a {} amount or {} specification)'.format(
-				val,desc,tx.proto.coin.upper(),tx.rel_fee_desc))
+			val,
+			desc,
+			tx.proto.coin.upper(),
+			tx.rel_fee_desc ))
 
 
 	if ret > tx.proto.max_tx_fee:
 	if ret > tx.proto.max_tx_fee:
 		raise UserOptError('{!r}: invalid {}\n({} > max_tx_fee ({} {}))'.format(
 		raise UserOptError('{!r}: invalid {}\n({} > max_tx_fee ({} {}))'.format(
-				val,desc,ret.fmt(fs='1.1'),tx.proto.max_tx_fee,tx.proto.coin.upper()))
+			val,
+			desc,
+			ret.fmt(fs='1.1'),
+			tx.proto.max_tx_fee,
+			tx.proto.coin.upper() ))
 
 
 def check_usr_opts(usr_opts): # Raises an exception if any check fails
 def check_usr_opts(usr_opts): # Raises an exception if any check fails
 
 
@@ -420,40 +422,45 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 		try:
 		try:
 			l = val.split(sep)
 			l = val.split(sep)
 		except:
 		except:
-			raise UserOptError('{!r}: invalid {} (not {}-separated list)'.format(val,desc,sepword))
+			raise UserOptError(f'{val!r}: invalid {desc} (not {sepword}-separated list)')
 
 
 		if len(l) != n:
 		if len(l) != n:
-			raise UserOptError('{!r}: invalid {} ({} {}-separated items required)'.format(val,desc,n,sepword))
+			raise UserOptError(f'{val!r}: invalid {desc} ({n} {sepword}-separated items required)')
 
 
 	def opt_compares(val,op_str,target,desc,desc2=''):
 	def opt_compares(val,op_str,target,desc,desc2=''):
 		import operator as o
 		import operator as o
 		op_f = { '<':o.lt, '<=':o.le, '>':o.gt, '>=':o.ge, '=':o.eq }[op_str]
 		op_f = { '<':o.lt, '<=':o.le, '>':o.gt, '>=':o.ge, '=':o.eq }[op_str]
 		if not op_f(val,target):
 		if not op_f(val,target):
 			d2 = desc2 + ' ' if desc2 else ''
 			d2 = desc2 + ' ' if desc2 else ''
-			raise UserOptError('{}: invalid {} ({}not {} {})'.format(val,desc,d2,op_str,target))
+			raise UserOptError(f'{val}: invalid {desc} ({d2}not {op_str} {target})')
 
 
 	def opt_is_int(val,desc):
 	def opt_is_int(val,desc):
 		if not is_int(val):
 		if not is_int(val):
-			raise UserOptError('{!r}: invalid {} (not an integer)'.format(val,desc))
+			raise UserOptError(f'{val!r}: invalid {desc} (not an integer)')
 
 
 	def opt_is_float(val,desc):
 	def opt_is_float(val,desc):
 		try:
 		try:
 			float(val)
 			float(val)
 		except:
 		except:
-			raise UserOptError('{!r}: invalid {} (not a floating-point number)'.format(val,desc))
+			raise UserOptError(f'{val!r}: invalid {desc} (not a floating-point number)')
 
 
 	def opt_is_in_list(val,tlist,desc):
 	def opt_is_in_list(val,tlist,desc):
 		if val not in tlist:
 		if val not in tlist:
 			q,sep = (('',','),("'","','"))[type(tlist[0]) == str]
 			q,sep = (('',','),("'","','"))[type(tlist[0]) == str]
-			fs = '{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'
-			raise UserOptError(fs.format(v=val,w=desc,q=q,o=sep.join(map(str,sorted(tlist)))))
+			raise UserOptError('{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format(
+				v = val,
+				w = desc,
+				q = q,
+				o = sep.join(map(str,sorted(tlist))) ))
 
 
 	def opt_unrecognized(key,val,desc='value'):
 	def opt_unrecognized(key,val,desc='value'):
-		raise UserOptError('{!r}: unrecognized {} for option {!r}'.format(val,desc,fmt_opt(key)))
+		raise UserOptError(f'{val!r}: unrecognized {desc} for option {fmt_opt(key)!r}')
 
 
 	def opt_display(key,val='',beg='For selected',end=':\n'):
 	def opt_display(key,val='',beg='For selected',end=':\n'):
-		s = '{}={}'.format(fmt_opt(key),val) if val else fmt_opt(key)
-		msg_r('{} option {!r}{}'.format(beg,s,end))
+		msg_r('{} option {!r}{}'.format(
+			beg,
+			f'{fmt_opt(key)}={val}' if val else fmt_opt(key),
+			end ))
 
 
 	def chk_in_fmt(key,val,desc):
 	def chk_in_fmt(key,val,desc):
 		from .wallet import Wallet,IncogWallet,Brainwallet,IncogWalletHidden
 		from .wallet import Wallet,IncogWallet,Brainwallet,IncogWalletHidden
@@ -463,9 +470,9 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 		if key == 'out_fmt':
 		if key == 'out_fmt':
 			p = 'hidden_incog_output_params'
 			p = 'hidden_incog_output_params'
 			if sstype == IncogWalletHidden and not getattr(opt,p):
 			if sstype == IncogWalletHidden and not getattr(opt,p):
-				m1 = 'Hidden incog format output requested.  '
-				m2 = 'You must supply a file and offset with the {!r} option'
-				raise UserOptError(m1+m2.format(fmt_opt(p)))
+				raise UserOptError(
+					'Hidden incog format output requested.  ' +
+					f'You must supply a file and offset with the {fmt_opt(p)!r} option' )
 			if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
 			if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
 				opt_display(key,val,beg='Selected',end=' ')
 				opt_display(key,val,beg='Selected',end=' ')
 				opt_display('old_incog_fmt',beg='conflicts with',end=':\n')
 				opt_display('old_incog_fmt',beg='conflicts with',end=':\n')
@@ -500,8 +507,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 			val2 = getattr(opt,key2)
 			val2 = getattr(opt,key2)
 			from .wallet import IncogWalletHidden
 			from .wallet import IncogWalletHidden
 			if val2 and val2 not in IncogWalletHidden.fmt_codes:
 			if val2 and val2 not in IncogWalletHidden.fmt_codes:
-				fs = 'Option conflict:\n  {}, with\n  {}={}'
-				raise UserOptError(fs.format(fmt_opt(key),fmt_opt(key2),val2))
+				raise UserOptError(f'Option conflict:\n  {fmt_opt(key)}, with\n  {fmt_opt(key2)}={val2}')
 
 
 	chk_hidden_incog_output_params = chk_hidden_incog_input_params
 	chk_hidden_incog_output_params = chk_hidden_incog_input_params
 
 
@@ -538,7 +544,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 
 
 	def chk_vsize_adj(key,val,desc):
 	def chk_vsize_adj(key,val,desc):
 		opt_is_float(val,desc)
 		opt_is_float(val,desc)
-		ymsg('Adjusting transaction vsize by a factor of {:1.2f}'.format(float(val)))
+		ymsg(f'Adjusting transaction vsize by a factor of {float(val):1.2f}')
 
 
 	def chk_key_generator(key,val,desc):
 	def chk_key_generator(key,val,desc):
 		opt_compares(val,'<=',len(g.key_generators),desc)
 		opt_compares(val,'<=',len(g.key_generators),desc)
@@ -551,16 +557,16 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 # TODO: move this check elsewhere
 # TODO: move this check elsewhere
 #	def chk_rbf(key,val,desc):
 #	def chk_rbf(key,val,desc):
 #		if not proto.cap('rbf'):
 #		if not proto.cap('rbf'):
-#			m = '--rbf requested, but {} does not support replace-by-fee transactions'
-#			raise UserOptError(m.format(proto.coin))
+#			raise UserOptError(f'--rbf requested, but {proto.coin} does not support replace-by-fee transactions')
 
 
 #	def chk_bob(key,val,desc):
 #	def chk_bob(key,val,desc):
-#		m = "Regtest (Bob and Alice) mode not set up yet.  Run '{}-regtest setup' to initialize."
 #		from .regtest import MMGenRegtest
 #		from .regtest import MMGenRegtest
 #		try:
 #		try:
 #			os.stat(os.path.join(MMGenRegtest(g.coin).d.datadir,'regtest','debug.log'))
 #			os.stat(os.path.join(MMGenRegtest(g.coin).d.datadir,'regtest','debug.log'))
 #		except:
 #		except:
-#			raise UserOptError(m.format(g.proj_name.lower()))
+#			raise UserOptError(
+#				'Regtest (Bob and Alice) mode not set up yet.  ' +
+#				f"Run '{g.proj_name.lower()}-regtest setup' to initialize." )
 #
 #
 #	chk_alice = chk_bob
 #	chk_alice = chk_bob
 
 
@@ -571,17 +577,17 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 # TODO: move this check elsewhere
 # TODO: move this check elsewhere
 #	def chk_token(key,val,desc):
 #	def chk_token(key,val,desc):
 #		if not 'token' in proto.caps:
 #		if not 'token' in proto.caps:
-#			raise UserOptError('Coin {!r} does not support the --token option'.format(tx.coin))
+#			raise UserOptError(f'Coin {tx.coin!r} does not support the --token option')
 #		if len(val) == 40 and is_hex_str(val):
 #		if len(val) == 40 and is_hex_str(val):
 #			return
 #			return
 #		if len(val) > 20 or not all(s.isalnum() for s in val):
 #		if len(val) > 20 or not all(s.isalnum() for s in val):
-#			raise UserOptError('{!r}: invalid parameter for --token option'.format(val))
+#			raise UserOptError(f'{val!r}: invalid parameter for --token option')
 
 
 	cfuncs = { k:v for k,v in locals().items() if k.startswith('chk_') }
 	cfuncs = { k:v for k,v in locals().items() if k.startswith('chk_') }
 
 
 	for key in usr_opts:
 	for key in usr_opts:
 		val = getattr(opt,key)
 		val = getattr(opt,key)
-		desc = 'parameter for {!r} option'.format(fmt_opt(key))
+		desc = f'parameter for {fmt_opt(key)!r} option'
 
 
 		if key in g.infile_opts:
 		if key in g.infile_opts:
 			check_infile(val) # file exists and is readable - dies on error
 			check_infile(val) # file exists and is readable - dies on error
@@ -590,7 +596,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 		elif 'chk_'+key in cfuncs:
 		elif 'chk_'+key in cfuncs:
 			cfuncs['chk_'+key](key,val,desc)
 			cfuncs['chk_'+key](key,val,desc)
 		elif g.debug:
 		elif g.debug:
-			Msg('check_usr_opts(): No test for opt {!r}'.format(key))
+			Msg(f'check_usr_opts(): No test for opt {key!r}')
 
 
 def set_auto_typeset_opts():
 def set_auto_typeset_opts():
 	for key,ref_type in g.auto_typeset_opts.items():
 	for key,ref_type in g.auto_typeset_opts.items():
@@ -622,8 +628,12 @@ def check_and_set_autoset_opts(): # Raises exception if any check fails
 			else:
 			else:
 				ret = locals()[asd.type](key,val,asd)
 				ret = locals()[asd.type](key,val,asd)
 				if type(ret) is str:
 				if type(ret) is str:
-					m = '{!r}: invalid parameter for option --{} (not {}: {})'
-					raise UserOptError(m.format(val,key.replace('_','-'),ret,fmt_list(asd.choices)))
+					raise UserOptError(
+						'{!r}: invalid parameter for option --{} (not {}: {})'.format(
+							val,
+							key.replace('_','-'),
+							ret,
+							fmt_list(asd.choices) ))
 				elif ret is True:
 				elif ret is True:
 					setattr(opt,key,val)
 					setattr(opt,key,val)
 				else:
 				else:

+ 12 - 10
mmgen/regtest.py

@@ -30,8 +30,8 @@ def create_data_dir(data_dir):
 	try: os.stat(os.path.join(data_dir,'regtest'))
 	try: os.stat(os.path.join(data_dir,'regtest'))
 	except: pass
 	except: pass
 	else:
 	else:
-		m = "Delete your existing MMGen regtest setup at '{}' and create a new one?"
-		if keypress_confirm(m.format(data_dir)):
+		if keypress_confirm(
+				f'Delete your existing MMGen regtest setup at {data_dir!r} and create a new one?'):
 			shutil.rmtree(data_dir)
 			shutil.rmtree(data_dir)
 		else:
 		else:
 			die()
 			die()
@@ -81,7 +81,7 @@ class MMGenRegtest(MMGenObject):
 		if len(out) != blocks:
 		if len(out) != blocks:
 			rdie(1,'Error generating blocks')
 			rdie(1,'Error generating blocks')
 
 
-		gmsg('Mined {} block{}'.format(blocks,suf(blocks)))
+		gmsg(f'Mined {blocks} block{suf(blocks)}')
 
 
 	async def setup(self):
 	async def setup(self):
 
 
@@ -93,7 +93,7 @@ class MMGenRegtest(MMGenObject):
 
 
 		create_data_dir(self.d.datadir)
 		create_data_dir(self.d.datadir)
 
 
-		gmsg('Starting {} regtest setup'.format(self.coin.upper()))
+		gmsg(f'Starting {self.coin.upper()} regtest setup')
 
 
 		self.d.start(silent=True)
 		self.d.start(silent=True)
 
 
@@ -164,7 +164,7 @@ class MMGenRegtest(MMGenObject):
 		msg(fs.format('Total balance:',sum(v for k,v in bal.items())))
 		msg(fs.format('Total balance:',sum(v for k,v in bal.items())))
 
 
 	async def send(self,addr,amt):
 	async def send(self,addr,amt):
-		gmsg('Sending {} miner {} to address {}'.format(amt,self.d.coin,addr))
+		gmsg(f'Sending {amt} miner {self.d.coin} to address {addr}')
 		cp = await self.rpc_call('sendtoaddress',addr,str(amt),wallet='miner')
 		cp = await self.rpc_call('sendtoaddress',addr,str(amt),wallet='miner')
 		await self.generate(1)
 		await self.generate(1)
 
 
@@ -185,14 +185,16 @@ class MMGenRegtest(MMGenObject):
 
 
 		proto = init_proto(coin,False)
 		proto = init_proto(coin,False)
 		if not [f for f in proto.forks if f[2] == proto.coin.lower() and f[3] == True]:
 		if not [f for f in proto.forks if f[2] == proto.coin.lower() and f[3] == True]:
-			die(1,"Coin {} is not a replayable fork of coin {}".format(proto.coin,coin))
+			die(1,f'Coin {proto.coin} is not a replayable fork of coin {coin}')
 
 
-		gmsg('Creating fork from coin {} to coin {}'.format(coin,proto.coin))
+		gmsg(f'Creating fork from coin {coin} to coin {proto.coin}')
 
 
 		source_rt = MMGenRegtest(coin)
 		source_rt = MMGenRegtest(coin)
 
 
-		try: os.stat(source_rt.d.datadir)
-		except: die(1,"Source directory '{}' does not exist!".format(source_rt.d.datadir))
+		try:
+			os.stat(source_rt.d.datadir)
+		except:
+			die(1,f'Source directory {source_rt.d.datadir!r} does not exist!')
 
 
 		# stop the source daemon
 		# stop the source daemon
 		if source_rt.d.state != 'stopped':
 		if source_rt.d.state != 'stopped':
@@ -211,4 +213,4 @@ class MMGenRegtest(MMGenObject):
 		await self.start_daemon(reindex=True)
 		await self.start_daemon(reindex=True)
 		await self.rpc_call('stop')
 		await self.rpc_call('stop')
 
 
-		gmsg('Fork {} successfully created'.format(proto.coin))
+		gmsg(f'Fork {proto.coin} successfully created')

+ 9 - 4
mmgen/rpc.py

@@ -51,7 +51,10 @@ rpc_credentials_msg = '\n'+fmt("""
 
 
 def dmsg_rpc(fs,data=None,is_json=False):
 def dmsg_rpc(fs,data=None,is_json=False):
 	if g.debug_rpc:
 	if g.debug_rpc:
-		msg(fs if data == None else fs.format(pp_fmt(json.loads(data) if is_json else data)))
+		msg(
+			fs if data == None else
+			fs.format(pp_fmt(json.loads(data) if is_json else data))
+		)
 
 
 class json_encoder(json.JSONEncoder):
 class json_encoder(json.JSONEncoder):
 	def default(self,obj):
 	def default(self,obj):
@@ -130,8 +133,10 @@ class RPCBackends:
 				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 })
-				fs = '    RPC AUTHORIZATION data ==> raw: [{}]\n{:>31}enc: [{}]\n'
-				dmsg_rpc(fs.format(auth_str,'',auth_str_b64))
+				dmsg_rpc('    RPC AUTHORIZATION data ==> raw: [{}]\n{:>31}enc: [{}]\n'.format(
+					auth_str,
+					'',
+					auth_str_b64 ))
 
 
 		async def run(self,payload,timeout,wallet):
 		async def run(self,payload,timeout,wallet):
 			dmsg_rpc('\n    RPC PAYLOAD data (httplib) ==>\n{}\n',payload)
 			dmsg_rpc('\n    RPC PAYLOAD data (httplib) ==>\n{}\n',payload)
@@ -247,7 +252,7 @@ class RPCClient(MMGenObject):
 
 
 	def __init__(self,host,port,test_connection=True):
 	def __init__(self,host,port,test_connection=True):
 
 
-		dmsg_rpc('=== {}.__init__() debug ==='.format(type(self).__name__))
+		dmsg_rpc(f'=== {type(self).__name__}.__init__() debug ===')
 		dmsg_rpc(f'    cls [{type(self).__name__}] host [{host}] port [{port}]\n')
 		dmsg_rpc(f'    cls [{type(self).__name__}] host [{host}] port [{port}]\n')
 
 
 		if test_connection:
 		if test_connection:

+ 31 - 23
mmgen/seed.py

@@ -34,7 +34,7 @@ class SeedBase(MMGenObject):
 			# Truncate random data for smaller seed lengths
 			# Truncate random data for smaller seed lengths
 			seed_bin = sha256(get_random(1033)).digest()[:(opt.seed_len or g.dfl_seed_len)//8]
 			seed_bin = sha256(get_random(1033)).digest()[:(opt.seed_len or g.dfl_seed_len)//8]
 		elif len(seed_bin)*8 not in g.seed_lens:
 		elif len(seed_bin)*8 not in g.seed_lens:
-			die(3,'{}: invalid seed length'.format(len(seed_bin)))
+			die(3,f'{len(seed_bin)}: invalid seed length')
 
 
 		self.data = seed_bin
 		self.data = seed_bin
 		self.sid  = SeedID(seed=self)
 		self.sid  = SeedID(seed=self)
@@ -83,14 +83,16 @@ class SubSeedList(MMGenObject):
 		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:
-			m = "{} != {}: self.data[{t!r}].key(i) does not match self.data[{t!r}][i]!"
-			die(3,m.format(idx,ss_idx.idx,t=ss_idx.type))
+			die(3, "{} != {}: self.data[{t!r}].key(i) does not match self.data[{t!r}][i]!".format(
+				idx,
+				ss_idx.idx,
+				t = ss_idx.type ))
 
 
 		if print_msg:
 		if print_msg:
-			msg('\b\b\b => {}'.format(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,'{} != {}: Seed ID mismatch!'.format(seed.sid,sid)
+		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):
@@ -130,9 +132,9 @@ class SubSeedList(MMGenObject):
 
 
 	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 = 'add_subseed(idx={},{}):'.format(idx,slen)
+		m1 = f'add_subseed(idx={idx},{slen}):'
 		if sid == self.parent_seed.sid:
 		if sid == self.parent_seed.sid:
-			m2 = 'collision with parent Seed ID {},'.format(sid)
+			m2 = f'collision with parent Seed ID {sid},'
 		else:
 		else:
 			if debug_last_share:
 			if debug_last_share:
 				sl = self.debug_last_share_sid_len
 				sl = self.debug_last_share_sid_len
@@ -140,8 +142,8 @@ class SubSeedList(MMGenObject):
 				sid = sid[:sl]
 				sid = sid[:sl]
 			else:
 			else:
 				colliding_idx = self.data[slen][sid][0]
 				colliding_idx = self.data[slen][sid][0]
-			m2 = 'collision with ID {} (idx={},{}),'.format(sid,colliding_idx,slen)
-		msg('{:30} {:46} incrementing {} to {}'.format(m1,m2,nonce_desc,nonce+1))
+			m2 = f'collision with ID {sid} (idx={colliding_idx},{slen}),'
+		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):
 
 
@@ -184,13 +186,13 @@ class SubSeedList(MMGenObject):
 		fs1 = '{:>18} {:>18}\n'
 		fs1 = '{:>18} {:>18}\n'
 		fs2 = '{i:>7}L: {:8} {i:>7}S: {:8}\n'
 		fs2 = '{i:>7}L: {:8} {i:>7}S: {:8}\n'
 
 
-		hdr = '{:>16} {} ({} bits)\n\n'.format('Parent Seed:',self.parent_seed.sid.hl(),self.parent_seed.bitlen)
+		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('Long Subseeds','Short Subseeds')
 		hdr += fs1.format('-------------','--------------')
 		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)
 
 
@@ -219,7 +221,7 @@ class Seed(SeedBase):
 
 
 		def add_share(ss):
 		def add_share(ss):
 			if d.byte_len:
 			if d.byte_len:
-				assert ss.byte_len == d.byte_len,'Seed length mismatch! {} != {}'.format(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)
@@ -277,8 +279,7 @@ class SeedShareList(SubSeedList):
 				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 g.debug_subseed:
 					if g.debug_subseed:
-						m = 'master_share seed ID collision with parent seed, incrementing nonce to {}'
-						msg(m.format(nonce+1))
+						msg(f'master_share seed ID collision with parent seed, incrementing nonce to {nonce+1}')
 				else:
 				else:
 					return ms
 					return ms
 			raise SubSeedNonceRangeExceeded('nonce range exceeded')
 			raise SubSeedNonceRangeExceeded('nonce range exceeded')
@@ -314,11 +315,11 @@ class SeedShareList(SubSeedList):
 		if g.debug_subseed:
 		if g.debug_subseed:
 			A = parent_seed.data
 			A = parent_seed.data
 			B = self.join().data
 			B = self.join().data
-			assert A == B,'Data mismatch!\noriginal seed: {!r}\nrejoined seed: {!r}'.format(A,B)
+			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:
-			raise RangeError('{}: share index out of range'.format(idx))
+			raise 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:
@@ -364,7 +365,7 @@ class SeedShareBase(MMGenObject):
 	@property
 	@property
 	def fn_stem(self):
 	def fn_stem(self):
 		pl = self.parent_list
 		pl = self.parent_list
-		msdata = '_with_master{}'.format(pl.master_share.idx) if pl.master_share else ''
+		msdata = f'_with_master{pl.master_share.idx}' if pl.master_share else ''
 		return '{}-{}-{}of{}{}[{}]'.format(
 		return '{}-{}-{}of{}{}[{}]'.format(
 			pl.parent_seed.sid,
 			pl.parent_seed.sid,
 			pl.id_str,
 			pl.id_str,
@@ -379,7 +380,7 @@ class SeedShareBase(MMGenObject):
 
 
 	def get_desc(self,ui=False):
 	def get_desc(self,ui=False):
 		pl = self.parent_list
 		pl = self.parent_list
-		mss = ', with master share #{}'.format(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()
@@ -398,10 +399,17 @@ class SeedShare(SeedShareBase,SubSeed):
 		assert parent_list.have_short == False
 		assert parent_list.have_short == 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 = '{}:{}:'.format(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')
+		scramble_key = (
+			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')
+		)
 		if parent_list.master_share:
 		if parent_list.master_share:
-			scramble_key += b':master:' + parent_list.master_share.idx.to_bytes(2,'big')
+			scramble_key += (
+				b':master:' +
+				parent_list.master_share.idx.to_bytes(2,'big')
+			)
 		return scramble_seed(seed.data,scramble_key)[:seed.byte_len]
 		return scramble_seed(seed.data,scramble_key)[:seed.byte_len]
 
 
 class SeedShareLast(SeedShareBase,SeedBase):
 class SeedShareLast(SeedShareBase,SeedBase):
@@ -443,7 +451,7 @@ class SeedShareMaster(SeedBase,SeedShareBase):
 		return '{}-MASTER{}[{}]'.format(
 		return '{}-MASTER{}[{}]'.format(
 			self.parent_list.parent_seed.sid,
 			self.parent_list.parent_seed.sid,
 			self.idx,
 			self.idx,
-			self.sid)
+			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
@@ -460,7 +468,7 @@ class SeedShareMaster(SeedBase,SeedShareBase):
 
 
 	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 = 'master share #{} of '.format(self.idx)
+		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
 
 
 class SeedShareMasterJoining(SeedShareMaster):
 class SeedShareMasterJoining(SeedShareMaster):

+ 1 - 1
mmgen/share/Opts.py

@@ -34,7 +34,7 @@ def make_usage_str(prog_name,caller,data):
 	def gen():
 	def gen():
 		ulbl = 'USAGE:'
 		ulbl = 'USAGE:'
 		for line in lines:
 		for line in lines:
-			yield '{:{w}} {} {}'.format(ulbl,prog_name,line,w=col1_w)
+			yield f'{ulbl:{col1_w}} {prog_name} {line}'
 			ulbl = ''
 			ulbl = ''
 	return ('\n'+(' '*indent)).join(gen())
 	return ('\n'+(' '*indent)).join(gen())
 
 

+ 48 - 45
mmgen/tool.py

@@ -28,7 +28,7 @@ from .addr import *
 NL = ('\n','\r\n')[g.platform=='win']
 NL = ('\n','\r\n')[g.platform=='win']
 
 
 def _options_annot_str(l):
 def _options_annot_str(l):
-	return "(valid options: '{}')".format("','".join(l))
+	return "(valid options: '{}')".format( "','".join(l) )
 
 
 def _create_argtuple(method,localvars):
 def _create_argtuple(method,localvars):
 	co = method.__code__
 	co = method.__code__
@@ -59,10 +59,11 @@ def _create_call_sig(cmd,parsed=False):
 		c_kwargs = [(a,dfls[n]) for n,a in enumerate(args[nargs:])]
 		c_kwargs = [(a,dfls[n]) for n,a in enumerate(args[nargs:])]
 		return c_args,dict(c_kwargs),'STDIN_OK' if c_args and ann[args[0]] == 'sstr' else flag
 		return c_args,dict(c_kwargs),'STDIN_OK' if c_args and ann[args[0]] == 'sstr' else flag
 	else:
 	else:
-		c_args = ['{} [{}]'.format(a,get_type_from_ann(a)) for a in args[:nargs]]
+		c_args = [f'{a} [{get_type_from_ann(a)}]' for a in args[:nargs]]
 		c_kwargs = ['"{}" [{}={!r}{}]'.format(
 		c_kwargs = ['"{}" [{}={!r}{}]'.format(
-					a, type(dfls[n]).__name__, dfls[n],
-					(' ' + ann[a] if a in ann else ''))
+					a,
+					type(dfls[n]).__name__, dfls[n],
+					(' ' + ann[a] if a in ann else '') )
 						for n,a in enumerate(args[nargs:])]
 						for n,a in enumerate(args[nargs:])]
 		return ' '.join(c_args + c_kwargs)
 		return ' '.join(c_args + c_kwargs)
 
 
@@ -100,7 +101,7 @@ def _usage(cmd=None,exit_val=1):
 			Msg('  {}{}\n'.format(cls_info[0].upper(),cls_info[1:]))
 			Msg('  {}{}\n'.format(cls_info[0].upper(),cls_info[1:]))
 			max_w = max(map(len,bc.user_commands))
 			max_w = max(map(len,bc.user_commands))
 			for cmd in sorted(bc.user_commands):
 			for cmd in sorted(bc.user_commands):
-				Msg('    {:{w}} {}'.format(cmd,_create_call_sig(cmd),w=max_w))
+				Msg(f'    {cmd:{max_w}} {_create_call_sig(cmd)}')
 			Msg('')
 			Msg('')
 		Msg(m2)
 		Msg(m2)
 	elif cmd in MMGenToolCmds:
 	elif cmd in MMGenToolCmds:
@@ -112,7 +113,7 @@ def _usage(cmd=None,exit_val=1):
 			_create_call_sig(cmd))
 			_create_call_sig(cmd))
 		)
 		)
 	else:
 	else:
-		die(1,"'{}': no such tool command".format(cmd))
+		die(1,f'{cmd!r}: no such tool command')
 
 
 	sys.exit(exit_val)
 	sys.exit(exit_val)
 
 
@@ -122,8 +123,7 @@ def _process_args(cmd,cmd_args):
 
 
 	if flag != 'VAR_ARGS':
 	if flag != 'VAR_ARGS':
 		if len(cmd_args) < len(c_args):
 		if len(cmd_args) < len(c_args):
-			m1 = 'Command requires exactly {} non-keyword argument{}'
-			msg(m1.format(len(c_args),suf(c_args)))
+			msg(f'Command requires exactly {len(c_args)} non-keyword argument{suf(c_args)}')
 			_usage(cmd)
 			_usage(cmd)
 
 
 		u_args = cmd_args[:len(c_args)]
 		u_args = cmd_args[:len(c_args)]
@@ -138,9 +138,9 @@ def _process_args(cmd,cmd_args):
 				u_args[0] = os.read(0,max_dlen)
 				u_args[0] = os.read(0,max_dlen)
 				have_stdin_input = True
 				have_stdin_input = True
 				if len(u_args[0]) >= max_dlen:
 				if len(u_args[0]) >= max_dlen:
-					die(2,'Maximum data input for this command is {}'.format(max_dlen_spec))
+					die(2,f'Maximum data input for this command is {max_dlen_spec}')
 				if not u_args[0]:
 				if not u_args[0]:
-					die(2,'{}: ERROR: no output from previous command in pipe'.format(cmd))
+					die(2,f'{cmd}: ERROR: no output from previous command in pipe')
 
 
 	u_nkwargs = len(cmd_args) - len(c_args)
 	u_nkwargs = len(cmd_args) - len(c_args)
 	u_kwargs = {}
 	u_kwargs = {}
@@ -149,21 +149,21 @@ def _process_args(cmd,cmd_args):
 		tk = [a[0] for a in t]
 		tk = [a[0] for a in t]
 		tk_bad = [a for a in tk if a not in c_kwargs]
 		tk_bad = [a for a in tk if a not in c_kwargs]
 		if set(tk_bad) != set(tk[:len(tk_bad)]): # permit non-kw args to contain '='
 		if set(tk_bad) != set(tk[:len(tk_bad)]): # permit non-kw args to contain '='
-			die(1,"'{}': illegal keyword argument".format(tk_bad[-1]))
+			die(1,f'{tk_bad[-1]!r}: illegal keyword argument')
 		u_kwargs = dict(t[len(tk_bad):])
 		u_kwargs = dict(t[len(tk_bad):])
 		u_args = cmd_args[:-len(u_kwargs) or None]
 		u_args = cmd_args[:-len(u_kwargs) or None]
 	elif u_nkwargs > 0:
 	elif u_nkwargs > 0:
 		u_kwargs = dict([a.split('=',1) for a in cmd_args[len(c_args):] if '=' in a])
 		u_kwargs = dict([a.split('=',1) for a in cmd_args[len(c_args):] if '=' in a])
 		if len(u_kwargs) != u_nkwargs:
 		if len(u_kwargs) != u_nkwargs:
-			msg('Command requires exactly {} non-keyword argument{}'.format(len(c_args),suf(c_args)))
+			msg(f'Command requires exactly {len(c_args)} non-keyword argument{suf(c_args)}')
 			_usage(cmd)
 			_usage(cmd)
 		if len(u_kwargs) > len(c_kwargs):
 		if len(u_kwargs) > len(c_kwargs):
-			msg('Command accepts no more than {} keyword argument{}'.format(len(c_kwargs),suf(c_kwargs)))
+			msg(f'Command accepts no more than {len(c_kwargs)} keyword argument{suf(c_kwargs)}')
 			_usage(cmd)
 			_usage(cmd)
 
 
 	for k in u_kwargs:
 	for k in u_kwargs:
 		if k not in c_kwargs:
 		if k not in c_kwargs:
-			msg("'{}': invalid keyword argument".format(k))
+			msg(f'{k!r}: invalid keyword argument')
 			_usage(cmd)
 			_usage(cmd)
 
 
 	def conv_type(arg,arg_name,arg_type):
 	def conv_type(arg,arg_name,arg_type):
@@ -179,13 +179,13 @@ def _process_args(cmd,cmd_args):
 			if arg.lower() in ('true','yes','1','on'): arg = True
 			if arg.lower() in ('true','yes','1','on'): arg = True
 			elif arg.lower() in ('false','no','0','off'): arg = False
 			elif arg.lower() in ('false','no','0','off'): arg = False
 			else:
 			else:
-				msg("'{}': invalid boolean value for keyword argument".format(arg))
+				msg(f'{arg!r}: invalid boolean value for keyword argument')
 				_usage(cmd)
 				_usage(cmd)
 
 
 		try:
 		try:
 			return __builtins__[arg_type](arg)
 			return __builtins__[arg_type](arg)
 		except:
 		except:
-			die(1,"'{}': Invalid argument for argument {} ('{}' required)".format(arg,arg_name,arg_type))
+			die(1,f'{arg!r}: Invalid argument for argument {arg_name} ({arg_type!r} required)')
 
 
 	if flag == 'VAR_ARGS':
 	if flag == 'VAR_ARGS':
 		args = [conv_type(u_args[i],c_args[0][0],c_args[0][1]) for i in range(len(u_args))]
 		args = [conv_type(u_args[i],c_args[0][0],c_args[0][1]) for i in range(len(u_args))]
@@ -223,7 +223,7 @@ def _process_result(ret,pager=False,print_result=False):
 			# don't add NL to binary data if it can't be converted to utf8
 			# don't add NL to binary data if it can't be converted to utf8
 			return ret if not print_result else os.write(1,ret)
 			return ret if not print_result else os.write(1,ret)
 	else:
 	else:
-		ydie(1,"tool.py: can't handle return value of type '{}'".format(type(ret).__name__))
+		ydie(1,f'tool.py: can’t handle return value of type {type(ret).__name__!r}')
 
 
 from .obj import MMGenAddrType
 from .obj import MMGenAddrType
 
 
@@ -534,9 +534,9 @@ class MMGenToolCmdCoin(MMGenToolCmds):
 
 
 	def redeem_script2addr(self,redeem_scripthex:'sstr'): # new
 	def redeem_script2addr(self,redeem_scripthex:'sstr'): # new
 		"convert a Segwit P2SH-P2WPKH redeem script to an address"
 		"convert a Segwit P2SH-P2WPKH redeem script to an address"
-		assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
-		assert redeem_scripthex[:4] == '0014','{!r}: invalid redeem script'.format(redeem_scripthex)
-		assert len(redeem_scripthex) == 44,'{} bytes: invalid redeem script length'.format(len(redeem_scripthex)//2)
+		assert self.mmtype.name == 'segwit', 'This command is meaningful only for --type=segwit'
+		assert redeem_scripthex[:4] == '0014', f'{redeem_scripthex!r}: invalid redeem script'
+		assert len(redeem_scripthex) == 44, f'{len(redeem_scripthex)//2} bytes: invalid redeem script length'
 		return self.pubhash2addr(hash160(redeem_scripthex))
 		return self.pubhash2addr(hash160(redeem_scripthex))
 
 
 	def pubhash2addr(self,pubhashhex:'sstr'):
 	def pubhash2addr(self,pubhashhex:'sstr'):
@@ -588,8 +588,9 @@ class MMGenToolCmdMnemonic(MMGenToolCmds):
 		from .protocol import init_proto
 		from .protocol import init_proto
 		proto = init_proto('xmr')
 		proto = init_proto('xmr')
 		if len(bytestr) != proto.privkey_len:
 		if len(bytestr) != proto.privkey_len:
-			m = '{!r}: invalid bit length for Monero private key (must be {})'
-			die(1,m.format(len(bytestr*8),proto.privkey_len*8))
+			die(1,'{!r}: invalid bit length for Monero private key (must be {})'.format(
+				len(bytestr*8),
+				proto.privkey_len*8 ))
 		return proto.preprocess_key(bytestr,None)
 		return proto.preprocess_key(bytestr,None)
 
 
 	def _do_random_mn(self,nbytes:int,fmt:str):
 	def _do_random_mn(self,nbytes:int,fmt:str):
@@ -598,7 +599,7 @@ class MMGenToolCmdMnemonic(MMGenToolCmds):
 		if fmt == 'xmrseed':
 		if fmt == 'xmrseed':
 			randbytes = self._xmr_reduce(randbytes)
 			randbytes = self._xmr_reduce(randbytes)
 		if opt.verbose:
 		if opt.verbose:
-			msg('Seed: {}'.format(randbytes.hex()))
+			msg(f'Seed: {randbytes.hex()}')
 		return self.hex2mn(randbytes.hex(),fmt=fmt)
 		return self.hex2mn(randbytes.hex(),fmt=fmt)
 
 
 	def mn_rand128(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
 	def mn_rand128(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
@@ -650,7 +651,7 @@ class MMGenToolCmdMnemonic(MMGenToolCmds):
 		conv_cls = mnemonic_fmts[fmt]['conv_cls']()
 		conv_cls = mnemonic_fmts[fmt]['conv_cls']()
 		ret = conv_cls.get_wordlist(fmt)
 		ret = conv_cls.get_wordlist(fmt)
 		if enum:
 		if enum:
-			ret = ['{:>4} {}'.format(n,e) for n,e in enumerate(ret)]
+			ret = [f'{n:>4} {e}' for n,e in enumerate(ret)]
 		return '\n'.join(ret)
 		return '\n'.join(ret)
 
 
 class MMGenToolCmdFile(MMGenToolCmds):
 class MMGenToolCmdFile(MMGenToolCmds):
@@ -738,7 +739,7 @@ class MMGenToolCmdFileCrypt(MMGenToolCmds):
 		data = get_data_from_file(infile,'data for encryption',binary=True)
 		data = get_data_from_file(infile,'data for encryption',binary=True)
 		enc_d = mmgen_encrypt(data,'user data',hash_preset)
 		enc_d = mmgen_encrypt(data,'user data',hash_preset)
 		if not outfile:
 		if not outfile:
-			outfile = '{}.{}'.format(os.path.basename(infile),g.mmenc_ext)
+			outfile = f'{os.path.basename(infile)}.{g.mmenc_ext}'
 		write_data_to_file(outfile,enc_d,'encrypted data',binary=True)
 		write_data_to_file(outfile,enc_d,'encrypted data',binary=True)
 		return True
 		return True
 
 
@@ -767,20 +768,22 @@ class MMGenToolCmdFileUtil(MMGenToolCmds):
 		f = os.open(filename,flgs)
 		f = os.open(filename,flgs)
 		for ch in incog_id:
 		for ch in incog_id:
 			if ch not in '0123456789ABCDEF':
 			if ch not in '0123456789ABCDEF':
-				die(2,"'{}': invalid Incog ID".format(incog_id))
+				die(2,f'{incog_id!r}: invalid Incog ID')
 		while True:
 		while True:
 			d = os.read(f,bsize)
 			d = os.read(f,bsize)
 			if not d: break
 			if not d: break
 			d = carry + d
 			d = carry + d
 			for i in range(bsize):
 			for i in range(bsize):
 				if sha256(d[i:i+ivsize]).hexdigest()[:8].upper() == incog_id:
 				if sha256(d[i:i+ivsize]).hexdigest()[:8].upper() == incog_id:
-					if n+i < ivsize: continue
-					msg('\rIncog data for ID {} found at offset {}'.format(incog_id,n+i-ivsize))
-					if not keep_searching: sys.exit(0)
+					if n+i < ivsize:
+						continue
+					msg(f'\rIncog data for ID {incog_id} found at offset {n+i-ivsize}')
+					if not keep_searching:
+						sys.exit(0)
 			carry = d[len(d)-ivsize:]
 			carry = d[len(d)-ivsize:]
 			n += bsize
 			n += bsize
 			if not n % mod:
 			if not n % mod:
-				msg_r('\rSearched: {} bytes'.format(n))
+				msg_r(f'\rSearched: {n} bytes')
 
 
 		msg('')
 		msg('')
 		os.close(f)
 		os.close(f)
@@ -826,7 +829,7 @@ class MMGenToolCmdFileUtil(MMGenToolCmds):
 		blk_size = 1024 * 1024
 		blk_size = 1024 * 1024
 		for i in range(nbytes // blk_size):
 		for i in range(nbytes // blk_size):
 			if not i % 4:
 			if not i % 4:
-				msg_r('\rRead: {} bytes'.format(i * blk_size))
+				msg_r(f'\rRead: {i * blk_size} bytes')
 			q1.put(os.urandom(blk_size))
 			q1.put(os.urandom(blk_size))
 
 
 		if nbytes % blk_size:
 		if nbytes % blk_size:
@@ -838,11 +841,11 @@ class MMGenToolCmdFileUtil(MMGenToolCmds):
 
 
 		fsize = os.stat(outfile).st_size
 		fsize = os.stat(outfile).st_size
 		if fsize != nbytes:
 		if fsize != nbytes:
-			die(3,'{}: incorrect random file size (should be {})'.format(fsize,nbytes))
+			die(3,f'{fsize}: incorrect random file size (should be {nbytes})')
 
 
 		if not silent:
 		if not silent:
-			msg('\rRead: {} bytes'.format(nbytes))
-			qmsg("\r{} byte{} of random data written to file '{}'".format(nbytes,suf(nbytes),outfile))
+			msg(f'\rRead: {nbytes} bytes')
+			qmsg(f'\r{nbytes} byte{suf(nbytes)} of random data written to file {outfile!r}')
 
 
 		return True
 		return True
 
 
@@ -872,10 +875,10 @@ class MMGenToolCmdWallet(MMGenToolCmds):
 		return Wallet(sf).seed.subseeds.format(*SubSeedIdxRange(subseed_idx_range))
 		return Wallet(sf).seed.subseeds.format(*SubSeedIdxRange(subseed_idx_range))
 
 
 	def list_shares(self,
 	def list_shares(self,
-			share_count:int,
-			id_str='default',
-			master_share:"(min:1, max:{}, 0=no master share)".format(MasterShareIdx.max_val)=0,
-			wallet=''):
+			share_count: int,
+			id_str = 'default',
+			master_share: f'(min:1, max:{MasterShareIdx.max_val}, 0=no master share)' = 0,
+			wallet = '' ):
 		"list the Seed IDs of the shares resulting from a split of default or specified wallet"
 		"list the Seed IDs of the shares resulting from a split of default or specified wallet"
 		opt.quiet = True
 		opt.quiet = True
 		sf = get_seed_file([wallet] if wallet else [],1)
 		sf = get_seed_file([wallet] if wallet else [],1)
@@ -894,8 +897,7 @@ class MMGenToolCmdWallet(MMGenToolCmds):
 		from .wallet import Wallet
 		from .wallet import Wallet
 		ss = Wallet(sf)
 		ss = Wallet(sf)
 		if ss.seed.sid != addr.sid:
 		if ss.seed.sid != addr.sid:
-			m = 'Seed ID of requested address ({}) does not match wallet ({})'
-			die(1,m.format(addr.sid,ss.seed.sid))
+			die(1,f'Seed ID of requested address ({addr.sid}) does not match wallet ({ss.seed.sid})')
 		al = AddrList(
 		al = AddrList(
 			proto     = self.proto,
 			proto     = self.proto,
 			seed      = ss.seed,
 			seed      = ss.seed,
@@ -955,14 +957,15 @@ class MMGenToolCmdRPC(MMGenToolCmds):
 			sort = set(sort.split(','))
 			sort = set(sort.split(','))
 			sort_params = {'reverse','age'}
 			sort_params = {'reverse','age'}
 			if not sort.issubset(sort_params):
 			if not sort.issubset(sort_params):
-				die(1,"The sort option takes the following parameters: '{}'".format("','".join(sort_params)))
+				die(1,"The sort option takes the following parameters: '{}'".format( "','".join(sort_params) ))
 
 
 		usr_addr_list = []
 		usr_addr_list = []
 		if mmgen_addrs:
 		if mmgen_addrs:
 			a = mmgen_addrs.rsplit(':',1)
 			a = mmgen_addrs.rsplit(':',1)
 			if len(a) != 2:
 			if len(a) != 2:
-				m = "'{}': invalid address list argument (must be in form <seed ID>:[<type>:]<idx list>)"
-				die(1,m.format(mmgen_addrs))
+				die(1,
+					f'{mmgen_addrs}: invalid address list argument ' +
+					'(must be in form <seed ID>:[<type>:]<idx list>)' )
 			usr_addr_list = [MMGenID(self.proto,f'{a[0]}:{i}') for i in AddrIdxList(a[1])]
 			usr_addr_list = [MMGenID(self.proto,f'{a[0]}:{i}') for i in AddrIdxList(a[1])]
 
 
 		al = await TwAddrList(self.proto,usr_addr_list,minconf,showempty,showbtcaddrs,all_labels)
 		al = await TwAddrList(self.proto,usr_addr_list,minconf,showempty,showbtcaddrs,all_labels)
@@ -1007,7 +1010,7 @@ class MMGenToolCmdRPC(MMGenToolCmds):
 		from .tw import TrackingWallet
 		from .tw import TrackingWallet
 		ret = await (await TrackingWallet(self.proto,mode='w')).remove_address(mmgen_or_coin_addr) # returns None on failure
 		ret = await (await TrackingWallet(self.proto,mode='w')).remove_address(mmgen_or_coin_addr) # returns None on failure
 		if ret:
 		if ret:
-			msg("Address '{}' deleted from tracking wallet".format(ret))
+			msg(f'Address {ret!r} deleted from tracking wallet')
 		return ret
 		return ret
 
 
 class tool_api(
 class tool_api(
@@ -1101,7 +1104,7 @@ class tool_api(
 		a description.  The first-listed is the default
 		a description.  The first-listed is the default
 		"""
 		"""
 		for t in [MMGenAddrType(proto=self.proto,id_str=id_str) for id_str in self.proto.mmtypes]:
 		for t in [MMGenAddrType(proto=self.proto,id_str=id_str) for id_str in self.proto.mmtypes]:
-			print('{:<12} - {}'.format(t.name,t.desc))
+			print(f'{t.name:<12} - {t.desc}')
 
 
 	@property
 	@property
 	def addrtype(self):
 	def addrtype(self):

+ 18 - 11
mmgen/tw.py

@@ -29,7 +29,9 @@ from .tx import is_mmgen_id,is_coin_addr
 from .rpc import rpc_init
 from .rpc import rpc_init
 
 
 CUR_HOME,ERASE_ALL = '\033[H','\033[0J'
 CUR_HOME,ERASE_ALL = '\033[H','\033[0J'
-def CUR_RIGHT(n): return '\033[{}C'.format(n)
+
+def CUR_RIGHT(n):
+	return f'\033[{n}C'
 
 
 def get_tw_label(proto,s):
 def get_tw_label(proto,s):
 	"""
 	"""
@@ -213,7 +215,7 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
 			'addr':  lambda i: i.addr,
 			'addr':  lambda i: i.addr,
 			'age':   lambda i: 0 - i.confs,
 			'age':   lambda i: 0 - i.confs,
 			'amt':   lambda i: i.amt,
 			'amt':   lambda i: i.amt,
-			'txid':  lambda i: '{} {:04}'.format(i.txid,i.vout),
+			'txid':  lambda i: f'{i.txid} {i.vout:04}',
 			'twmmid':  lambda i: i.twmmid.sort_key
 			'twmmid':  lambda i: i.twmmid.sort_key
 		}
 		}
 		key = key or self.sort_key
 		key = key or self.sort_key
@@ -296,16 +298,21 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
 				n  = 'Num',
 				n  = 'Num',
 				t  = 'TXid'.ljust(c.tx_w - 2) + ' Vout',
 				t  = 'TXid'.ljust(c.tx_w - 2) + ' Vout',
 				a  = 'Address'.ljust(c.addr_w),
 				a  = 'Address'.ljust(c.addr_w),
-				A  = 'Amt({})'.format(self.proto.dcoin).ljust(self.disp_prec+5),
-				A2 = ' Amt({})'.format(self.proto.coin).ljust(self.disp_prec+4),
+				A  = f'Amt({self.proto.dcoin})'.ljust(self.disp_prec+5),
+				A2 = f' Amt({self.proto.coin})'.ljust(self.disp_prec+4),
 				c  =  date_hdr[self.age_fmt],
 				c  =  date_hdr[self.age_fmt],
 				).rstrip()
 				).rstrip()
 
 
 			for n,i in enumerate(unsp):
 			for n,i in enumerate(unsp):
 				addr_dots = '|' + '.'*(c.addr_w-1)
 				addr_dots = '|' + '.'*(c.addr_w-1)
-				mmid_disp = MMGenID.fmtc('.'*c.mmid_w if i.skip=='addr'
-					else i.twmmid if i.twmmid.type=='mmgen'
-						else 'Non-{}'.format(g.proj_name),width=c.mmid_w,color=True)
+				mmid_disp = MMGenID.fmtc(
+					(
+						'.'*c.mmid_w if i.skip == 'addr' else
+						i.twmmid if i.twmmid.type == 'mmgen' else
+						f'Non-{g.proj_name}'
+					),
+					width = c.mmid_w,
+					color = True )
 
 
 				if self.show_mmid:
 				if self.show_mmid:
 					addr_out = '{} {}{}'.format((
 					addr_out = '{} {}{}'.format((
@@ -354,8 +361,8 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
 				t  = 'Tx ID,Vout',
 				t  = 'Tx ID,Vout',
 				a  = 'Address'.ljust(addr_w),
 				a  = 'Address'.ljust(addr_w),
 				m  = 'MMGen ID'.ljust(mmid_w),
 				m  = 'MMGen ID'.ljust(mmid_w),
-				A  = 'Amount({})'.format(self.proto.dcoin),
-				A2 = 'Amount({})'.format(self.proto.coin),
+				A  = f'Amount({self.proto.dcoin})',
+				A2 = f'Amount({self.proto.coin})',
 				c  = 'Confs',  # skipped for eth
 				c  = 'Confs',  # skipped for eth
 				b  = 'Block',  # skipped for eth
 				b  = 'Block',  # skipped for eth
 				D  = 'Date',
 				D  = 'Date',
@@ -713,7 +720,7 @@ class TrackingWallet(MMGenObject,metaclass=AsyncInit):
 
 
 	async def __init__(self,proto,mode='r',token_addr=None):
 	async def __init__(self,proto,mode='r',token_addr=None):
 
 
-		assert mode in ('r','w','i'), "{!r}: wallet mode must be 'r','w' or 'i'".format(mode)
+		assert mode in ('r','w','i'), f"{mode!r}: wallet mode must be 'r','w' or 'i'"
 		if mode == 'i':
 		if mode == 'i':
 			self.importing = True
 			self.importing = True
 			mode = 'w'
 			mode = 'w'
@@ -892,7 +899,7 @@ class TrackingWallet(MMGenObject,metaclass=AsyncInit):
 
 
 		if self.orig_data != wdata:
 		if self.orig_data != wdata:
 			if g.debug:
 			if g.debug:
-				print_stack_trace('TW DATA CHANGED {!r}'.format(self))
+				print_stack_trace(f'TW DATA CHANGED {self!r}')
 				print_diff(self.orig_data,wdata,from_json=True)
 				print_diff(self.orig_data,wdata,from_json=True)
 			self.write_changed(wdata)
 			self.write_changed(wdata)
 		elif g.debug:
 		elif g.debug:

+ 8 - 7
mmgen/tx.py

@@ -60,7 +60,7 @@ def strfmt_locktime(num,terse=False):
 	elif num > 0:
 	elif num > 0:
 		return '{}{}'.format(('block height ','')[terse],num)
 		return '{}{}'.format(('block height ','')[terse],num)
 	else:
 	else:
-		die(2,"'{}': invalid nLockTime value!".format(num))
+		die(2,f'{num!r}: invalid nLockTime value!')
 
 
 def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
 def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
 
 
@@ -83,7 +83,7 @@ def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
 
 
 def addr2pubhash(proto,addr):
 def addr2pubhash(proto,addr):
 	ap = proto.parse_addr(addr)
 	ap = proto.parse_addr(addr)
-	assert ap,'coin address {!r} could not be parsed'.format(addr)
+	assert ap,f'coin address {addr!r} could not be parsed'
 	return ap.bytes.hex()
 	return ap.bytes.hex()
 
 
 def addr2scriptPubKey(proto,addr):
 def addr2scriptPubKey(proto,addr):
@@ -101,7 +101,7 @@ def scriptPubKey2addr(proto,s):
 	elif len(s) == 44 and s[:4] == proto.witness_vernum_hex + '14':
 	elif len(s) == 44 and s[:4] == proto.witness_vernum_hex + '14':
 		return proto.pubhash2bech32addr(s[4:]),'bech32'
 		return proto.pubhash2bech32addr(s[4:]),'bech32'
 	else:
 	else:
-		raise NotImplementedError('Unknown scriptPubKey ({})'.format(s))
+		raise NotImplementedError(f'Unknown scriptPubKey ({s})')
 
 
 class DeserializedTX(dict,MMGenObject):
 class DeserializedTX(dict,MMGenObject):
 	"""
 	"""
@@ -159,7 +159,7 @@ class DeserializedTX(dict,MMGenObject):
 		if has_witness:
 		if has_witness:
 			u = bshift(2,skip=True).hex()
 			u = bshift(2,skip=True).hex()
 			if u != '0001':
 			if u != '0001':
-				raise IllegalWitnessFlagValue("'{}': Illegal value for flag in transaction!".format(u))
+				raise IllegalWitnessFlagValue(f'{u!r}: Illegal value for flag in transaction!')
 
 
 		d['num_txins'] = readVInt()
 		d['num_txins'] = readVInt()
 
 
@@ -1021,8 +1021,9 @@ class MMGenTX:
 		def format_view_body(self,blockcount,nonmm_str,max_mmwid,enl,terse,sort):
 		def format_view_body(self,blockcount,nonmm_str,max_mmwid,enl,terse,sort):
 
 
 			if sort not in self.view_sort_orders:
 			if sort not in self.view_sort_orders:
-				die(1,f'{sort!r}: invalid transaction view sort order. Valid options: {{}}'.format(
-						','.join(self.view_sort_orders) ))
+				die(1,'{!r}: invalid transaction view sort order. Valid options: {}'.format(
+					sort,
+					','.join(self.view_sort_orders) ))
 
 
 			def format_io(desc):
 			def format_io(desc):
 				io = getattr(self,desc)
 				io = getattr(self,desc)
@@ -1323,7 +1324,7 @@ class MMGenTX:
 
 
 			uh = dtx['unsigned_hex']
 			uh = dtx['unsigned_hex']
 			if str(self.txid) != make_chksum_6(bytes.fromhex(uh)).upper():
 			if str(self.txid) != make_chksum_6(bytes.fromhex(uh)).upper():
-				raise TxHexMismatch('MMGen TxID ({}) does not match hex transaction data!\n{}'.format(self.txid,m))
+				raise TxHexMismatch(f'MMGen TxID ({self.txid}) does not match hex transaction data!\n{m}')
 
 
 		def compare_size_and_estimated_size(self,tx_decoded):
 		def compare_size_and_estimated_size(self,tx_decoded):
 			est_vsize = self.estimate_size()
 			est_vsize = self.estimate_size()

+ 3 - 3
mmgen/txfile.py

@@ -45,9 +45,9 @@ class MMGenTxFile:
 					ymsg('Warning: transaction data appears to be in old format')
 					ymsg('Warning: transaction data appears to be in old format')
 				import re
 				import re
 				d = literal_eval(re.sub(r"[A-Za-z]+?\(('.+?')\)",r'\1',raw_data))
 				d = literal_eval(re.sub(r"[A-Za-z]+?\(('.+?')\)",r'\1',raw_data))
-			assert type(d) == list,'{} data not a list!'.format(desc)
+			assert type(d) == list, f'{desc} data not a list!'
 			if not (desc == 'outputs' and tx.proto.base_coin == 'ETH'): # ETH txs can have no outputs
 			if not (desc == 'outputs' and tx.proto.base_coin == 'ETH'): # ETH txs can have no outputs
-				assert len(d),'no {}!'.format(desc)
+				assert len(d), f'no {desc}!'
 			for e in d:
 			for e in d:
 				e['amt'] = tx.proto.coin_amt(e['amt'])
 				e['amt'] = tx.proto.coin_amt(e['amt'])
 			io,io_list = (
 			io,io_list = (
@@ -167,7 +167,7 @@ class MMGenTxFile:
 				tx.send_amt,
 				tx.send_amt,
 				tx.timestamp,
 				tx.timestamp,
 				tx.blockcount,
 				tx.blockcount,
-				('',' LT={}'.format(tx.locktime))[bool(tx.locktime)]
+				(f' LT={tx.locktime}' if tx.locktime else ''),
 			),
 			),
 			tx.hex,
 			tx.hex,
 			ascii([amt_to_str(e._asdict()) for e in tx.inputs]),
 			ascii([amt_to_str(e._asdict()) for e in tx.inputs]),

+ 6 - 5
mmgen/txsign.py

@@ -88,7 +88,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
 	for e in need_keys:
 	for e in need_keys:
 		for kal in d:
 		for kal in d:
 			for f in kal.data:
 			for f in kal.data:
-				mmid = '{}:{}'.format(kal.al_id,f.idx)
+				mmid = f'{kal.al_id}:{f.idx}'
 				if mmid == e.mmid:
 				if mmid == e.mmid:
 					if f.addr == e.addr:
 					if f.addr == e.addr:
 						e.have_wif = True
 						e.have_wif = True
@@ -151,10 +151,11 @@ async def txsign(tx,seed_files,kl,kal,tx_num_str=''):
 		tmp.add_wifs(kl)
 		tmp.add_wifs(kl)
 		m = tmp.list_missing('sec')
 		m = tmp.list_missing('sec')
 		if m:
 		if m:
-			die(2, fmt(f"""
-				ERROR: a key file must be supplied for the following non-{g.proj_name} address{suf(m,'es')}:
-				    {{}}
-				""".format('\n    '.join(m)),strip_char='\t').strip())
+			die(2, fmt("""
+					ERROR: a key file must be supplied for the following non-{} address{}:
+						{{}}
+					""".format( g.proj_name, suf(m,'es'), '\n    '.join(m) ),
+				strip_char='\t').strip() )
 		keys += tmp.data
 		keys += tmp.data
 
 
 	if opt.mmgen_keys_from_file:
 	if opt.mmgen_keys_from_file:

+ 3 - 3
mmgen/util.py

@@ -230,7 +230,7 @@ def parse_bytespec(nbytes):
 		else:
 		else:
 			return int(nbytes)
 			return int(nbytes)
 
 
-	die(1,"'{}': invalid byte specifier".format(nbytes))
+	die(1,f'{nbytes!r}: invalid byte specifier')
 
 
 def check_or_create_dir(path):
 def check_or_create_dir(path):
 	try:
 	try:
@@ -290,7 +290,7 @@ def suf(arg,suf_type='s',verb='none'):
 	elif isinstance(arg,(list,tuple,set,dict)):
 	elif isinstance(arg,(list,tuple,set,dict)):
 		n = len(arg)
 		n = len(arg)
 	else:
 	else:
-		die(2,'{}: invalid parameter for suf()'.format(arg))
+		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):
@@ -795,7 +795,7 @@ def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
 def keypress_confirm(prompt,default_yes=False,verbose=False,no_nl=False,complete_prompt=False):
 def keypress_confirm(prompt,default_yes=False,verbose=False,no_nl=False,complete_prompt=False):
 
 
 	q = ('(y/N)','(Y/n)')[bool(default_yes)]
 	q = ('(y/N)','(Y/n)')[bool(default_yes)]
-	p = prompt if complete_prompt else '{} {}: '.format(prompt,q)
+	p = prompt if complete_prompt else f'{prompt} {q}: '
 	nl = ('\n','\r{}\r'.format(' '*len(p)))[no_nl]
 	nl = ('\n','\r{}\r'.format(' '*len(p)))[no_nl]
 
 
 	if g.accept_defaults:
 	if g.accept_defaults:

+ 87 - 64
mmgen/wallet.py

@@ -30,7 +30,7 @@ from .seed import Seed
 
 
 def check_usr_seed_len(seed_len):
 def check_usr_seed_len(seed_len):
 	if opt.seed_len and opt.seed_len != seed_len:
 	if opt.seed_len and opt.seed_len != seed_len:
-		die(1,f"ERROR: requested seed length ({opt.seed_len}) doesn't match seed length of source ({seed_len})")
+		die(1,f'ERROR: requested seed length ({opt.seed_len}) doesn’t match seed length of source ({seed_len})')
 
 
 def _is_mnemonic(s,fmt):
 def _is_mnemonic(s,fmt):
 	oq_save = bool(opt.quiet)
 	oq_save = bool(opt.quiet)
@@ -80,7 +80,7 @@ class Wallet(MMGenObject,metaclass=WalletMeta):
 		if hasattr(opt,'out_fmt') and opt.out_fmt:
 		if hasattr(opt,'out_fmt') and opt.out_fmt:
 			out_cls = cls.fmt_code_to_type(opt.out_fmt)
 			out_cls = cls.fmt_code_to_type(opt.out_fmt)
 			if not out_cls:
 			if not out_cls:
-				die(1,'{!r}: unrecognized output format'.format(opt.out_fmt))
+				die(1,f'{opt.out_fmt!r}: unrecognized output format')
 		else:
 		else:
 			out_cls = None
 			out_cls = None
 
 
@@ -148,12 +148,15 @@ class Wallet(MMGenObject,metaclass=WalletMeta):
 			self._decrypt_retry()
 			self._decrypt_retry()
 		else:
 		else:
 			if not self.stdin_ok:
 			if not self.stdin_ok:
-				die(1,'Reading from standard input not supported for {} format'.format(self.desc))
+				die(1,f'Reading from standard input not supported for {self.desc} format')
 			self._deformat_retry()
 			self._deformat_retry()
 			self._decrypt_retry()
 			self._decrypt_retry()
 
 
-		m = ('',', seed length {}'.format(self.seed.bitlen))[self.seed.bitlen!=256]
-		qmsg('Valid {} for Seed ID {}{}'.format(self.desc,self.seed.sid.hl(),m))
+		qmsg('Valid {} for Seed ID {}{}'.format(
+			self.desc,
+			self.seed.sid.hl(),
+			(f', seed length {self.seed.bitlen}' if self.seed.bitlen != 256 else '')
+		))
 
 
 	def _get_data(self):
 	def _get_data(self):
 		if hasattr(self,'infile'):
 		if hasattr(self,'infile'):
@@ -212,7 +215,7 @@ class Wallet(MMGenObject,metaclass=WalletMeta):
 					for c in cls.wallet_classes
 					for c in cls.wallet_classes
 				if hasattr(c,'fmt_codes')]
 				if hasattr(c,'fmt_codes')]
 		w = max(len(i[0]) for i in d)
 		w = max(len(i[0]) for i in d)
-		ret = ['{:<{w}}  {:<9} {}'.format(a,b,c,w=w) for a,b,c in [
+		ret = [f'{a:<{w}}  {b:<9} {c}' for a,b,c in [
 			('Format','FileExt','Valid codes'),
 			('Format','FileExt','Valid codes'),
 			('------','-------','-----------')
 			('------','-------','-----------')
 			] + sorted(d)]
 			] + sorted(d)]
@@ -260,9 +263,10 @@ class WalletUnenc(Wallet):
 			msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r')
 			msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r')
 			return ok_lens[int(r)-1]
 			return ok_lens[int(r)-1]
 
 
-		m1 = blue('{} type:'.format(capfirst(desc)))
-		m2 = yellow(subtype)
-		msg('{} {}'.format(m1,m2))
+		msg('{} {}'.format(
+			blue(f'{capfirst(desc)} type:'),
+			yellow(subtype)
+		))
 
 
 		while True:
 		while True:
 			usr_len = choose_len()
 			usr_len = choose_len()
@@ -311,7 +315,7 @@ class WalletEnc(Wallet):
 			else: # Prompt, using old value as default
 			else: # Prompt, using old value as default
 				hp = self._get_hash_preset_from_user(old_hp,add_desc)
 				hp = self._get_hash_preset_from_user(old_hp,add_desc)
 			if (not opt.keep_hash_preset) and self.op == 'pwchg_new':
 			if (not opt.keep_hash_preset) and self.op == 'pwchg_new':
-				qmsg('Hash preset {}'.format('unchanged' if hp==old_hp else f'changed to {hp!r}'))
+				qmsg('Hash preset {}'.format( 'unchanged' if hp == old_hp else f'changed to {hp!r}' ))
 		elif opt.hash_preset:
 		elif opt.hash_preset:
 			hp = opt.hash_preset
 			hp = opt.hash_preset
 			qmsg(f'Using hash preset {hp!r} requested on command line')
 			qmsg(f'Using hash preset {hp!r} requested on command line')
@@ -379,7 +383,7 @@ class WalletEnc(Wallet):
 			else:
 			else:
 				pw = self._get_new_passphrase()
 				pw = self._get_new_passphrase()
 				if self.op == 'pwchg_new':
 				if self.op == 'pwchg_new':
-					qmsg('Passphrase {}'.format('unchanged' if pw==old_pw else 'changed'))
+					qmsg('Passphrase {}'.format( 'unchanged' if pw == old_pw else 'changed' ))
 		else:
 		else:
 			self._get_new_passphrase()
 			self._get_new_passphrase()
 
 
@@ -434,13 +438,14 @@ class Mnemonic(WalletUnenc):
 		mn = self.fmt_data.split()
 		mn = self.fmt_data.split()
 
 
 		if len(mn) not in self.mn_lens:
 		if len(mn) not in self.mn_lens:
-			m = 'Invalid mnemonic ({} words).  Valid numbers of words: {}'
-			msg(m.format(len(mn),', '.join(map(str,self.mn_lens))))
+			msg('Invalid mnemonic ({} words).  Valid numbers of words: {}'.format(
+				len(mn),
+				', '.join(map(str,self.mn_lens)) ))
 			return False
 			return False
 
 
 		for n,w in enumerate(mn,1):
 		for n,w in enumerate(mn,1):
 			if w not in self.conv_cls.digits[self.wl_id]:
 			if w not in self.conv_cls.digits[self.wl_id]:
-				msg('Invalid mnemonic: word #{} is not in the {} wordlist'.format(n,self.wl_id.upper()))
+				msg(f'Invalid mnemonic: word #{n} is not in the {self.wl_id.upper()} wordlist')
 				return False
 				return False
 
 
 		hexseed = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
 		hexseed = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
@@ -492,27 +497,29 @@ class MMGenSeedFile(WalletUnenc):
 		b58seed = baseconv.frombytes(self.seed.data,'b58',pad='seed',tostr=True)
 		b58seed = baseconv.frombytes(self.seed.data,'b58',pad='seed',tostr=True)
 		self.ssdata.chksum = make_chksum_6(b58seed)
 		self.ssdata.chksum = make_chksum_6(b58seed)
 		self.ssdata.b58seed = b58seed
 		self.ssdata.b58seed = b58seed
-		self.fmt_data = '{} {}\n'.format(self.ssdata.chksum,split_into_cols(4,b58seed))
+		self.fmt_data = '{} {}\n'.format(
+			self.ssdata.chksum,
+			split_into_cols(4,b58seed) )
 
 
 	def _deformat(self):
 	def _deformat(self):
 		desc = self.desc
 		desc = self.desc
 		ld = self.fmt_data.split()
 		ld = self.fmt_data.split()
 
 
 		if not (7 <= len(ld) <= 12): # 6 <= padded b58 data (ld[1:]) <= 11
 		if not (7 <= len(ld) <= 12): # 6 <= padded b58 data (ld[1:]) <= 11
-			msg('Invalid data length ({}) in {}'.format(len(ld),desc))
+			msg(f'Invalid data length ({len(ld)}) in {desc}')
 			return False
 			return False
 
 
 		a,b = ld[0],''.join(ld[1:])
 		a,b = ld[0],''.join(ld[1:])
 
 
 		if not is_chksum_6(a):
 		if not is_chksum_6(a):
-			msg("'{}': invalid checksum format in {}".format(a, desc))
+			msg(f'{a!r}: invalid checksum format in {desc}')
 			return False
 			return False
 
 
 		if not is_b58_str(b):
 		if not is_b58_str(b):
-			msg("'{}': not a base 58 string, in {}".format(b, desc))
+			msg(f'{b!r}: not a base 58 string, in {desc}')
 			return False
 			return False
 
 
-		vmsg_r('Validating {} checksum...'.format(desc))
+		vmsg_r(f'Validating {desc} checksum...')
 
 
 		if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
 		if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
 			return False
 			return False
@@ -520,7 +527,7 @@ class MMGenSeedFile(WalletUnenc):
 		ret = baseconv.tobytes(b,'b58',pad='seed')
 		ret = baseconv.tobytes(b,'b58',pad='seed')
 
 
 		if ret == False:
 		if ret == False:
-			msg('Invalid base-58 encoded seed: {}'.format(val))
+			msg(f'Invalid base-58 encoded seed: {val}')
 			return False
 			return False
 
 
 		self.seed = Seed(ret)
 		self.seed = Seed(ret)
@@ -556,8 +563,10 @@ class DieRollSeedFile(WalletUnenc):
 
 
 		rmap = self.conv_cls.seedlen_map_rev['b6d']
 		rmap = self.conv_cls.seedlen_map_rev['b6d']
 		if not len(d) in rmap:
 		if not len(d) in rmap:
-			m = '{!r}: invalid length for {} (must be one of {})'
-			raise SeedLengthError(m.format(len(d),self.desc,list(rmap)))
+			raise SeedLengthError('{!r}: invalid length for {} (must be one of {})'.format(
+				len(d),
+				self.desc,
+				list(rmap) ))
 
 
 		# truncate seed to correct length, discarding high bits
 		# truncate seed to correct length, discarding high bits
 		seed_len = rmap[len(d)]
 		seed_len = rmap[len(d)]
@@ -595,7 +604,7 @@ class DieRollSeedFile(WalletUnenc):
 		b6d_digits = self.conv_cls.digits['b6d']
 		b6d_digits = self.conv_cls.digits['b6d']
 
 
 		cr = '\n' if g.test_suite else '\r'
 		cr = '\n' if g.test_suite else '\r'
-		prompt_fs = '\b\b\b   {}Enter die roll #{{}}: {}'.format(cr,CUR_SHOW)
+		prompt_fs = f'\b\b\b   {cr}Enter die roll #{{}}: {CUR_SHOW}'
 		clear_line = '' if g.test_suite else '\r' + ' ' * 25
 		clear_line = '' if g.test_suite else '\r' + ' ' * 25
 		invalid_msg = CUR_HIDE + cr + 'Invalid entry' + ' ' * 11
 		invalid_msg = CUR_HIDE + cr + 'Invalid entry' + ' ' * 11
 
 
@@ -638,11 +647,11 @@ class PlainHexSeedFile(WalletUnenc):
 		d = self.fmt_data.strip()
 		d = self.fmt_data.strip()
 
 
 		if not is_hex_str_lc(d):
 		if not is_hex_str_lc(d):
-			msg("'{}': not a lowercase hexadecimal string, in {}".format(d,desc))
+			msg(f'{d!r}: not a lowercase hexadecimal string, in {desc}')
 			return False
 			return False
 
 
 		if not len(d)*4 in g.seed_lens:
 		if not len(d)*4 in g.seed_lens:
-			msg('Invalid data length ({}) in {}'.format(len(d),desc))
+			msg(f'Invalid data length ({len(d)}) in {desc}')
 			return False
 			return False
 
 
 		self.seed = Seed(bytes.fromhex(d))
 		self.seed = Seed(bytes.fromhex(d))
@@ -663,7 +672,9 @@ class MMGenHexSeedFile(WalletUnenc):
 		h = self.seed.hexdata
 		h = self.seed.hexdata
 		self.ssdata.chksum = make_chksum_6(h)
 		self.ssdata.chksum = make_chksum_6(h)
 		self.ssdata.hexseed = h
 		self.ssdata.hexseed = h
-		self.fmt_data = '{} {}\n'.format(self.ssdata.chksum, split_into_cols(4,h))
+		self.fmt_data = '{} {}\n'.format(
+			self.ssdata.chksum,
+			split_into_cols(4,h) )
 
 
 	def _deformat(self):
 	def _deformat(self):
 		desc = self.desc
 		desc = self.desc
@@ -672,22 +683,22 @@ class MMGenHexSeedFile(WalletUnenc):
 			d[1]
 			d[1]
 			chk,hstr = d[0],''.join(d[1:])
 			chk,hstr = d[0],''.join(d[1:])
 		except:
 		except:
-			msg("'{}': invalid {}".format(self.fmt_data.strip(),desc))
+			msg(f'{self.fmt_data.strip()!r}: invalid {desc}')
 			return False
 			return False
 
 
 		if not len(hstr)*4 in g.seed_lens:
 		if not len(hstr)*4 in g.seed_lens:
-			msg('Invalid data length ({}) in {}'.format(len(hstr),desc))
+			msg(f'Invalid data length ({len(hstr)}) in {desc}')
 			return False
 			return False
 
 
 		if not is_chksum_6(chk):
 		if not is_chksum_6(chk):
-			msg("'{}': invalid checksum format in {}".format(chk, desc))
+			msg(f'{chk!r}: invalid checksum format in {desc}')
 			return False
 			return False
 
 
 		if not is_hex_str(hstr):
 		if not is_hex_str(hstr):
-			msg("'{}': not a hexadecimal string, in {}".format(hstr, desc))
+			msg(f'{hstr!r}: not a hexadecimal string, in {desc}')
 			return False
 			return False
 
 
-		vmsg_r('Validating {} checksum...'.format(desc))
+		vmsg_r(f'Validating {desc} checksum...')
 
 
 		if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True):
 		if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True):
 			return False
 			return False
@@ -738,17 +749,17 @@ class MMGenWallet(WalletEnc):
 			old_lbl = self.ss_in.ssdata.label
 			old_lbl = self.ss_in.ssdata.label
 			if opt.keep_label:
 			if opt.keep_label:
 				lbl = old_lbl
 				lbl = old_lbl
-				qmsg('Reusing label {} at user request'.format(lbl.hl(encl="''")))
+				qmsg('Reusing label {} at user request'.format( lbl.hl(encl="''") ))
 			elif self.label:
 			elif self.label:
 				lbl = self.label
 				lbl = self.label
-				qmsg('Using label {} requested on command line'.format(lbl.hl(encl="''")))
+				qmsg('Using label {} requested on command line'.format( lbl.hl(encl="''") ))
 			else: # Prompt, using old value as default
 			else: # Prompt, using old value as default
 				lbl = self._get_label_from_user(old_lbl)
 				lbl = self._get_label_from_user(old_lbl)
 			if (not opt.keep_label) and self.op == 'pwchg_new':
 			if (not opt.keep_label) and self.op == 'pwchg_new':
-				qmsg('Label {}'.format('unchanged' if lbl==old_lbl else f'changed to {lbl!r}'))
+				qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' ))
 		elif self.label:
 		elif self.label:
 			lbl = self.label
 			lbl = self.label
-			qmsg('Using label {} requested on command line'.format(lbl.hl(encl="''")))
+			qmsg('Using label {} requested on command line'.format( lbl.hl(encl="''") ))
 		else:
 		else:
 			lbl = self._get_label_from_user()
 			lbl = self._get_label_from_user()
 		self.ssdata.label = lbl
 		self.ssdata.label = lbl
@@ -767,11 +778,10 @@ class MMGenWallet(WalletEnc):
 		es_fmt = baseconv.frombytes(d.enc_seed,'b58',pad='seed',tostr=True)
 		es_fmt = baseconv.frombytes(d.enc_seed,'b58',pad='seed',tostr=True)
 		lines = (
 		lines = (
 			d.label,
 			d.label,
-			'{} {} {} {} {}'.format(s.sid.lower(), d.key_id.lower(),
-										s.bitlen, d.pw_status, d.timestamp),
-			'{}: {} {} {}'.format(d.hash_preset,*get_hash_params(d.hash_preset)),
-			'{} {}'.format(make_chksum_6(slt_fmt),split_into_cols(4,slt_fmt)),
-			'{} {}'.format(make_chksum_6(es_fmt), split_into_cols(4,es_fmt))
+			'{} {} {} {} {}'.format( s.sid.lower(), d.key_id.lower(), s.bitlen, d.pw_status, d.timestamp ),
+			'{}: {} {} {}'.format( d.hash_preset, *get_hash_params(d.hash_preset) ),
+			'{} {}'.format( make_chksum_6(slt_fmt), split_into_cols(4,slt_fmt) ),
+			'{} {}'.format( make_chksum_6(es_fmt),  split_into_cols(4,es_fmt) )
 		)
 		)
 		chksum = make_chksum_6(' '.join(lines).encode())
 		chksum = make_chksum_6(' '.join(lines).encode())
 		self.fmt_data = '\n'.join((chksum,)+lines) + '\n'
 		self.fmt_data = '\n'.join((chksum,)+lines) + '\n'
@@ -781,11 +791,11 @@ class MMGenWallet(WalletEnc):
 		def check_master_chksum(lines,desc):
 		def check_master_chksum(lines,desc):
 
 
 			if len(lines) != 6:
 			if len(lines) != 6:
-				msg('Invalid number of lines ({}) in {} data'.format(len(lines),desc))
+				msg(f'Invalid number of lines ({len(lines)}) in {desc} data')
 				return False
 				return False
 
 
 			if not is_chksum_6(lines[0]):
 			if not is_chksum_6(lines[0]):
-				msg('Incorrect master checksum ({}) in {} data'.format(lines[0],desc))
+				msg(f'Incorrect master checksum ({lines[0]}) in {desc} data')
 				return False
 				return False
 
 
 			chk = make_chksum_6(' '.join(lines[1:]))
 			chk = make_chksum_6(' '.join(lines[1:]))
@@ -811,14 +821,14 @@ class MMGenWallet(WalletEnc):
 		hpdata = lines[3].split()
 		hpdata = lines[3].split()
 
 
 		d.hash_preset = hp = hpdata[0][:-1]  # a string!
 		d.hash_preset = hp = hpdata[0][:-1]  # a string!
-		qmsg("Hash preset of wallet: '{}'".format(hp))
+		qmsg(f'Hash preset of wallet: {hp!r}')
 		if opt.hash_preset and opt.hash_preset != hp:
 		if opt.hash_preset and opt.hash_preset != hp:
 			qmsg('Warning: ignoring user-requested hash preset {opt.hash_preset}')
 			qmsg('Warning: ignoring user-requested hash preset {opt.hash_preset}')
 
 
 		hash_params = tuple(map(int,hpdata[1:]))
 		hash_params = tuple(map(int,hpdata[1:]))
 
 
 		if hash_params != get_hash_params(d.hash_preset):
 		if hash_params != get_hash_params(d.hash_preset):
-			msg(f"Hash parameters {' '.join(hash_params)!r} don't match hash preset {d.hash_preset!r}")
+			msg(f'Hash parameters {" ".join(hash_params)!r} don’t match hash preset {d.hash_preset!r}')
 			return False
 			return False
 
 
 		lmin,foo,lmax = sorted(baseconv.seedlen_map_rev['b58']) # 22,33,44
 		lmin,foo,lmax = sorted(baseconv.seedlen_map_rev['b58']) # 22,33,44
@@ -828,7 +838,7 @@ class MMGenWallet(WalletEnc):
 			b58_val = ''.join(l)
 			b58_val = ''.join(l)
 
 
 			if len(b58_val) < lmin or len(b58_val) > lmax:
 			if len(b58_val) < lmin or len(b58_val) > lmax:
-				msg('Invalid format for {} in {}: {}'.format(key,self.desc,l))
+				msg(f'Invalid format for {key} in {self.desc}: {l}')
 				return False
 				return False
 
 
 			if not compare_chksums(chk,key,
 			if not compare_chksums(chk,key,
@@ -837,7 +847,7 @@ class MMGenWallet(WalletEnc):
 
 
 			val = baseconv.tobytes(b58_val,'b58',pad='seed')
 			val = baseconv.tobytes(b58_val,'b58',pad='seed')
 			if val == False:
 			if val == False:
-				msg('Invalid base 58 number: {}'.format(b58_val))
+				msg(f'Invalid base 58 number: {b58_val}')
 				return False
 				return False
 
 
 			setattr(d,key,val)
 			setattr(d,key,val)
@@ -907,7 +917,7 @@ class Brainwallet(WalletEnc):
 			buflen = bw_seed_len // 8 )
 			buflen = bw_seed_len // 8 )
 		qmsg('Done')
 		qmsg('Done')
 		self.seed = Seed(seed)
 		self.seed = Seed(seed)
-		msg('Seed ID: {}'.format(self.seed.sid))
+		msg(f'Seed ID: {self.seed.sid}')
 		qmsg('Check this value against your records')
 		qmsg('Check this value against your records')
 		return True
 		return True
 
 
@@ -971,7 +981,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 		# IV is used BOTH to initialize counter and to salt password!
 		# IV is used BOTH to initialize counter and to salt password!
 		d.iv = get_random(g.aesctr_iv_len)
 		d.iv = get_random(g.aesctr_iv_len)
 		d.iv_id = self._make_iv_chksum(d.iv)
 		d.iv_id = self._make_iv_chksum(d.iv)
-		msg('New Incog Wallet ID: {}'.format(d.iv_id))
+		msg(f'New Incog Wallet ID: {d.iv_id}')
 		qmsg('Make a record of this value')
 		qmsg('Make a record of this value')
 		vmsg(self.msg['record_incog_id'])
 		vmsg(self.msg['record_incog_id'])
 
 
@@ -982,7 +992,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 
 
 		d.wrapper_key = make_key(d.passwd, d.iv, d.hash_preset, 'incog wrapper key')
 		d.wrapper_key = make_key(d.passwd, d.iv, d.hash_preset, 'incog wrapper key')
 		d.key_id = make_chksum_8(d.wrapper_key)
 		d.key_id = make_chksum_8(d.wrapper_key)
-		vmsg('Key ID: {}'.format(d.key_id))
+		vmsg(f'Key ID: {d.key_id}')
 		d.target_data_len = self._get_incog_data_len(self.seed.bitlen)
 		d.target_data_len = self._get_incog_data_len(self.seed.bitlen)
 
 
 	def _format(self):
 	def _format(self):
@@ -1010,7 +1020,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 		d.iv             = self.fmt_data[0:g.aesctr_iv_len]
 		d.iv             = self.fmt_data[0:g.aesctr_iv_len]
 		d.incog_id       = self._make_iv_chksum(d.iv)
 		d.incog_id       = self._make_iv_chksum(d.iv)
 		d.enc_incog_data = self.fmt_data[g.aesctr_iv_len:]
 		d.enc_incog_data = self.fmt_data[g.aesctr_iv_len:]
-		msg('Incog Wallet ID: {}'.format(d.incog_id))
+		msg(f'Incog Wallet ID: {d.incog_id}')
 		qmsg('Check this value against your records')
 		qmsg('Check this value against your records')
 		vmsg(self.msg['check_incog_id'])
 		vmsg(self.msg['check_incog_id'])
 
 
@@ -1019,14 +1029,14 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 	def _verify_seed_newfmt(self,data):
 	def _verify_seed_newfmt(self,data):
 		chk,seed = data[:8],data[8:]
 		chk,seed = data[:8],data[8:]
 		if sha256(seed).digest()[:8] == chk:
 		if sha256(seed).digest()[:8] == chk:
-			qmsg('Passphrase{} are correct'.format(self.msg['dec_chk'].format('and')))
+			qmsg('Passphrase{} are correct'.format( self.msg['dec_chk'].format('and') ))
 			return seed
 			return seed
 		else:
 		else:
-			msg('Incorrect passphrase{}'.format(self.msg['dec_chk'].format('or')))
+			msg('Incorrect passphrase{}'.format( self.msg['dec_chk'].format('or') ))
 			return False
 			return False
 
 
 	def _verify_seed_oldfmt(self,seed):
 	def _verify_seed_oldfmt(self,seed):
-		m = 'Seed ID: {}.  Is the Seed ID correct?'.format(make_chksum_8(seed))
+		m = f'Seed ID: {make_chksum_8(seed)}.  Is the Seed ID correct?'
 		if keypress_confirm(m, True):
 		if keypress_confirm(m, True):
 			return seed
 			return seed
 		else:
 		else:
@@ -1045,7 +1055,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 		d.enc_seed = dd[g.salt_len:]
 		d.enc_seed = dd[g.salt_len:]
 
 
 		key = make_key(d.passwd, d.salt, d.hash_preset, 'main key')
 		key = make_key(d.passwd, d.salt, d.hash_preset, 'main key')
-		qmsg('Key ID: {}'.format(make_chksum_8(key)))
+		qmsg(f'Key ID: {make_chksum_8(key)}')
 
 
 		verify_seed = getattr(self,'_verify_seed_'+
 		verify_seed = getattr(self,'_verify_seed_'+
 						('newfmt','oldfmt')[bool(opt.old_incog_fmt)])
 						('newfmt','oldfmt')[bool(opt.old_incog_fmt)])
@@ -1054,7 +1064,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 
 
 		if seed:
 		if seed:
 			self.seed = Seed(seed)
 			self.seed = Seed(seed)
-			msg('Seed ID: {}'.format(self.seed.sid))
+			msg(f'Seed ID: {self.seed.sid}')
 			return True
 			return True
 		else:
 		else:
 			return False
 			return False
@@ -1123,14 +1133,19 @@ harder to find, you're advised to choose a much larger file size than this.
 		d = self.ssdata
 		d = self.ssdata
 		m = ('Input','Destination')[action=='write']
 		m = ('Input','Destination')[action=='write']
 		if fn.size < d.hincog_offset + d.target_data_len:
 		if fn.size < d.hincog_offset + d.target_data_len:
-			fs = "{} file '{}' has length {}, too short to {} {} bytes of data at offset {}"
-			die(1,fs.format(m,fn.name,fn.size,action,d.target_data_len,d.hincog_offset))
+			die(1,'{} file {!r} has length {}, too short to {} {} bytes of data at offset {}'.format(
+				m,
+				fn.name,
+				fn.size,
+				action,
+				d.target_data_len,
+				d.hincog_offset ))
 
 
 	def _get_data(self):
 	def _get_data(self):
 		d = self.ssdata
 		d = self.ssdata
 		d.hincog_offset = self._get_hincog_params('input')[1]
 		d.hincog_offset = self._get_hincog_params('input')[1]
 
 
-		qmsg("Getting hidden incog data from file '{}'".format(self.infile.name))
+		qmsg(f'Getting hidden incog data from file {self.infile.name!r}')
 
 
 		# Already sanity-checked:
 		# Already sanity-checked:
 		d.target_data_len = self._get_incog_data_len(opt.seed_len or g.dfl_seed_len)
 		d.target_data_len = self._get_incog_data_len(opt.seed_len or g.dfl_seed_len)
@@ -1141,7 +1156,7 @@ harder to find, you're advised to choose a much larger file size than this.
 		os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
 		os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
 		self.fmt_data = os.read(fh,d.target_data_len)
 		self.fmt_data = os.read(fh,d.target_data_len)
 		os.close(fh)
 		os.close(fh)
-		qmsg("Data read from file '{}' at offset {}".format(self.infile.name,d.hincog_offset))
+		qmsg(f'Data read from file {self.infile.name!r} at offset {d.hincog_offset}')
 
 
 	# overrides method in Wallet
 	# overrides method in Wallet
 	def write_to_file(self):
 	def write_to_file(self):
@@ -1160,14 +1175,16 @@ harder to find, you're advised to choose a much larger file size than this.
 		try:
 		try:
 			os.stat(fn)
 			os.stat(fn)
 		except:
 		except:
-			if keypress_confirm("Requested file '{}' does not exist.  Create?".format(fn),default_yes=True):
+			if keypress_confirm(
+					f'Requested file {fn!r} does not exist.  Create?',
+					default_yes = True ):
 				min_fsize = d.target_data_len + d.hincog_offset
 				min_fsize = d.target_data_len + d.hincog_offset
 				msg(self.msg['choose_file_size'].format(min_fsize))
 				msg(self.msg['choose_file_size'].format(min_fsize))
 				while True:
 				while True:
 					fsize = parse_bytespec(my_raw_input('Enter file size: '))
 					fsize = parse_bytespec(my_raw_input('Enter file size: '))
 					if fsize >= min_fsize:
 					if fsize >= min_fsize:
 						break
 						break
-					msg('File size must be an integer no less than {}'.format(min_fsize))
+					msg(f'File size must be an integer no less than {min_fsize}')
 
 
 				from .tool import MMGenToolCmdFileUtil
 				from .tool import MMGenToolCmdFileUtil
 				MMGenToolCmdFileUtil().rand2file(fn,str(fsize))
 				MMGenToolCmdFileUtil().rand2file(fn,str(fsize))
@@ -1178,16 +1195,22 @@ harder to find, you're advised to choose a much larger file size than this.
 		from .filename import Filename
 		from .filename import Filename
 		f = Filename(fn,ftype=type(self),write=True)
 		f = Filename(fn,ftype=type(self),write=True)
 
 
-		dmsg('{} data len {}, offset {}'.format(capfirst(self.desc),d.target_data_len,d.hincog_offset))
+		dmsg('{} data len {}, offset {}'.format(
+			capfirst(self.desc),
+			d.target_data_len,
+			d.hincog_offset ))
 
 
 		if check_offset:
 		if check_offset:
 			self._check_valid_offset(f,'write')
 			self._check_valid_offset(f,'write')
 			if not opt.quiet:
 			if not opt.quiet:
-				confirm_or_raise('',"alter file '{}'".format(f.name))
+				confirm_or_raise( '', f'alter file {f.name!r}' )
 
 
 		flgs = os.O_RDWR|os.O_BINARY if g.platform == 'win' else os.O_RDWR
 		flgs = os.O_RDWR|os.O_BINARY if g.platform == 'win' else os.O_RDWR
 		fh = os.open(f.name,flgs)
 		fh = os.open(f.name,flgs)
 		os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)
 		os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)
 		os.write(fh, self.fmt_data)
 		os.write(fh, self.fmt_data)
 		os.close(fh)
 		os.close(fh)
-		msg("{} written to file '{}' at offset {}".format(capfirst(self.desc),f.name,d.hincog_offset))
+		msg('{} written to file {!r} at offset {}'.format(
+			capfirst(self.desc),
+			f.name,
+			d.hincog_offset ))

+ 12 - 7
mmgen/xmrwallet.py

@@ -32,7 +32,7 @@ from .obj import CoinAddr,CoinTxID,SeedID,AddrIdx,Hilite,InitErrors
 xmrwallet_uarg_info = (
 xmrwallet_uarg_info = (
 	lambda e,hp: {
 	lambda e,hp: {
 		'daemon':          e('HOST:PORT', hp),
 		'daemon':          e('HOST:PORT', hp),
-		'tx_relay_daemon': e('HOST:PORT[:PROXY_HOST:PROXY_PORT]', r'({p})(?::({p}))?'.format(p=hp)),
+		'tx_relay_daemon': e('HOST:PORT[:PROXY_HOST:PROXY_PORT]', rf'({hp})(?::({hp}))?'),
 		'transfer_spec':   e('SOURCE_WALLET_NUM:ACCOUNT:ADDRESS,AMOUNT', rf'(\d+):(\d+):([{_b58a}]+),([0-9.]+)'),
 		'transfer_spec':   e('SOURCE_WALLET_NUM:ACCOUNT:ADDRESS,AMOUNT', rf'(\d+):(\d+):([{_b58a}]+),([0-9.]+)'),
 		'sweep_spec':      e('SOURCE_WALLET_NUM:ACCOUNT[,DEST_WALLET_NUM]', r'(\d+):(\d+)(?:,(\d+))?'),
 		'sweep_spec':      e('SOURCE_WALLET_NUM:ACCOUNT[,DEST_WALLET_NUM]', r'(\d+):(\d+)(?:,(\d+))?'),
 	})(
 	})(
@@ -53,7 +53,7 @@ class XMRWalletAddrSpec(str,Hilite,InitErrors,MMGenObject):
 		try:
 		try:
 			if isinstance(arg1,str):
 			if isinstance(arg1,str):
 				me = str.__new__(cls,arg1)
 				me = str.__new__(cls,arg1)
-				m = re.fullmatch('({n}):({n}):({n}|None)'.format(n=r'[0-9]{1,4}'),arg1)
+				m = re.fullmatch( '({n}):({n}):({n}|None)'.format(n=r'[0-9]{1,4}'), arg1 )
 				assert m is not None, f'{arg1!r}: invalid XMRWalletAddrSpec'
 				assert m is not None, f'{arg1!r}: invalid XMRWalletAddrSpec'
 				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':
@@ -371,7 +371,10 @@ class MoneroWalletOps:
 					'-α' if g.debug_utf8 else '' ))
 					'-α' if g.debug_utf8 else '' ))
 
 
 		async def main(self):
 		async def main(self):
-			gmsg('\n{}ing {} wallet{}'.format(self.desc,len(self.addr_data),suf(self.addr_data)))
+			gmsg('\n{}ing {} wallet{}'.format(
+				self.desc,
+				len(self.addr_data),
+				suf(self.addr_data) ))
 			processed = 0
 			processed = 0
 			for n,d in enumerate(self.addr_data): # [d.sec,d.addr,d.wallet_passwd,d.viewkey]
 			for n,d in enumerate(self.addr_data): # [d.sec,d.addr,d.wallet_passwd,d.viewkey]
 				fn = self.get_wallet_fn(d)
 				fn = self.get_wallet_fn(d)
@@ -382,7 +385,7 @@ class MoneroWalletOps:
 					os.path.basename(fn),
 					os.path.basename(fn),
 				))
 				))
 				processed += await self.process_wallet(d,fn)
 				processed += await self.process_wallet(d,fn)
-			gmsg('\n{} wallet{} {}'.format(processed,suf(processed),self.past))
+			gmsg(f'\n{processed} wallet{suf(processed)} {self.past}')
 			return processed
 			return processed
 
 
 		class rpc:
 		class rpc:
@@ -578,7 +581,7 @@ class MoneroWalletOps:
 				restore_height = uopt.restore_height,
 				restore_height = uopt.restore_height,
 				language       = 'English' )
 				language       = 'English' )
 
 
-			pp_msg(ret) if opt.debug else msg('  Address: {}'.format(ret['address']))
+			pp_msg(ret) if opt.debug else msg('  Address: {}'.format( ret['address'] ))
 			return True
 			return True
 
 
 	class sync(wallet):
 	class sync(wallet):
@@ -651,8 +654,10 @@ class MoneroWalletOps:
 
 
 			self.accts_data[bn] = { 'accts': a, 'addrs': b }
 			self.accts_data[bn] = { 'accts': a, 'addrs': b }
 
 
-			msg('  Wallet height: {}'.format(wallet_height))
-			msg('  Sync time: {:02}:{:02}'.format( t_elapsed//60, t_elapsed%60 ))
+			msg(f'  Wallet height: {wallet_height}')
+			msg('  Sync time: {:02}:{:02}'.format(
+				t_elapsed // 60,
+				t_elapsed % 60 ))
 
 
 			await self.c.call('close_wallet')
 			await self.c.call('close_wallet')
 			return wallet_height >= chain_height
 			return wallet_height >= chain_height

+ 4 - 4
setup.py

@@ -43,10 +43,10 @@ class my_build_ext(build_ext):
 setup(
 setup(
 	cmdclass = { 'build_ext': my_build_ext },
 	cmdclass = { 'build_ext': my_build_ext },
 	ext_modules = [Extension(
 	ext_modules = [Extension(
-		name         = 'mmgen.secp256k1',
-		sources      = ['extmod/secp256k1mod.c'],
-		libraries    = ([],['gmp'])[have_msys2],
+		name          = 'mmgen.secp256k1',
+		sources       = ['extmod/secp256k1mod.c'],
+		libraries     = ([],['gmp'])[have_msys2],
 		extra_objects = [os.path.join(ext_path,'.libs/libsecp256k1.a')],
 		extra_objects = [os.path.join(ext_path,'.libs/libsecp256k1.a')],
-		include_dirs = [os.path.join(ext_path,'include')],
+		include_dirs  = [os.path.join(ext_path,'include')],
 	)]
 	)]
 )
 )