string formatting, whitespace, minor fixes throughout

This commit is contained in:
The MMGen Project 2020-06-01 09:25:56 +00:00
commit 5e745f2a08
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
7 changed files with 155 additions and 113 deletions

View file

@ -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(':')

View file

@ -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)

View file

@ -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):

View file

@ -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):

View file

@ -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()

View file

@ -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},

View file

@ -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)