obj.py, Hilite: cleanups

This commit is contained in:
The MMGen Project 2020-04-11 15:51:25 +00:00
commit 370a972b4b
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2

View file

@ -25,8 +25,8 @@ from decimal import *
from string import hexdigits,ascii_letters,digits
from .exception import *
from .globalvars import *
from .color import *
from .devtools import *
def is_mmgen_seed_id(s): return SeedID(sid=s,on_fail='silent')
def is_mmgen_idx(s): return AddrIdx(s,on_fail='silent')
@ -105,7 +105,6 @@ class InitErrors(object):
if m2: errmsg = '{!r}\n{}'.format(m2,errmsg)
from .globalvars import g
from .util import die,msg
if cls.on_fail == 'silent':
return None # TODO: return False instead?
@ -120,28 +119,38 @@ class InitErrors(object):
elif cls.on_fail == 'die':
die(1,errmsg)
@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__))
class Hilite(object):
color = 'red'
color_always = False
width = 0
trunc_ok = True
dtype = str
@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):
if cls.dtype == bytes: s = s.decode()
s_wide_count = len([1 for ch in s if unicodedata.east_asian_width(ch) in ('F','W')])
assert isinstance(encl,str) and len(encl) in (0,2),"'encl' must be 2-character str"
a,b = list(encl) if encl else ('','')
add_len = len(a) + len(b) + len(append_chars)
if width == None: width = cls.width
if trunc_ok == None: trunc_ok = cls.trunc_ok
assert width >= 2 + add_len,( # 2 because CJK
"'{!r}': invalid width ({}) (width must be at least 2)".format(s,width))
if encl:
a,b = list(encl)
add_len = len(append_chars) + 2
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,( # 2 because CJK
"'{!r}': invalid width ({}) (width must be at least 2)".format(s,width))
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)
@ -149,18 +158,17 @@ class Hilite(object):
s = nullrepl.center(width)
else:
s = a+s+b
if center: s = s.center(width)
if center:
s = s.center(width)
if append_chars:
return cls.colorize(s,color=color) + \
cls.colorize(append_chars.ljust(width-len(s)-s_wide_count),color=append_color)
cls.colorize(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)
@classmethod
def colorize(cls,s,color=True):
if cls.dtype == bytes: s = s.decode()
k = color if type(color) is str else cls.color # hack: override color with str value
return globals()[k](s) if (color or cls.color_always) else s
def colorize(cls,s,color=True,color_override=''):
return globals()[color_override or cls.color](s) if color else s
def fmt(self,*args,**kwargs):
assert args == () # forbid invocation w/o keywords
@ -177,9 +185,6 @@ class Hilite(object):
assert args == () # forbid invocation w/o keywords
return self.hlc(self,*args,**kwargs)
def __str__(self):
return self.colorize(self,color=False)
class Str(str,Hilite): pass
class Int(int,Hilite,InitErrors):
@ -204,9 +209,13 @@ class Int(int,Hilite,InitErrors):
except Exception as e:
return cls.init_fail(e,n)
@classmethod
def fmtc(cls,*args,**kwargs):
cls.method_not_implemented()
@classmethod
def colorize(cls,n,color=True):
return Hilite.colorize(repr(n),color=color)
return super().colorize(repr(n),color=color)
# For attrs that are always present in the data instance
# Reassignment and deletion forbidden
@ -443,7 +452,7 @@ class BTCAmt(Decimal,Hilite,InitErrors):
@classmethod
def fmtc(cls):
raise NotImplementedError
cls.method_not_implemented()
def fmt(self,fs=None,color=False,suf='',prec=1000):
if fs == None: fs = self.amt_fs
@ -505,7 +514,6 @@ class CoinAddr(str,Hilite,InitErrors,MMGenObject):
def __new__(cls,s,on_fail='die'):
if type(s) == cls: return s
cls.arg_chk(on_fail)
from .globalvars import g
try:
assert set(s) <= set(ascii_letters+digits),'contains non-alphanumeric characters'
me = str.__new__(cls,s)
@ -519,17 +527,11 @@ class CoinAddr(str,Hilite,InitErrors,MMGenObject):
@classmethod
def fmtc(cls,s,**kwargs):
# True -> 'cyan': use the str value override hack
if 'color' in kwargs and kwargs['color'] == True:
kwargs['color'] = cls.color
if not 'width' in kwargs: kwargs['width'] = cls.width
if kwargs['width'] < len(s):
s = s[:kwargs['width']-2] + '..'
return Hilite.fmtc(s,**kwargs)
w = kwargs['width'] or cls.width
return super().fmtc(s[:w-2]+'..' if w < len(s) else s, **kwargs)
def is_for_chain(self,chain):
from .globalvars import g
if g.proto.__name__[:8] == 'Ethereum':
return True
@ -545,7 +547,6 @@ class TokenAddr(CoinAddr):
class ViewKey(object):
def __new__(cls,s,on_fail='die'):
from .globalvars import g
if g.proto.name == 'zcash':
return ZcashViewKey.__new__(ZcashViewKey,s,on_fail)
elif g.proto.name == 'monero':
@ -605,7 +606,6 @@ class MMGenID(str,Hilite,InitErrors,MMGenObject):
trunc_ok = False
def __new__(cls,s,on_fail='die'):
cls.arg_chk(on_fail)
from .globalvars import g
try:
ss = str(s).split(':')
assert len(ss) in (2,3),'not 2 or 3 colon-separated items'
@ -634,7 +634,6 @@ class TwMMGenID(str,Hilite,InitErrors,MMGenObject):
sort_key,idtype = ret.sort_key,'mmgen'
except Exception as e:
try:
from .globalvars import g
assert s.split(':',1)[0] == g.proto.base_coin.lower(),(
"not a string beginning with the prefix '{}:'".format(g.proto.base_coin.lower()))
assert set(s[4:]) <= set(ascii_letters+digits),'contains non-alphanumeric characters'
@ -670,7 +669,6 @@ class HexStr(str,Hilite,InitErrors):
width = None
hexcase = 'lower'
trunc_ok = False
dtype = str
def __new__(cls,s,on_fail='die',case=None):
if type(s) == cls: return s
cls.arg_chk(on_fail)
@ -682,7 +680,7 @@ class HexStr(str,Hilite,InitErrors):
assert not len(s) % 2,'odd-length string'
if cls.width:
assert len(s) == cls.width,'Value is not {} characters wide'.format(cls.width)
return cls.dtype.__new__(cls,s)
return str.__new__(cls,s)
except Exception as e:
return cls.init_fail(e,s)
@ -703,7 +701,6 @@ class WifKey(str,Hilite,InitErrors):
cls.arg_chk(on_fail)
try:
assert set(s) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
from .globalvars import g
g.proto.parse_wif(s) # raises exception on error
return str.__new__(cls,s)
except Exception as e:
@ -736,7 +733,6 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
# initialize with (priv_bin,compressed), WIF or self
def __new__(cls,s=None,compressed=None,wif=None,pubkey_type=None,on_fail='die'):
from .globalvars import g
if type(s) == cls: return s
cls.arg_chk(on_fail)
@ -904,7 +900,6 @@ class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
def __new__(cls,s,on_fail='die',errmsg=None):
if type(s) == cls: return s
cls.arg_chk(on_fail)
from .globalvars import g
try:
for k,v in list(cls.mmtypes.items()):
if s in (k,v.name):