Browse Source

obj.py, Hilite: cleanups

The MMGen Project 4 years ago
parent
commit
370a972b4b
1 changed files with 35 additions and 40 deletions
  1. 35 40
      mmgen/obj.py

+ 35 - 40
mmgen/obj.py

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