Browse Source

Use new-style string formatting

MMGen 7 years ago
parent
commit
351f5f9118

+ 16 - 16
mmgen/addr.py

@@ -328,7 +328,7 @@ Record this checksum: it will be used to verify the address file in the future
 """.strip(),
 	'check_chksum': 'Check this value against your records',
 	'removed_dup_keys': """
-Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
+Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 """.strip().format(pnm=pnm)
 	}
 	entry_type = AddrListEntry
@@ -371,7 +371,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 		elif al_id or adata:
 			die(3,'Must specify both al_id and adata')
 		else:
-			die(3,'Incorrect arguments for %s' % type(self).__name__)
+			die(3,'Incorrect arguments for {}'.format(type(self).__name__))
 
 		# al_id,adata now set
 		self.data = adata
@@ -389,8 +389,8 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 			if chksum_only:
 				Msg(self.chksum)
 			else:
-				qmsg('Checksum for %s data %s: %s' %
-						(self.data_desc,self.id_str.hl(),self.chksum.hl()))
+				qmsg('Checksum for {} data {}: {}'.format(
+						self.data_desc,self.id_str.hl(),self.chksum.hl()))
 				qmsg(self.msgs[('check_chksum','record_chksum')[src=='gen']])
 
 	def update_msgs(self):
@@ -426,7 +426,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 			pos += 1
 
 			if not g.debug:
-				qmsg_r('\rGenerating %s #%s (%s of %s)' % (self.gen_desc,num,pos,t_addrs))
+				qmsg_r('\rGenerating {} #{} ({} of {})'.format(self.gen_desc,num,pos,t_addrs))
 
 			e = le(idx=num)
 
@@ -448,7 +448,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 			out.append(e)
 			if g.debug: Msg('generate():\n{}'.format(e.pformat()))
 
-		qmsg('\r%s: %s %s%s generated%s' % (
+		qmsg('\r{}: {} {}{} generated{}'.format(
 				self.al_id.hl(),t_addrs,self.gen_desc,suf(t_addrs,self.gen_desc_pl),' '*15))
 		return out
 
@@ -481,7 +481,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 		return [e.idx for e in self.data]
 
 	def addrs(self):
-		return ['%s:%s'%(self.al_id.sid,e.idx) for e in self.data]
+		return ['{}:{}'.format(self.al_id.sid,e.idx) for e in self.data]
 
 	def addrpairs(self):
 		return [(e.idx,e.addr) for e in self.data]
@@ -526,7 +526,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 					pop_list.append(n)
 		for n in reversed(pop_list): self.data.pop(n)
 		if pop_list:
-			vmsg(self.msgs['removed_dup_keys'] % (len(pop_list),suf(removed,'s')))
+			vmsg(self.msgs['removed_dup_keys'].format(len(pop_list),suf(removed,'s')))
 
 	def add_wifs(self,key_list):
 		if not key_list: return
@@ -543,9 +543,9 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 		ag = AddrGenerator('p2pkh')
 		d = self.data
 		for n,e in enumerate(d,1):
-			qmsg_r('\rGenerating addresses from keylist: %s/%s' % (n,len(d)))
+			qmsg_r('\rGenerating addresses from keylist: {}/{}'.format(n,len(d)))
 			e.addr = ag.to_addr(kg.to_pubhex(e.sec))
-		qmsg('\rGenerated addresses from keylist: %s/%s ' % (n,len(d)))
+		qmsg('\rGenerated addresses from keylist: {}/{} '.format(n,len(d)))
 
 	def format(self,enable_comments=False):
 
@@ -600,7 +600,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 		while lines:
 			d = get_line()
 
-			assert is_mmgen_idx(d[0]),"'%s': invalid address num. in line: '%s'" % (d[0],' '.join(d))
+			assert is_mmgen_idx(d[0]),"'{}': invalid address num. in line: '{}'".format(d[0],' '.join(d))
 			assert self.check_format(d[1]),"'{}': invalid {}".format(d[1],self.data_desc)
 
 			if len(d) != 3: d.append('')
@@ -623,9 +623,9 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 			ag = AddrGenerator(self.al_id.mmtype)
 			llen = len(ret)
 			for n,e in enumerate(ret):
-				msg_r('\rVerifying keys %s/%s' % (n+1,llen))
+				msg_r('\rVerifying keys {}/{}'.format(n+1,llen))
 				assert e.addr == ag.to_addr(kg.to_pubhex(e.sec)),(
-					"Key doesn't match address!\n  %s\n  %s" % (e.sec.wif,e.addr))
+					"Key doesn't match address!\n  {}\n  {}".format(e.sec.wif,e.addr))
 			msg(' - done')
 
 		return ret
@@ -849,7 +849,7 @@ Record this checksum: it will be used to verify the password file in the future
 class AddrData(MMGenObject):
 	msgs = {
 	'too_many_acct_addresses': """
-ERROR: More than one address found for account: '%s'.
+ERROR: More than one address found for account: '{{}}'.
 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
 re-import your addresses.
@@ -890,7 +890,7 @@ re-import your addresses.
 				obj = l.mmid.obj
 				i += 1
 				if len(addrlist) != 1:
-					die(2,self.msgs['too_many_acct_addresses'] % acct)
+					die(2,self.msgs['too_many_acct_addresses'].format(acct))
 				al_id = AddrListID(SeedID(sid=obj.sid),MMGenAddrType(obj.mmtype))
 				if al_id not in data:
 					data[al_id] = []
@@ -904,7 +904,7 @@ re-import your addresses.
 			self.al_ids[addrlist.al_id] = addrlist
 			return True
 		else:
-			raise TypeError, 'Error: object %s is not of type AddrList' % repr(addrlist)
+			raise TypeError, 'Error: object {!r} is not of type AddrList'.format(addrlist)
 
 	def make_reverse_dict(self,coinaddrs):
 		d = MMGenDict()

+ 4 - 4
mmgen/filename.py

@@ -44,14 +44,14 @@ class Filename(MMGenObject):
 					self.ftype = ftype
 				# elif: # other MMGen file types
 				else:
-					die(3,"'%s': not a recognized file type for SeedSource" % ftype)
+					die(3,"'{}': not a recognized file type for SeedSource".format(ftype))
 			else:
-				die(3,"'%s': not a class" % ftype)
+				die(3,"'{}': not a class".format(ftype))
 		else:
 			# TODO: other file types
 			self.ftype = SeedSource.ext_to_type(self.ext)
 			if not self.ftype:
-				die(3,"'%s': not a recognized extension for SeedSource" % self.ext)
+				die(3,"'{}': not a recognized extension for SeedSource".format(self.ext))
 
 
 		import stat
@@ -62,7 +62,7 @@ class Filename(MMGenObject):
 				fd = os.open(fn, mode)
 			except OSError as e:
 				if e.errno == 13:
-					die(2,"'%s': permission denied" % fn)
+					die(2,"'{}': permission denied".format(fn))
 #				if e.errno != 17: raise
 			else:
 				self.size = os.lseek(fd, 0, os.SEEK_END)

+ 1 - 1
mmgen/globalvars.py

@@ -97,7 +97,7 @@ class g(object):
 		if sys.platform[:len(k)] == k:
 			platform = k; break
 	else:
-		die(1,"'%s': platform not supported by %s\n" % (sys.platform,proj_name))
+		die(1,"'{}': platform not supported by {}\n".format(sys.platform,proj_name))
 
 	if os.getenv('HOME'):                             # Linux or MSYS
 		home_dir = os.getenv('HOME')

+ 1 - 1
mmgen/main_tool.py

@@ -147,7 +147,7 @@ import mmgen.tool as tool
 if Command == 'Help' and not cmd_args: tool.usage(None)
 
 if Command not in tool.cmd_data:
-	die(1,"'%s': no such command" % Command.lower())
+	die(1,"'{}': no such command".format(Command.lower()))
 
 args,kwargs = tool.process_args(Command,cmd_args)
 

+ 1 - 1
mmgen/main_txsend.py

@@ -49,7 +49,7 @@ if not opt.status: do_license_msg()
 from mmgen.tx import *
 
 tx = MMGenTX(infile,silent_open=True) # sig check performed here
-vmsg("Signed transaction file '%s' is valid" % infile)
+vmsg("Signed transaction file '{}' is valid".format(infile))
 
 if not tx.marked_signed():
 	die(1,'Transaction is not signed!')

+ 4 - 4
mmgen/main_txsign.py

@@ -92,13 +92,13 @@ if kl and kal: kl.remove_dup_keys(kal)
 tx_num_str = ''
 for tx_num,tx_file in enumerate(tx_files,1):
 	if len(tx_files) > 1:
-		msg('\nTransaction #%s of %s:' % (tx_num,len(tx_files)))
-		tx_num_str = ' #%s' % tx_num
+		msg('\nTransaction #{} of {}:'.format(tx_num,len(tx_files)))
+		tx_num_str = ' #{}'.format(tx_num)
 	tx = MMGenTX(tx_file)
 
 	if tx.marked_signed():
 		die(1,'Transaction is already signed!')
-	vmsg("Successfully opened transaction file '%s'" % tx_file)
+	vmsg("Successfully opened transaction file '{}'".format(tx_file))
 
 	if opt.tx_id:
 		msg(tx.txid); continue
@@ -107,7 +107,7 @@ for tx_num,tx_file in enumerate(tx_files,1):
 		tx.view(pause=False,terse=opt.terse_info); continue
 
 	if not opt.yes:
-		tx.view_with_prompt('View data for transaction%s?' % tx_num_str)
+		tx.view_with_prompt('View data for transaction{}?'.format(tx_num_str))
 
 	txsign(tx,seed_files,kl,kal,tx_num_str)
 

+ 7 - 7
mmgen/main_wallet.py

@@ -53,7 +53,7 @@ elif invoked_as == 'passchg':
 	iaction = 'input'
 	bw_note = False
 else:
-	die(1,"'%s': unrecognized invocation" % g.prog_name)
+	die(1,"'{}': unrecognized invocation".format(g.prog_name))
 
 opts_data = lambda: {
 # Can't use: share/Opts doesn't know anything about fmt codes
@@ -117,10 +117,10 @@ sf = get_seed_file(cmd_args,nargs,invoked_as=invoked_as)
 
 if not invoked_as == 'chk': do_license_msg()
 
-dw_msg = ('',yellow(' (default wallet)'))[bool(sf and os.path.dirname(sf)==g.data_dir)]
-
 if invoked_as in ('conv','passchg'):
-	msg(green('Processing input wallet')+dw_msg)
+	m1 = green('Processing input wallet')
+	m2 = yellow(' (default wallet)') if sf and os.path.dirname(sf) == g.data_dir else ''
+	msg(m1+m2)
 
 ss_in = None if invoked_as == 'gen' else SeedSource(sf,passchg=(invoked_as=='passchg'))
 if invoked_as == 'chk':
@@ -130,12 +130,12 @@ if invoked_as == 'chk':
 	sys.exit(0)
 
 if invoked_as in ('conv','passchg'):
-	msg(green('Processing output wallet'))
+	gmsg('Processing output wallet')
 
 ss_out = SeedSource(ss=ss_in,passchg=invoked_as=='passchg')
 
 if invoked_as == 'gen':
-	qmsg("This wallet's Seed ID: %s" % ss_out.seed.sid.hl())
+	qmsg("This wallet's Seed ID: {}".format(ss_out.seed.sid.hl()))
 
 if invoked_as == 'passchg':
 	if not (opt.force_update or [k for k in ('passwd','hash_preset','label')
@@ -155,7 +155,7 @@ if invoked_as == 'passchg' and ss_in.infile.dirname == g.data_dir:
 	try:
 		check_output(sd_cmd + [ss_in.infile.name])
 	except:
-		msg(yellow("WARNING: '%s' command failed, using regular file delete instead" % sd_cmd[0]))
+		ymsg("WARNING: '{}' command failed, using regular file delete instead".format(sd_cmd[0]))
 #		msg('Command output: {}\nReturn value {}'.format(e.output,e.returncode))
 		os.unlink(ss_in.infile.name)
 elif invoked_as == 'gen' and not find_file_in_dir(Wallet,g.data_dir) \

+ 18 - 21
mmgen/opts.py

@@ -27,13 +27,13 @@ from mmgen.globalvars import g
 import mmgen.share.Opts
 from mmgen.util import *
 
-def usage(): Die(2,'USAGE: %s %s' % (g.prog_name, usage_txt))
+def usage(): Die(2,'USAGE: {} {}'.format((g.prog_name,usage_txt)))
 
 def die_on_incompatible_opts(incompat_list):
 	for group in incompat_list:
 		bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in group]
 		if len(bad) > 1:
-			die(1,'Conflicting options: %s' % ', '.join([fmt_opt(b) for b in bad]))
+			die(1,'Conflicting options: {}'.format(', '.join(map(fmt_opt,bad))))
 
 def fmt_opt(o): return '--' + o.replace('_','-')
 
@@ -42,7 +42,7 @@ def _show_hash_presets():
 	msg('Available parameters for scrypt.hash():')
 	msg(fs.format('Preset','N','r','p'))
 	for i in sorted(g.hash_presets.keys()):
-		msg(fs.format("'%s'" % i, *g.hash_presets[i]))
+		msg(fs.format("'{}'".format(i,*g.hash_presets[i])))
 	msg('N = memory usage (power of two), p = iterations (rounds)')
 
 def opt_preproc_debug(short_opts,long_opts,skipped_opts,uopts,args):
@@ -64,9 +64,9 @@ def opt_postproc_debug():
 	Msg('    Opts after processing:')
 	for k in a:
 		v = getattr(opt,k)
-		Msg('        %-18s: %-6s [%s]' % (k,v,type(v).__name__))
+		Msg('        {:18}: {:<6} [{}]'.format(k,v,type(v).__name__))
 	Msg("    Opts set to 'None':")
-	Msg('        %s\n' % '\n        '.join(b))
+	Msg('        {}\n'.format('\n        '.join(b)))
 	Msg('    Global vars:')
 	for e in [d for d in dir(g) if d[:2] != '__']:
 		Msg('        {:<20}: {}'.format(e, getattr(g,e)))
@@ -341,16 +341,15 @@ def opt_is_tx_fee(val,desc):
 def check_opts(usr_opts):       # Returns false if any check fails
 
 	def opt_splits(val,sep,n,desc):
-		sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else "'%s'" % sep
+		sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else "'{}'".format(sep)
 		try: l = val.split(sep)
 		except:
-			msg("'%s': invalid %s (not %s-separated list)" % (val,desc,sepword))
+			msg("'{}': invalid {} (not {}-separated list)".format(val,desc,sepword))
 			return False
 
 		if len(l) == n: return True
 		else:
-			msg("'%s': invalid %s (%s %s-separated items required)" %
-					(val,desc,n,sepword))
+			msg("'{}': invalid {} ({} {}-separated items required)".format(val,desc,n,sepword))
 			return False
 
 	def opt_compares(val,op_str,target,desc,what=''):
@@ -385,18 +384,17 @@ def check_opts(usr_opts):       # Returns false if any check fails
 		return True
 
 	def opt_unrecognized(key,val,desc):
-		msg("'%s': unrecognized %s for option '%s'"
-				% (val,desc,fmt_opt(key)))
+		msg("'{}': unrecognized {} for option '{}'".format(val,desc,fmt_opt(key)))
 		return False
 
 	def opt_display(key,val='',beg='For selected',end=':\n'):
-		s = '%s=%s' % (fmt_opt(key),val) if val else fmt_opt(key)
-		msg_r("%s option '%s'%s" % (beg,s,end))
+		s = '{}={}'.format(fmt_opt(key),val) if val else fmt_opt(key)
+		msg_r("{} option '{}'{}".format(beg,s,end))
 
 	global opt
 	for key,val in [(k,getattr(opt,k)) for k in usr_opts]:
 
-		desc = "parameter for '%s' option" % fmt_opt(key)
+		desc = "parameter for '{}' option".format(fmt_opt(key))
 
 		from mmgen.util import check_infile,check_outfile,check_outdir
 		# Check for file existence and readability
@@ -416,8 +414,9 @@ def check_opts(usr_opts):       # Returns false if any check fails
 			if key == 'out_fmt':
 				p = 'hidden_incog_output_params'
 				if sstype == IncogWalletHidden and not getattr(opt,p):
-						die(1,'Hidden incog format output requested. You must supply'
-						+ " a file and offset with the '%s' option" % fmt_opt(p))
+					m1 = 'Hidden incog format output requested.  '
+					m2 = "You must supply a file and offset with the '{}' option"
+					die(1,m1+m2.format(fmt_opt(p)))
 				if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
 					opt_display(key,val,beg='Selected',end=' ')
 					opt_display('old_incog_fmt',beg='conflicts with',end=':\n')
@@ -446,10 +445,8 @@ def check_opts(usr_opts):       # Returns false if any check fails
 				val2 = getattr(opt,key2)
 				from mmgen.seed import IncogWalletHidden
 				if val2 and val2 not in IncogWalletHidden.fmt_codes:
-					die(1,
-						'Option conflict:\n  %s, with\n  %s=%s' % (
-						fmt_opt(key),fmt_opt(key2),val2
-					))
+					fs = 'Option conflict:\n  {}, with\n  {}={}'
+					die(1,fs.format(fmt_opt(key),fmt_opt(key2),val2))
 		elif key == 'seed_len':
 			if not opt_is_int(val,desc): return False
 			if not opt_is_in_list(int(val),g.seed_lens,desc): return False
@@ -497,6 +494,6 @@ def check_opts(usr_opts):       # Returns false if any check fails
 			if not opt_is_int(val,desc): return False
 			if not opt_compares(val,'>',0,desc): return False
 		else:
-			if g.debug: Msg("check_opts(): No test for opt '%s'" % key)
+			if g.debug: Msg("check_opts(): No test for opt '{}'".format(key))
 
 	return True

+ 3 - 3
mmgen/rpc.py

@@ -102,7 +102,7 @@ class CoinDaemonRPCConnection(object):
 				die(args[1],yellow(s))
 
 		dmsg_rpc('=== request() debug ===')
-		dmsg_rpc('    RPC POST data ==> %s\n' % p)
+		dmsg_rpc('    RPC POST data ==> {}\n'.format(p))
 		caller = self
 		class MyJSONEncoder(json.JSONEncoder):
 			def default(self, obj):
@@ -132,7 +132,7 @@ class CoinDaemonRPCConnection(object):
 			m = 'Unable to connect to {} at {}:{} (but port is bound?)'
 			return do_fail(None,2,m.format(g.proto.daemon_name,self.host,self.port))
 
-		dmsg_rpc('    RPC GETRESPONSE data ==> %s\n' % r.__dict__)
+		dmsg_rpc('    RPC GETRESPONSE data ==> {}\n'.format(r.__dict__))
 
 		if r.status != 200:
 			if cf['on_fail'] not in ('silent','raise'):
@@ -148,7 +148,7 @@ class CoinDaemonRPCConnection(object):
 
 		r2 = r.read()
 
-		dmsg_rpc('    RPC REPLY data ==> %s\n' % r2)
+		dmsg_rpc('    RPC REPLY data ==> {}\n'.format(r2))
 
 		if not r2:
 			return do_fail(r,2,'Error: empty reply')

+ 79 - 97
mmgen/seed.py

@@ -32,9 +32,8 @@ pnm = g.proj_name
 
 def check_usr_seed_len(seed_len):
 	if opt.seed_len != seed_len and 'seed_len' in opt.set_by_user:
-		m = 'ERROR: requested seed length (%s) ' + \
-			"doesn't match seed length of source (%s)"
-		die(1, m % (opt.seed_len,seed_len))
+		m = "ERROR: requested seed length ({}) doesn't match seed length of source ({})"
+		die(1,m.format((opt.seed_len,seed_len)))
 
 class Seed(MMGenObject):
 	def __init__(self,seed_bin=None):
@@ -42,7 +41,7 @@ class Seed(MMGenObject):
 			# Truncate random data for smaller seed lengths
 			seed_bin = sha256(get_random(1033)).digest()[:opt.seed_len/8]
 		elif len(seed_bin)*8 not in g.seed_lens:
-			die(3,'%s: invalid seed length' % len(seed_bin))
+			die(3,'{}: invalid seed length'.format(len(seed_bin)))
 
 		self.data      = seed_bin
 		self.hexdata   = hexlify(seed_bin)
@@ -121,13 +120,12 @@ class SeedSource(MMGenObject):
 			self._decrypt_retry()
 		else:
 			if not self.stdin_ok:
-				die(1,'Reading from standard input not supported for %s format'
-						% self.desc)
+				die(1,'Reading from standard input not supported for {} format'.format(self.desc))
 			self._deformat_retry()
 			self._decrypt_retry()
 
-		m = ('',', seed length %s' % self.seed.length)[self.seed.length!=256]
-		qmsg('Valid %s for Seed ID %s%s' % (self.desc,self.seed.sid.hl(),m))
+		m = ('',', seed length {}'.format(self.seed.length))[self.seed.length!=256]
+		qmsg('Valid {} for Seed ID {}{}'.format(self.desc,self.seed.sid.hl(),m))
 
 	def _get_data(self):
 		if hasattr(self,'infile'):
@@ -229,14 +227,14 @@ class SeedSourceUnenc(SeedSource):
 	def _encrypt(self): pass
 
 	def _filename(self):
-		return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
+		return '{}[{}].{}'.format(self.seed.sid,self.seed.length,self.ext)
 
 class SeedSourceEnc(SeedSource):
 
 	_msg = {
 		'choose_passphrase': """
-You must choose a passphrase to encrypt your new %s with.
-A key will be generated from your passphrase using a hash preset of '%s'.
+You must choose a passphrase to encrypt your new {} with.
+A key will be generated from your passphrase using a hash preset of '{}'.
 Please note that no strength checking of passphrases is performed.  For
 an empty passphrase, just hit ENTER twice.
 	""".strip()
@@ -263,8 +261,7 @@ an empty passphrase, just hit ENTER twice.
 					self.ssdata.hash_preset = ret
 					return ret
 				else:
-					msg('Invalid input.  Valid choices are %s' %
-							', '.join(sorted(g.hash_presets.keys())))
+					msg('Invalid input.  Valid choices are {}'.format(', '.join(sorted(g.hash_presets.keys()))))
 			else:
 				self.ssdata.hash_preset = hp
 				return hp
@@ -273,21 +270,20 @@ an empty passphrase, just hit ENTER twice.
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'):
 			old_hp = self.ss_in.ssdata.hash_preset
 			if opt.keep_hash_preset:
-				qmsg("Reusing hash preset '%s' at user request" % old_hp)
+				qmsg("Reusing hash preset '{}' at user request".format(old_hp))
 				self.ssdata.hash_preset = old_hp
 			elif 'hash_preset' in opt.set_by_user:
 				hp = self.ssdata.hash_preset = opt.hash_preset
-				qmsg("Using hash preset '%s' requested on command line"
-						% opt.hash_preset)
+				qmsg("Using hash preset '{}' requested on command line".format(opt.hash_preset))
 			else: # Prompt, using old value as default
 				hp = self._get_hash_preset_from_user(old_hp,desc_suf)
 
 			if (not opt.keep_hash_preset) and self.op == 'pwchg_new':
-				m = ("changed to '%s'" % hp,'unchanged')[hp==old_hp]
-				qmsg('Hash preset %s' % m)
+				m = ("changed to '{}'".format(hp),'unchanged')[hp==old_hp]
+				qmsg('Hash preset {}'.format(m))
 		elif 'hash_preset' in opt.set_by_user:
 			self.ssdata.hash_preset = opt.hash_preset
-			qmsg("Using hash preset '%s' requested on command line"%opt.hash_preset)
+			qmsg("Using hash preset '{}' requested on command line".format(opt.hash_preset))
 		else:
 			self._get_hash_preset_from_user(opt.hash_preset,desc_suf)
 
@@ -301,18 +297,17 @@ an empty passphrase, just hit ENTER twice.
 			w = pwfile_reuse_warning()
 			pw = ' '.join(get_words_from_file(opt.passwd_file,desc,silent=w))
 		elif opt.echo_passphrase:
-			pw = ' '.join(get_words_from_user('Enter %s: ' % desc))
+			pw = ' '.join(get_words_from_user('Enter {}: '.format(desc)))
 		else:
 			for i in range(g.passwd_max_tries):
-				pw = ' '.join(get_words_from_user('Enter %s: ' % desc))
+				pw = ' '.join(get_words_from_user('Enter {}: '.format(desc)))
 				pw2 = ' '.join(get_words_from_user('Repeat passphrase: '))
-				dmsg('Passphrases: [%s] [%s]' % (pw,pw2))
+				dmsg('Passphrases: [{}] [{}]'.format(pw,pw2))
 				if pw == pw2:
 					vmsg('Passphrases match'); break
 				else: msg('Passphrases do not match.  Try again.')
 			else:
-				die(2,'User failed to duplicate passphrase in %s attempts' %
-						g.passwd_max_tries)
+				die(2,'User failed to duplicate passphrase in {} attempts'.format(g.passwd_max_tries))
 
 		if pw == '': qmsg('WARNING: Empty passphrase')
 		self.ssdata.passwd = pw
@@ -328,7 +323,7 @@ an empty passphrase, just hit ENTER twice.
 			w = pwfile_reuse_warning()
 			ret = ' '.join(get_words_from_file(opt.passwd_file,desc,silent=w))
 		else:
-			ret = ' '.join(get_words_from_user('Enter %s: ' % desc))
+			ret = ' '.join(get_words_from_user('Enter {}: '.format(desc)))
 		self.ssdata.passwd = ret
 
 	def _get_first_pw_and_hp_and_encrypt_seed(self):
@@ -344,9 +339,9 @@ an empty passphrase, just hit ENTER twice.
 				pw = self._get_new_passphrase()
 				if self.op == 'pwchg_new':
 					m = ('changed','unchanged')[pw==old_pw]
-					qmsg('Passphrase %s' % m)
+					qmsg('Passphrase {}'.format(m))
 		else:
-			qmsg(self.msg['choose_passphrase'] % (self.desc,d.hash_preset))
+			qmsg(self.msg['choose_passphrase'].format(self.desc,d.hash_preset))
 			self._get_new_passphrase()
 
 		d.salt     = sha256(get_random(128)).digest()[:g.salt_len]
@@ -449,7 +444,7 @@ class Mnemonic (SeedSourceUnenc):
 
 		for n,w in enumerate(mn,1):
 			if w not in baseconv.digits[self.wl_id]:
-				msg('Invalid mnemonic: word #%s is not in the wordlist' % n)
+				msg('Invalid mnemonic: word #{} is not in the wordlist'.format(n))
 				return False
 
 		hexseed = baseconv.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
@@ -480,30 +475,27 @@ class SeedFile (SeedSourceUnenc):
 		b58seed = baseconv.b58encode(self.seed.data,pad=True)
 		self.ssdata.chksum = make_chksum_6(b58seed)
 		self.ssdata.b58seed = b58seed
-		self.fmt_data = '%s %s\n' % (
-				self.ssdata.chksum,
-				split_into_cols(4,b58seed)
-			)
+		self.fmt_data = '{} {}\n'.format(self.ssdata.chksum,split_into_cols(4,b58seed))
 
 	def _deformat(self):
 		desc = self.desc
 		ld = self.fmt_data.split()
 
 		if not (7 <= len(ld) <= 12): # 6 <= padded b58 data (ld[1:]) <= 11
-			msg('Invalid data length (%s) in %s' % (len(ld),desc))
+			msg('Invalid data length ({}) in {}'.format(len(ld),desc))
 			return False
 
 		a,b = ld[0],''.join(ld[1:])
 
 		if not is_chksum_6(a):
-			msg("'%s': invalid checksum format in %s" % (a, desc))
+			msg("'{}': invalid checksum format in {}".format(a, desc))
 			return False
 
 		if not is_b58_str(b):
-			msg("'%s': not a base 58 string, in %s" % (b, desc))
+			msg("'{}': not a base 58 string, in {}".format(b, desc))
 			return False
 
-		vmsg_r('Validating %s checksum...' % desc)
+		vmsg_r('Validating {} checksum...'.format(desc))
 
 		if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
 			return False
@@ -511,7 +503,7 @@ class SeedFile (SeedSourceUnenc):
 		ret = baseconv.b58decode(b,pad=True)
 
 		if ret == False:
-			msg('Invalid base-58 encoded seed: %s' % val)
+			msg('Invalid base-58 encoded seed: {}'.format(val))
 			return False
 
 		self.seed = Seed(ret)
@@ -533,7 +525,7 @@ class HexSeedFile (SeedSourceUnenc):
 		h = self.seed.hexdata
 		self.ssdata.chksum = make_chksum_6(h)
 		self.ssdata.hexseed = h
-		self.fmt_data = '%s %s\n' % (self.ssdata.chksum, split_into_cols(4,h))
+		self.fmt_data = '{} {}\n'.format(self.ssdata.chksum, split_into_cols(4,h))
 
 	def _deformat(self):
 		desc = self.desc
@@ -542,22 +534,22 @@ class HexSeedFile (SeedSourceUnenc):
 			d[1]
 			chk,hstr = d[0],''.join(d[1:])
 		except:
-			msg("'%s': invalid %s" % (self.fmt_data.strip(),desc))
+			msg("'{}': invalid {}".format(self.fmt_data.strip(),desc))
 			return False
 
 		if not len(hstr)*4 in g.seed_lens:
-			msg('Invalid data length (%s) in %s' % (len(hstr),desc))
+			msg('Invalid data length ({}) in {}'.format(len(hstr),desc))
 			return False
 
 		if not is_chksum_6(chk):
-			msg("'%s': invalid checksum format in %s" % (chk, desc))
+			msg("'{}': invalid checksum format in {}".format(chk, desc))
 			return False
 
 		if not is_hex_str(hstr):
-			msg("'%s': not a hexadecimal string, in %s" % (hstr, desc))
+			msg("'{}': not a hexadecimal string, in {}".format(hstr, desc))
 			return False
 
-		vmsg_r('Validating %s checksum...' % desc)
+		vmsg_r('Validating {} checksum...'.format(desc))
 
 		if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True):
 			return False
@@ -577,8 +569,8 @@ class Wallet (SeedSourceEnc):
 	ext = 'mmdat'
 
 	def _get_label_from_user(self,old_lbl=''):
-		d = ("to reuse the label '%s'" % old_lbl.hl()) if old_lbl else 'for no label'
-		p = 'Enter a wallet label, or hit ENTER %s: ' % d
+		d = "to reuse the label '{}'".format(old_lbl.hl()) if old_lbl else 'for no label'
+		p = 'Enter a wallet label, or hit ENTER {}: '.format(d)
 		while True:
 			msg_r(p)
 			ret = my_raw_input('')
@@ -598,19 +590,19 @@ class Wallet (SeedSourceEnc):
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'label'):
 			old_lbl = self.ss_in.ssdata.label
 			if opt.keep_label:
-				qmsg("Reusing label '%s' at user request" % old_lbl.hl())
+				qmsg("Reusing label '{}' at user request".format(old_lbl.hl()))
 				self.ssdata.label = old_lbl
 			elif opt.label:
-				qmsg("Using label '%s' requested on command line" % opt.label.hl())
+				qmsg("Using label '{}' requested on command line".format(opt.label.hl()))
 				lbl = self.ssdata.label = opt.label
 			else: # Prompt, using old value as default
 				lbl = self._get_label_from_user(old_lbl)
 
 			if (not opt.keep_label) and self.op == 'pwchg_new':
-				m = ("changed to '%s'" % lbl,'unchanged')[lbl==old_lbl]
-				qmsg('Label %s' % m)
+				m = ("changed to '{}'".format(lbl),'unchanged')[lbl==old_lbl]
+				qmsg('Label {}'.format(m))
 		elif opt.label:
-			qmsg("Using label '%s' requested on command line" % opt.label.hl())
+			qmsg("Using label '{}' requested on command line".format(opt.label.hl()))
 			self.ssdata.label = opt.label
 		else:
 			self._get_label_from_user()
@@ -636,20 +628,18 @@ class Wallet (SeedSourceEnc):
 			'{} {}'.format(make_chksum_6(es_fmt), split_into_cols(4,es_fmt))
 		)
 		chksum = make_chksum_6(' '.join(lines))
-		self.fmt_data = '%s\n' % '\n'.join((chksum,)+lines)
+		self.fmt_data = '{}\n'.format('\n'.join((chksum,)+lines))
 
 	def _deformat(self):
 
 		def check_master_chksum(lines,desc):
 
 			if len(lines) != 6:
-				msg('Invalid number of lines (%s) in %s data' %
-						(len(lines),desc))
+				msg('Invalid number of lines ({}) in {} data'.format(len(lines),desc))
 				return False
 
 			if not is_chksum_6(lines[0]):
-				msg('Incorrect master checksum (%s) in %s data' %
-						(lines[0],desc))
+				msg('Incorrect master checksum ({}) in {} data'.format(lines[0],desc))
 				return False
 
 			chk = make_chksum_6(' '.join(lines[1:]))
@@ -674,17 +664,16 @@ class Wallet (SeedSourceEnc):
 		hpdata = lines[3].split()
 
 		d.hash_preset = hp = hpdata[0][:-1]  # a string!
-		qmsg("Hash preset of wallet: '%s'" % hp)
+		qmsg("Hash preset of wallet: '{}'".format(hp))
 		if 'hash_preset' in opt.set_by_user:
 			uhp = opt.hash_preset
 			if uhp != hp:
-				qmsg("Warning: ignoring user-requested hash preset '%s'" % uhp)
+				qmsg("Warning: ignoring user-requested hash preset '{}'".format(uhp))
 
 		hash_params = map(int,hpdata[1:])
 
 		if hash_params != get_hash_params(d.hash_preset):
-			msg("Hash parameters '%s' don't match hash preset '%s'" %
-					(' '.join(hash_params), d.hash_preset))
+			msg("Hash parameters '{}' don't match hash preset '{}'".format(' '.join(hash_params),d.hash_preset))
 			return False
 
 		lmin,foo,lmax = [v for k,v in baseconv.b58pad_lens] # 22,33,44
@@ -694,7 +683,7 @@ class Wallet (SeedSourceEnc):
 			b58_val = ''.join(l)
 
 			if len(b58_val) < lmin or len(b58_val) > lmax:
-				msg('Invalid format for %s in %s: %s' % (key,self.desc,l))
+				msg('Invalid format for {} in {}: {}'.format(key,self.desc,l))
 				return False
 
 			if not compare_chksums(chk,key,
@@ -703,7 +692,7 @@ class Wallet (SeedSourceEnc):
 
 			val = baseconv.b58decode(b58_val,pad=True)
 			if val == False:
-				msg('Invalid base 58 number: %s' % b58_val)
+				msg('Invalid base 58 number: {}'.format(b58_val))
 				return False
 
 			setattr(d,key,val)
@@ -756,9 +745,9 @@ class Brainwallet (SeedSourceEnc):
 			seed_len,d.hash_preset = self.get_bw_params()
 		else:
 			if 'seed_len' not in opt.set_by_user:
-				m1 = 'Using default seed length of %s bits'
+				m1 = 'Using default seed length of {} bits\n'
 				m2 = 'If this is not what you want, use the --seed-len option'
-				qmsg((m1+'\n'+m2) % yellow(str(opt.seed_len)))
+				qmsg((m1+m2).format(yellow(str(opt.seed_len))))
 			self._get_hash_preset()
 			seed_len = opt.seed_len
 		qmsg_r('Hashing brainwallet data.  Please wait...')
@@ -767,7 +756,7 @@ class Brainwallet (SeedSourceEnc):
 					d.hash_preset, buflen=seed_len/8)
 		qmsg('Done')
 		self.seed = Seed(seed)
-		msg('Seed ID: %s' % self.seed.sid)
+		msg('Seed ID: {}'.format(self.seed.sid))
 		qmsg('Check this value against your records')
 		return True
 
@@ -796,7 +785,7 @@ Try again? (Y)es, (n)o, (m)ore information:
 If the Seed ID above is correct but you're seeing this message, then you need
 to exit and re-run the program with the '--old-incog-fmt' option.
 """.strip(),
-		'dec_chk': " %s hash preset"
+		'dec_chk': " {} hash preset"
 	}
 
 	def _make_iv_chksum(self,s): return sha256(s).hexdigest()[:8].upper()
@@ -813,16 +802,14 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 			return True
 		else:
 			if opt.old_incog_fmt:
-				msg('WARNING: old-style incognito format requested.  ' +
-					'Are you sure this is correct?')
-			msg(('Invalid incognito data size (%s bytes) for this ' +
-				'seed length (%s bits)') % (dlen,opt.seed_len))
-			msg('Valid data size for this seed length: %s bytes' % valid_dlen)
+				msg('WARNING: old-style incognito format requested.  Are you sure this is correct?')
+			m = 'Invalid incognito data size ({} bytes) for this seed length ({} bits)'
+			msg(m.format(dlen,opt.seed_len))
+			msg('Valid data size for this seed length: {} bytes'.format(valid_dlen))
 			for sl in g.seed_lens:
 				if dlen == self._get_incog_data_len(sl):
-					die(1,'Valid seed length for this data size: %s bits' % sl)
-			msg(('This data size (%s bytes) is invalid for all available ' +
-				'seed lengths') % dlen)
+					die(1,'Valid seed length for this data size: {} bits'.format(sl))
+			msg('This data size ({} bytes) is invalid for all available seed lengths'.format(dlen))
 			return False
 
 	def _encrypt (self):
@@ -833,7 +820,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!
 		d.iv = get_random(g.aesctr_iv_len)
 		d.iv_id = self._make_iv_chksum(d.iv)
-		msg('New Incog Wallet ID: %s' % d.iv_id)
+		msg('New Incog Wallet ID: {}'.format(d.iv_id))
 		qmsg('Make a record of this value')
 		vmsg(self.msg['record_incog_id'])
 
@@ -844,7 +831,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.key_id = make_chksum_8(d.wrapper_key)
-		vmsg('Key ID: %s' % d.key_id)
+		vmsg('Key ID: {}'.format(d.key_id))
 		d.target_data_len = self._get_incog_data_len(self.seed.length)
 
 	def _format(self):
@@ -876,7 +863,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.incog_id       = self._make_iv_chksum(d.iv)
 		d.enc_incog_data = self.fmt_data[g.aesctr_iv_len:]
-		msg('Incog Wallet ID: %s' % d.incog_id)
+		msg('Incog Wallet ID: {}'.format(d.incog_id))
 		qmsg('Check this value against your records')
 		vmsg(self.msg['check_incog_id'])
 
@@ -885,14 +872,14 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 	def _verify_seed_newfmt(self,data):
 		chk,seed = data[:8],data[8:]
 		if sha256(seed).digest()[:8] == chk:
-			qmsg('Passphrase%s are correct' % (self.msg['dec_chk'] % 'and'))
+			qmsg('Passphrase{} are correct'.format(self.msg['dec_chk'].format('and')))
 			return seed
 		else:
-			msg('Incorrect passphrase%s' % (self.msg['dec_chk'] % 'or'))
+			msg('Incorrect passphrase{}'.format(self.msg['dec_chk'].format('or')))
 			return False
 
 	def _verify_seed_oldfmt(self,seed):
-		m = 'Seed ID: %s.  Is the Seed ID correct?' % make_chksum_8(seed)
+		m = 'Seed ID: {}.  Is the Seed ID correct?'.format(make_chksum_8(seed))
 		if keypress_confirm(m, True):
 			return seed
 		else:
@@ -912,7 +899,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 		d.enc_seed = dd[g.salt_len:]
 
 		key = make_key(d.passwd, d.salt, d.hash_preset, 'main key')
-		qmsg('Key ID: %s' % make_chksum_8(key))
+		qmsg('Key ID: {}'.format(make_chksum_8(key)))
 
 		verify_seed = getattr(self,'_verify_seed_'+
 						('newfmt','oldfmt')[bool(opt.old_incog_fmt)])
@@ -921,7 +908,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 
 		if seed:
 			self.seed = Seed(seed)
-			msg('Seed ID: %s' % self.seed.sid)
+			msg('Seed ID: {}'.format(self.seed.sid))
 			return True
 		else:
 			return False
@@ -969,7 +956,7 @@ harder to find, you're advised to choose a much larger file size than this.
   identify the incog wallet data in the future and to locate the offset
   where the data is hidden in the event you forget it.
 	""",
-		'dec_chk': ', hash preset, offset %s seed length'
+		'dec_chk': ', hash preset, offset {} seed length'
 	}
 
 	def _get_hincog_params(self,wtype):
@@ -980,15 +967,14 @@ harder to find, you're advised to choose a much larger file size than this.
 		d = self.ssdata
 		m = ('Input','Destination')[action=='write']
 		if fn.size < d.hincog_offset + d.target_data_len:
-			die(1,
-	"%s file '%s' has length %s, too short to %s %s bytes of data at offset %s"
-				% (m,fn.name,fn.size,action,d.target_data_len,d.hincog_offset))
+			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))
 
 	def _get_data(self):
 		d = self.ssdata
 		d.hincog_offset = self._get_hincog_params('input')[1]
 
-		qmsg("Getting hidden incog data from file '%s'" % self.infile.name)
+		qmsg("Getting hidden incog data from file '{}'".format(self.infile.name))
 
 		# Already sanity-checked:
 		d.target_data_len = self._get_incog_data_len(opt.seed_len)
@@ -999,8 +985,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)
 		self.fmt_data = os.read(fh,d.target_data_len)
 		os.close(fh)
-		qmsg("Data read from file '%s' at offset %s" %
-				(self.infile.name,d.hincog_offset), 'Data read from file')
+		qmsg("Data read from file '{}' at offset {}".format(self.infile.name,d.hincog_offset))
 
 	# overrides method in SeedSource
 	def write_to_file(self):
@@ -1019,15 +1004,13 @@ harder to find, you're advised to choose a much larger file size than this.
 		try:
 			os.stat(fn)
 		except:
-			if keypress_confirm("Requested file '%s' does not exist.  Create?"
-					% fn, default_yes=True):
+			if keypress_confirm("Requested file '{}' does not exist.  Create?".format(fn),default_yes=True):
 				min_fsize = d.target_data_len + d.hincog_offset
 				msg(self.msg['choose_file_size'].format(min_fsize))
 				while True:
 					fsize = parse_nbytes(my_raw_input('Enter file size: '))
 					if fsize >= min_fsize: break
-					msg('File size must be an integer no less than %s' %
-							min_fsize)
+					msg('File size must be an integer no less than {}'.format(min_fsize))
 
 				from mmgen.tool import Rand2file # threaded routine
 				Rand2file(fn,str(fsize))
@@ -1037,17 +1020,16 @@ harder to find, you're advised to choose a much larger file size than this.
 
 		f = Filename(fn,ftype=type(self),write=True)
 
-		dmsg('%s data len %s, offset %s' % (
-				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:
 			self._check_valid_offset(f,'write')
-			if not opt.quiet: confirm_or_exit('',"alter file '%s'" % f.name)
+			if not opt.quiet:
+				confirm_or_exit('',"alter file '{}'".format(f.name))
 
 		flgs = os.O_RDWR|os.O_BINARY if g.platform == 'win' else os.O_RDWR
 		fh = os.open(f.name,flgs)
 		os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)
 		os.write(fh, self.fmt_data)
 		os.close(fh)
-		msg("%s written to file '%s' at offset %s" % (
-				capfirst(self.desc),f.name,d.hincog_offset))
+		msg("{} written to file '{}' at offset {}".format(capfirst(self.desc),f.name,d.hincog_offset))

+ 4 - 5
mmgen/share/Opts.py

@@ -24,7 +24,7 @@ import sys,getopt
 # from mmgen.util import mdie,die,pdie,pmsg # DEBUG
 
 def usage(opts_data):
-	print('USAGE: %s %s' % (opts_data['prog_name'], opts_data['usage']))
+	print('USAGE: {} {}'.format(opts_data['prog_name'], opts_data['usage']))
 	sys.exit(2)
 
 def print_help_and_exit(opts_data,longhelp=False):
@@ -81,10 +81,9 @@ def process_opts(argv,opts_data,short_opts,long_opts,defer_help=False):
 				if (v and v_in == bool) or v == v_in:
 					if o_out in opts and opts[o_out] != v_out:
 						sys.stderr.write(
-				'Option conflict:\n  --%s=%s, with\n  --%s=%s\n' % (
-					o_out.replace('_','-'),opts[o_out],
-					o_in.replace('_','-'),opts[o_in]
-				))
+							'Option conflict:\n  --{}={}, with\n  --{}={}\n'.format(
+								o_out.replace('_','-'),opts[o_out],
+								o_in.replace('_','-'),opts[o_in]))
 						sys.exit(1)
 					else:
 						opts[o_out] = v_out

+ 7 - 10
mmgen/test.py

@@ -30,7 +30,7 @@ def cleandir(d):
 	try:    files = os.listdir(d)
 	except: return
 
-	msg(green("Cleaning directory '%s'" % d))
+	gmsg("Cleaning directory '{}'".format(d))
 	for f in files:
 		try:
 			os.unlink(os.path.join(d,f))
@@ -49,7 +49,7 @@ def mk_tmpdir(d):
 	except OSError as e:
 		if e.errno != 17: raise
 	else:
-		vmsg("Created directory '%s'" % d)
+		vmsg("Created directory '{}'".format(d))
 
 def mk_tmpdir_path(path,cfg):
 	try:
@@ -64,7 +64,7 @@ def mk_tmpdir_path(path,cfg):
 			os.symlink(src,cfg['tmpdir'])
 	except OSError as e:
 		if e.errno != 17: raise
-	else: msg("Created directory '%s'" % cfg['tmpdir'])
+	else: msg("Created directory '{}'".format(cfg['tmpdir']))
 
 def get_tmpfile_fn(cfg,fn):
 	return os.path.join(cfg['tmpdir'],fn)
@@ -87,7 +87,7 @@ def read_from_tmpfile(cfg,fn,binary=False):
 def ok():
 	if opt.profile: return
 	if opt.verbose or opt.exact_output:
-		sys.stderr.write(green('OK\n'))
+		gmsg('OK')
 	else: msg(' OK')
 
 def ok_or_die(val,chk_func,s,skip_ok=False):
@@ -96,17 +96,14 @@ def ok_or_die(val,chk_func,s,skip_ok=False):
 	if ret:
 		if not skip_ok: ok()
 	else:
-		msg(red("Returned value '%s' is not a %s" % (val,s)))
-		sys.exit(3)
+		rdie(3,"Returned value '{}' is not a {}".format((val,s)))
 
 def cmp_or_die(s,t,skip_ok=False):
 	if s == t:
 		if not skip_ok: ok()
 	else:
-		sys.stderr.write(red(
-			'ERROR: recoded data:\n%s\ndiffers from original data:\n%s\n' %
-				(repr(t),repr(s))))
-		sys.exit(3)
+		m = 'ERROR: recoded data:\n{}\ndiffers from original data:\n{}'
+		rdie(3,m.format(repr(t),repr(s)))
 
 def init_coverage():
 	coverdir = os.path.join('test','trace')

+ 1 - 1
mmgen/tool.py

@@ -122,7 +122,7 @@ def usage(command):
 				c,h = line.split('-',1)
 				Msg('MMGEN-TOOL {}: {}'.format(c.strip().upper(),h.strip()))
 		cd = cmd_data[Command]
-		msg('USAGE: {} {} {}'.format(g.prog_name,command,' '.join(cd)))
+		msg('USAGE: {} {} {}'.format(g.prog_name,command.lower(),' '.join(cd)))
 	else:
 		msg("'{}': no such tool command".format(command))
 	sys.exit(1)

+ 14 - 16
mmgen/tw.py

@@ -103,7 +103,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 			'addr':  lambda i: i.addr,
 			'age':   lambda i: 0 - i.confs,
 			'amt':   lambda i: i.amt,
-			'txid':  lambda i: '%s %03s' % (i.txid,i.vout),
+			'txid':  lambda i: '{} {:04}'.format(i.txid,i.vout),
 			'mmid':  lambda i: i.twmmid.sort_key
 		}
 		key = key or self.sort_key
@@ -173,12 +173,11 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 				else i.twmmid if i.twmmid.type=='mmgen'
 					else 'Non-{}'.format(g.proj_name),width=mmid_w,color=True)
 			if self.show_mmid:
-				addr_out = '%s %s' % (
+				addr_out = '{} {}'.format(
 					type(i.addr).fmtc(addr_dots,width=btaddr_w,color=True) if i.skip == 'addr' \
-						else i.addr.fmt(width=btaddr_w,color=True),
+							else i.addr.fmt(width=btaddr_w,color=True),
 					'{} {}'.format(mmid_disp,i.label.fmt(width=label_w,color=True) \
-							if label_w > 0 else '')
-				)
+							if label_w > 0 else ''))
 			else:
 				addr_out = type(i.addr).fmtc(addr_dots,width=addr_w,color=True) \
 					if i.skip=='addr' else i.addr.fmt(width=addr_w,color=True)
@@ -239,18 +238,17 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 			if ret == 'q': return None,None
 			n = AddrIdx(ret,on_fail='silent') # hacky way to test and convert to integer
 			if not n or n < 1 or n > len(self.unspent):
-				msg('Choice must be a single number between 1 and %s' % len(self.unspent))
+				msg('Choice must be a single number between 1 and {}'.format(len(self.unspent)))
 # 			elif not self.unspent[n-1].mmid:
-# 				msg('Address #%s is not an %s address. No label can be added to it' %
-# 						(n,g.proj_name))
+# 				msg('Address #{} is not an {} address. No label can be added to it'.format(n,g.proj_name))
 			else:
 				while True:
 					s = my_raw_input("Enter label text (or 'q' to return to main menu): ")
 					if s == 'q':
 						return None,None
 					elif s == '':
-						if keypress_confirm(
-							"Removing label for address #%s.  Is this what you want?" % n):
+						fs = "Removing label for address #{}.  Is this what you want?"
+						if keypress_confirm(fs.format(n)):
 							return n,s
 					elif s:
 						if TwComment(s,on_fail='return'):
@@ -274,7 +272,7 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 			elif reply == 'A': self.do_sort('age')
 			elif reply == 'd': self.do_sort('addr')
 			elif reply == 'D': self.show_days = not self.show_days
-			elif reply == 'e': msg('\n%s\n%s\n%s' % (self.fmt_display,prompt,p))
+			elif reply == 'e': msg('\n{}\n{}\n{}'.format(self.fmt_display,prompt,p))
 			elif reply == 'g': self.group = not self.group
 			elif reply == 'l':
 				idx,lbl = self.get_idx_and_label_from_user()
@@ -283,17 +281,17 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 					if type(self).add_label(e.twmmid,lbl,addr=e.addr):
 						self.get_unspent_data()
 						self.do_sort()
-						msg('%s\n%s\n%s' % (self.fmt_display,prompt,p))
+						msg('{}\n{}\n{}'.format(self.fmt_display,prompt,p))
 					else:
-						msg('Label could not be added\n%s\n%s' % (prompt,p))
+						msg('Label could not be added\n{}\n{}'.format(prompt,p))
 			elif reply == 'M': self.do_sort('mmid'); self.show_mmid = True
 			elif reply == 'm': self.show_mmid = not self.show_mmid
 			elif reply == 'p':
 				msg('')
-				of = 'listunspent[%s].out' % ','.join(self.sort_info(include_group=False)).lower()
+				of = 'listunspent[{}].out'.format(','.join(self.sort_info(include_group=False))).lower()
 				write_data_to_file(of,self.format_for_printing(),'unspent outputs listing')
-				m = yellow("Data written to '%s'" % of)
-				msg('\n%s\n%s\n\n%s' % (self.fmt_display,m,prompt))
+				m = yellow("Data written to '{}'".format(of))
+				msg('\n{}\n{}\n\n{}'.format(self.fmt_display,m,prompt))
 				continue
 			elif reply == 'q': return self.unspent
 			elif reply == 'r': self.unspent.reverse(); self.reverse = not self.reverse

+ 7 - 6
mmgen/tx.py

@@ -87,7 +87,7 @@ def select_unspent(unspent,prompt):
 			if selected:
 				if selected[-1] <= len(unspent):
 					return selected
-				msg('Unspent output number must be <= %s' % len(unspent))
+				msg('Unspent output number must be <= {}'.format(len(unspent)))
 
 def mmaddr2coinaddr(mmaddr,ad_w,ad_f):
 
@@ -381,12 +381,13 @@ class MMGenTX(MMGenObject):
 		d = g.rpch.decoderawtransaction(self.hex)
 		vsize = d['vsize'] if 'vsize' in d else d['size']
 		vmsg('\nSize: {}, Vsize: {} (true) {} (estimated)'.format(d['size'],vsize,est_vsize))
-		m1 = 'Estimated transaction vsize is {:1.2f} times the true vsize\n'
+		m1 = '\nERROR: Estimated transaction vsize is {:1.2f} times the true vsize\n'
 		m2 = 'Your transaction fee estimates will be inaccurate\n'
 		m3 = 'Please re-create and re-sign the transaction using the option --vsize-adj={:1.2f}'
 		# allow for 5% error
 		ratio = float(est_vsize) / vsize
-		assert 0.95 < ratio < 1.05, (m1+m2+m3).format(ratio,1/ratio)
+		if not (0.95 < ratio < 1.05):
+			die(2,(m1+m2+m3).format(ratio,1/ratio))
 
 	# https://bitcoin.stackexchange.com/questions/1195/how-to-calculate-transaction-size-before-sending
 	# 180: uncompressed, 148: compressed
@@ -847,7 +848,7 @@ class MMGenTX(MMGenObject):
 			return True
 
 	def write_txid_to_file(self,ask_write=False,ask_write_default_yes=True):
-		fn = '%s[%s].%s' % (self.txid,self.send_amt,self.txid_ext)
+		fn = '{}[{}].{}'.format(self.txid,self.send_amt,self.txid_ext)
 		write_data_to_file(fn,self.coin_txid+'\n','transaction ID',
 			ask_write=ask_write,
 			ask_write_default_yes=ask_write_default_yes)
@@ -968,7 +969,7 @@ class MMGenTX(MMGenObject):
 		enl = ('\n','')[bool(terse)]
 		out += enl
 		if self.label:
-			out += 'Comment: %s\n%s' % (self.label.hl(),enl)
+			out += u'Comment: {}\n{}'.format(self.label.hl(),enl)
 		out += 'Inputs:\n' + enl + format_io(self.inputs)
 		out += 'Outputs:\n' + enl + format_io(self.outputs)
 
@@ -1125,7 +1126,7 @@ class MMGenTX(MMGenObject):
 					coin_addr = mmaddr2coinaddr(a1,ad_w,ad_f) if is_mmgen_id(a1) else CoinAddr(a1)
 					self.add_output(coin_addr,g.proto.coin_amt(a2))
 				else:
-					die(2,"%s: invalid subargument in command-line argument '%s'" % (a1,a))
+					die(2,"{}: invalid subargument in command-line argument '{}'".format(a1,a))
 			elif is_mmgen_id(a) or is_coin_addr(a):
 				if self.get_chg_output_idx() != None:
 					die(2,'ERROR: More than one change address listed on command line')

+ 5 - 5
mmgen/txsign.py

@@ -49,11 +49,11 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds):
 		if infiles:
 			ss = SeedSource(infiles.pop(0),ignore_in_fmt=True)
 		elif opt.in_fmt:
-			qmsg('Need seed data for Seed ID %s' % sid)
+			qmsg('Need seed data for Seed ID {}'.format(sid))
 			ss = SeedSource()
-			msg('User input produced Seed ID %s' % ss.seed.sid)
+			msg('User input produced Seed ID {}'.format(ss.seed.sid))
 		else:
-			die(2,'ERROR: No seed source found for Seed ID: %s' % sid)
+			die(2,'ERROR: No seed source found for Seed ID: {}'.format(sid))
 
 		saved_seeds[ss.seed.sid] = ss.seed
 		if ss.seed.sid == sid: return ss.seed
@@ -61,7 +61,7 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds):
 def generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds):
 	mmids = [e.mmid for e in need_keys]
 	sids = set(i.sid for i in mmids)
-	vmsg('Need seed%s: %s' % (suf(sids,'s'),' '.join(sids)))
+	vmsg('Need seed{}: {}'.format(suf(sids,'s'),' '.join(sids)))
 	d = MMGenList()
 	from mmgen.addr import KeyAddrList
 	for sid in sids:
@@ -95,7 +95,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
 					else:
 						die(3,wmsg['mapping_error'].format(m1,mmid,f.addr,'tx file:',e.mmid,e.addr))
 	if new_keys:
-		vmsg('Added %s wif key%s from %s' % (len(new_keys),suf(new_keys,'s'),desc))
+		vmsg('Added {} wif key{} from {}'.format(len(new_keys),suf(new_keys,'s'),desc))
 	return new_keys
 
 def _pop_and_return(args,cmplist): # strips found args

+ 44 - 48
mmgen/util.py

@@ -31,6 +31,8 @@ def msg_r(s):  sys.stderr.write(s.encode('utf8'))
 def Msg(s):    sys.stdout.write(s.encode('utf8') + '\n')
 def Msg_r(s):  sys.stdout.write(s.encode('utf8'))
 def msgred(s): msg(red(s))
+def rmsg(s):   msg(red(s))
+def rmsg_r(s): msg_r(red(s))
 def ymsg(s):   msg(yellow(s))
 def ymsg_r(s): msg_r(yellow(s))
 def gmsg(s):   msg(green(s))
@@ -107,11 +109,11 @@ def parse_nbytes(nbytes):
 				if k == m.group(2):
 					return int(m.group(1)) * v
 			else:
-				msg("Valid byte specifiers: '%s'" % "' '".join([i[0] for i in smap]))
+				msg("Valid byte specifiers: '{}'".format("' '".join([i[0] for i in smap])))
 		else:
 			return int(nbytes)
 
-	die(1,"'%s': invalid byte specifier" % nbytes)
+	die(1,"'{}': invalid byte specifier".format(nbytes))
 
 def check_or_create_dir(path):
 	try:
@@ -152,7 +154,7 @@ def suf(arg,suf_type='s'):
 	elif any(issubclass(t,c) for c in (list,tuple,set,dict)):
 		n = len(arg)
 	else:
-		die(2,'%s: invalid parameter for suf()' % arg)
+		die(2,'{}: invalid parameter for suf()'.format(arg))
 	return suf_types[suf_type][n==1]
 
 def get_extension(f):
@@ -300,13 +302,13 @@ class baseconv(object):
 	def check_wordlist(cls,wl_id):
 
 		wl = baseconv.digits[wl_id]
-		Msg('Wordlist: %s\nLength: %i words' % (capfirst(wl_id),len(wl)))
+		Msg('Wordlist: {}\nLength: {} words'.format(capfirst(wl_id),len(wl)))
 		new_chksum = cls.get_wordlist_chksum(wl_id)
 
 		a,b = 'generated checksum','saved checksum'
 		compare_chksums(new_chksum,a,cls.wl_chksums[wl_id],b,die_on_fail=True)
 
-		Msg('Checksum %s matches' % new_chksum)
+		Msg('Checksum {} matches'.format(new_chksum))
 		Msg('List is sorted') if tuple(sorted(wl)) == wl else die(3,'ERROR: List is not sorted!')
 
 
@@ -330,7 +332,7 @@ class baseconv(object):
 
 		hexnum = hexnum.strip()
 		if not is_hex_str(hexnum):
-			die(2,"'%s': not a hexadecimal number" % hexnum)
+			die(2,"'{}': not a hexadecimal number".format(hexnum))
 
 		wl = cls.digits[wl_id]
 		base = len(wl)
@@ -378,7 +380,7 @@ def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
 
 def decode_pretty_hexdump(data):
 	from string import hexdigits
-	pat = r'^[%s]+:\s+' % hexdigits
+	pat = r'^[{}]+:\s+'.format(hexdigits)
 	lines = [re.sub(pat,'',l) for l in data.splitlines()]
 	try:
 		return unhexlify(''.join((''.join(lines).split())))
@@ -405,27 +407,26 @@ def get_hash_params(hash_preset):
 	if hash_preset in g.hash_presets:
 		return g.hash_presets[hash_preset] # N,p,r,buflen
 	else: # Shouldn't be here
-		die(3,"%s: invalid 'hash_preset' value" % hash_preset)
+		die(3,"{}: invalid 'hash_preset' value".format(hash_preset))
 
 def compare_chksums(chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False):
 
 	if not chk1 == chk2:
-		m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
-				% ((hdr+':\n   ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
+		fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})"
+		m = fs.format((hdr+':\n   ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
 		if die_on_fail:
 			die(3,m)
 		else:
 			vmsg(m,force=verbose)
 			return False
 
-	vmsg('%s checksum OK (%s)' % (capfirst(desc1),chk1))
+	vmsg('{} checksum OK ({})'.format(capfirst(desc1),chk1))
 	return True
 
 def compare_or_die(val1, desc1, val2, desc2, e='Error'):
 	if cmp(val1,val2):
-		die(3,"%s: %s (%s) doesn't match %s (%s)"
-				% (e,desc2,val2,desc1,val1))
-	dmsg('%s OK (%s)' % (capfirst(desc2),val2))
+		die(3,"{}: {} ({}) doesn't match {} ({})".format(e,desc2,val2,desc1,val1))
+	dmsg('{} OK ({})'.format(capfirst(desc2),val2))
 	return True
 
 def open_file_or_exit(filename,mode,silent=False):
@@ -450,16 +451,15 @@ def check_file_type_and_access(fname,ftype,blkdev_ok=False):
 
 	try: mode = os.stat(fname).st_mode
 	except:
-		die(1,"Unable to stat requested %s '%s'" % (ftype,fname))
+		die(1,"Unable to stat requested {} '{}'".format(ftype,fname))
 
 	for t in ok_types:
 		if t[0](mode): break
 	else:
-		die(1,"Requested %s '%s' is not a %s" % (ftype,fname,
-				' or '.join([t[1] for t in ok_types])))
+		die(1,"Requested {} '{}' is not a {}".format(ftype,fname,' or '.join([t[1] for t in ok_types])))
 
 	if not os.access(fname, access):
-		die(1,"Requested %s '%s' is not %sable by you" % (ftype,fname,m))
+		die(1,"Requested {} '{}' is not {}able by you".format(ftype,fname,m))
 
 	return True
 
@@ -506,23 +506,21 @@ def get_new_passphrase(desc,passchg=False):
 		for i in range(g.passwd_max_tries):
 			pw = ' '.join(get_words_from_user('Enter {}: '.format(w)))
 			pw2 = ' '.join(get_words_from_user('Repeat passphrase: '))
-			dmsg('Passphrases: [%s] [%s]' % (pw,pw2))
+			dmsg('Passphrases: [{}] [{}]'.format(pw,pw2))
 			if pw == pw2:
 				vmsg('Passphrases match'); break
 			else: msg('Passphrases do not match.  Try again.')
 		else:
-			die(2,'User failed to duplicate passphrase in %s attempts' %
-					g.passwd_max_tries)
+			die(2,'User failed to duplicate passphrase in {} attempts'.format(g.passwd_max_tries))
 
 	if pw == '': qmsg('WARNING: Empty passphrase')
 	return pw
 
-def confirm_or_exit(message,question,expect='YES',exit_msg='Exiting at user request'):
+def confirm_or_exit(message,q,expect='YES',exit_msg='Exiting at user request'):
 	m = message.strip()
 	if m: msg(m)
-	a = question+'  ' if question[0].isupper() else \
-			'Are you sure you want to %s?\n' % question
-	b = "Type uppercase '%s' to confirm: " % expect
+	a = q+'  ' if q[0].isupper() else 'Are you sure you want to {}?\n'.format(q)
+	b = "Type uppercase '{}' to confirm: ".format(expect)
 	if my_raw_input(a+b).strip() != expect:
 		die(1,exit_msg)
 
@@ -553,22 +551,22 @@ def write_data_to_file(
 		qmsg('Output to STDOUT requested')
 		if sys.stdout.isatty():
 			if no_tty:
-				die(2,'Printing %s to screen is not allowed' % desc)
+				die(2,'Printing {} to screen is not allowed'.format(desc))
 			if (ask_tty and not opt.quiet) or binary:
-				confirm_or_exit('','output %s to screen' % desc)
+				confirm_or_exit('','output {} to screen'.format(desc))
 		else:
-			try:    of = os.readlink('/proc/%d/fd/1' % os.getpid()) # Linux
+			try:    of = os.readlink('/proc/{}/fd/1'.format(os.getpid())) # Linux
 			except: of = None # Windows
 
 			if of:
 				if of[:5] == 'pipe:':
 					if no_tty:
-						die(2,'Writing %s to pipe is not allowed' % desc)
+						die(2,'Writing {} to pipe is not allowed'.format(desc))
 					if ask_tty and not opt.quiet:
-						confirm_or_exit('','output %s to pipe' % desc)
+						confirm_or_exit('','output {} to pipe'.format(desc))
 						msg('')
 				of2,pd = os.path.relpath(of),os.path.pardir
-				msg("Redirecting output to file '%s'" % (of2,of)[of2[:len(pd)] == pd])
+				msg("Redirecting output to file '{}'".format((of2,of)[of2[:len(pd)] == pd]))
 			else:
 				msg('Redirecting output to file')
 
@@ -583,27 +581,27 @@ def write_data_to_file(
 			outfile = make_full_path(opt.outdir,outfile)
 
 		if ask_write:
-			if not ask_write_prompt: ask_write_prompt = 'Save %s?' % desc
+			if not ask_write_prompt: ask_write_prompt = 'Save {}?'.format(desc)
 			if not keypress_confirm(ask_write_prompt,
 						default_yes=ask_write_default_yes):
-				die(1,'%s not saved' % capfirst(desc))
+				die(1,'{} not saved'.format(capfirst(desc)))
 
 		hush = False
 		if file_exists(outfile) and ask_overwrite:
-			q = "File '%s' already exists\nOverwrite?" % outfile
+			q = "File '{}' already exists\nOverwrite?".format(outfile)
 			confirm_or_exit('',q)
-			msg("Overwriting file '%s'" % outfile)
+			msg("Overwriting file '{}'".format(outfile))
 			hush = True
 
 		f = open_file_or_exit(outfile,('w','wb')[bool(binary)])
 		try:
 			f.write(data)
 		except:
-			die(2,"Failed to write %s to file '%s'" % (desc,outfile))
+			die(2,"Failed to write {} to file '{}'".format(desc,outfile))
 		f.close
 
 		if not (hush or silent):
-			msg("%s written to file '%s'" % (capfirst(desc),outfile))
+			msg(u"{} written to file '{}'".format(capfirst(desc),outfile))
 
 		return True
 
@@ -617,21 +615,19 @@ def write_data_to_file(
 def get_words_from_user(prompt):
 	# split() also strips
 	words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
-	dmsg('Sanitized input: [%s]' % ' '.join(words))
+	dmsg('Sanitized input: [{}]'.format(' '.join(words)))
 	return words
 
-
 def get_words_from_file(infile,desc,silent=False):
 	if not silent:
-		qmsg("Getting %s from file '%s'" % (desc,infile))
+		qmsg(u"Getting {} from file '{}'".format(desc,infile))
 	f = open_file_or_exit(infile, 'r')
 	# split() also strips
 	words = f.read().split()
 	f.close()
-	dmsg('Sanitized input: [%s]' % ' '.join(words))
+	dmsg('Sanitized input: [{}]'.format(' '.join(words)))
 	return words
 
-
 def get_words(infile,desc,prompt):
 	if infile:
 		return get_words_from_file(infile,desc)
@@ -643,7 +639,7 @@ def mmgen_decrypt_file_maybe(fn,desc='',silent=False):
 	have_enc_ext = get_extension(fn) == g.mmenc_ext
 	if have_enc_ext or not is_utf8(d):
 		m = ('Attempting to decrypt','Decrypting')[have_enc_ext]
-		msg("%s %s '%s'" % (m,desc,fn))
+		msg("{} {} '{}'".format(m,desc,fn))
 		from mmgen.crypto import mmgen_decrypt_retry
 		d = mmgen_decrypt_retry(d,desc)
 	return d
@@ -658,13 +654,13 @@ def get_lines_from_file(fn,desc='',trim_comments=False,silent=False):
 def get_data_from_user(desc='data',silent=False):
 	p = ('','Enter {}: '.format(desc))[g.stdin_tty]
 	data = my_raw_input(p,echo=opt.echo_passphrase)
-	dmsg('User input: [%s]' % data)
+	dmsg('User input: [{}]'.format(data))
 	return data
 
 def get_data_from_file(infile,desc='data',dash=False,silent=False,binary=False):
 	if dash and infile == '-': return sys.stdin.read()
 	if not opt.quiet and not silent and desc:
-		qmsg("Getting %s from file '%s'" % (desc,infile))
+		qmsg(u"Getting {} from file '{}'".format(desc,infile))
 	f = open_file_or_exit(infile,('r','rb')[bool(binary)],silent=silent)
 	data = f.read()
 	f.close()
@@ -672,7 +668,7 @@ def get_data_from_file(infile,desc='data',dash=False,silent=False,binary=False):
 
 def pwfile_reuse_warning():
 	if 'passwd_file_used' in globals():
-		qmsg("Reusing passphrase from file '%s' at user request" % opt.passwd_file)
+		qmsg(u"Reusing passphrase from file '{}' at user request".format(opt.passwd_file))
 		return True
 	globals()['passwd_file_used'] = True
 	return False
@@ -736,7 +732,7 @@ def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
 	from mmgen.term import get_char
 
 	while True:
-		reply = get_char('%s: ' % prompt).strip('\n\r')
+		reply = get_char('{}: '.format(prompt)).strip('\n\r')
 
 		if reply in chars or (enter_ok and not reply):
 			msg('')
@@ -777,7 +773,7 @@ def do_license_msg(immed=False):
 
 	p = "Press 'w' for conditions and warranty info, or 'c' to continue:"
 	msg(gpl.warning)
-	prompt = '%s ' % p.strip()
+	prompt = '{} '.format(p.strip())
 
 	from mmgen.term import get_char
 

+ 1 - 2
scripts/compute-file-chksum.py

@@ -22,6 +22,5 @@ start = (1,0)[bool(opt.include_first_line)]
 a = make_chksum_6(' '.join(lines[start:]))
 if start == 1:
 	b = lines[0]
-	m = ("Checksum in file (%s) doesn't match computed value!" % b,'Checksum in file OK')[a==b]
-	msg(m)
+	msg(("Checksum in file ({}) doesn't match computed value!".format(b),'Checksum in file OK')[a==b])
 Msg(a)

+ 1 - 1
scripts/tx-v1-to-v3.py

@@ -86,7 +86,7 @@ def find_block_by_time(timestamp):
 			bot = block_num
 		block_num = (top + bot) / 2
 		if top - bot < 2:
-			msg('\nFound: %s ' % block_num)
+			msg('\nFound: {} '.format(block_num))
 			break
 
 	return block_num

+ 7 - 7
test/gentest.py

@@ -170,7 +170,7 @@ def compare_test():
 
 	for i in range(rounds):
 		if opt.verbose or time.time() - last_t >= 0.1:
-			qmsg_r('\rRound %s/%s ' % (i+1,rounds))
+			qmsg_r('\rRound {}/{} '.format(i+1,rounds))
 			last_t = time.time()
 		sec = PrivKey(os.urandom(32),compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
 		ph = kg_a.to_pubhex(sec)
@@ -188,10 +188,10 @@ def compare_test():
 				match_error(sec,sec.wif,sec.wif,b_wif,a,b)
 		else:
 			b_addr = ag.to_addr(kg_b.to_pubhex(sec))
-		vmsg('\nkey:  %s\naddr: %s\n' % (sec.wif,a_addr))
+		vmsg('\nkey:  {}\naddr: {}\n'.format(sec.wif,a_addr))
 		if a_addr != b_addr:
 			match_error(sec,sec.wif,a_addr,b_addr,a,ext_lib if b == 'ext' else b)
-	qmsg_r('\rRound %s/%s ' % (i+1,rounds))
+	qmsg_r('\rRound {}/{} '.format(i+1,rounds))
 	qmsg(green(('\n','')[bool(opt.verbose)] + 'OK'))
 
 def speed_test():
@@ -206,19 +206,19 @@ def speed_test():
 
 	for i in range(rounds):
 		if time.time() - last_t >= 0.1:
-			qmsg_r('\rRound %s/%s ' % (i+1,rounds))
+			qmsg_r('\rRound {}/{} '.format(i+1,rounds))
 			last_t = time.time()
 		sec = PrivKey(seed+pack('I',i),compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
 		a_addr = ag.to_addr(kg_a.to_pubhex(sec))
-		vmsg('\nkey:  %s\naddr: %s\n' % (sec.wif,a_addr))
-	qmsg_r('\rRound %s/%s ' % (i+1,rounds))
+		vmsg('\nkey:  {}\naddr: {}\n'.format(sec.wif,a_addr))
+	qmsg_r('\rRound {}/{} '.format(i+1,rounds))
 	qmsg('\n{} addresses generated in {:.2f} seconds'.format(rounds,time.time()-start))
 
 def dump_test():
 	m = "Comparing output of address generator '{}' against wallet dump '{}'"
 	qmsg(green(m.format(kg_a.desc,cmd_args[1])))
 	for n,[wif,a_addr] in enumerate(dump,1):
-		qmsg_r('\rKey %s/%s ' % (n,len(dump)))
+		qmsg_r('\rKey {}/{} '.format(n,len(dump)))
 		try:
 			sec = PrivKey(wif=wif)
 		except:

+ 23 - 24
test/mmgen_pexpect.py

@@ -46,14 +46,14 @@ def my_send(p,t,delay=send_delay,s=False):
 	if opt.verbose:
 		ls = (' ','')[bool(opt.debug or not s)]
 		es = ('  ','')[bool(s)]
-		msg('%sSEND %s%s' % (ls,es,yellow("'%s'"%t.replace('\n',r'\n'))))
+		msg('{}SEND {}{}'.format(ls,es,yellow("'{}'"%t.replace('\n',r'\n'))))
 	return ret
 
 def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False,silent=False):
 	quo = ('',"'")[type(s) == str]
 
 	if not silent:
-		if opt.verbose: msg_r('EXPECT %s' % yellow(quo+str(s)+quo))
+		if opt.verbose: msg_r('EXPECT {}'.format(yellow(quo+str(s)+quo)))
 		elif not opt.exact_output: msg_r('+')
 
 	try:
@@ -63,14 +63,15 @@ def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False,silent=False):
 			ret = f(s,timeout=(60,5)[bool(opt.debug_pexpect)])
 	except pexpect.TIMEOUT:
 		if opt.debug_pexpect: raise
-		errmsg(red('\nERROR.  Expect %s%s%s timed out.  Exiting' % (quo,s,quo)))
+		errmsg(red('\nERROR.  Expect {}{}{} timed out.  Exiting'.format(quo,s,quo)))
 		sys.exit(1)
 	debug_pexpect_msg(p)
 
-	if opt.debug or (opt.verbose and type(s) != str): msg_r(' ==> %s ' % ret)
+	if opt.debug or (opt.verbose and type(s) != str):
+		msg_r(' ==> {} '.format(ret))
 
 	if ret == -1:
-		errmsg('Error.  Expect returned %s' % ret)
+		errmsg('Error.  Expect returned {}'.format(ret))
 		sys.exit(1)
 	else:
 		if t == '':
@@ -125,7 +126,7 @@ class MMGenPexpect(object):
 				if not msg_only:
 					sys.stderr.write(clr1('Executing {}{}'.format(clr2(cmd_str),eol)))
 			else:
-				m = 'Testing %s: ' % desc
+				m = 'Testing {}: '.format(desc)
 				msg_r(m)
 
 		if msg_only: return
@@ -136,8 +137,7 @@ class MMGenPexpect(object):
 			f = (call,check_output)[bool(no_output)]
 			ret = f([cmd] + args)
 			if f == call and ret != 0:
-				m = 'ERROR: process returned a non-zero exit status (%s)'
-				die(1,red(m % ret))
+				die(1,red('ERROR: process returned a non-zero exit status ({})'.format(ret)))
 		else:
 			if opt.traceback:
 				cmd,args = g.traceback_cmd,[cmd]+args
@@ -162,14 +162,12 @@ class MMGenPexpect(object):
 	def cmp_or_die(self,s,t,skip_ok=False,exit_val=0):
 		ret = self.p.wait()
 		if ret != exit_val:
-			die(1,red('test.py: spawned program exited with value {}'.format(ret)))
+			rdie(1,'test.py: spawned program exited with value {}'.format(ret))
 		if s == t:
 			if not skip_ok: ok()
 		else:
-			sys.stderr.write(red(
-				'ERROR: recoded data:\n%s\ndiffers from original data:\n%s\n' %
-					(repr(t),repr(s))))
-			sys.exit(3)
+			fs = 'ERROR: recoded data:\n{}\ndiffers from original data:\n{}'
+			rdie(3,fs.format(repr(t),repr(s)))
 
 	def license(self):
 		if 'MMGEN_NO_LICENSE' in os.environ: return
@@ -181,8 +179,8 @@ class MMGenPexpect(object):
 		my_expect(self.p,p,label+'\n')
 
 	def usr_rand_out(self,saved=False):
-		m = '%suser-supplied entropy' % (('','saved ')[saved])
-		my_expect(self.p,'Generating encryption key from OS random data plus ' + m)
+		fs = 'Generating encryption key from OS random data plus {}user-supplied entropy'
+		my_expect(self.p,fs.format(('','saved ')[saved]))
 
 	def usr_rand(self,num_chars):
 		if opt.usr_random:
@@ -202,20 +200,21 @@ class MMGenPexpect(object):
 			my_expect(self.p,'ENTER to continue: ','\n')
 
 	def passphrase_new(self,desc,passphrase):
-		my_expect(self.p,('Enter passphrase for %s: ' % desc), passphrase+'\n')
-		my_expect(self.p,'Repeat passphrase: ', passphrase+'\n')
+		my_expect(self.p,'Enter passphrase for {}: '.format(desc),passphrase+'\n')
+		my_expect(self.p,'Repeat passphrase: ',passphrase+'\n')
 
 	def passphrase(self,desc,passphrase,pwtype=''):
 		if pwtype: pwtype += ' '
-		my_expect(self.p,('Enter %spassphrase for %s.*?: ' % (pwtype,desc)),
+		my_expect(self.p,
+				'Enter {}passphrase for {}.*?: '.format(pwtype,desc),
 				passphrase+'\n',regex=True)
 
 	def hash_preset(self,desc,preset=''):
-		my_expect(self.p,('Enter hash preset for %s' % desc))
-		my_expect(self.p,('or hit ENTER .*?:'), str(preset)+'\n',regex=True)
+		my_expect(self.p,'Enter hash preset for {}'.format(desc))
+		my_expect(self.p,'or hit ENTER .*?:',str(preset)+'\n',regex=True)
 
 	def written_to_file(self,desc,overwrite_unlikely=False,query='Overwrite?  ',oo=False):
-		s1 = '%s written to file ' % desc
+		s1 = '{} written to file '.format(desc)
 		s2 = query + "Type uppercase 'YES' to confirm: "
 		ret = my_expect(self.p,([s1,s2],s1)[overwrite_unlikely])
 		if ret == 1:
@@ -227,8 +226,8 @@ class MMGenPexpect(object):
 # 				ret = my_expect(self.p,s1)
 		self.expect(self.NL,nonl=True)
 		outfile = self.p.before.strip().strip("'")
-		if opt.debug_pexpect: msgred('Outfile [%s]' % outfile)
-		vmsg('%s file: %s' % (desc,cyan(outfile.replace("'",''))))
+		if opt.debug_pexpect: rmsg('Outfile [{}]'.format(outfile))
+		vmsg('{} file: {}'.format(desc,cyan(outfile.replace("'",''))))
 		return outfile
 
 	def no_overwrite(self):
@@ -249,7 +248,7 @@ class MMGenPexpect(object):
 		self.expect(self.NL,nonl=True,silent=True)
 		debug_pexpect_msg(self.p)
 		end = self.p.before
-		vmsg(' ==> %s' % cyan(end))
+		vmsg(' ==> {}'.format(cyan(end)))
 		return end
 
 	def interactive(self):

+ 1 - 1
test/scrambletest.py

@@ -111,4 +111,4 @@ run_tests()
 
 t = int(time.time()) - start_time
 m = '\nAll requested tests finished OK, elapsed time: {:02}:{:02}'
-msg(green(m.format(t/60,t%60)))
+gmsg(m.format(t/60,t%60))

+ 56 - 64
test/test.py

@@ -91,14 +91,14 @@ if not any(e in ('--skip-deps','--resume','-S','-r') for e in sys.argv+shortopts
 	else:
 		d,pfx = '/dev/shm','mmgen-test-'
 		try:
-			subprocess.call('rm -rf %s/%s*'%(d,pfx),shell=True)
+			subprocess.call('rm -rf {}/{}*'.format(d,pfx),shell=True)
 		except Exception as e:
-			die(2,'Unable to delete directory tree %s/%s* (%s)'%(d,pfx,e))
+			die(2,'Unable to delete directory tree {}/{}* ({})'.format(d,pfx,e))
 		try:
 			import tempfile
 			shm_dir = tempfile.mkdtemp('',pfx,d)
 		except Exception as e:
-			die(2,'Unable to create temporary directory in %s (%s)'%(d,e))
+			die(2,'Unable to create temporary directory in {} ({})'.format(d,e))
 		dd = os.path.join(shm_dir,'data_dir')
 		os.mkdir(dd,0755)
 		try: os.unlink(data_dir)
@@ -851,7 +851,7 @@ for a,b in cmd_group['ref']:
 	for i,j in ((1,128),(2,192),(3,256)):
 		k = a+str(i)
 		cmd_list['ref'].append(k)
-		cmd_data[k] = (5+i,'%s (%s-bit)' % (b[1],j),[[b[0],5+i]])
+		cmd_data[k] = (5+i,'{} ({}-bit)'.format(b[1],j),[[b[0],5+i]])
 
 cmd_data['info_ref_other'] = 'other reference data',[8]
 for a,b in cmd_group['ref_other']:
@@ -863,14 +863,14 @@ for a,b in cmd_group['conv_in']:
 	for i,j in ((1,128),(2,192),(3,256)):
 		k = a+str(i)
 		cmd_list['conv_in'].append(k)
-		cmd_data[k] = (10+i,'%s (%s-bit)' % (b,j),[[[],10+i]])
+		cmd_data[k] = (10+i,'{} ({}-bit)'.format(b,j),[[[],10+i]])
 
 cmd_data['info_conv_out'] = 'wallet conversion to reference data',[11,12,13]
 for a,b in cmd_group['conv_out']:
 	for i,j in ((1,128),(2,192),(3,256)):
 		k = a+str(i)
 		cmd_list['conv_out'].append(k)
-		cmd_data[k] = (10+i,'%s (%s-bit)' % (b,j),[[[],10+i]])
+		cmd_data[k] = (10+i,'{} ({}-bit)'.format(b,j),[[[],10+i]])
 
 cmd_data['info_regtest'] = 'regtest mode',[17]
 for a,b in cmd_group['regtest']:
@@ -939,10 +939,10 @@ if opt.profile: opt.names = True
 if opt.resume: opt.skip_deps = True
 if opt.log:
 	log_fd = open(log_file,'a')
-	log_fd.write('\nLog started: %s\n' % make_timestr())
+	log_fd.write('\nLog started: {}\n'.format(make_timestr()))
 
 usr_rand_chars = (5,30)[bool(opt.usr_random)]
-usr_rand_arg = '-r%s' % usr_rand_chars
+usr_rand_arg = '-r{}'.format(usr_rand_chars)
 cmd_total = 0
 
 # Disable color in spawned scripts so we can parse their output
@@ -993,8 +993,7 @@ if opt.list_cmds:
 	w = max(map(len,cmd_data))
 	for cmd in cmd_data:
 		if cmd[:5] == 'info_':
-			m = capfirst(cmd_data[cmd][0])
-			Msg(green('  %s:' % m))
+			Msg(green('  {}:'.format(capfirst(cmd_data[cmd][0]))))
 			continue
 		Msg('  '+fs.format(cmd,cmd_data[cmd][1],w=w))
 
@@ -1047,15 +1046,15 @@ def get_addrfile_checksum(display=False):
 	addrfile = get_file_with_ext('addrs',cfg['tmpdir'])
 	silence()
 	chk = AddrList(addrfile).chksum
-	if opt.verbose and display: msg('Checksum: %s' % cyan(chk))
+	if opt.verbose and display: msg('Checksum: {}'.format(cyan(chk)))
 	end_silence()
 	return chk
 
 def verify_checksum_or_exit(checksum,chk):
 	if checksum != chk:
-		errmsg(red('Checksum error: %s' % chk))
+		errmsg(red('Checksum error: {}'.format(chk)))
 		sys.exit(1)
-	vmsg(green('Checksums match: %s') % (cyan(chk)))
+	vmsg(green('Checksums match: ') + cyan(chk))
 
 from test.mmgen_pexpect import MMGenPexpect
 class MMGenExpect(MMGenPexpect):
@@ -1096,7 +1095,7 @@ def create_fake_unspent_entry(coinaddr,al_id=None,idx=None,lbl=None,non_mmgen=Fa
 			else (u'{}:{}{}'.format(al_id,idx,lbl.decode('utf8'))),
 		'vout': int(getrandnum(4) % 8),
 		'txid': hexlify(os.urandom(32)).decode('utf8'),
-		'amount': g.proto.coin_amt('%s.%s' % (amt1+(getrandnum(4) % amt2), getrandnum(4) % 100000000)),
+		'amount': g.proto.coin_amt('{}.{}'.format(amt1 + getrandnum(4) % amt2, getrandnum(4) % 100000000)),
 		'address': coinaddr,
 		'spendable': False,
 		'scriptPubKey': '{}{}{}'.format(spk_beg,coinaddr.hex,spk_end),
@@ -1155,11 +1154,11 @@ def write_fake_data_to_file(d):
 	unspent_data_file = os.path.join(cfg['tmpdir'],'unspent.json')
 	write_data_to_file(unspent_data_file,d,'Unspent outputs',silent=True)
 	os.environ['MMGEN_BOGUS_WALLET_DATA'] = unspent_data_file
-	bwd_msg = 'MMGEN_BOGUS_WALLET_DATA=%s' % unspent_data_file
+	bwd_msg = 'MMGEN_BOGUS_WALLET_DATA={}'.format(unspent_data_file)
 	if opt.print_cmdline: msg(bwd_msg)
 	if opt.log: log_fd.write(bwd_msg + ' ')
 	if opt.verbose or opt.exact_output:
-		sys.stderr.write("Fake transaction wallet data written to file '%s'\n" % unspent_data_file)
+		sys.stderr.write("Fake transaction wallet data written to file '{}'\n".format(unspent_data_file))
 
 def create_tx_data(sources):
 	tx_data,ad = {},AddrData()
@@ -1169,8 +1168,7 @@ def create_tx_data(sources):
 		ad.add(al)
 		aix = AddrIdxList(fmt_str=cfgs[s]['addr_idx_list'])
 		if len(aix) != addrs_per_wallet:
-			errmsg(red('Address index list length != %s: %s' %
-						(addrs_per_wallet,repr(aix))))
+			errmsg(red('Address index list length != {}: {}'.format(addrs_per_wallet,repr(aix))))
 			sys.exit(0)
 		tx_data[s] = {
 			'addrfile': afile,
@@ -1191,8 +1189,7 @@ def make_txcreate_cmdline(tx_data):
 	for k in cfgs:
 		cfgs[k]['amts'] = [None,None]
 		for idx,mod in enumerate(mods):
-			cfgs[k]['amts'][idx] = '%s.%s' % ((getrandnum(4) % mod), str(getrandnum(4))[:5])
-
+			cfgs[k]['amts'][idx] = '{}.{}'.format(getrandnum(4) % mod, str(getrandnum(4))[:5])
 
 	cmd_args = ['-d',cfg['tmpdir']]
 	for num in tx_data:
@@ -1209,10 +1206,10 @@ def make_txcreate_cmdline(tx_data):
 
 def add_comments_to_addr_file(addrfile,outfile):
 	silence()
-	msg(green("Adding comments to address file '%s'" % addrfile))
+	gmsg("Adding comments to address file '{}'".format(addrfile))
 	a = AddrList(addrfile)
 	for n,idx in enumerate(a.idxs(),1):
-		if n % 2: a.set_comment(idx,'Test address %s' % n)
+		if n % 2: a.set_comment(idx,'Test address {}'.format(n))
 	a.format(enable_comments=True)
 	write_data_to_file(outfile,a.fmt_data,silent=True)
 	end_silence()
@@ -1227,7 +1224,7 @@ def make_brainwallet_file(fn):
 		return ''.join([ws_list[getrandnum(1)%len(ws_list)] for i in range(nchars)])
 	rand_pairs = [wl[getrandnum(4) % len(wl)] + rand_ws_seq() for i in range(nwords)]
 	d = ''.join(rand_pairs).rstrip() + '\n'
-	if opt.verbose: msg_r('Brainwallet password:\n%s' % cyan(d))
+	if opt.verbose: msg_r('Brainwallet password:\n{}'.format(cyan(d)))
 	write_data_to_file(fn,d,'brainwallet password',silent=True)
 
 def do_between():
@@ -1296,27 +1293,26 @@ def check_needs_rerun(
 	return rerun
 
 def refcheck(desc,chk,refchk):
-	vmsg("Comparing %s '%s' to stored reference" % (desc,chk))
+	vmsg("Comparing {} '{}' to stored reference".format(desc,chk))
 	if chk == refchk:
 		ok()
 	else:
 		if not opt.verbose: errmsg('')
-		errmsg(red("""
-Fatal error - %s '%s' does not match reference value '%s'.  Aborting test
-""".strip() % (desc,chk,refchk)))
+		m = "Fatal error - {} '{}' does not match reference value '{}'.  Aborting test"
+		errmsg(red(m.format(desc,chk,refchk)))
 		sys.exit(3)
 
 def check_deps(cmds):
 	if len(cmds) != 1:
-		die(1,'Usage: %s check_deps <command>' % g.prog_name)
+		die(1,'Usage: {} check_deps <command>'.format(g.prog_name))
 
 	cmd = cmds[0]
 
 	if cmd not in cmd_data:
-		die(1,"'%s': unrecognized command" % cmd)
+		die(1,"'{}': unrecognized command".format(cmd))
 
 	if not opt.quiet:
-		msg("Checking dependencies for '%s'" % (cmd))
+		msg("Checking dependencies for '{}'".format(cmd))
 
 	check_needs_rerun(ts,cmd,build=False)
 
@@ -1336,7 +1332,7 @@ def clean(usr_dirs=[]):
 		if str(d) in all_dirs:
 			cleandir(all_dirs[str(d)])
 		else:
-			die(1,'%s: invalid directory number' % d)
+			die(1,'{}: invalid directory number'.format(d))
 	cleandir(os.path.join('test','data_dir'))
 
 def skip_for_win():
@@ -1384,7 +1380,7 @@ class MMGenTestSuite(object):
 
 		if opt.resume:
 			if cmd == opt.resume:
-				msg(yellow("Resuming at '%s'" % cmd))
+				ymsg("Resuming at '{}'".format(cmd))
 				opt.resume = False
 				opt.skip_deps = False
 			else:
@@ -1408,7 +1404,7 @@ class MMGenTestSuite(object):
 			'walletgen','walletconv','walletchk','txcreate','txsign','txsend','txdo','txbump',
 			'addrgen','addrimport','keygen','passchg','tool','passgen','regtest','autosign')
 		for s in scripts:
-			t = MMGenExpect(name,('mmgen-'+s),[arg],extra_desc='(mmgen-%s)'%s,no_output=True)
+			t = MMGenExpect(name,('mmgen-'+s),[arg],extra_desc='(mmgen-{})'.format(s),no_output=True)
 			t.read()
 			t.ok()
 
@@ -1434,10 +1430,9 @@ class MMGenTestSuite(object):
 		self.walletgen(name,seed_len=seed_len,gen_dfl_wallet=True)
 
 	def brainwalletgen_ref(self,name):
-		sl_arg = '-l%s' % cfg['seed_len']
-		hp_arg = '-p%s' % ref_wallet_hash_preset
-		label = "test.py ref. wallet (pw '%s', seed len %s)" \
-				% (ref_wallet_brainpass,cfg['seed_len'])
+		sl_arg = '-l{}'.format(cfg['seed_len'])
+		hp_arg = '-p{}'.format(ref_wallet_hash_preset)
+		label = "test.py ref. wallet (pw '{}', seed len {})".format(ref_wallet_brainpass,cfg['seed_len'])
 		bf = 'ref.mmbrain'
 		args = ['-d',cfg['tmpdir'],hp_arg,sl_arg,'-ib','-L',label]
 		write_to_tmpfile(cfg,bf,ref_wallet_brainpass)
@@ -1489,14 +1484,14 @@ class MMGenTestSuite(object):
 				add_args+args+['-p',hp]+wf_arg,
 				extra_desc=extra_desc)
 		if desc != 'hidden incognito data':
-			t.expect("Getting %s from file '" % (desc))
+			t.expect("Getting {} from file '".format(desc))
 		if pw:
 			t.passphrase(desc,cfg['wpasswd'])
 			t.expect(
 				['Passphrase is OK', 'Passphrase.* are correct'],
 				regex=True
 				)
-		chk = t.expect_getend('Valid %s for Seed ID ' % desc)[:8]
+		chk = t.expect_getend('Valid {} for Seed ID '.format(desc))[:8]
 		if sid: t.cmp_or_die(chk,sid)
 		else: t.ok()
 
@@ -1653,7 +1648,7 @@ class MMGenTestSuite(object):
 		if seed_args: # sign and send
 			t.expect('Edit transaction comment? (y/N): ','\n')
 			for cnum,desc in (('1','incognito data'),('3','MMGen wallet'),('4','MMGen wallet')):
-				t.passphrase(('%s' % desc),cfgs[cnum]['wpasswd'])
+				t.passphrase(desc,cfgs[cnum]['wpasswd'])
 			t.expect("Type uppercase 'YES' to confirm: ",'YES\n')
 		else:
 			t.expect('Add a comment to transaction? (y/N): ','\n')
@@ -1674,7 +1669,7 @@ class MMGenTestSuite(object):
 	def txsign_end(self,t,tnum=None,has_label=False):
 		t.expect('Signing transaction')
 		cprompt = ('Add a comment to transaction','Edit transaction comment')[has_label]
-		t.expect('%s? (y/N): ' % cprompt,'\n')
+		t.expect('{}? (y/N): '.format(cprompt),'\n')
 		t.expect('Save signed transaction.*?\? \(Y/n\): ','y',regex=True)
 		add = ' #' + tnum if tnum else ''
 		t.written_to_file('Signed transaction' + add, oo=True)
@@ -1693,7 +1688,7 @@ class MMGenTestSuite(object):
 			t.ok()
 		else:
 			cprompt = ('Add a comment to transaction','Edit transaction comment')[has_label]
-			t.expect('%s? (y/N): ' % cprompt,'\n')
+			t.expect('{}? (y/N): '.format(cprompt),'\n')
 			t.expect('Save signed transaction? (Y/n): ','n')
 			t.ok(exit_val=1)
 
@@ -1712,7 +1707,7 @@ class MMGenTestSuite(object):
 			t.expect('Add a comment to transaction? (y/N): ','\n')
 		t.expect('Are you sure you want to broadcast this')
 		m = 'YES, I REALLY WANT TO DO THIS'
-		t.expect("'%s' to confirm: " % m,m+'\n')
+		t.expect("'{}' to confirm: ".format(m),m+'\n')
 		if really_send:
 			txid = t.expect_getend('Transaction sent: ')
 			assert len(txid) == 64
@@ -1751,7 +1746,7 @@ class MMGenTestSuite(object):
 	def export_seed(self,name,wf,desc='seed data',out_fmt='seed',pf=None):
 		f,t = self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt,pf=pf)
 		silence()
-		msg('%s: %s' % (capfirst(desc),cyan(get_data_from_file(f,desc))))
+		msg('{}: {}'.format(capfirst(desc),cyan(get_data_from_file(f,desc))))
 		end_silence()
 		t.ok()
 
@@ -1775,7 +1770,7 @@ class MMGenTestSuite(object):
 	# TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
 	def export_incog_hidden(self,name,wf):
 		rf = os.path.join(cfg['tmpdir'],hincog_fn)
-		add_args = ['-J','%s,%s'%(rf,hincog_offset)]
+		add_args = ['-J','{},{}'.format(rf,hincog_offset)]
 		self.export_incog(
 			name,wf,desc='hidden incognito data',out_fmt='hi',add_args=add_args)
 
@@ -1785,7 +1780,7 @@ class MMGenTestSuite(object):
 		t = MMGenExpect(name,'mmgen-addrgen', add_args +
 				['-i'+in_fmt,'-d',cfg['tmpdir'],wf,cfg['addr_idx_list']])
 		t.license()
-		t.expect_getend('Valid %s for Seed ID ' % desc)
+		t.expect_getend('Valid {} for Seed ID '.format(desc))
 		vmsg('Comparing generated checksum with checksum from previous address file')
 		chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
 		if stdout: t.read()
@@ -1808,7 +1803,7 @@ class MMGenTestSuite(object):
 		t.license()
 		t.expect_getend('Incog Wallet ID: ')
 		t.hash_preset(desc,'1')
-		t.passphrase('%s \w{8}' % desc, cfg['wpasswd'])
+		t.passphrase('{} \w{{8}}'.format(desc),cfg['wpasswd'])
 		vmsg('Comparing generated checksum with checksum from address file')
 		chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
 		verify_checksum_or_exit(get_addrfile_checksum(),chk)
@@ -1821,7 +1816,7 @@ class MMGenTestSuite(object):
 	def addrgen_incog_hidden(self,name,wf,foo):
 		rf = os.path.join(cfg['tmpdir'],hincog_fn)
 		self.addrgen_incog(name,[],'',in_fmt='hi',desc='hidden incognito data',
-			args=['-H','%s,%s'%(rf,hincog_offset),'-l',str(hincog_seedlen)])
+			args=['-H','{},{}'.format(rf,hincog_offset),'-l',str(hincog_seedlen)])
 
 	def keyaddrgen(self,name,wf,pf=None,check_ref=False,mmtype=None):
 		if cfg['segwit'] and not mmtype:
@@ -1937,7 +1932,7 @@ class MMGenTestSuite(object):
 		non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
 		add_args = ['-d',cfg['tmpdir'],'-i','brain','-b'+cfg['bw_params'],'-p1','-k',non_mm_fn,'-M',f12]
 		t = self.txcreate_common(name,sources=['1','2','3','4','14'],non_mmgen_input='4',do_label=1,txdo_args=[f7,f8,f9,f10],add_args=add_args)
-		os.system('rm -f %s/*.sigtx' % cfg['tmpdir'])
+		os.system('rm -f {}/*.sigtx'.format(cfg['tmpdir']))
 		self.txsign4(name,f7,f8,f9,f10,f11,f12,txdo_handle=t)
 		self.txsend(name,'',txdo_handle=t)
 		os.system('touch ' + os.path.join(cfg['tmpdir'],'txdo'))
@@ -1960,7 +1955,7 @@ class MMGenTestSuite(object):
 			t.tx_view(view='terse')
 
 		for cnum,desc in (('1','incognito data'),('3','MMGen wallet')):
-			t.passphrase(('%s' % desc),cfgs[cnum]['wpasswd'])
+			t.passphrase('{}'.format(desc),cfgs[cnum]['wpasswd'])
 
 		if txdo_handle: return
 		self.txsign_end(t,has_label=True)
@@ -2037,10 +2032,10 @@ class MMGenTestSuite(object):
 
 	def tool_find_incog_data(self,name,f1,f2):
 		i_id = read_from_file(f2).rstrip()
-		vmsg('Incog ID: %s' % cyan(i_id))
+		vmsg('Incog ID: {}'.format(cyan(i_id)))
 		t = MMGenExpect(name,'mmgen-tool',
 				['-d',cfg['tmpdir'],'find_incog_data',f1,i_id])
-		o = t.expect_getend('Incog data for ID %s found at offset ' % i_id)
+		o = t.expect_getend('Incog data for ID {} found at offset '.format(i_id))
 		os.unlink(f1)
 		cmp_or_die(hincog_offset,int(o))
 
@@ -2112,7 +2107,7 @@ class MMGenTestSuite(object):
 	def ref_hincog_conv(self,name,wfk='hic_wallet',add_uopts=[]):
 		ic_f = os.path.join(ref_dir,cfg[wfk])
 		uopts = ['-i','hi','-p','1','-l',str(cfg['seed_len'])] + add_uopts
-		hi_opt = ['-H','%s,%s' % (ic_f,ref_wallet_incog_offset)]
+		hi_opt = ['-H','{},{}'.format(ic_f,ref_wallet_incog_offset)]
 		self.walletconv_in(name,None,'hidden incognito data',uopts+hi_opt,oo=True,pw=True)
 
 	def ref_hincog_conv_old(self,name):
@@ -2138,7 +2133,7 @@ class MMGenTestSuite(object):
 
 	def ref_hincog_conv_out(self,name,extra_uopts=[]):
 		ic_f = os.path.join(cfg['tmpdir'],hincog_fn)
-		hi_parms = '%s,%s' % (ic_f,ref_wallet_incog_offset)
+		hi_parms = '{},{}'.format(ic_f,ref_wallet_incog_offset)
 		sl_parm = '-l' + str(cfg['seed_len'])
 		self.walletconv_out(name,
 			'hidden incognito data', 'hi',
@@ -2152,7 +2147,7 @@ class MMGenTestSuite(object):
 		self.walletchk(name,wf,pf=None,pw=True,sid=cfg['seed_id'])
 
 	def ref_ss_chk(self,name,ss=None):
-		wf = os.path.join(ref_dir,'%s.%s' % (cfg['seed_id'],ss.ext))
+		wf = os.path.join(ref_dir,'{}.{}'.format(cfg['seed_id'],ss.ext))
 		self.walletchk(name,wf,pf=None,desc=ss.desc,sid=cfg['seed_id'])
 
 	def ref_seed_chk(self,name):
@@ -2169,7 +2164,7 @@ class MMGenTestSuite(object):
 
 	def ref_brain_chk(self,name,bw_file=ref_bw_file):
 		wf = os.path.join(ref_dir,bw_file)
-		add_args = ['-l%s' % cfg['seed_len'], '-p'+ref_bw_hash_preset]
+		add_args = ['-l{}'.format(cfg['seed_len']), '-p'+ref_bw_hash_preset]
 		self.walletchk(name,wf,pf=None,add_args=add_args,
 			desc='brainwallet',sid=cfg['ref_bw_seed_id'])
 
@@ -2179,11 +2174,8 @@ class MMGenTestSuite(object):
 	def ref_hincog_chk(self,name,desc='hidden incognito data'):
 		for wtype,edesc,of_arg in ('hic_wallet','',[]), \
 								('hic_wallet_old','(old format)',['-O']):
-			ic_arg = ['-H%s,%s' % (
-						os.path.join(ref_dir,cfg[wtype]),
-						ref_wallet_incog_offset
-					)]
-			slarg = ['-l%s ' % cfg['seed_len']]
+			ic_arg = ['-H{},{}'.format(os.path.join(ref_dir,cfg[wtype]),ref_wallet_incog_offset)]
+			slarg = ['-l{} '.format(cfg['seed_len'])]
 			hparg = ['-p1']
 			if wtype == 'hic_wallet_old' and opt.profile: msg('')
 			t = MMGenExpect(name,'mmgen-walletchk',
@@ -2325,7 +2317,7 @@ class MMGenTestSuite(object):
 		infile = os.path.join(ref_dir,cfg['seed_id']+'.mmwords')
 		t = MMGenExpect(name,'mmgen-walletconv',[usr_rand_arg]+opts+[infile],extra_desc='(convert)')
 
-		add_args = ['-l%s' % cfg['seed_len']]
+		add_args = ['-l{}'.format(cfg['seed_len'])]
 		t.license()
 		if pw:
 			t.passphrase_new('new '+desc,cfg['wpasswd'])
@@ -3009,13 +3001,13 @@ try:
 			elif arg in cmd_data:
 				check_needs_rerun(ts,arg,build=True)
 			else:
-				die(1,'%s: unrecognized command' % arg)
+				die(1,'{}: unrecognized command'.format(arg))
 	else:
 		clean()
 		for cmd in cmd_data:
 			if cmd == 'info_regtest': break # don't run everything after this by default
 			if cmd[:5] == 'info_':
-				msg(green('%sTesting %s' % (('\n','')[bool(opt.resume)],cmd_data[cmd][0])))
+				gmsg('{}Testing {}'.format(('\n','')[bool(opt.resume)],cmd_data[cmd][0]))
 				continue
 			ts.do_cmd(cmd)
 			if cmd is not cmd_data.keys()[-1]: do_between()

+ 14 - 15
test/tooltest.py

@@ -260,8 +260,8 @@ class MMGenToolTestSuite(object):
 		a,b = p.communicate()
 		retcode = p.wait()
 		if retcode != 0:
-			msg('%s\n%s\n%s'%(red('FAILED'),yellow('Command stderr output:'),b))
-			die(1,red('Called process returned with an error (retcode %s)' % retcode))
+			msg('{}\n{}\n{}'.format(red('FAILED'),yellow('Command stderr output:'),b))
+			rdie(1,'Called process returned with an error (retcode {})'.format(retcode))
 		return (a,a.rstrip())[bool(strip)]
 
 	def run_cmd_chk(self,name,f1,f2,kwargs='',extra_msg='',strip_hex=False,add_opts=[]):
@@ -274,8 +274,8 @@ class MMGenToolTestSuite(object):
 			return (a.lstrip('0') == b.lstrip('0')) if strip_hex else (a == b)
 		if cmp_equal(ret,idata): ok()
 		else:
-			die(3,red(
-	"Error: values don't match:\nIn:  %s\nOut: %s" % (repr(idata),repr(ret))))
+			fs = "Error: values don't match:\nIn:  {!r}\nOut: {!r}"
+			rdie(3,fs.format(idata,ret))
 		return ret
 
 	def run_cmd_nochk(self,name,f1,kwargs='',add_opts=[]):
@@ -286,12 +286,12 @@ class MMGenToolTestSuite(object):
 		return ret
 
 	def run_cmd_out(self,name,carg=None,Return=False,kwargs='',fn_idx='',extra_msg='',literal=False,chkdata='',hush=False,add_opts=[]):
-		if carg: write_to_tmpfile(cfg,'%s%s.in' % (name,fn_idx),carg+'\n')
+		if carg: write_to_tmpfile(cfg,'{}{}.in'.format(name,fn_idx),carg+'\n')
 		ret = self.run_cmd(name,([],[carg])[bool(carg)],kwargs=kwargs,extra_msg=extra_msg,add_opts=add_opts)
 		if carg: vmsg('In:   ' + repr(carg))
 		vmsg('Out:  ' + (repr(ret),ret.decode('utf8'))[literal])
 		if ret or ret == '':
-			write_to_tmpfile(cfg,'%s%s.out' % (name,fn_idx),ret+'\n')
+			write_to_tmpfile(cfg,'{}{}.out'.format(name,fn_idx),ret+'\n')
 			if chkdata:
 				cmp_or_die(ret,chkdata)
 				return
@@ -299,7 +299,7 @@ class MMGenToolTestSuite(object):
 			else:
 				if not hush: ok()
 		else:
-			die(3,red("Error for command '%s'" % name))
+			rdie(3,"Error for command '{}'".format(name))
 
 	def run_cmd_randinput(self,name,strip=True,add_opts=[]):
 		s = os.urandom(128)
@@ -309,7 +309,7 @@ class MMGenToolTestSuite(object):
 		fn = name+'.out'
 		write_to_tmpfile(cfg,fn,ret+'\n')
 		ok()
-		vmsg('Returned: %s' % ret)
+		vmsg('Returned: {}'.format(ret))
 
 	# Util
 	def Strtob58(self,name):       self.run_cmd_out(name,getrandstr(16))
@@ -328,11 +328,11 @@ class MMGenToolTestSuite(object):
 	def Id8(self,name):     self.run_cmd_randinput(name)
 	def Str2id6(self,name):
 		s = getrandstr(120,no_space=True)
-		s2 = ' %s %s %s %s %s ' % (s[:3],s[3:9],s[9:29],s[29:50],s[50:120])
+		s2 = ' {} {} {} {} {} '.format(s[:3],s[3:9],s[9:29],s[29:50],s[50:120])
 		ret1 = self.run_cmd(name,[s],extra_msg='unspaced input'); ok()
 		ret2 = self.run_cmd(name,[s2],extra_msg='spaced input')
 		cmp_or_die(ret1,ret2)
-		vmsg('Returned: %s' % ret1)
+		vmsg('Returned: {}'.format(ret1))
 	def Hash160(self,name):        self.run_cmd_out(name,getrandhex(16))
 	def Hash256(self,name):        self.run_cmd_out(name,getrandstr(16))
 	def Hexreverse(self,name):     self.run_cmd_out(name,getrandhex(24))
@@ -488,20 +488,19 @@ if cmd_args:
 		die(1,'Only one command may be specified')
 	cmd = cmd_args[0]
 	if cmd in cmd_data:
-		msg('Running tests for %s:' % cmd_data[cmd]['desc'])
+		msg('Running tests for {}:'.format(cmd_data[cmd]['desc']))
 		ts.do_cmds(cmd)
 	elif cmd == 'clean':
 		cleandir(cfg['tmpdir'])
 		sys.exit(0)
 	else:
-		die(1,"'%s': unrecognized command" % cmd)
+		die(1,"'{}': unrecognized command".format(cmd))
 else:
 	cleandir(cfg['tmpdir'])
 	for cmd in cmd_data:
-		msg('Running tests for %s:' % cmd_data[cmd]['desc'])
+		msg('Running tests for {}:'.format(cmd_data[cmd]['desc']))
 		ts.do_cmds(cmd)
 		if cmd is not cmd_data.keys()[-1]: msg('')
 
 t = int(time.time()) - start_time
-msg(green(
-	'All requested tests finished OK, elapsed time: %02i:%02i' % (t/60,t%60)))
+gmsg('All requested tests finished OK, elapsed time: {:02}:{:02}'.format(t/60,t%60))