Browse Source

mmnode-blocks-info: --fields: support +<list>-<list>, 'all'-<list>

The MMGen Project 4 years ago
parent
commit
ae03007bba
3 changed files with 70 additions and 18 deletions
  1. 31 17
      mmgen/node_tools/BlocksInfo.py
  2. 11 1
      mmnode-blocks-info
  3. 28 0
      test/unit_tests_d/nt_BlocksInfo.py

+ 31 - 17
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):
-
-		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
-			)
+	@classmethod
+	def parse_cslist(cls,uarg,full_set,dfl_set,desc):
+
+		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():

+ 11 - 1
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
 

+ 28 - 0
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())