From ae03007bbad308ec01b127fbfe2a50de894de1a7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 25 Mar 2021 11:59:14 +0000 Subject: [PATCH] mmnode-blocks-info: --fields: support +-, 'all'- --- mmgen/node_tools/BlocksInfo.py | 46 +++++++++++++++++++----------- mmnode-blocks-info | 12 +++++++- test/unit_tests_d/nt_BlocksInfo.py | 28 ++++++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/mmgen/node_tools/BlocksInfo.py b/mmgen/node_tools/BlocksInfo.py index c25d1a2..4521741 100644 --- a/mmgen/node_tools/BlocksInfo.py +++ b/mmgen/node_tools/BlocksInfo.py @@ -124,28 +124,42 @@ class BlocksInfo: t_fmt = lambda self,t: f'{t/86400:.2f} days' if t > 172800 else f'{t/3600:.2f} hrs' - def __init__(self,cmd_args,opt,rpc): + @classmethod + def parse_cslist(cls,uarg,full_set,dfl_set,desc): - def parse_cslist(uarg,full_set,dfl_set,desc): - m = re.match('([+-]){1}',uarg) - pfx = m[1] if m else None - usr_set = set((uarg[1:] if m else uarg).split(',')) - dfl_set = set(dfl_set) - for e in usr_set: - if e not in full_set: - die(1,f'{e!r}: unrecognized {desc}') - res = ( - dfl_set | usr_set if pfx == '+' else - dfl_set - usr_set if pfx == '-' else - usr_set - ) + def make_list(m,func): + groups_lc = [set(e.lower() for e in g.split(',')) for g in m.groups()] + for group in groups_lc: + for e in group: + if e not in full_set_lc: + die(1,f'{e!r}: unrecognized {desc}') # display elements in order: - return [e for e in full_set if e in res] + return [e for e in full_set if e.lower() in func(groups_lc)] + + full_set_lc = set(e.lower() for e in full_set) + dfl_set_lc = set(e.lower() for e in dfl_set) + cspat = r'(\w+(?:,\w+)*)' + + for pat,func in ( + ( rf'{cspat}$', lambda g: g[0] ), + ( rf'\+{cspat}$', lambda g: dfl_set_lc | g[0] ), + ( rf'\-{cspat}$', lambda g: dfl_set_lc - g[0] ), + ( rf'\+{cspat}\-{cspat}$', lambda g: ( dfl_set_lc | g[0] ) - g[1] ), + ( rf'\-{cspat}\+{cspat}$', lambda g: ( dfl_set_lc - g[0] ) | g[1] ), + ( rf'all\-{cspat}$', lambda g: full_set_lc - g[0] ) + ): + m = re.match(pat,uarg,re.ASCII|re.IGNORECASE) + if m: + return make_list(m,func) + else: + die(1,f'{uarg}: invalid parameter') + + def __init__(self,cmd_args,opt,rpc): def parse_cs_uarg(uarg,full_set,dfl_set,desc): return ( full_set if uarg == 'all' else [] if uarg == 'none' else - parse_cslist(uarg,full_set,dfl_set,desc) + self.parse_cslist(uarg,full_set,dfl_set,desc) ) def get_fields(): diff --git a/mmnode-blocks-info b/mmnode-blocks-info index 2a8c9c6..62e1a59 100755 --- a/mmnode-blocks-info +++ b/mmnode-blocks-info @@ -57,7 +57,10 @@ opts_data = { See AVAILABLE FIELDS below. Prefix the list with '+' to add the fields to the defaults, or '-' to remove them. The special values 'all' and 'none' select all - available fields or none, respectively. + available fields or none, respectively. The '+' and + '-'-prefixed lists may be concatenated to specify both + addition and removal of fields. A single '-'-prefixed + list may be additionally prefixed by 'all'. -s, --stats= Display the specified stats (comma-separated list). See AVAILABLE STATS below. The prefixes and special values available to the --fields option are recognized. @@ -120,9 +123,16 @@ EXAMPLES: fields from the defaults and skipping stats: $ {p} -o -size,subsidy -s none +10 + Display data for the last ten blocks, omitting the 'size' and 'version' + fields from the defaults and adding the 'inputs' and 'utxo_inc' fields: + $ {p} -o -version,size+utxo_inc,inputs +10 + Display all fields and stats for the last ten blocks: $ {p} -o all -s all +10 + Same as above, but omit the 'miner' and 'hash' fields: + $ {p} -o all-miner,hash -s all +10 + Same as above, but display only fields relating to stats: $ {p} -o none -s all -f +10 diff --git a/test/unit_tests_d/nt_BlocksInfo.py b/test/unit_tests_d/nt_BlocksInfo.py index 96b7299..307f6e8 100755 --- a/test/unit_tests_d/nt_BlocksInfo.py +++ b/test/unit_tests_d/nt_BlocksInfo.py @@ -45,6 +45,24 @@ range_vecs = ( ( '+144*10+12*12', (tip-1439, tip, None, 1440, 144 ), (tip-1439, tip, list(range(tip-1439,tip+1,144))) ), ) +full_set = ['aa','bbb','ccc_P2','ddddd','eeeeee','ffffffff','gg'] +dfl_set = ['aa','ddddd','ffffffff'] +fields_vecs = ( + ( 'Ccc_P2', ['ccc_P2'] ), + ( '+CCC_P2', ['aa','ccc_P2','ddddd','ffffffff'] ), + ( '+Aa', dfl_set ), + ( '+ddDDD,FffffffF', dfl_set ), + ( '+bBb', ['aa','bbb','ddddd','ffffffff'] ), + ( '-ddddd', ['aa','ffffffff'] ), + ( '-DDDDD,fFffffff', ['aa'] ), + ( '-ffffffff,AA,DdDdD', [] ), + ( '+aa,gG,ccC_P2', ['aa','ccc_P2','ddddd','ffffffff','gg'] ), + ( '+BBB,gg-dDddD,fFffffff', ['aa','bbb','gg'] ), + ( '-dDddD,fFffffff+BBB,gg', ['aa','bbb','gg'] ), + ( 'aLL-Ccc_P2', [e for e in full_set if e != 'ccc_P2'] ), + ( 'All-dDddd,aa', [e for e in full_set if e not in ('aa','ddddd')] ), +) + class dummyRPC: blockcount = tip @@ -57,6 +75,16 @@ class dummyOpt: class unit_tests: + def parse_field(self,name,ut): + vmsg('{:28} => {}'.format('FULL SET:',full_set)) + vmsg('{:28} => {}'.format('DFL SET: ',dfl_set)) + b = BlocksInfo + for opt,chk in fields_vecs: + ret = b.parse_cslist(opt,full_set,dfl_set,'field') + vmsg(f'{opt:28} => {ret}') + assert ret == chk, f'{ret} != {chk}' + return True + def parse_rangespec(self,name,ut): b = BlocksInfo(0,dummyOpt(),dummyRPC())