tool.py: new MMGenToolCmdMeta metaclass
Testing:
$ test/test.py tool_help
$ test/tooltest2.py
$ test/tooltest2.py -A
$ test/tooltest2.py -f
$ test/tooltest.py
$ test/test.py tool
This commit is contained in:
parent
99beeb0d49
commit
068377cf1b
4 changed files with 55 additions and 39 deletions
|
|
@ -26,7 +26,7 @@ from mmgen.common import *
|
|||
def make_cmd_help():
|
||||
import mmgen.tool
|
||||
def make_help():
|
||||
for bc in mmgen.tool.MMGenToolCmd.__bases__:
|
||||
for bc in mmgen.tool.MMGenToolCmds.classes.values():
|
||||
cls_doc = bc.__doc__.strip().split('\n')
|
||||
for l in cls_doc:
|
||||
if l is cls_doc[0]:
|
||||
|
|
@ -39,10 +39,9 @@ def make_cmd_help():
|
|||
yield ''
|
||||
yield ''
|
||||
|
||||
max_w = max(map(len,bc._user_commands()))
|
||||
max_w = max(map(len,bc.user_commands))
|
||||
fs = ' {{:{}}} - {{}}'.format(max_w)
|
||||
for name in bc._user_commands():
|
||||
code = getattr(bc,name)
|
||||
for name,code in bc.user_commands.items():
|
||||
if code.__doc__:
|
||||
yield fs.format(name,
|
||||
pretty_format(
|
||||
|
|
@ -98,16 +97,15 @@ if len(cmd_args) < 1: opts.usage()
|
|||
cmd = cmd_args.pop(0)
|
||||
|
||||
import mmgen.tool as tool
|
||||
tc = tool.MMGenToolCmd()
|
||||
|
||||
if cmd in ('help','usage') and cmd_args:
|
||||
cmd_args[0] = 'command_name=' + cmd_args[0]
|
||||
|
||||
if cmd not in dir(tc):
|
||||
if cmd not in tool.MMGenToolCmds:
|
||||
die(1,"'{}': no such command".format(cmd))
|
||||
|
||||
args,kwargs = tool._process_args(cmd,cmd_args)
|
||||
|
||||
ret = getattr(tc,cmd)(*args,**kwargs)
|
||||
ret = tool.MMGenToolCmds.call(cmd,*args,**kwargs)
|
||||
|
||||
tool._process_result(ret,pager='pager' in kwargs and kwargs['pager'],print_result=True)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ def _options_annot_str(l):
|
|||
|
||||
def _create_call_sig(cmd,parsed=False):
|
||||
|
||||
m = getattr(MMGenToolCmd,cmd)
|
||||
m = MMGenToolCmds[cmd]
|
||||
|
||||
if 'varargs_call_sig' in m.__code__.co_varnames: # hack
|
||||
flag = 'VAR_ARGS'
|
||||
|
|
@ -90,16 +90,16 @@ def _usage(cmd=None,exit_val=1):
|
|||
|
||||
if not cmd:
|
||||
Msg(m1)
|
||||
for bc in MMGenToolCmd.__bases__:
|
||||
for bc in MMGenToolCmds.classes.values():
|
||||
cls_info = bc.__doc__.strip().split('\n')[0]
|
||||
Msg(' {}{}\n'.format(cls_info[0].upper(),cls_info[1:]))
|
||||
max_w = max(map(len,bc._user_commands()))
|
||||
for cmd in bc._user_commands():
|
||||
max_w = max(map(len,bc.user_commands))
|
||||
for cmd in bc.user_commands:
|
||||
Msg(' {:{w}} {}'.format(cmd,_create_call_sig(cmd),w=max_w))
|
||||
Msg('')
|
||||
Msg(m2)
|
||||
elif cmd in MMGenToolCmd._user_commands():
|
||||
msg('{}'.format(capfirst(getattr(MMGenToolCmd,cmd).__doc__.strip())))
|
||||
elif cmd in MMGenToolCmds:
|
||||
msg('{}'.format(capfirst(MMGenToolCmds[cmd].__doc__.strip())))
|
||||
msg('USAGE: {} {} {}'.format(g.prog_name,cmd,_create_call_sig(cmd)))
|
||||
else:
|
||||
die(1,"'{}': no such tool command".format(cmd))
|
||||
|
|
@ -236,11 +236,43 @@ mnemonic_fmts = {
|
|||
}
|
||||
mn_opts_disp = "(valid options: '{}')".format("', '".join(mnemonic_fmts))
|
||||
|
||||
class MMGenToolCmds(object):
|
||||
class MMGenToolCmdMeta(type):
|
||||
classes = {}
|
||||
methods = {}
|
||||
def __new__(mcls,name,bases,namespace):
|
||||
methods = {k:v for k,v in namespace.items() if k[0] != '_' and callable(v)}
|
||||
if g.test_suite:
|
||||
if name in mcls.classes:
|
||||
raise ValueError(f'Class {name!r} already defined!')
|
||||
for m in methods:
|
||||
if m in mcls.methods:
|
||||
raise ValueError(f'Method {m!r} already defined!')
|
||||
if not getattr(m,'__doc__',None):
|
||||
raise ValueError(f'Method {m!r} has no doc string!')
|
||||
cls = super().__new__(mcls,name,bases,namespace)
|
||||
if bases and name != 'tool_api':
|
||||
mcls.classes[name] = cls
|
||||
mcls.methods.update(methods)
|
||||
return cls
|
||||
|
||||
@classmethod
|
||||
def _user_commands(cls):
|
||||
return [e for e in dir(cls) if e[0] != '_' and getattr(cls,e).__doc__ and callable(getattr(cls,e))]
|
||||
def __iter__(cls):
|
||||
return cls.methods.__iter__()
|
||||
|
||||
def __getitem__(cls,val):
|
||||
return cls.methods.__getitem__(val)
|
||||
|
||||
def __contains__(cls,val):
|
||||
return cls.methods.__contains__(val)
|
||||
|
||||
def call(cls,cmd_name,*args,**kwargs):
|
||||
subcls = cls.classes[cls.methods[cmd_name].__qualname__.split('.')[0]]
|
||||
return getattr(subcls(),cmd_name)(*args,**kwargs)
|
||||
|
||||
@property
|
||||
def user_commands(cls):
|
||||
return {k:v for k,v in cls.__dict__.items() if k in cls.methods}
|
||||
|
||||
class MMGenToolCmds(metaclass=MMGenToolCmdMeta): pass
|
||||
|
||||
class MMGenToolCmdMisc(MMGenToolCmds):
|
||||
"miscellaneous commands"
|
||||
|
|
@ -1097,19 +1129,6 @@ class MMGenToolCmdMonero(MMGenToolCmds):
|
|||
|
||||
return True
|
||||
|
||||
class MMGenToolCmd(
|
||||
MMGenToolCmdMisc,
|
||||
MMGenToolCmdUtil,
|
||||
MMGenToolCmdCoin,
|
||||
MMGenToolCmdMnemonic,
|
||||
MMGenToolCmdFile,
|
||||
MMGenToolCmdFileCrypt,
|
||||
MMGenToolCmdFileUtil,
|
||||
MMGenToolCmdWallet,
|
||||
MMGenToolCmdRPC,
|
||||
MMGenToolCmdMonero,
|
||||
): pass
|
||||
|
||||
class tool_api(
|
||||
MMGenToolCmdUtil,
|
||||
MMGenToolCmdCoin,
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ if opt.list_names:
|
|||
ignore = ()
|
||||
from mmgen.tool import MMGenToolCmd
|
||||
uc = sorted(
|
||||
set(MMGenToolCmd._user_commands()) -
|
||||
set(MMGenToolCmds) -
|
||||
set(ignore) -
|
||||
set(tested_in['tooltest.py']) -
|
||||
set(tested_in['tooltest2.py']) -
|
||||
|
|
|
|||
|
|
@ -783,8 +783,7 @@ def run_test(gid,cmd_name):
|
|||
else:
|
||||
if g.coin != 'BTC' or g.testnet: return
|
||||
m2 = ''
|
||||
m = '{} {}{}'.format(purple('Testing'), cmd_name if opt.names else
|
||||
docstring_head(getattr(getattr(tool,'MMGenToolCmd'+gid),cmd_name)),m2)
|
||||
m = '{} {}{}'.format(purple('Testing'), cmd_name if opt.names else docstring_head(tc[cmd_name]),m2)
|
||||
|
||||
msg_r(green(m)+'\n' if opt.verbose else m)
|
||||
|
||||
|
|
@ -821,7 +820,7 @@ def run_test(gid,cmd_name):
|
|||
os.close(fd1)
|
||||
stdin_save = os.dup(0)
|
||||
os.dup2(fd0,0)
|
||||
cmd_out = getattr(tc,cmd_name)(*aargs,**kwargs)
|
||||
cmd_out = tc.call(cmd_name,*aargs,**kwargs)
|
||||
os.dup2(stdin_save,0)
|
||||
os.wait()
|
||||
opt.quiet = oq_save
|
||||
|
|
@ -832,7 +831,7 @@ def run_test(gid,cmd_name):
|
|||
vmsg('Input: {!r}'.format(stdin_input))
|
||||
sys.exit(0)
|
||||
else:
|
||||
ret = getattr(tc,cmd_name)(*aargs,**kwargs)
|
||||
ret = tc.call(cmd_name,*aargs,**kwargs)
|
||||
opt.quiet = oq_save
|
||||
return ret
|
||||
|
||||
|
|
@ -917,9 +916,9 @@ def docstring_head(obj):
|
|||
def do_group(gid):
|
||||
qmsg(blue("Testing {}".format(
|
||||
"command group '{}'".format(gid) if opt.names
|
||||
else docstring_head(getattr(tool,'MMGenToolCmd'+gid)))))
|
||||
else docstring_head(tc.classes['MMGenToolCmd'+gid]))))
|
||||
|
||||
for cname in [e for e in dir(getattr(tool,'MMGenToolCmd'+gid)) if e[0] != '_']:
|
||||
for cname in tc.classes['MMGenToolCmd'+gid].user_commands:
|
||||
if cname not in tests[gid]:
|
||||
m = 'No test for command {!r} in group {!r}!'.format(cname,gid)
|
||||
if opt.die_on_missing:
|
||||
|
|
@ -950,11 +949,12 @@ if opt.tool_api:
|
|||
del tests['File']
|
||||
|
||||
import mmgen.tool as tool
|
||||
tc = tool.MMGenToolCmds
|
||||
|
||||
if opt.list_tests:
|
||||
Msg('Available tests:')
|
||||
for gid in tests:
|
||||
Msg(' {:6} - {}'.format(gid,docstring_head(getattr(tool,'MMGenToolCmd'+gid))))
|
||||
Msg(' {:6} - {}'.format(gid,docstring_head(tc.classes['MMGenToolCmd'+gid])))
|
||||
sys.exit(0)
|
||||
|
||||
if opt.list_tested_cmds:
|
||||
|
|
@ -986,7 +986,6 @@ if opt.fork:
|
|||
tool_cmd = ('python3',) + tool_cmd
|
||||
else:
|
||||
opt.usr_randchars = 0
|
||||
tc = tool.MMGenToolCmd()
|
||||
|
||||
start_time = int(time.time())
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue