opts.py: refactor usage(), version(), show_hash_presets()

This commit is contained in:
The MMGen Project 2024-10-08 12:56:00 +00:00
commit f6843a3fcd
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
21 changed files with 85 additions and 37 deletions

View file

@ -543,6 +543,11 @@ class Config(Lockable):
# Check user-set opts without modifying them # Check user-set opts without modifying them
check_opts(self) check_opts(self)
def _usage(self):
from .help import make_usage_str
print(make_usage_str(self, caller='user'))
sys.exit(1) # called only on bad invocation
def _set_cfg_from_env(self): def _set_cfg_from_env(self):
for name,val in ((k,v) for k,v in os.environ.items() if k.startswith('MMGEN_')): for name,val in ((k,v) for k,v in os.environ.items() if k.startswith('MMGEN_')):
if name == 'MMGEN_DEBUG_ALL': if name == 'MMGEN_DEBUG_ALL':

View file

@ -129,9 +129,9 @@ def get_seed_file(cfg,nargs,wallets=None,invoked_as=None):
if len(wallets) + (wd_from_opt or bool(wf)) < nargs: if len(wallets) + (wd_from_opt or bool(wf)) < nargs:
if not wf: if not wf:
msg('No default wallet found, and no other seed source was specified') msg('No default wallet found, and no other seed source was specified')
cfg._opts.usage() cfg._usage()
elif len(wallets) > nargs: elif len(wallets) > nargs:
cfg._opts.usage() cfg._usage()
elif len(wallets) == nargs and wf and invoked_as != 'gen': elif len(wallets) == nargs and wf and invoked_as != 'gen':
cfg._util.qmsg('Warning: overriding default wallet with user-supplied wallet') cfg._util.qmsg('Warning: overriding default wallet with user-supplied wallet')

View file

@ -20,8 +20,46 @@
help: help notes for MMGen suite commands help: help notes for MMGen suite commands
""" """
import sys
from ..cfg import gc from ..cfg import gc
def version(cfg):
from ..util import fmt
print(fmt(f"""
{gc.prog_name.upper()} version {gc.version}
Part of {gc.proj_name} Wallet, an online/offline cryptocurrency wallet for the
command line. Copyright (C){gc.Cdates} {gc.author} {gc.email}
""", indent=' ').rstrip())
sys.exit(0)
def show_hash_presets(cfg):
fs = ' {:<6} {:<3} {:<2} {}'
from ..util import msg
from ..crypto import Crypto
msg(' Available parameters for scrypt.hash():')
msg(fs.format('Preset', 'N', 'r', 'p'))
for i in sorted(Crypto.hash_presets.keys()):
msg(fs.format(i, *Crypto.hash_presets[i]))
msg(' N = memory usage (power of two)\n p = iterations (rounds)')
sys.exit(0)
def make_usage_str(cfg, caller):
indent, col1_w = {
'help': (2, len(gc.prog_name) + 1),
'user': (0, len('USAGE:')),
}[caller]
def gen():
ulbl = 'USAGE:'
for line in [cfg._usage_data.strip()] if isinstance(cfg._usage_data, str) else cfg._usage_data:
yield f'{ulbl:{col1_w}} {gc.prog_name} {line}'
ulbl = ''
return ('\n' + (' ' * indent)).join(gen())
def usage(cfg):
print(make_usage_str(cfg, caller='user'))
sys.exit(0)
def help_notes_func(proto,cfg,k): def help_notes_func(proto,cfg,k):
def fee_spec_letters(use_quotes=False): def fee_spec_letters(use_quotes=False):

View file

@ -134,7 +134,7 @@ addr_type = MMGenAddrType(
errmsg = f'{cfg.type!r}: invalid parameter for --type option' ) errmsg = f'{cfg.type!r}: invalid parameter for --type option' )
if len(cfg._args) < 1: if len(cfg._args) < 1:
cfg._opts.usage() cfg._usage()
if cfg.keygen_backend: if cfg.keygen_backend:
from .keygen import check_backend from .keygen import check_backend

View file

@ -194,7 +194,7 @@ cfg = Config(
}, },
do_post_init = True ) do_post_init = True )
cmd = cfg._args[0] if len(cfg._args) == 1 else 'sign' if not cfg._args else cfg._opts.usage() cmd = cfg._args[0] if len(cfg._args) == 1 else 'sign' if not cfg._args else cfg._usage()
if cmd not in Autosign.cmds + Autosign.util_cmds: if cmd not in Autosign.cmds + Autosign.util_cmds:
die(1,f'{cmd}’: unrecognized command') die(1,f'{cmd}’: unrecognized command')

View file

@ -207,7 +207,7 @@ cfg = Config( opts_data=opts_data, need_amt=False )
cmd_args = cfg._args cmd_args = cfg._args
if len(cmd_args) < 2: if len(cmd_args) < 2:
cfg._opts.usage() cfg._usage()
op = cmd_args.pop(0) op = cmd_args.pop(0)
@ -217,15 +217,15 @@ if cfg.msghash_type and op != 'create':
async def main(): async def main():
if op == 'create': if op == 'create':
if len(cmd_args) < 2: if len(cmd_args) < 2:
cfg._opts.usage() cfg._usage()
MsgOps.create( cmd_args[0], ' '.join(cmd_args[1:]) ) MsgOps.create( cmd_args[0], ' '.join(cmd_args[1:]) )
elif op == 'sign': elif op == 'sign':
if len(cmd_args) < 1: if len(cmd_args) < 1:
cfg._opts.usage() cfg._usage()
await MsgOps.sign( cmd_args[0], cmd_args[1:] ) await MsgOps.sign( cmd_args[0], cmd_args[1:] )
elif op in ('verify','export'): elif op in ('verify','export'):
if len(cmd_args) not in (1,2): if len(cmd_args) not in (1,2):
cfg._opts.usage() cfg._usage()
await getattr(MsgOps,op)( cmd_args[0], cmd_args[1] if len(cmd_args) == 2 else None ) await getattr(MsgOps,op)( cmd_args[0], cmd_args[1] if len(cmd_args) == 2 else None )
else: else:
die(1,f'{op!r}: unrecognized operation') die(1,f'{op!r}: unrecognized operation')

View file

@ -138,7 +138,7 @@ FMT CODES:
cfg = Config(opts_data=opts_data) cfg = Config(opts_data=opts_data)
if len(cfg._args) < 2: if len(cfg._args) < 2:
cfg._opts.usage() cfg._usage()
pw_idxs = AddrIdxList(fmt_str=cfg._args.pop()) pw_idxs = AddrIdxList(fmt_str=cfg._args.pop())

View file

@ -77,7 +77,7 @@ def check_num_args():
die(1,m.format(args,'many','more',amax)) die(1,m.format(args,'many','more',amax))
if not cmd_args: if not cmd_args:
cfg._opts.usage() cfg._usage()
elif cmd_args[0] not in MMGenRegtest.usr_cmds: elif cmd_args[0] not in MMGenRegtest.usr_cmds:
die(1,f'{cmd_args[0]!r}: invalid command') die(1,f'{cmd_args[0]!r}: invalid command')
elif cmd_args[0] not in ('cli','wallet_cli','balances'): elif cmd_args[0] not in ('cli','wallet_cli','balances'):

View file

@ -111,7 +111,7 @@ def print_shares_info():
cfg = Config(opts_data=opts_data) cfg = Config(opts_data=opts_data)
if len(cfg._args) + bool(cfg.hidden_incog_input_params) < 2: if len(cfg._args) + bool(cfg.hidden_incog_input_params) < 2:
cfg._opts.usage() cfg._usage()
if cfg.master_share: if cfg.master_share:
master_idx = MasterShareIdx(cfg.master_share) master_idx = MasterShareIdx(cfg.master_share)

View file

@ -359,7 +359,7 @@ if gc.prog_name.endswith('-tool'):
sys.exit(0) sys.exit(0)
if len(po.cmd_args) < 1: if len(po.cmd_args) < 1:
cfg._opts.usage() cfg._usage()
cmd = po.cmd_args[0] cmd = po.cmd_args[0]

View file

@ -78,7 +78,7 @@ elif not cfg._args and cfg.autosign:
infile = si.get_unsent() infile = si.get_unsent()
cfg._util.qmsg(f'Got signed transaction file ‘{infile}') cfg._util.qmsg(f'Got signed transaction file ‘{infile}')
else: else:
cfg._opts.usage() cfg._usage()
if not cfg.status: if not cfg.status:
from .ui import do_license_msg from .ui import do_license_msg

View file

@ -102,7 +102,7 @@ cfg = Config(opts_data=opts_data)
infiles = cfg._args infiles = cfg._args
if not infiles: if not infiles:
cfg._opts.usage() cfg._usage()
from .fileutil import check_infile from .fileutil import check_infile
for i in infiles: for i in infiles:

View file

@ -166,17 +166,17 @@ elif invoked_as == 'seedsplit':
m2 = 'To generate a master share, omit the seed split specifier.' m2 = 'To generate a master share, omit the seed split specifier.'
die(1,m1+' '+m2) die(1,m1+' '+m2)
elif not sss: elif not sss:
cfg._opts.usage() cfg._usage()
elif master_share: elif master_share:
sss = SeedSplitSpecifier('1:2') sss = SeedSplitSpecifier('1:2')
else: else:
cfg._opts.usage() cfg._usage()
from .fileutil import check_infile,get_seed_file from .fileutil import check_infile,get_seed_file
if cmd_args: if cmd_args:
if invoked_as == 'gen' or len(cmd_args) > 1: if invoked_as == 'gen' or len(cmd_args) > 1:
cfg._opts.usage() cfg._usage()
check_infile(cmd_args[0]) check_infile(cmd_args[0])
sf = get_seed_file(cfg,nargs,invoked_as=invoked_as) sf = get_seed_file(cfg,nargs,invoked_as=invoked_as)

View file

@ -128,7 +128,7 @@ if cmd_args and cfg.autosign and (
cmd_args.insert(1,None) cmd_args.insert(1,None)
if len(cmd_args) < 2: if len(cmd_args) < 2:
cfg._opts.usage() cfg._usage()
op = cmd_args.pop(0) op = cmd_args.pop(0)
infile = cmd_args.pop(0) infile = cmd_args.pop(0)
@ -136,22 +136,22 @@ wallets = spec = None
if op in ('relay', 'submit', 'resubmit', 'abort'): if op in ('relay', 'submit', 'resubmit', 'abort'):
if len(cmd_args) != 0: if len(cmd_args) != 0:
cfg._opts.usage() cfg._usage()
elif op in ('txview','txlist'): elif op in ('txview','txlist'):
infile = [infile] + cmd_args infile = [infile] + cmd_args
elif op in ('create','sync','list','view','listview','dump','restore'): # kafile_arg_ops elif op in ('create','sync','list','view','listview','dump','restore'): # kafile_arg_ops
if len(cmd_args) > 1: if len(cmd_args) > 1:
cfg._opts.usage() cfg._usage()
wallets = cmd_args.pop(0) if cmd_args else None wallets = cmd_args.pop(0) if cmd_args else None
elif op in ('new', 'transfer', 'sweep', 'sweep_all', 'label'): elif op in ('new', 'transfer', 'sweep', 'sweep_all', 'label'):
if len(cmd_args) != 1: if len(cmd_args) != 1:
cfg._opts.usage() cfg._usage()
spec = cmd_args[0] spec = cmd_args[0]
elif op in ('export-outputs', 'export-outputs-sign', 'import-key-images'): elif op in ('export-outputs', 'export-outputs-sign', 'import-key-images'):
if not cfg.autosign: # --autosign only for now - TODO if not cfg.autosign: # --autosign only for now - TODO
die(f'--autosign must be used with command {op!r}') die(f'--autosign must be used with command {op!r}')
if len(cmd_args) > 1: if len(cmd_args) > 1:
cfg._opts.usage() cfg._usage()
wallets = cmd_args.pop(0) if cmd_args else None wallets = cmd_args.pop(0) if cmd_args else None
else: else:
die(1,f'{op!r}: unrecognized operation') die(1,f'{op!r}: unrecognized operation')

View file

@ -66,6 +66,7 @@ long_opts_data = {
--, --tw-name=NAME Specify alternate name for the BTC/LTC/BCH tracking --, --tw-name=NAME Specify alternate name for the BTC/LTC/BCH tracking
wallet (default: {tw_name}) wallet (default: {tw_name})
--, --skip-cfg-file Skip reading the configuration file --, --skip-cfg-file Skip reading the configuration file
--, --usage Print usage information and exit
--, --version Print version information and exit --, --version Print version information and exit
--, --bob Specify user Bob in MMGen regtest mode --, --bob Specify user Bob in MMGen regtest mode
--, --alice Specify user Alice in MMGen regtest mode --, --alice Specify user Alice in MMGen regtest mode
@ -105,9 +106,6 @@ class UserOpts:
od['text']['long_options'] = long_opts_data['text'] od['text']['long_options'] = long_opts_data['text']
# Make this available to usage()
self.usage_data = od['text'].get('usage2') or od['text']['usage']
# po: (user_opts,cmd_args,opts,filtered_opts) # po: (user_opts,cmd_args,opts,filtered_opts)
po = parsed_opts or Opts.parse_opts(od,opt_filter=opt_filter) po = parsed_opts or Opts.parse_opts(od,opt_filter=opt_filter)
@ -123,15 +121,16 @@ class UserOpts:
cfg._parsed_opts = po cfg._parsed_opts = po
cfg._use_env = True cfg._use_env = True
cfg._use_cfg_file = not 'skip_cfg_file' in uopts cfg._use_cfg_file = not 'skip_cfg_file' in uopts
# Make this available to usage()
cfg._usage_data = opts_data['text'].get('usage2') or opts_data['text']['usage']
if os.getenv('MMGEN_DEBUG_OPTS'): if os.getenv('MMGEN_DEBUG_OPTS'):
opt_preproc_debug(po) opt_preproc_debug(po)
if 'version' in uopts: for funcname in ('usage', 'version', 'show_hash_presets'):
self.version() # exits if funcname in uopts:
import importlib
if 'show_hash_presets' in uopts: getattr(importlib.import_module('mmgen.help'), funcname)(cfg) # exits
self.show_hash_presets() # exits
if parse_only: if parse_only:
return return

View file

@ -100,7 +100,7 @@ def main():
global slip44_data, bip_utils_data global slip44_data, bip_utils_data
if len(cfg._args) != 1: if len(cfg._args) != 1:
cfg._opts.usage() cfg._usage()
with open(cfg._args[0]) as fh: with open(cfg._args[0]) as fh:
slip44_data = json.loads(fh.read()) slip44_data = json.loads(fh.read())

View file

@ -275,7 +275,7 @@ def main():
die(1,'--coin option must be ETH or ETC') die(1,'--coin option must be ETH or ETC')
if not len(cfg._args) == 1: if not len(cfg._args) == 1:
cfg._opts.usage() cfg._usage()
code = create_src( cfg, solidity_code_template, token_data ) code = create_src( cfg, solidity_code_template, token_data )

View file

@ -29,7 +29,7 @@ opts_data = {
cfg = Config(opts_data=opts_data) cfg = Config(opts_data=opts_data)
if len(cfg._args) != 1: if len(cfg._args) != 1:
cfg._opts.usage() cfg._usage()
tx = asyncio.run(CompletedTX(cfg._args[0],quiet_open=True)) tx = asyncio.run(CompletedTX(cfg._args[0],quiet_open=True))
tx.file.write(ask_tty=False,ask_overwrite=not cfg.quiet,ask_write=not cfg.quiet) tx.file.write(ask_tty=False,ask_overwrite=not cfg.quiet,ask_write=not cfg.quiet)

View file

@ -25,7 +25,8 @@ class CmdTestHelp(CmdTestBase):
networks = ('btc', 'ltc', 'bch', 'eth', 'xmr', 'doge') networks = ('btc', 'ltc', 'bch', 'eth', 'xmr', 'doge')
passthru_opts = ('daemon_data_dir','rpc_port','coin','testnet') passthru_opts = ('daemon_data_dir','rpc_port','coin','testnet')
cmd_group = ( cmd_group = (
('usage', (1,'usage message (via bad invocation)',[])), ('usage1', (1,'usage message (via --usage)',[])),
('usage2', (1,'usage message (via bad invocation)',[])),
('version', (1,'version message',[])), ('version', (1,'version message',[])),
('license', (1,'license message',[])), ('license', (1,'license message',[])),
('helpscreens', (1,'help screens', [])), ('helpscreens', (1,'help screens', [])),
@ -37,7 +38,12 @@ class CmdTestHelp(CmdTestBase):
('tooltest_help', (1,"'tooltest.py' help screens",[])), ('tooltest_help', (1,"'tooltest.py' help screens",[])),
) )
def usage(self): def usage1(self):
t = self.spawn('mmgen-txsend', ['--usage'], no_passthru_opts=True)
t.expect('USAGE: mmgen-txsend')
return t
def usage2(self):
t = self.spawn('mmgen-walletgen', ['foo'], exit_val=1, no_passthru_opts=True) t = self.spawn('mmgen-walletgen', ['foo'], exit_val=1, no_passthru_opts=True)
t.expect('USAGE: mmgen-walletgen') t.expect('USAGE: mmgen-walletgen')
return t return t

View file

@ -479,7 +479,7 @@ def get_protos(proto,addr_type,toolname):
def parse_args(): def parse_args():
if len(cfg._args) != 2: if len(cfg._args) != 2:
cfg._opts.usage() cfg._usage()
arg1,arg2 = cfg._args arg1,arg2 = cfg._args
gen1,gen2,rounds = (0,0,0) gen1,gen2,rounds = (0,0,0)

View file

@ -140,7 +140,7 @@ def main():
ids = cfg._args ids = cfg._args
network_ids = CoinDaemon.get_network_ids(cfg) network_ids = CoinDaemon.get_network_ids(cfg)
if not ids: if not ids:
cfg._opts.usage() cfg._usage()
for i in ids: for i in ids:
if i not in network_ids + list(xmr_wallet_network_ids): if i not in network_ids + list(xmr_wallet_network_ids):
die(1,f'{i!r}: invalid network ID') die(1,f'{i!r}: invalid network ID')