Browse Source

string formatting, whitespace, minor fixes throughout

The MMGen Project 4 years ago
parent
commit
5e745f2a08
7 changed files with 155 additions and 113 deletions
  1. 42 25
      mmgen/obj.py
  2. 1 1
      mmgen/protocol.py
  3. 98 69
      mmgen/tw.py
  4. 1 1
      mmgen/tx.py
  5. 11 5
      test/objtest.py
  6. 0 12
      test/objtest_py_d/ot_btc_mainnet.py
  7. 2 0
      test/test.py

+ 42 - 25
mmgen/obj.py

@@ -108,13 +108,15 @@ class InitErrors(object):
 			e2_fmt = '({}) '.format(e2.args[0]) if e2 else ''
 			errmsg = fs.format(m,objname or cls.__name__,e2_fmt,e.args[0])
 
-		if m2: errmsg = '{!r}\n{}'.format(m2,errmsg)
+		if m2:
+			errmsg = '{!r}\n{}'.format(m2,errmsg)
 
 		from .util import die,msg
 		if cls.on_fail == 'silent':
 			return None # TODO: return False instead?
 		elif cls.on_fail == 'return':
-			if errmsg: msg(errmsg)
+			if errmsg:
+				msg(errmsg)
 			return None # TODO: return False instead?
 		elif g.traceback or cls.on_fail == 'raise':
 			if hasattr(cls,'exc'):
@@ -127,8 +129,9 @@ class InitErrors(object):
 	@classmethod
 	def method_not_implemented(cls):
 		import traceback
-		raise NotImplementedError('method {!r} not implemented for class {!r}'.format(
-				traceback.extract_stack()[-2].name, cls.__name__))
+		raise NotImplementedError(
+			'method {!r} not implemented for class {!r}'.format(
+				traceback.extract_stack()[-2].name, cls.__name__) )
 
 class Hilite(object):
 
@@ -231,8 +234,10 @@ class ImmutableAttr: # Descriptor
 
 	def __init__(self,dtype,typeconv=True,set_none_ok=False,include_proto=False):
 		assert isinstance(dtype,self.ok_dtypes), 'ImmutableAttr_check1'
-		if include_proto: assert typeconv and type(dtype) == str, 'ImmutableAttr_check2'
-		if set_none_ok:   assert typeconv and type(dtype) != str, 'ImmutableAttr_check3'
+		if include_proto:
+			assert typeconv and type(dtype) == str, 'ImmutableAttr_check2'
+		if set_none_ok:
+			assert typeconv and type(dtype) != str, 'ImmutableAttr_check3'
 
 		if dtype is None:
 			'use instance-defined conversion function for this attribute'
@@ -365,14 +370,16 @@ class AddrIdxList(list,InitErrors,MMGenObject):
 					j = i.split('-')
 					if len(j) == 1:
 						idx = AddrIdx(i,on_fail='raise')
-						if not idx: break
+						if not idx:
+							break
 						ret.append(idx)
 					elif len(j) == 2:
 						beg = AddrIdx(j[0],on_fail='raise')
-						if not beg: break
+						if not beg:
+							break
 						end = AddrIdx(j[1],on_fail='raise')
-						if not beg: break
-						if end < beg: break
+						if not beg or (end < beg):
+							break
 						ret.extend([AddrIdx(x,on_fail='raise') for x in range(beg,end+1)])
 					else: break
 				else:
@@ -391,7 +398,8 @@ class MMGenRange(tuple,InitErrors,MMGenObject):
 		try:
 			if len(args) == 1:
 				s = args[0]
-				if type(s) == cls: return s
+				if type(s) == cls:
+					return s
 				assert isinstance(s,str),'not a string or string subclass'
 				ss = s.split('-',1)
 				first = int(ss[0])
@@ -442,7 +450,8 @@ class BTCAmt(Decimal,Hilite,InitErrors):
 
 	# NB: 'from_decimal' rounds down to precision of 'min_coin_unit'
 	def __new__(cls,num,from_unit=None,from_decimal=False,on_fail='die'):
-		if type(num) == cls: return num
+		if type(num) == cls:
+			return num
 		cls.arg_chk(on_fail)
 		try:
 			if from_unit:
@@ -480,7 +489,8 @@ class BTCAmt(Decimal,Hilite,InitErrors):
 		cls.method_not_implemented()
 
 	def fmt(self,fs=None,color=False,suf='',prec=1000):
-		if fs == None: fs = self.amt_fs
+		if fs == None:
+			fs = self.amt_fs
 		s = str(int(self)) if int(self) == self else self.normalize().__format__('f')
 		if '.' in fs:
 			p1,p2 = list(map(int,fs.split('.',1)))
@@ -576,7 +586,8 @@ class SeedID(str,Hilite,InitErrors):
 	width = 8
 	trunc_ok = False
 	def __new__(cls,seed=None,sid=None,on_fail='die'):
-		if type(sid) == cls: return sid
+		if type(sid) == cls:
+			return sid
 		cls.arg_chk(on_fail)
 		try:
 			if seed:
@@ -586,7 +597,7 @@ class SeedID(str,Hilite,InitErrors):
 				return str.__new__(cls,make_chksum_8(seed.data))
 			elif sid:
 				assert set(sid) <= set(hexdigits.upper()),'not uppercase hex digits'
-				assert len(sid) == cls.width,'not {} characters wide'.format(cls.width)
+				assert len(sid) == cls.width, f'not {cls.width} characters wide'
 				return str.__new__(cls,sid)
 			raise ValueError('no arguments provided')
 		except Exception as e:
@@ -596,7 +607,8 @@ class SubSeedIdx(str,Hilite,InitErrors):
 	color = 'red'
 	trunc_ok = False
 	def __new__(cls,s,on_fail='die'):
-		if type(s) == cls: return s
+		if type(s) == cls:
+			return s
 		cls.arg_chk(on_fail)
 		try:
 			assert isinstance(s,str),'not a string or string subclass'
@@ -676,7 +688,7 @@ class TwLabel(str,InitErrors,MMGenObject):
 			ts = text.split(None,1)
 			mmid = TwMMGenID(proto,ts[0],on_fail='raise')
 			comment = TwComment(ts[1] if len(ts) == 2 else '',on_fail='raise')
-			me = str.__new__(cls,'{}{}'.format(mmid,' {}'.format(comment) if comment else ''))
+			me = str.__new__( cls, mmid + (' ' + comment if comment else '') )
 			me.mmid = mmid
 			me.comment = comment
 			me.proto = proto
@@ -690,16 +702,18 @@ class HexStr(str,Hilite,InitErrors):
 	hexcase = 'lower'
 	trunc_ok = False
 	def __new__(cls,s,on_fail='die',case=None):
-		if type(s) == cls: return s
+		if type(s) == cls:
+			return s
 		cls.arg_chk(on_fail)
-		if case == None: case = cls.hexcase
+		if case == None:
+			case = cls.hexcase
 		try:
 			assert isinstance(s,str),'not a string or string subclass'
-			assert case in ('upper','lower'),"'{}' incorrect case specifier".format(case)
-			assert set(s) <= set(getattr(hexdigits,case)()),'not {}case hexadecimal symbols'.format(case)
+			assert case in ('upper','lower'), f'{case!r} incorrect case specifier'
+			assert set(s) <= set(getattr(hexdigits,case)()), f'not {case}case hexadecimal symbols'
 			assert not len(s) % 2,'odd-length string'
 			if cls.width:
-				assert len(s) == cls.width,'Value is not {} characters wide'.format(cls.width)
+				assert len(s) == cls.width, f'Value is not {cls.width} characters wide'
 			return str.__new__(cls,s)
 		except Exception as e:
 			return cls.init_fail(e,s)
@@ -755,7 +769,8 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
 	# initialize with (priv_bin,compressed), WIF or self
 	def __new__(cls,proto,s=None,compressed=None,wif=None,pubkey_type=None,on_fail='die'):
 
-		if type(s) == cls: return s
+		if type(s) == cls:
+			return s
 		cls.arg_chk(on_fail)
 
 		if wif:
@@ -821,7 +836,8 @@ class MMGenLabel(str,Hilite,InitErrors):
 	max_screen_width = 0 # if != 0, overrides max_len
 	desc = 'label'
 	def __new__(cls,s,on_fail='die',msg=None):
-		if type(s) == cls: return s
+		if type(s) == cls:
+			return s
 		cls.arg_chk(on_fail)
 		for k in cls.forbidden,cls.allowed:
 			assert type(k) == list
@@ -874,7 +890,8 @@ class MMGenPWIDString(MMGenLabel):
 class SeedSplitSpecifier(str,Hilite,InitErrors,MMGenObject):
 	color = 'red'
 	def __new__(cls,s,on_fail='raise'):
-		if type(s) == cls: return s
+		if type(s) == cls:
+			return s
 		cls.arg_chk(on_fail)
 		try:
 			arr = s.split(':')

+ 1 - 1
mmgen/protocol.py

@@ -166,7 +166,7 @@ class CoinProtocol(MMGenObject):
 			return False
 
 		def coin_addr(self,addr):
-			return CoinAddr(proto=self,addr=addr)
+			return CoinAddr( proto=self, addr=addr )
 
 		def addr_type(self,id_str,on_fail='die'):
 			return MMGenAddrType(proto=self,id_str=id_str,on_fail=on_fail)

+ 98 - 69
mmgen/tw.py

@@ -32,9 +32,12 @@ CUR_HOME,ERASE_ALL = '\033[H','\033[0J'
 def CUR_RIGHT(n): return '\033[{}C'.format(n)
 
 def get_tw_label(proto,s):
-	try: return TwLabel(proto,s,on_fail='raise')
-	except BadTwComment: raise
-	except: return None
+	try:
+		return TwLabel(proto,s,on_fail='raise')
+	except BadTwComment:
+		raise
+	except:
+		return None
 
 _date_formatter = {
 	'days':      lambda rpc,secs: (rpc.cur_date - secs) // 86400,
@@ -113,13 +116,6 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
 			def amt2(self,value):
 				return self.proto.coin_amt(value)
 
-	wmsg = {
-	'no_spendable_outputs': """
-No spendable outputs found!  Import addresses with balances into your
-watch-only wallet using '{}-addrimport' and then re-run this program.
-""".strip().format(g.proj_name.lower())
-	}
-
 	async def __ainit__(self,proto,minconf=1,addrs=[]):
 		self.proto        = proto
 		self.unspent      = self.MMGenTwOutputList()
@@ -146,7 +142,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 	@age_fmt.setter
 	def age_fmt(self,val):
 		if val not in self.age_fmts:
-			raise BadAgeFormat("'{}': invalid age format (must be one of {!r})".format(val,self.age_fmts))
+			raise BadAgeFormat(f'{val!r}: invalid age format (must be one of {self.age_fmts!r})')
 		self._age_fmt = val
 
 	def get_display_precision(self):
@@ -176,7 +172,10 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 			us_raw = await self.get_unspent_rpc()
 
 		if not us_raw:
-			die(0,self.wmsg['no_spendable_outputs'])
+			die(0,fmt(f"""
+				No spendable outputs found!  Import addresses with balances into your
+				watch-only wallet using '{g.proj_name.lower()}-addrimport' and then re-run this program.
+			""").strip())
 
 		lbl_id = ('account','label')['label_api' in self.rpc.caps]
 
@@ -214,7 +213,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 		}
 		key = key or self.sort_key
 		if key not in sort_funcs:
-			die(1,"'{}': invalid sort key.  Valid options: {}".format(key,' '.join(sort_funcs.keys())))
+			die(1,f'{key!r}: invalid sort key.  Valid options: {" ".join(sort_funcs.keys())}')
 		self.sort_key = key
 		assert type(reverse) == bool
 		self.unspent.sort(key=sort_funcs[key],reverse=reverse or self.reverse)
@@ -230,10 +229,11 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 		from .term import get_terminal_size
 		while True:
 			self.cols = g.terminal_width or get_terminal_size().width
-			if self.cols >= g.min_screen_width: break
-			m1 = 'Screen too narrow to display the tracking wallet\n'
-			m2 = 'Please resize your screen to at least {} characters and hit ENTER '
-			my_raw_input((m1+m2).format(g.min_screen_width))
+			if self.cols >= g.min_screen_width:
+				break
+			my_raw_input(
+				'Screen too narrow to display the tracking wallet\n'
+				+ f'Please resize your screen to at least {g.min_screen_width} characters and hit ENTER ' )
 
 	def get_display_constants(self):
 		unsp = self.unspent
@@ -360,20 +360,29 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 			max_lbl_len = max([len(i.label) for i in self.unspent if i.label] or [2])
 			for n,i in enumerate(self.unspent):
 				yield fs.format(
-					n  = str(n+1)+')',
-					t  = '{},{}'.format('|'+'.'*63 if i.skip == 'txid' and self.group else i.txid,i.vout),
+					n  = str(n+1) + ')',
+					t  = '{},{}'.format(
+							('|'+'.'*63 if i.skip == 'txid' and self.group else i.txid),
+							i.vout ),
 					a  = (
 						'|'+'.' * addr_w if i.skip == 'addr' and self.group else
 						i.addr.fmt(color=color,width=addr_w) ),
-					m  = MMGenID.fmtc(i.twmmid if i.twmmid.type=='mmgen' else
-						'Non-{}'.format(g.proj_name),width = mmid_w,color=color),
+					m  = MMGenID.fmtc(
+							(i.twmmid if i.twmmid.type == 'mmgen' else f'Non-{g.proj_name}'),
+							width = mmid_w,
+							color = color ),
 					A  = i.amt.fmt(color=color),
 					A2 = ( i.amt2.fmt(color=color) if i.amt2 is not None else '' ),
 					c  = i.confs,
 					b  = self.rpc.blockcount - (i.confs - 1),
 					D  = self.age_disp(i,'date_time'),
 					l  = i.label.hl(color=color) if i.label else
-						TwComment.fmtc('',color = color,nullrepl='-',width=max_lbl_len) ).rstrip()
+						TwComment.fmtc(
+							s        = '',
+							color    = color,
+							nullrepl = '-',
+							width    = max_lbl_len )
+					).rstrip()
 
 		fs2 = '{} (block #{}, {} UTC)\n{}Sort order: {}\n{}\n\nTotal {}: {}\n'
 		self.fmt_print = fs2.format(
@@ -399,11 +408,11 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 	def get_idx_from_user(self,action):
 		msg('')
 		while True:
-			ret = my_raw_input('Enter {} number (or RETURN to return to main menu): '.format(self.item_desc))
+			ret = my_raw_input(f'Enter {self.item_desc} number (or RETURN to return to main menu): ')
 			if ret == '': return (None,None) if action == 'a_lbl_add' else None
 			n = AddrIdx(ret,on_fail='silent')
 			if not n or n < 1 or n > len(self.unspent):
-				msg('Choice must be a single number between 1 and {}'.format(len(self.unspent)))
+				msg(f'Choice must be a single number between 1 and {len(self.unspent)}')
 			else:
 				if action == 'a_lbl_add':
 					while True:
@@ -411,17 +420,17 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 						if s == 'q':
 							return None,None
 						elif s == '':
-							fs = "Removing label for {} #{}.  Is this what you want?"
-							if keypress_confirm(fs.format(self.item_desc,n)):
+							if keypress_confirm(
+									f'Removing label for {self.item_desc} #{n}.  Is this what you want?'):
 								return n,s
 						elif s:
 							if TwComment(s,on_fail='return'):
 								return n,s
 				else:
 					if action == 'a_addr_delete':
-						fs = "Removing {} #{} from tracking wallet.  Is this what you want?"
+						fs = 'Removing {} #{} from tracking wallet.  Is this what you want?'
 					elif action == 'a_balance_refresh':
-						fs = "Refreshing tracking wallet {} #{}.  Is this what you want?"
+						fs = 'Refreshing tracking wallet {} #{}.  Is this what you want?'
 					if keypress_confirm(fs.format(self.item_desc,n)):
 						return n
 
@@ -468,7 +477,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 					e = self.unspent[idx-1]
 					bal = await self.wallet.get_balance(e.addr,force_rpc=True)
 					await self.get_unspent_data()
-					oneshot_msg = yellow('{} balance for account #{} refreshed\n\n'.format(self.proto.dcoin,idx))
+					oneshot_msg = yellow(f'{self.proto.dcoin} balance for account #{idx} refreshed\n\n')
 				self.display_constants = self.get_display_constants()
 			elif action == 'a_lbl_add':
 				idx,lbl = self.get_idx_from_user(action)
@@ -476,8 +485,10 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 					e = self.unspent[idx-1]
 					if await self.wallet.add_label(e.twmmid,lbl,addr=e.addr):
 						await self.get_unspent_data()
-						a = 'added to' if lbl else 'removed from'
-						oneshot_msg = yellow("Label {} {} #{}\n\n".format(a,self.item_desc,idx))
+						oneshot_msg = yellow('Label {} {} #{}\n\n'.format(
+							('added to' if lbl else 'removed from'),
+							self.item_desc,
+							idx ))
 					else:
 						oneshot_msg = red('Label could not be added\n\n')
 				self.display_constants = self.get_display_constants()
@@ -487,22 +498,29 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 					e = self.unspent[idx-1]
 					if await self.wallet.remove_address(e.addr):
 						await self.get_unspent_data()
-						oneshot_msg = yellow("{} #{} removed\n\n".format(capfirst(self.item_desc),idx))
+						oneshot_msg = yellow(f'{capfirst(self.item_desc)} #{idx} removed\n\n')
 					else:
 						oneshot_msg = red('Address could not be removed\n\n')
 				self.display_constants = self.get_display_constants()
 			elif action == 'a_print':
-				of = '{}-{}[{}].out'.format(self.dump_fn_pfx,self.proto.dcoin,
-										','.join(self.sort_info(include_group=False)).lower())
+				of = '{}-{}[{}].out'.format(
+					self.dump_fn_pfx,
+					self.proto.dcoin,
+					','.join(self.sort_info(include_group=False)).lower() )
 				msg('')
 				try:
-					write_data_to_file(of,await self.format_for_printing(),desc='{} listing'.format(self.desc))
+					write_data_to_file(
+						of,
+						await self.format_for_printing(),
+						desc = f'{self.desc} listing' )
 				except UserNonConfirmation as e:
-					oneshot_msg = red("File '{}' not overwritten by user request\n\n".format(of))
+					oneshot_msg = red(f'File {of!r} not overwritten by user request\n\n')
 				else:
-					oneshot_msg = yellow("Data written to '{}'\n\n".format(of))
+					oneshot_msg = yellow(f'Data written to {of!r}\n\n')
 			elif action in ('a_view','a_view_wide'):
-				do_pager(self.fmt_display if action == 'a_view' else await self.format_for_printing(color=True))
+				do_pager(
+					self.fmt_display if action == 'a_view' else
+					await self.format_for_printing(color=True) )
 				if g.platform == 'linux' and oneshot_msg == None:
 					msg_r(CUR_RIGHT(len(prompt.split('\n')[-1])-2))
 					no_output = True
@@ -533,7 +551,7 @@ class TwAddrList(MMGenDict,metaclass=aInitMeta):
 			for mmid in sorted(a.mmid for a in acct_labels if a):
 				if mmid == mmid_prev:
 					err = True
-					msg('Duplicate MMGen ID ({}) discovered in tracking wallet!\n'.format(mmid))
+					msg(f'Duplicate MMGen ID ({mmid}) discovered in tracking wallet!\n')
 				mmid_prev = mmid
 			if err: rdie(3,'Tracking wallet is corrupted!')
 
@@ -567,14 +585,15 @@ class TwAddrList(MMGenDict,metaclass=aInitMeta):
 						die(2,'duplicate {} address ({}) for this MMGen address! ({})'.format(
 							proto.coin,
 							d['address'],
-							self[lm]['addr']) )
+							self[lm]['addr'] ))
 				else:
 					lm.confs = d['confirmations']
 					lm.txid = d['txid']
 					lm.date = None
-					self[lm] = {'amt': proto.coin_amt('0'),
-								'lbl': label,
-								'addr': CoinAddr(proto,d['address'])}
+					self[lm] = {
+						'amt': proto.coin_amt('0'),
+						'lbl': label,
+						'addr': CoinAddr(proto,d['address']) }
 				self[lm]['amt'] += d['amount']
 				self.total += d['amount']
 
@@ -614,7 +633,7 @@ class TwAddrList(MMGenDict,metaclass=aInitMeta):
 		if not self.has_age:
 			show_age = False
 		if age_fmt not in self.age_fmts:
-			raise BadAgeFormat("'{}': invalid age format (must be one of {!r})".format(age_fmt,self.age_fmts))
+			raise BadAgeFormat(f'{age_fmt!r}: invalid age format (must be one of {self.age_fmts!r})')
 		fs = '{mid}' + ('',' {addr}')[showbtcaddrs] + ' {cmt} {amt}' + ('',' {age}')[show_age]
 		mmaddrs = [k for k in self.keys() if k.type == 'mmgen']
 		max_mmid_len = max(len(k) for k in mmaddrs) + 2 if mmaddrs else 10
@@ -671,7 +690,9 @@ class TwAddrList(MMGenDict,metaclass=aInitMeta):
 					age=self.age_disp(mmid,age_fmt) if show_age and hasattr(mmid,'confs') else '-'
 					).rstrip()
 
-			yield '\nTOTAL: {} {}'.format(self.total.hl(color=True),self.proto.dcoin)
+			yield '\nTOTAL: {} {}'.format(
+				self.total.hl(color=True),
+				self.proto.dcoin )
 
 		return '\n'.join(gen_output())
 
@@ -694,7 +715,7 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 			mode = 'w'
 
 		if g.debug:
-			print_stack_trace('TW INIT {!r} {!r}'.format(mode,self))
+			print_stack_trace(f'TW INIT {mode!r} {self!r}')
 
 		self.rpc = await rpc_init(proto) # TODO: create on demand - only certain ops require RPC
 		self.proto = proto
@@ -707,8 +728,10 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 			self.init_empty()
 
 		if self.data['coin'] != self.proto.coin: # TODO remove?
-			m = 'Tracking wallet coin ({}) does not match current coin ({})!'
-			raise WalletFileError(m.format(self.data['coin'],self.proto.coin))
+			raise WalletFileError(
+				'Tracking wallet coin ({}) does not match current coin ({})!'.format(
+					self.data['coin'],
+					self.proto.coin ))
 
 		self.conv_types(self.data[self.data_key])
 		self.cur_balances = {} # cache balances to prevent repeated lookups per program invocation
@@ -739,8 +762,7 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 				self.init_empty()
 				self.force_write()
 			else:
-				m = "File '{}' exists but does not contain valid json data"
-				raise WalletFileError(m.format(self.tw_fn))
+				raise WalletFileError(f'File {self.tw_fn!r} exists but does not contain valid json data')
 		else:
 			self.upgrade_wallet_maybe()
 
@@ -748,7 +770,7 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 		if self.mode == 'w':
 			import atexit
 			def del_tw(tw):
-				dmsg('Running exit handler del_tw() for {!r}'.format(tw))
+				dmsg(f'Running exit handler del_tw() for {tw!r}')
 				del tw
 			atexit.register(del_tw,self)
 
@@ -766,7 +788,7 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 		Since no exceptions are raised, errors will not be caught by the test suite.
 		"""
 		if g.debug:
-			print_stack_trace('TW DEL {!r}'.format(self))
+			print_stack_trace(f'TW DEL {self!r}')
 
 		if self.mode == 'w':
 			self.write()
@@ -846,17 +868,22 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 	@write_mode
 	def write_changed(self,data):
 		write_data_to_file(
-			self.tw_fn,data,
-			desc='{} data'.format(self.base_desc),
-			ask_overwrite=False,ignore_opt_outdir=True,quiet=True,
-			check_data=True,cmp_data=self.orig_data)
+			self.tw_fn,
+			data,
+			desc              = f'{self.base_desc} data',
+			ask_overwrite     = False,
+			ignore_opt_outdir = True,
+			quiet             = True,
+			check_data        = True,
+			cmp_data          = self.orig_data )
+
 		self.orig_data = data
 
 	def write(self): # use 'check_data' to check wallet hasn't been altered by another program
 		if not self.use_tw_file:
 			dmsg("'use_tw_file' is False, doing nothing")
 			return
-		dmsg('write(): checking if {} data has changed'.format(self.desc))
+		dmsg(f'write(): checking if {self.desc} data has changed')
 		wdata = json.dumps(self.data)
 
 		if self.orig_data != wdata:
@@ -903,11 +930,11 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 
 		try:
 			if not is_mmgen_id(self.proto,arg1):
-				assert coinaddr,"Invalid coin address for this chain: {}".format(arg1)
-			assert coinaddr,"{pn} address '{ma}' not found in tracking wallet"
-			assert await self.is_in_wallet(coinaddr),"Address '{ca}' not found in tracking wallet"
+				assert coinaddr, f'Invalid coin address for this chain: {arg1}'
+			assert coinaddr, f'{g.proj_name} address {mmaddr!r} not found in tracking wallet'
+			assert await self.is_in_wallet(coinaddr), f'Address {coinaddr!r} not found in tracking wallet'
 		except Exception as e:
-			msg(e.args[0].format(pn=g.proj_name,ma=mmaddr,ca=coinaddr))
+			msg(str(e))
 			return False
 
 		# Allow for the possibility that BTC addr of MMGen addr was entered.
@@ -917,7 +944,7 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 			mmaddr = (await TwAddrData(proto=self.proto)).coinaddr2mmaddr(coinaddr)
 
 		if not mmaddr:
-			mmaddr = '{}:{}'.format(self.proto.base_coin.lower(),coinaddr)
+			mmaddr = f'{self.proto.base_coin.lower()}:{coinaddr}'
 
 		mmaddr = TwMMGenID(self.proto,mmaddr)
 
@@ -929,14 +956,16 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 
 		if await self.set_label(coinaddr,lbl) == False:
 			if not silent:
-				msg('Label could not be {}'.format(('removed','added')[bool(label)]))
+				msg( 'Label could not be {}'.format('added' if label else 'removed') )
 			return False
 		else:
-			m = mmaddr.type.replace('mmg','MMG')
-			a = mmaddr.replace(self.proto.base_coin.lower()+':','')
-			s = '{} address {} in tracking wallet'.format(m,a)
-			if label: msg("Added label '{}' to {}".format(label,s))
-			else:     msg('Removed label from {}'.format(s))
+			desc = '{} address {} in tracking wallet'.format(
+				mmaddr.type.replace('mmg','MMG'),
+				mmaddr.replace(self.proto.base_coin.lower()+':','') )
+			if label:
+				msg(f'Added label {label!r} to {desc}')
+			else:
+				msg(f'Removed label from {desc}')
 			return True
 
 	@write_mode
@@ -945,7 +974,7 @@ class TrackingWallet(MMGenObject,metaclass=aInitMeta):
 
 	@write_mode
 	async def remove_address(self,addr):
-		raise NotImplementedError('address removal not implemented for coin {}'.format(self.proto.coin))
+		raise NotImplementedError(f'address removal not implemented for coin {self.proto.coin}')
 
 class TwGetBalance(MMGenObject,metaclass=aInitMeta):
 

+ 1 - 1
mmgen/tx.py

@@ -737,7 +737,7 @@ class MMGenTX:
 					if selected:
 						if selected[-1] <= len(unspent):
 							return selected
-						msg('Unspent output number must be <= {}'.format(len(unspent)))
+						msg(f'Unspent output number must be <= {len(unspent)}')
 
 		def select_unspent_cmdline(self,unspent):
 

+ 11 - 5
test/objtest.py

@@ -100,6 +100,7 @@ def run_test(test,arg,input_data):
 			raise UserWarning("Non-'None' return value {} with bad input data".format(repr(ret)))
 		if opt.silent and input_data=='good' and ret==bad_ret:
 			raise UserWarning("'None' returned with good input data")
+
 		if input_data=='good':
 			if ret_idx:
 				ret_chk = arg[list(arg.keys())[ret_idx]].encode()
@@ -108,7 +109,8 @@ def run_test(test,arg,input_data):
 		if not opt.super_silent:
 			try: ret_disp = ret.decode()
 			except: ret_disp = ret
-			msg('==> {!r}'.format(ret_disp))
+			msg(f'==> {ret_disp!r}')
+
 		if opt.verbose and issubclass(cls,MMGenObject):
 			ret.pmsg() if hasattr(ret,'pmsg') else pmsg(ret)
 	except Exception as e:
@@ -121,10 +123,10 @@ def run_test(test,arg,input_data):
 		if input_data == 'good':
 			raise ValueError('Error on good input data')
 		if opt.verbose:
-			msg('exitval: {}'.format(e.code))
+			msg(f'exitval: {e.code}')
 	except UserWarning as e:
-		msg('==> {!r}'.format(ret))
-		die(2,red('{}'.format(e.args[0])))
+		msg(f'==> {ret!r}')
+		die(2,red(str(e)))
 
 def do_loop():
 	import importlib
@@ -143,7 +145,11 @@ def do_loop():
 			if not opt.silent:
 				msg(purple(capfirst(k)+' input:'))
 			for arg in test_data[test][k]:
-				run_test(test,arg,input_data=k)
+				run_test(
+					test,
+					arg,
+					input_data = k,
+				)
 
 from mmgen.protocol import init_proto_from_opts
 proto = init_proto_from_opts()

+ 0 - 12
test/objtest_py_d/ot_btc_mainnet.py

@@ -149,18 +149,6 @@ tests = {
 			{'id_str':'F00BAA12:S:9999999', 'proto':proto},
 		),
 	},
-	'TwLabel': {
-		'bad':  ('x x','x я','я:я',1,'f00f00f','a:b','x:L:3','F00BAA12:0 x',
-				'F00BAA12:Z:99',tw_pfx+' x',tw_pfx+'я x',
-				'F00BAA12:S:1 '+ utf8_ctrl[:40],
-				{'s':'F00BAA12:S:1 '+ utf8_ctrl[:40],'on_fail':'raise','ExcType':'BadTwComment'},
-				),
-		'good': (
-			('F00BAA12:99 a comment','F00BAA12:L:99 a comment'),
-			'F00BAA12:L:99 comment (UTF-8) α',
-			'F00BAA12:S:9999999 comment',
-			tw_pfx+'x comment')
-	},
 	'TwLabel': {
 		'bad':  (
 			{'text':'x x',           'proto':proto},

+ 2 - 0
test/test.py

@@ -167,6 +167,8 @@ def add_cmdline_opts():
 
 # add_cmdline_opts()
 
+opts.UserOpts._reset_ok += ('skip_deps',)
+
 # step 2: opts.init will create new data_dir in ./test (if not 'resume' or 'skip_deps'):
 usr_args = opts.init(opts_data)