Hilite: new hl2(), fmt2() methods; optimize fmt(), fmtc()
This commit is contained in:
parent
749dff6dd1
commit
217cd7ad39
15 changed files with 106 additions and 81 deletions
|
|
@ -172,9 +172,14 @@ class CoinAddr(str,Hilite,InitErrors,MMGenObject):
|
|||
return self._parsed
|
||||
|
||||
@classmethod
|
||||
def fmtc(cls,addr,**kwargs):
|
||||
w = kwargs['width'] or cls.width
|
||||
return super().fmtc(addr[:w-2]+'..' if w < len(addr) else addr, **kwargs)
|
||||
def fmtc(cls,s,width,**kwargs):
|
||||
return super().fmtc( s=s[:width-2]+'..' if len(s) > width else s, width=width, **kwargs )
|
||||
|
||||
def fmt(self,width,**kwargs):
|
||||
return (
|
||||
super().fmtc( s=self[:width-2]+'..', width=width, **kwargs ) if len(self) > width else
|
||||
super().fmt( width=width, **kwargs )
|
||||
)
|
||||
|
||||
def is_coin_addr(proto,s):
|
||||
return get_obj( CoinAddr, proto=proto, addr=s, silent=True, return_bool=True )
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ def print_shares_info():
|
|||
shares[0].sid,
|
||||
share1.sid,
|
||||
master_idx,
|
||||
id_str.hl(encl="''"),
|
||||
id_str.hl2(encl='‘’'),
|
||||
len(shares) )
|
||||
si = 1
|
||||
for n,s in enumerate(shares[si:],si+1):
|
||||
|
|
|
|||
15
mmgen/obj.py
15
mmgen/obj.py
|
|
@ -272,13 +272,15 @@ class Int(int,Hilite,InitErrors):
|
|||
except Exception as e:
|
||||
return cls.init_fail(e,n)
|
||||
|
||||
def fmt(self,**kwargs):
|
||||
return super().fmtc(self.__str__(),**kwargs)
|
||||
|
||||
@classmethod
|
||||
def fmtc(cls,s,**kwargs):
|
||||
return super().fmtc(s.__str__(),**kwargs)
|
||||
|
||||
@classmethod
|
||||
def colorize(cls,s,**kwargs):
|
||||
return super().colorize(s.__str__(),**kwargs)
|
||||
def hl(self,**kwargs):
|
||||
return super().colorize(self.__str__(),**kwargs)
|
||||
|
||||
class NonNegativeInt(Int):
|
||||
min_val = 0
|
||||
|
|
@ -315,11 +317,10 @@ class HexStr(str,Hilite,InitErrors):
|
|||
except Exception as e:
|
||||
return cls.init_fail(e,s)
|
||||
|
||||
def truncate(self,width,color=True,color_override=''):
|
||||
def truncate(self,width,color=True):
|
||||
return self.colorize(
|
||||
self if width == None or width >= self.width else self[:width-2] + '..',
|
||||
color = color,
|
||||
color_override = color_override )
|
||||
self if width >= self.width else self[:width-2] + '..',
|
||||
color = color )
|
||||
|
||||
class CoinTxID(HexStr):
|
||||
color,width,hexcase = ('purple',64,'lower')
|
||||
|
|
|
|||
|
|
@ -48,61 +48,79 @@ class Hilite:
|
|||
width = 0
|
||||
trunc_ok = True
|
||||
|
||||
# supports single-width characters only
|
||||
def fmt( self, width, color=False ):
|
||||
if len(self) > width:
|
||||
assert self.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= width of string"
|
||||
return self.colorize( self[:width].ljust(width), color=color )
|
||||
else:
|
||||
return self.colorize( self.ljust(width), color=color )
|
||||
|
||||
# class method equivalent of fmt()
|
||||
@classmethod
|
||||
# 'width' is screen width (greater than len(s) for CJK strings)
|
||||
# 'append_chars' and 'encl' must consist of single-width chars only
|
||||
def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None,
|
||||
center=False,nullrepl='',append_chars='',append_color=False,color_override=''):
|
||||
s_wide_count = len([1 for ch in s if unicodedata.east_asian_width(ch) in ('F','W')])
|
||||
if encl:
|
||||
a,b = list(encl)
|
||||
add_len = len(append_chars) + 2
|
||||
def fmtc( cls, s, width, color=False ):
|
||||
if len(s) > width:
|
||||
assert cls.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= width of string"
|
||||
return cls.colorize( s[:width].ljust(width), color=color )
|
||||
else:
|
||||
a,b = ('','')
|
||||
add_len = len(append_chars)
|
||||
if width == None:
|
||||
width = cls.width
|
||||
if trunc_ok == None:
|
||||
trunc_ok = cls.trunc_ok
|
||||
if g.test_suite:
|
||||
assert isinstance(encl,str) and len(encl) in (0,2),"'encl' must be 2-character str"
|
||||
assert width >= 2 + add_len, f'{s!r}: invalid width ({width}) (must be at least 2)' # CJK: 2 cells
|
||||
if len(s) + s_wide_count + add_len > width:
|
||||
assert trunc_ok, "If 'trunc_ok' is false, 'width' must be >= screen width of string"
|
||||
s = truncate_str(s,width-add_len)
|
||||
if s == '' and nullrepl:
|
||||
s = nullrepl
|
||||
return cls.colorize( s.ljust(width), color=color )
|
||||
|
||||
# an alternative to fmt(), with double-width char support and other features
|
||||
def fmt2(
|
||||
self,
|
||||
width, # screen width - must be at least 2 (one wide char)
|
||||
color = False,
|
||||
encl = '', # if set, must be exactly 2 single-width chars
|
||||
nullrepl = '',
|
||||
append_chars = '', # single-width chars only
|
||||
append_color = False,
|
||||
color_override = '' ):
|
||||
|
||||
if self == '':
|
||||
return getattr( color_mod, self.color )(nullrepl.ljust(width)) if color else nullrepl.ljust(width)
|
||||
|
||||
s_wide_count = len(['' for ch in self if unicodedata.east_asian_width(ch) in ('F','W')])
|
||||
|
||||
a,b = encl or ('','')
|
||||
add_len = len(append_chars) + len(encl)
|
||||
|
||||
if len(self) + s_wide_count + add_len > width:
|
||||
assert self.trunc_ok, "If 'trunc_ok' is false, 'width' must be >= screen width of string"
|
||||
s = a + (truncate_str(self,width-add_len) if s_wide_count else self[:width-add_len]) + b
|
||||
else:
|
||||
s = a+s+b
|
||||
if center:
|
||||
s = s.center(width)
|
||||
s = a + self + b
|
||||
|
||||
if append_chars:
|
||||
return (
|
||||
cls.colorize(s,color=color)
|
||||
+ cls.colorize(
|
||||
self.colorize(s,color=color)
|
||||
+ self.colorize2(
|
||||
append_chars.ljust(width-len(s)-s_wide_count),
|
||||
color_override = append_color ))
|
||||
else:
|
||||
return cls.colorize(s.ljust(width-s_wide_count),color=color,color_override=color_override)
|
||||
return self.colorize2( s.ljust(width-s_wide_count), color=color, color_override=color_override )
|
||||
|
||||
@classmethod
|
||||
def colorize(cls,s,color=True,color_override=''):
|
||||
def colorize(cls,s,color=True):
|
||||
return getattr( color_mod, cls.color )(s) if color else s
|
||||
|
||||
@classmethod
|
||||
def colorize2(cls,s,color=True,color_override=''):
|
||||
return getattr( color_mod, color_override or cls.color )(s) if color else s
|
||||
|
||||
def fmt(self,*args,**kwargs):
|
||||
assert args == () # forbid invocation w/o keywords
|
||||
return self.fmtc(self,*args,**kwargs)
|
||||
def hl(self,color=True):
|
||||
return getattr( color_mod, self.color )(self) if color else self
|
||||
|
||||
@classmethod
|
||||
def hlc(cls,s,color=True,encl='',color_override=''):
|
||||
if encl:
|
||||
assert isinstance(encl,str) and len(encl) == 2, "'encl' must be 2-character str"
|
||||
s = encl[0] + s + encl[1]
|
||||
return cls.colorize(s,color=color,color_override=color_override)
|
||||
def hlc(cls,s,color=True):
|
||||
return getattr( color_mod, cls.color )(s) if color else s
|
||||
|
||||
def hl(self,*args,**kwargs):
|
||||
assert args == () # forbid invocation w/o keywords
|
||||
return self.hlc(self,*args,**kwargs)
|
||||
# an alternative to hl(), with enclosure and color override
|
||||
# can be called as an unbound method with class as first argument
|
||||
def hl2(self,s=None,color=True,encl='',color_override=''):
|
||||
if encl:
|
||||
return self.colorize2( encl[0]+(s or self)+encl[1], color=color, color_override=color_override )
|
||||
else:
|
||||
return self.colorize2( (s or self), color=color, color_override=color_override )
|
||||
|
||||
class InitErrors:
|
||||
|
||||
|
|
|
|||
|
|
@ -141,8 +141,8 @@ class BitcoinTwTransaction:
|
|||
def txdate_disp(self,age_fmt):
|
||||
return self.parent.date_formatter[age_fmt](self.rpc,self.time)
|
||||
|
||||
def txid_disp(self,width,color):
|
||||
return self.txid.truncate(width=width,color=color)
|
||||
def txid_disp(self,color,width=None):
|
||||
return self.txid.hl(color=color) if width == None else self.txid.truncate(width=width,color=color)
|
||||
|
||||
def vouts_list_disp(self,src,color,indent=''):
|
||||
|
||||
|
|
@ -167,8 +167,9 @@ class BitcoinTwTransaction:
|
|||
yield fs2.format(
|
||||
i = CoinTxID(e.txid).hl(color=color),
|
||||
n = (nocolor,red)[color](str(e.data['n']).ljust(3)),
|
||||
a = TwMMGenID.hlc(
|
||||
'{:{w}}'.format( addr_out + bal_star, w=self.max_addrlen[src] ),
|
||||
a = TwMMGenID.hl2(
|
||||
TwMMGenID,
|
||||
s = '{:{w}}'.format( addr_out + bal_star, w=self.max_addrlen[src] ),
|
||||
color = color,
|
||||
color_override = co ),
|
||||
A = self.proto.coin_amt( e.data['value'] ).fmt(color=color),
|
||||
|
|
@ -194,13 +195,14 @@ class BitcoinTwTransaction:
|
|||
mmid_disp = mmid + bal_star
|
||||
if width and x.space_left < len(mmid_disp):
|
||||
break
|
||||
yield TwMMGenID.hlc( mmid_disp, color=color, color_override=co )
|
||||
yield TwMMGenID.hl2( TwMMGenID, s=mmid_disp, color=color, color_override=co )
|
||||
x.space_left -= len(mmid_disp)
|
||||
else:
|
||||
if width and x.space_left < addr_w:
|
||||
break
|
||||
yield TwMMGenID.hlc(
|
||||
CoinAddr.fmtc( mmid.split(':',1)[1] + bal_star, width=addr_w ),
|
||||
yield TwMMGenID.hl2(
|
||||
TwMMGenID,
|
||||
s = CoinAddr.fmtc( mmid.split(':',1)[1] + bal_star, width=addr_w ),
|
||||
color = color,
|
||||
color_override = co )
|
||||
x.space_left -= addr_w
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class TxInfo(TxInfo):
|
|||
confs = e.confs + blockcount - tx.blockcount
|
||||
days = int(confs // confs_per_day)
|
||||
if e.mmid:
|
||||
mmid_fmt = e.mmid.fmt(
|
||||
mmid_fmt = e.mmid.fmt2(
|
||||
width=max_mmwid,
|
||||
encl='()',
|
||||
color=True,
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ class SeedShareBase(MMGenObject):
|
|||
m = ( yellow("(share {} of {} of ")
|
||||
+ pl.parent_seed.sid.hl()
|
||||
+ yellow(', split id ')
|
||||
+ pl.id_str.hl(encl="''")
|
||||
+ pl.id_str.hl2(encl='‘’')
|
||||
+ yellow('{})') )
|
||||
else:
|
||||
m = "share {} of {} of " + pl.parent_seed.sid + ", split id '" + pl.id_str + "'{}"
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ class TwAddresses(TwView):
|
|||
m = d.twmmid.fmt( width=cw.mmid, color=color ),
|
||||
u = yes if d.recvd else no,
|
||||
a = d.addr.fmt( color=color, width=cw.addr ),
|
||||
c = d.comment.fmt( width=cw.comment, color=color, nullrepl='-' ),
|
||||
c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ),
|
||||
A = d.amt.fmt( color=color, iwidth=cw.iwidth, prec=self.disp_prec ),
|
||||
d = self.age_disp( d, self.age_fmt )
|
||||
)
|
||||
|
|
@ -195,7 +195,7 @@ class TwAddresses(TwView):
|
|||
m = d.twmmid.fmt( width=cw.mmid, color=color ),
|
||||
u = yes if d.recvd else no,
|
||||
a = d.addr.fmt( color=color, width=cw.addr ),
|
||||
c = d.comment.fmt( width=cw.comment, color=color, nullrepl='-' ),
|
||||
c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ),
|
||||
A = d.amt.fmt( color=color, iwidth=cw.iwidth, prec=self.disp_prec ),
|
||||
b = self.age_disp( d, 'block' ),
|
||||
D = self.age_disp( d, 'date_time' ))
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
|
|||
res.twmmid.type.replace('mmgen','MMGen'),
|
||||
res.twmmid.addr.hl() )
|
||||
if comment:
|
||||
msg('Added label {} to {}'.format(comment.hl(encl="''"),desc))
|
||||
msg('Added label {} to {}'.format(comment.hl2(encl='‘’'),desc))
|
||||
else:
|
||||
msg(f'Removed label from {desc}')
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -46,9 +46,8 @@ class TwMMGenID(str,Hilite,InitErrors,MMGenObject):
|
|||
me.proto = proto
|
||||
return me
|
||||
|
||||
@classmethod
|
||||
def fmtc(cls,twmmid,*args,**kwargs):
|
||||
return super().fmtc(twmmid.disp,*args,**kwargs)
|
||||
def fmt(self,**kwargs):
|
||||
return super().fmtc(self.disp,**kwargs)
|
||||
|
||||
# non-displaying container for TwMMGenID,TwComment
|
||||
class TwLabel(str,InitErrors,MMGenObject):
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class TwTxHistory(TwView):
|
|||
i = d.vouts_disp( 'inputs', width=cw.inputs, color=color ),
|
||||
A = d.amt_disp(self.show_total_amt).fmt( iwidth=cw.iwidth, prec=self.disp_prec, color=color ),
|
||||
o = d.vouts_disp( 'outputs', width=cw.outputs, color=color ),
|
||||
c = d.comment.fmt( width=cw.comment, color=color, nullrepl='-' ) )
|
||||
c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ) )
|
||||
|
||||
def gen_detail_display(self,data,cw,fs,color,fmt_method):
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ class TwTxHistory(TwView):
|
|||
d = d.age_disp( 'date_time', width=None, color=None ),
|
||||
b = d.blockheight_disp(color=color),
|
||||
D = d.txdate_disp( 'date_time' ),
|
||||
t = d.txid_disp( width=None, color=color ),
|
||||
t = d.txid_disp( color=color ),
|
||||
A = d.amt_disp(show_total_amt=True).hl( color=color ),
|
||||
B = d.amt_disp(show_total_amt=False).hl( color=color ),
|
||||
f = d.fee_disp( color=color ),
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from ..obj import (
|
|||
HexStr,
|
||||
CoinTxID,
|
||||
NonNegativeInt )
|
||||
from ..addr import CoinAddr,MMGenID
|
||||
from ..addr import CoinAddr
|
||||
from .shared import TwMMGenID,get_tw_label
|
||||
from .view import TwView
|
||||
|
||||
|
|
@ -183,14 +183,14 @@ class TwUnspentOutputs(TwView):
|
|||
for n,d in enumerate(data):
|
||||
yield fs.format(
|
||||
n = str(n+1) + ')',
|
||||
t = (CoinTxID.fmtc('|' + '.'*(cw.txid-1),color=color) if d.skip == 'txid'
|
||||
t = (d.txid.fmtc( '|' + '.'*(cw.txid-1), width=d.txid.width, color=color ) if d.skip == 'txid'
|
||||
else d.txid.truncate( width=cw.txid, color=color )) if cw.txid else None,
|
||||
v = ' ' + d.vout.fmt( width=cw.vout-1, color=color ) if cw.vout else None,
|
||||
a = d.addr.fmtc( '|' + '.'*(cw.addr-1), width=cw.addr, color=color ) if d.skip == 'addr'
|
||||
else d.addr.fmt( width=cw.addr, color=color ),
|
||||
m = (MMGenID.fmtc( '.'*cw.mmid, color=color ) if d.skip == 'addr'
|
||||
m = (d.twmmid.fmtc( '.'*cw.mmid, color=color ) if d.skip == 'addr'
|
||||
else d.twmmid.fmt( width=cw.mmid, color=color )) if cw.mmid else None,
|
||||
c = d.comment.fmt( width=cw.comment, color=color, nullrepl='-' ) if cw.comment else None,
|
||||
c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ) if cw.comment else None,
|
||||
A = d.amt.fmt( color=color, iwidth=cw.iwidth, prec=self.disp_prec ),
|
||||
B = d.amt2.fmt( color=color, iwidth=cw.iwidth2, prec=self.disp_prec ) if cw.amt2 else None,
|
||||
d = self.age_disp(d,self.age_fmt),
|
||||
|
|
@ -201,7 +201,7 @@ class TwUnspentOutputs(TwView):
|
|||
for n,d in enumerate(data):
|
||||
yield fs.format(
|
||||
n = str(n+1) + ')',
|
||||
t = d.txid.fmt( color=color ) if cw.txid else None,
|
||||
t = d.txid.fmt( width=d.txid.width, color=color ) if cw.txid else None,
|
||||
v = ' ' + d.vout.fmt( width=cw.vout-1, color=color ) if cw.vout else None,
|
||||
a = d.addr.fmt( width=cw.addr, color=color ),
|
||||
m = d.twmmid.fmt( width=cw.mmid, color=color ),
|
||||
|
|
@ -209,7 +209,7 @@ class TwUnspentOutputs(TwView):
|
|||
B = d.amt2.fmt( color=color, iwidth=cw.iwidth2, prec=self.disp_prec ) if cw.amt2 else None,
|
||||
b = self.age_disp(d,'block'),
|
||||
D = self.age_disp(d,'date_time'),
|
||||
c = d.comment.fmt( width=cw.comment, color=color, nullrepl='-' ))
|
||||
c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ))
|
||||
|
||||
def display_total(self):
|
||||
msg('\nTotal unspent: {} {} ({} output{})'.format(
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class wallet(wallet):
|
|||
# logic identical to _get_hash_preset_from_user()
|
||||
def _get_label_from_user(self,old_lbl=''):
|
||||
prompt = 'Enter a wallet label, or hit ENTER {}: '.format(
|
||||
'to reuse the label {}'.format(old_lbl.hl(encl="''")) if old_lbl else
|
||||
'to reuse the label {}'.format(old_lbl.hl2(encl='‘’')) if old_lbl else
|
||||
'for no label' )
|
||||
from ..ui import line_input
|
||||
while True:
|
||||
|
|
@ -61,17 +61,17 @@ class wallet(wallet):
|
|||
old_lbl = self.ss_in.ssdata.label
|
||||
if opt.keep_label:
|
||||
lbl = old_lbl
|
||||
qmsg('Reusing label {} at user request'.format( lbl.hl(encl="''") ))
|
||||
qmsg('Reusing label {} at user request'.format( lbl.hl2(encl='‘’') ))
|
||||
elif self.label:
|
||||
lbl = self.label
|
||||
qmsg('Using label {} requested on command line'.format( lbl.hl(encl="''") ))
|
||||
qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') ))
|
||||
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':
|
||||
qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' ))
|
||||
elif self.label:
|
||||
lbl = self.label
|
||||
qmsg('Using label {} requested on command line'.format( lbl.hl(encl="''") ))
|
||||
qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') ))
|
||||
else:
|
||||
lbl = self._get_label_from_user()
|
||||
self.ssdata.label = lbl
|
||||
|
|
|
|||
|
|
@ -910,7 +910,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
|
|||
res = await tx.get_receipt(txid)
|
||||
imsg(f'Gas sent: {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}')
|
||||
imsg(f'Gas used: {res.gas_used.hl():<9} {(res.gas_used*res.gas_price).hl2(encl="()")}')
|
||||
imsg(f'Gas price: {res.gas_price.hl2()}')
|
||||
imsg(f'Gas price: {res.gas_price.hl()}')
|
||||
if res.gas_used == res.gas_sent:
|
||||
omsg(yellow(f'Warning: all gas was used!'))
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ class TestSuiteSeedSplit(TestSuiteBase):
|
|||
if spec:
|
||||
from mmgen.seedsplit import SeedSplitSpecifier
|
||||
sss = SeedSplitSpecifier(spec)
|
||||
pat = rf'Processing .*\b{sss.idx}\b of \b{sss.count}\b of .* id .*{sss.id!r}'
|
||||
pat = rf'Processing .*\b{sss.idx}\b of \b{sss.count}\b of .* id .*‘{sss.id}’'
|
||||
else:
|
||||
pat = f'master share #{master}'
|
||||
t.expect(pat,regex=True)
|
||||
|
|
@ -144,7 +144,7 @@ class TestSuiteSeedSplit(TestSuiteBase):
|
|||
if icls:
|
||||
t.passphrase(icls.desc,sh1_passwd)
|
||||
if master:
|
||||
fs = "master share #{}, split id.*'{}'.*, share count {}"
|
||||
fs = "master share #{}, split id.*‘{}’.*, share count {}"
|
||||
pat = fs.format(
|
||||
master,
|
||||
id_str or 'default',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue