From f7e54cce93d96e3000f092ced4c1a5da1e3b4f30 Mon Sep 17 00:00:00 2001 From: MMGen Date: Sat, 17 Feb 2018 12:34:15 +0300 Subject: [PATCH] Minor fixes and improvements - mmgen-tool listaddresses: add 'show_age','show_days' options - init_fail(): make 'silent' override 'MMGEN_TRACEBACK', fixing regression introduced by 3909339 - test/test.py: improve formatting of --list output - test/{test,objtest,scrambletest}.py, mmgen/tool.py: indentation (whitespace) fixes --- mmgen/main_tool.py | 8 +++----- mmgen/obj.py | 30 +++++++++++++----------------- mmgen/tool.py | 37 +++++++++++++++++++++++++------------ mmgen/util.py | 6 +++--- scripts/traceback.py | 8 +++++--- test/objtest.py | 14 +++++++------- test/scrambletest.py | 2 +- test/test.py | 22 +++++++++------------- 8 files changed, 66 insertions(+), 61 deletions(-) diff --git a/mmgen/main_tool.py b/mmgen/main_tool.py index 52285140..f6a84904 100755 --- a/mmgen/main_tool.py +++ b/mmgen/main_tool.py @@ -150,9 +150,7 @@ if Command not in tool.cmd_data: die(1,"'%s': no such command" % Command.lower()) args,kwargs = tool.process_args(Command,cmd_args) -try: - ret = tool.__dict__[Command](*args,**kwargs) -except Exception as e: - die(1,'{}'.format(e)) -sys.exit(0 if ret in (None,True) else 1) # some commands die, some return False on failure +ret = tool.__dict__[Command](*args,**kwargs) + +sys.exit((1,0)[ret in (None,True)]) # some commands die, some return False on failure diff --git a/mmgen/obj.py b/mmgen/obj.py index 839b07ce..2bdd01a7 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -110,17 +110,15 @@ class InitErrors(object): assert on_fail in ('die','return','silent','raise'),'arg_chk in class {}'.format(cls.__name__) @staticmethod - def init_fail(m,on_fail,silent=False): + def init_fail(m,on_fail): + if os.getenv('MMGEN_TRACEBACK'): on_fail == 'raise' from mmgen.util import die,msg - if silent: m = '' - if os.getenv('MMGEN_TRACEBACK'): - raise ValueError,m - elif on_fail == 'die': die(1,m) + if on_fail == 'silent': return None # TODO: return False instead? + elif on_fail == 'raise': raise ValueError,m + elif on_fail == 'die': die(1,m) elif on_fail == 'return': if m: msg(m) - return None # TODO: change to False - elif on_fail == 'silent': return None # same here - elif on_fail == 'raise': raise ValueError,m + return None # TODO: here too? class Hilite(object): @@ -134,11 +132,11 @@ class Hilite(object): center=False,nullrepl='',app='',appcolor=False): if width == None: width = cls.width if trunc_ok == None: trunc_ok = cls.trunc_ok - assert width > 0 + assert width > 0,'Width must be > 0' if s == '' and nullrepl: s,center = nullrepl,True if center: s = s.center(width) - assert type(encl) is str and len(encl) in (0,2) + assert type(encl) is str and len(encl) in (0,2),'type(encl) must be str and len(encl) be in (0,2)' a,b = list(encl) if encl else ('','') if trunc_ok and len(s) > width: s = s[:width] if app: @@ -310,9 +308,9 @@ class BTCAmt(Decimal,Hilite,InitErrors): raise NotImplementedError def fmt(self,fs='3.8',color=False,suf=''): - s = self.__str__(color=False) + s = str(int(self)) if int(self) == self else self.normalize().__format__('f') if '.' in fs: - p1,p2 = [int(i) for i in fs.split('.',1)] + p1,p2 = map(int,fs.split('.',1)) ss = s.split('.',1) if len(ss) == 2: a,b = ss @@ -327,11 +325,9 @@ class BTCAmt(Decimal,Hilite,InitErrors): return self.__str__(color=color) def __str__(self,color=False): # format simply, no exponential notation - if int(self) == self: - ret = str(int(self)) - else: - ret = self.normalize().__format__('f') - return self.colorize(ret,color=color) + return self.colorize( + str(int(self)) if int(self) == self else self.normalize().__format__('f'), + color=color) def __repr__(self): return "{}('{}')".format(type(self).__name__,self.__str__()) diff --git a/mmgen/tool.py b/mmgen/tool.py index 5dc64d48..9f292bdd 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -77,8 +77,8 @@ cmd_data = OrderedDict([ ('Mn_stats', ["wordlist [str='electrum']"]), ('Mn_printlist', ["wordlist [str='electrum']"]), - ('Listaddress',['<{} address> [str]'.format(pnm),'minconf [int=1]','pager [bool=False]','showempty [bool=True]''showbtcaddr [bool=True]']), - ('Listaddresses',["addrs [str='']",'minconf [int=1]','showempty [bool=False]','pager [bool=False]','showbtcaddrs [bool=True]','all_labels [bool=False]',"sort [str=''] (options: reverse, age)"]), + ('Listaddress',['<{} address> [str]'.format(pnm),'minconf [int=1]','pager [bool=False]','showempty [bool=True]','showbtcaddr [bool=True]','show_age [bool=False]','show_days [bool=True]']), + ('Listaddresses',["addrs [str='']",'minconf [int=1]','showempty [bool=False]','pager [bool=False]','showbtcaddrs [bool=True]','all_labels [bool=False]',"sort [str=''] (options: reverse, age)",'show_age [bool=False]','show_days [bool=True]']), ('Getbalance', ['minconf [int=1]','quiet [bool=False]']), ('Txview', ['<{} TX file(s)> [str]'.format(pnm),'pager [bool=False]','terse [bool=False]',"sort [str='mtime'] (options: ctime, atime)",'MARGS']), ('Twview', ["sort [str='age']",'reverse [bool=False]','show_days [bool=True]','show_mmid [bool=True]','minconf [int=1]','wide [bool=False]','pager [bool=False]']), @@ -625,11 +625,16 @@ def monero_wallet_ops(infile,op,blockheight=None,addrs=None): # ================ RPC commands ================== # -def Listaddress(addr,minconf=1,pager=False,showempty=True,showbtcaddr=True): - return Listaddresses(addrs=addr,minconf=minconf,pager=pager,showempty=showempty,showbtcaddrs=showbtcaddr) +def Listaddress(addr,minconf=1,pager=False,showempty=True,showbtcaddr=True,show_age=False,show_days=None): + return Listaddresses(addrs=addr,minconf=minconf,pager=pager, + showempty=showempty,showbtcaddrs=showbtcaddr,show_age=show_age,show_days=show_days) # List MMGen addresses and their balances. TODO: move this code to AddrList -def Listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=True,all_labels=False,sort=None): +def Listaddresses(addrs='',minconf=1, + showempty=False,pager=False,showbtcaddrs=True,all_labels=False,sort=None,show_age=False,show_days=None): + + if show_days == None: show_days = False # user-set show_days triggers show_age + else: show_age = True if sort: sort = set(sort.split(',')) @@ -716,15 +721,19 @@ def Listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Tr out = ([],[green('Chain: {}'.format(g.chain.upper()))])[g.chain in ('testnet','regtest')] - fs = ('{mid} {cmt} {amt}','{mid} {addr} {cmt} {amt}')[showbtcaddrs] + fs = '{{mid}}{} {{cmt}} {{amt}}{}'.format(('',' {addr}')[showbtcaddrs],('',' {age}')[show_age]) mmaddrs = [k for k in addrs.keys() if k.type == 'mmgen'] max_mmid_len = max(len(k) for k in mmaddrs) + 2 if mmaddrs else 10 - max_cmt_len = max(max(len(addrs[k]['lbl'].comment) for k in addrs),7) + max_cmt_len = max(max(len(v['lbl'].comment) for v in addrs.values()),7) +# pmsg([a.split('.')[1] for a in [str(v['amt']) for v in addrs.values()] if '.' in a]) + # fp: fractional part + max_fp_len = max([len(a.split('.')[1]) for a in [str(v['amt']) for v in addrs.values()] if '.' in a] or [1]) out += [fs.format( mid=MMGenID.fmtc('MMGenID',width=max_mmid_len), addr=CoinAddr.fmtc('ADDRESS'), cmt=TwComment.fmtc('COMMENT',width=max_cmt_len), - amt='BALANCE' + amt='{:{w}}'.format('BALANCE',w=max_fp_len+4), + age=('CONFS','AGE')[show_days], )] def sort_algo(j): @@ -737,6 +746,7 @@ def Listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Tr return j.sort_key al_id_save = None + confs_per_day = 60*60*24 / g.proto.secs_per_block for mmid in sorted(addrs,key=sort_algo,reverse=bool(sort and 'reverse' in sort)): if mmid.type == 'mmgen': if al_id_save and al_id_save != mmid.obj.al_id: @@ -748,11 +758,14 @@ def Listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Tr out.append('') al_id_save = None mmid_disp = 'Non-MMGen' + e = addrs[mmid] out.append(fs.format( - mid = MMGenID.fmtc(mmid_disp,width=max_mmid_len,color=True), - addr=(addrs[mmid]['addr'].fmt(color=True) if showbtcaddrs else None), - cmt=addrs[mmid]['lbl'].comment.fmt(width=max_cmt_len,color=True,nullrepl='-'), - amt=addrs[mmid]['amt'].fmt('3.0',color=True))) + mid=MMGenID.fmtc(mmid_disp,width=max_mmid_len,color=True), + addr=(e['addr'].fmt(color=True) if showbtcaddrs else None), + cmt=e['lbl'].comment.fmt(width=max_cmt_len,color=True,nullrepl='-'), + amt=e['amt'].fmt('3.{}'.format(max_fp_len),color=True), + age=mmid.confs / (1,confs_per_day)[show_days] if hasattr(mmid,'confs') else '-' + )) out.append('\nTOTAL: {} {}'.format(total.hl(color=True),g.coin)) o = '\n'.join(out) return do_pager(o) if pager else Msg(o) diff --git a/mmgen/util.py b/mmgen/util.py index 2c89c4f2..e382f967 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -860,13 +860,13 @@ def rpc_init(reinit=False): g.rpch = conn return conn -def format_text(s,indent=0,width=80): +def format_par(s,indent=0,width=80,as_list=False): words,lines = s.split(),[] assert width >= indent + 4,'width must be >= indent + 4' while words: line = '' while len(line) <= (width-indent) and words: - if len(line) + len(words[0]) + 1 > width-indent: break + if line and len(line) + len(words[0]) + 1 > width-indent: break line += ('',' ')[bool(line)] + words.pop(0) lines.append(' '*indent + line) - return '\n'.join(lines) + '\n' + return lines if as_list else '\n'.join(lines) + '\n' diff --git a/scripts/traceback.py b/scripts/traceback.py index 8860ce5d..bf3bc86a 100755 --- a/scripts/traceback.py +++ b/scripts/traceback.py @@ -5,12 +5,14 @@ sys.path.insert(0,'.') if 'TMUX' in os.environ: del os.environ['TMUX'] os.environ['MMGEN_TRACEBACK'] = '1' -f = open('my.err','w') +tb_source = open(sys.argv[1]) +tb_file = open('my.err','w') try: sys.argv.pop(0) - execfile(sys.argv[0]) + exec tb_source except SystemExit: +# pass e = sys.exc_info() sys.exit(int(str(e[1]))) except: @@ -19,5 +21,5 @@ except: def red(s): return '{e}[31;1m{}{e}[0m'.format(s,e='\033') def yellow(s): return '{e}[33;1m{}{e}[0m'.format(s,e='\033') sys.stdout.write('{}{}'.format(yellow(''.join(l)),red(exc))) - traceback.print_exc(file=f) + traceback.print_exc(file=tb_file) sys.exit(1) diff --git a/test/objtest.py b/test/objtest.py index d3aecc17..fff93833 100755 --- a/test/objtest.py +++ b/test/objtest.py @@ -193,19 +193,19 @@ tests = OrderedDict([ 'bad': ({'wif':1},), 'good': ({ 'btc': (({'wif':'5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb', - 'ret':'e0aef965b905a2fedf907151df8e0a6bac832aa697801c51f58bd2ecb4fd381c'}, + 'ret':'e0aef965b905a2fedf907151df8e0a6bac832aa697801c51f58bd2ecb4fd381c'}, {'wif':'KwWr9rDh8KK5TtDa3HLChEvQXNYcUXpwhRFUPc5uSNnMtqNKLFhk', - 'ret':'08d0ed83b64b68d56fa064be48e2385060ed205be2b1e63cd56d218038c3a05f'}), + 'ret':'08d0ed83b64b68d56fa064be48e2385060ed205be2b1e63cd56d218038c3a05f'}), ({'wif':'93HsQEpH75ibaUJYi3QwwiQxnkW4dUuYFPXZxcbcKds7XrqHkY6', - 'ret':'e0aef965b905a2fedf907151df8e0a6bac832aa697801c51f58bd2ecb4fd381c'}, + 'ret':'e0aef965b905a2fedf907151df8e0a6bac832aa697801c51f58bd2ecb4fd381c'}, {'wif':'cMsqcmDYZP1LdKgqRh9L4ZRU9br28yvdmTPwW2YQwVSN9aQiMAoR', 'ret':'08d0ed83b64b68d56fa064be48e2385060ed205be2b1e63cd56d218038c3a05f'})), 'ltc': (({'wif':'6ufJhtQQiRYA3w2QvDuXNXuLgPFp15i3HR1Wp8An2mx1JnhhJAh', - 'ret':'470a974ffca9fca1299b706b09142077bea3acbab6d6480b87dbba79d5fd279b'}, + 'ret':'470a974ffca9fca1299b706b09142077bea3acbab6d6480b87dbba79d5fd279b'}, {'wif':'T41Fm7J3mtZLKYPMCLVSFARz4QF8nvSDhLAfW97Ds56Zm9hRJgn8', - 'ret':'1c6feab55a4c3b4ad1823d4ecacd1565c64228c01828cf44fb4db1e2d82c3d56'}), + 'ret':'1c6feab55a4c3b4ad1823d4ecacd1565c64228c01828cf44fb4db1e2d82c3d56'}), ({'wif':'92iqzh6NqiKawyB1ronw66YtEHrU4rxRJ5T4aHniZqvuSVZS21f', - 'ret':'95b2aa7912550eacdd3844dcc14bee08ce7bc2434ad4858beb136021e945afeb'}, + 'ret':'95b2aa7912550eacdd3844dcc14bee08ce7bc2434ad4858beb136021e945afeb'}, {'wif':'cSaJAXBAm9ooHpVJgoxqjDG3AcareFy29Cz8mhnNTRijjv2HLgta', 'ret':'94fa8b90c11fea8fb907c9376b919534b0a75b9a9621edf71a78753544b4101c'})), }[g.coin.lower()][bool(g.testnet)], @@ -234,7 +234,7 @@ tests = OrderedDict([ 'bad': ('This text is too long for a transaction comment. '*2,), 'good': (u'UTF-8 is OK: я','a good comment',) }), - ('MMGenPWIDString', { # forbidden = list(u' :/\\') + ('MMGenPWIDString', { # forbidden = list(u' :/\\') 'bad': ('foo/','foo:','foo:\\'), 'good': (u'qwerty@яяя',) }), diff --git a/test/scrambletest.py b/test/scrambletest.py index 1c093a9c..decddcf4 100755 --- a/test/scrambletest.py +++ b/test/scrambletest.py @@ -106,5 +106,5 @@ start_time = int(time.time()) run_tests() t = int(time.time()) - start_time -m = '\nAll requested tests finished OK, elapsed time: {:02}:{:02}' +m = '\nAll requested tests finished OK, elapsed time: {:02}:{:02}' msg(green(m.format(t/60,t%60))) diff --git a/test/test.py b/test/test.py index c62262f6..cefda1a4 100755 --- a/test/test.py +++ b/test/test.py @@ -907,6 +907,7 @@ if opt.list_cmds: from mmgen.term import get_terminal_size tw = get_terminal_size()[0] fs = ' {:<{w}} - {}' + Msg(green('AVAILABLE COMMANDS:')) w = max([len(i) for i in cmd_data]) for cmd in cmd_data: @@ -916,24 +917,19 @@ if opt.list_cmds: continue Msg(' '+fs.format(cmd,cmd_data[cmd][1],w=w)) - w = max([len(i) for i in meta_cmds]) - Msg('\n'+green('AVAILABLE METACOMMANDS:')) - for cmd in meta_cmds: - ft = format_text(' '.join(meta_cmds[cmd]),width=tw,indent=4).lstrip() - sep = '\n' if not ft else ' ' if len(ft.splitlines()[0]) + len(cmd) < tw - 4 else '\n ' - Msg_r(' {}{}{}'.format(yellow(cmd+':'),sep,ft)) - - w = max([len(i) for i in cmd_list]) - Msg('\n'+green('AVAILABLE COMMAND GROUPS:')) - for cmd in cmd_list: - ft = format_text(' '.join(cmd_list[cmd]),width=tw,indent=4).lstrip() - sep = '\n' if not ft else ' ' if len(ft.splitlines()[0]) + len(cmd) < tw - 4 else '\n ' - Msg_r(' {}{}{}'.format(yellow(cmd+':'),sep,ft)) + for cl,lbl in ((meta_cmds,'METACOMMANDS'),(cmd_list,'COMMAND GROUPS')): + w = max([len(i) for i in cl]) + Msg('\n'+green('AVAILABLE {}:'.format(lbl))) + for cmd in cl: + ft = format_par(' '.join(cl[cmd]),width=tw,indent=4,as_list=True) + sep = '' if not ft else ' ' if len(ft[0]) + len(cmd) < tw - 4 else '\n ' + Msg(' {}{}{}'.format(yellow(cmd+':'),sep,'\n'.join(ft).lstrip())) Msg('\n'+green('AVAILABLE UTILITIES:')) w = max([len(i) for i in utils]) for cmd in sorted(utils): Msg(fs.format(cmd,utils[cmd],w=w)) + sys.exit(0) NL = ('\r\n','\n')[g.platform=='linux' and bool(opt.popen_spawn)]