diff --git a/mmgen/main_addrgen.py b/mmgen/main_addrgen.py index 723f5c93..0d67e8f6 100755 --- a/mmgen/main_addrgen.py +++ b/mmgen/main_addrgen.py @@ -41,12 +41,15 @@ note_secp256k1 = """ If available, the secp256k1 library will be used for address generation. """.strip() -opts_data = lambda: { +opts_data = { 'sets': [('print_checksum',True,'quiet',True)], - 'desc': """Generate a range or list of {desc} from an {pnm} wallet, - mnemonic, seed or brainwallet""".format(desc=gen_desc,pnm=g.proj_name), - 'usage':'[opts] [seed source] ', - 'options': """ + 'text': { + 'desc': """ + Generate a range or list of {desc} from an {pnm} wallet, + mnemonic, seed or brainwallet + """.format(desc=gen_desc,pnm=g.proj_name), + 'usage':'[opts] [seed source] ', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -A, --no-addresses Print only secret keys, no addresses @@ -77,14 +80,7 @@ opts_data = lambda: { (default: {dmat}) -v, --verbose Produce more verbose output -x, --b16 Print secret keys in hexadecimal too -""".format( - seed_lens=', '.join(map(str,g.seed_lens)), - pnm=g.proj_name, - kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), - kg=g.key_generator, - what=gen_what,g=g, - dmat="'{}' or '{}'".format(g.proto.dfl_mmtype,MAT.mmtypes[g.proto.dfl_mmtype]['name']) -), +""", 'notes': """ NOTES FOR THIS COMMAND @@ -105,16 +101,30 @@ ADDRESS TYPES: {n_bw} FMT CODES: + {n_fmt} -""".format( - n_secp=note_secp256k1, - n_addrkey=note_addrkey, - n_pw=help_notes('passwd'), - n_bw=help_notes('brainwallet'), - n_fmt='\n '.join(SeedSource.format_fmt_codes().splitlines()), - n_at='\n '.join(["'{}','{:<12} - {}".format(k,v['name']+"'",v['desc']) for k,v in list(MAT.mmtypes.items())]), - o=opts - ) +""" + }, + 'code': { + 'options': lambda s: s.format( + seed_lens=', '.join(map(str,g.seed_lens)), + dmat="'{}' or '{}'".format(g.proto.dfl_mmtype,MAT.mmtypes[g.proto.dfl_mmtype]['name']), + kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), + kg=g.key_generator, + pnm=g.proj_name, + what=gen_what, + g=g, + ), + 'notes': lambda s: s.format( + n_secp=note_secp256k1, + n_addrkey=note_addrkey, + n_pw=help_notes('passwd'), + n_bw=help_notes('brainwallet'), + n_fmt='\n '.join(SeedSource.format_fmt_codes().splitlines()), + n_at='\n '.join(["'{}','{:<12} - {}".format( + k,v['name']+"'",v['desc']) for k,v in list(MAT.mmtypes.items())]) + ) + } } cmd_args = opts.init(opts_data,add_opts=['b16'],opt_filter=opt_filter) diff --git a/mmgen/main_addrimport.py b/mmgen/main_addrimport.py index 9d6c42e7..69ce17ac 100755 --- a/mmgen/main_addrimport.py +++ b/mmgen/main_addrimport.py @@ -47,10 +47,11 @@ option, or a list of non-{pnm} addresses with the '--addrlist' option # In batch mode, daemon just rescans each address separately anyway, so make # --batch and --rescan incompatible. -opts_data = lambda: { - 'desc': """Import addresses into an {} tracking wallet""".format(g.proj_name), - 'usage':'[opts] [mmgen address file]', - 'options': """ +opts_data = { + 'text': { + 'desc': """Import addresses into an {} tracking wallet""".format(g.proj_name), + 'usage':'[opts] [mmgen address file]', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -a, --address=a Import the single coin address 'a' @@ -67,6 +68,7 @@ already in the tracking wallet. The --batch and --rescan options cannot be used together. """ + } } cmd_args = opts.init(opts_data) diff --git a/mmgen/main_autosign.py b/mmgen/main_autosign.py index 4c301834..6abd84ac 100755 --- a/mmgen/main_autosign.py +++ b/mmgen/main_autosign.py @@ -31,10 +31,11 @@ key_fn = 'autosign.key' from mmgen.common import * prog_name = os.path.basename(sys.argv[0]) -opts_data = lambda: { - 'desc': 'Auto-sign MMGen transactions', - 'usage':'[opts] [command]', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Auto-sign MMGen transactions', + 'usage':'[opts] [command]', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -c, --coins=c Coins to sign for (comma-separated list) @@ -97,6 +98,7 @@ each signing session. This command is currently available only on Linux-based platforms. """.format(pnm=prog_name,wd=wallet_dir,td=tx_dir,kf=key_fn,mp=mountpoint) + } } cmd_args = opts.init(opts_data,add_opts=['mmgen_keys_from_file','in_fmt']) diff --git a/mmgen/main_passgen.py b/mmgen/main_passgen.py index 1df2d2ce..25e6d1f4 100755 --- a/mmgen/main_passgen.py +++ b/mmgen/main_passgen.py @@ -33,12 +33,15 @@ dfl_len = { 'hex': PasswordList.pw_info['hex']['dfl_len'] } -opts_data = lambda: { +opts_data = { 'sets': [('print_checksum',True,'quiet',True)], - 'desc': """Generate a range or list of passwords from an {pnm} wallet, - mnemonic, seed or brainwallet for the given ID string""".format(pnm=g.proj_name), - 'usage':'[opts] [seed source] ', - 'options': """ + 'text': { + 'desc': """ + Generate a range or list of passwords from an {pnm} wallet, + mnemonic, seed or brainwallet for the given ID string + """.format(pnm=g.proj_name), + 'usage':'[opts] [seed source] ', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -b, --base32 Generate passwords in Base32 format instead of Base58 @@ -65,11 +68,7 @@ opts_data = lambda: { (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}) -S, --stdout Print passwords to stdout -v, --verbose Produce more verbose output -""".format( - seed_lens=', '.join(map(str,g.seed_lens)), - g=g,pnm=g.proj_name,d58=dfl_len['b58'],d32=dfl_len['b32'],dhex=dfl_len['hex'], - kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]) -), +""", 'notes': """ NOTES FOR THIS COMMAND @@ -84,6 +83,7 @@ Changing either the password format (base32,base58) or length alters the seed and thus generates a completely new set of passwords. EXAMPLE: + Generate ten base58 passwords of length {d58} for Alice's email account: {g.prog_name} alice@nowhere.com 1-10 @@ -104,15 +104,25 @@ EXAMPLE: {n_bw} FMT CODES: + {n_fmt} -""".format( - o=opts,g=g,d58=dfl_len['b58'],d32=dfl_len['b32'], - ml=MMGenPWIDString.max_len, - fs="', '".join(MMGenPWIDString.forbidden), - n_pw=help_notes('passwd'), - n_bw=help_notes('brainwallet'), - n_fmt='\n '.join(SeedSource.format_fmt_codes().splitlines()) - ) +""" + }, + 'code': { + 'options': lambda s: s.format( + seed_lens=', '.join(map(str,g.seed_lens)), + g=g,pnm=g.proj_name,d58=dfl_len['b58'],d32=dfl_len['b32'],dhex=dfl_len['hex'], + kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]) + ), + 'notes': lambda s: s.format( + o=opts,g=g,d58=dfl_len['b58'],d32=dfl_len['b32'], + ml=MMGenPWIDString.max_len, + fs="', '".join(MMGenPWIDString.forbidden), + n_pw=help_notes('passwd'), + n_bw=help_notes('brainwallet'), + n_fmt='\n '.join(SeedSource.format_fmt_codes().splitlines()) + ) + } } cmd_args = opts.init(opts_data,add_opts=['b16']) diff --git a/mmgen/main_regtest.py b/mmgen/main_regtest.py index f5c96d25..f7792743 100755 --- a/mmgen/main_regtest.py +++ b/mmgen/main_regtest.py @@ -23,11 +23,12 @@ mmgen-regtest: Coin daemon regression test mode setup and operations for the MMG from mmgen.common import * -opts_data = lambda: { - 'desc': 'Coin daemon regression test mode setup and operations for the {} suite'.format(g.proj_name), - 'usage': '[opts] ', - 'sets': ( ('yes', True, 'quiet', True), ), - 'options': """ +opts_data = { + 'sets': [('yes', True, 'quiet', True)], + 'text': { + 'desc': 'Coin daemon regression test mode setup and operations for the {} suite'.format(g.proj_name), + 'usage': '[opts] ', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -e, --empty Don't fund Bob and Alice's wallets on setup @@ -52,6 +53,7 @@ opts_data = lambda: { show_mempool - show transaction IDs in mempool cli [arguments] - execute an RPC call with arguments """ + } } cmd_args = opts.init(opts_data) diff --git a/mmgen/main_split.py b/mmgen/main_split.py index 9e4045f1..91c64b83 100755 --- a/mmgen/main_split.py +++ b/mmgen/main_split.py @@ -26,11 +26,14 @@ import time from mmgen.common import * -opts_data = lambda: { - 'desc': """Split funds in a {pnm} wallet after a chain fork using a - timelocked transaction""".format(pnm=g.proj_name), - 'usage':'[opts] [output addr1] [output addr2]', - 'options': """ +opts_data = { + 'text': { + 'desc': """ + Split funds in a {pnm} wallet after a chain fork using a + timelocked transaction + """.format(pnm=g.proj_name), + 'usage':'[opts] [output addr1] [output addr2]', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -f, --tx-fees= f The transaction fees for each chain (comma-separated) @@ -46,7 +49,7 @@ opts_data = lambda: { -R, --rpc-host2= h Host the other coin daemon is running on (default: none) -L, --locktime= t Lock time (block height or unix seconds) (default: {bh}) -""".format(oc=g.proto.forks[-1][2].upper(),bh='current block height'), +""", 'notes': """\n This command creates two transactions: one (with the timelock) to be broadcast on the long chain and one on the short chain after a replayable chain fork. @@ -74,6 +77,12 @@ minority chain is ahead of the timelock. If the reorg'd minority chain is behind the timelock, protection is contingent on getting the non-timelocked transaction reconfirmed before the timelock expires. Use at your own risk. """.format(pnm=g.proj_name) + }, + 'code': { + 'options': lambda s: s.format( + oc=g.proto.forks[-1][2].upper(), + bh='current block height'), + } } cmd_args = opts.init(opts_data,add_opts=['tx_fee','tx_fee_adj','comment_file']) diff --git a/mmgen/main_tool.py b/mmgen/main_tool.py index ab4c8e05..cbf186b2 100755 --- a/mmgen/main_tool.py +++ b/mmgen/main_tool.py @@ -53,10 +53,11 @@ def make_cmd_help(): return '\n'.join(out) -opts_data = lambda: { - 'desc': 'Perform various {pnm}- and cryptocoin-related operations'.format(pnm=g.proj_name), - 'usage': '[opts] ', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Perform various {pnm}- and cryptocoin-related operations'.format(pnm=g.proj_name), + 'usage': '[opts] ', + 'options': """ -d, --outdir= d Specify an alternate directory 'd' for output -h, --help Print this help message --, --longhelp Print help message for long options (common options) @@ -70,14 +71,21 @@ opts_data = lambda: { -t, --type=t Specify address type (valid options: 'legacy', 'compressed', 'segwit', 'bech32', 'zcash_z') -v, --verbose Produce more verbose output -""".format(g=g), +""", 'notes': """ COMMANDS {ch} Type '{pn} help ' for help on a particular command -""".format(pn=g.prog_name,ch=make_cmd_help()) +""" + }, + 'code': { + 'options': lambda s: s.format(g=g), + 'notes': lambda s: s.format( + ch=make_cmd_help(), + pn=g.prog_name) + } } cmd_args = opts.init(opts_data,add_opts=['hidden_incog_input_params','in_fmt','use_old_ed25519']) diff --git a/mmgen/main_txbump.py b/mmgen/main_txbump.py index 5da5c123..8436e334 100755 --- a/mmgen/main_txbump.py +++ b/mmgen/main_txbump.py @@ -23,11 +23,16 @@ mmgen-txbump: Increase the fee on a replaceable (replace-by-fee) MMGen from mmgen.common import * -opts_data = lambda: { - 'desc': 'Increase the fee on a replaceable (RBF) {g.proj_name} transaction, creating a new transaction, and optionally sign and send the new transaction'.format(g=g), - 'usage': '[opts] <{g.proj_name} TX file> [seed source] ...'.format(g=g), - 'sets': ( ('yes', True, 'quiet', True), ), - 'options': """ +opts_data = { + 'sets': [('yes', True, 'quiet', True)], + 'text': { + 'desc': """ + Increase the fee on a replaceable (RBF) {g.proj_name} transaction, + creating a new transaction, and optionally sign and send the + new transaction + """.format(g=g), + 'usage': '[opts] <{g.proj_name} TX file> [seed source] ...'.format(g=g), + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for @@ -66,13 +71,19 @@ opts_data = lambda: { -y, --yes Answer 'yes' to prompts, suppress non-essential output -z, --show-hash-presets Show information on available hash presets """, - 'options_fmt_args': lambda: dict( - g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),dn=g.proto.daemon_name, - fu=help_notes('rel_fee_desc'),fl=help_notes('fee_spec_letters'), - kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), - kg=g.key_generator, - cu=g.coin), - 'notes': lambda: '\n' + help_notes('fee') + help_notes('txsign') + 'notes': '\n{}{}' + }, + 'code': { + 'options': lambda s: s.format( + g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),dn=g.proto.daemon_name, + fu=help_notes('rel_fee_desc'),fl=help_notes('fee_spec_letters'), + kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), + kg=g.key_generator, + cu=g.coin), + 'notes': lambda s: s.format( + help_notes('fee'), + help_notes('txsign')) + } } cmd_args = opts.init(opts_data) diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index 2e99a3ed..cf502f76 100755 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -23,11 +23,12 @@ mmgen-txcreate: Create a cryptocoin transaction with MMGen- and/or non-MMGen from mmgen.common import * -opts_data = lambda: { - 'desc': 'Create a transaction with outputs to specified coin or {g.proj_name} addresses'.format(g=g), - 'usage': '[opts] ... [change addr] [addr file] ...', - 'sets': ( ('yes', True, 'quiet', True), ), - 'options': """ +opts_data = { + 'sets': [('yes', True, 'quiet', True)], + 'text': { + 'desc': 'Create a transaction with outputs to specified coin or {g.proj_name} addresses'.format(g=g), + 'usage': '[opts] ... [change addr] [addr file] ...', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -a, --tx-fee-adj= f Adjust transaction fee by factor 'f' (see below) @@ -54,11 +55,18 @@ opts_data = lambda: { -V, --vsize-adj= f Adjust transaction's estimated vsize by factor 'f' -y, --yes Answer 'yes' to prompts, suppress non-essential output """, - 'options_fmt_args': lambda: dict( - g=g,cu=g.coin, - fu=help_notes('rel_fee_desc'), - fl=help_notes('fee_spec_letters') ), - 'notes': lambda: '\n' + help_notes('txcreate') + help_notes('fee') + 'notes': '\n{}{}', + }, + 'code': { + 'options': lambda s: s.format( + fu=help_notes('rel_fee_desc'), + fl=help_notes('fee_spec_letters'), + cu=g.coin, + g=g), + 'notes': lambda s: s.format( + help_notes('txcreate'), + help_notes('fee')) + } } cmd_args = opts.init(opts_data) diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index ec637a27..c28459ad 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -22,11 +22,12 @@ mmgen-txdo: Create, sign and broadcast an online MMGen transaction from mmgen.common import * -opts_data = lambda: { - 'desc': 'Create, sign and send an {g.proj_name} transaction'.format(g=g), - 'usage': '[opts] ... [change addr] [addr file] ... [seed source] ...', - 'sets': ( ('yes', True, 'quiet', True), ), - 'options': """ +opts_data = { + 'sets': [('yes', True, 'quiet', True)], + 'text': { + 'desc': 'Create, sign and send an {g.proj_name} transaction'.format(g=g), + 'usage': '[opts] ... [change addr] [addr file] ... [seed source] ...', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -a, --tx-fee-adj= f Adjust transaction fee by factor 'f' (see below) @@ -75,14 +76,21 @@ opts_data = lambda: { -y, --yes Answer 'yes' to prompts, suppress non-essential output -z, --show-hash-presets Show information on available hash presets """, - 'options_fmt_args': lambda: dict( - g=g,pnm=g.proj_name,pnl=g.proj_name.lower(), - kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), - fu=help_notes('rel_fee_desc'), - fl=help_notes('fee_spec_letters'), - kg=g.key_generator, - cu=g.coin), - 'notes': lambda: '\n' + help_notes('txcreate') + help_notes('fee') + help_notes('txsign') + 'notes': '\n{}{}{}', + }, + 'code': { + 'options': lambda s: s.format( + g=g,pnm=g.proj_name,pnl=g.proj_name.lower(), + kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), + fu=help_notes('rel_fee_desc'), + fl=help_notes('fee_spec_letters'), + kg=g.key_generator, + cu=g.coin), + 'notes': lambda s: s.format( + help_notes('txcreate'), + help_notes('fee'), + help_notes('txsign')) + } } cmd_args = opts.init(opts_data) diff --git a/mmgen/main_txsend.py b/mmgen/main_txsend.py index cb7cbd00..dfee6650 100755 --- a/mmgen/main_txsend.py +++ b/mmgen/main_txsend.py @@ -22,11 +22,12 @@ mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network from mmgen.common import * -opts_data = lambda: { - 'desc': 'Send a cryptocoin transaction signed by {pnm}-txsign'.format(pnm=g.proj_name.lower()), - 'usage': '[opts] ', - 'sets': ( ('yes', True, 'quiet', True), ), - 'options': """ +opts_data = { + 'sets': [('yes', True, 'quiet', True)], + 'text': { + 'desc': 'Send a signed {pnm} cryptocoin transaction'.format(pnm=g.proj_name), + 'usage': '[opts] ', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -d, --outdir= d Specify an alternate directory 'd' for output @@ -34,6 +35,7 @@ opts_data = lambda: { -s, --status Get status of a sent transaction -y, --yes Answer 'yes' to prompts, suppress non-essential output """ + } } cmd_args = opts.init(opts_data) diff --git a/mmgen/main_txsign.py b/mmgen/main_txsign.py index 1a2973b3..93cc8b31 100755 --- a/mmgen/main_txsign.py +++ b/mmgen/main_txsign.py @@ -23,11 +23,12 @@ mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate' from mmgen.common import * # -w, --use-wallet-dat (keys from running coin daemon) removed: use walletdump rpc instead -opts_data = lambda: { - 'desc': 'Sign cryptocoin transactions generated by {pnl}-txcreate'.format(pnl=g.proj_name.lower()), - 'usage': '[opts] ... [seed source]...', - 'sets': ( ('yes', True, 'quiet', True), ), - 'options': """ +opts_data = { + 'sets': [('yes', True, 'quiet', True)], + 'text': { + 'desc': 'Sign cryptocoin transactions generated by {pnl}-txcreate'.format(pnl=g.proj_name.lower()), + 'usage': '[opts] ... [seed source]...', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for @@ -62,13 +63,17 @@ opts_data = lambda: { -V, --vsize-adj= f Adjust transaction's estimated vsize by factor 'f' -y, --yes Answer 'yes' to prompts, suppress non-essential output """, - 'options_fmt_args': lambda: dict( - g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),dn=g.proto.daemon_name, - kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), - kg=g.key_generator, - cu=g.coin - ), - 'notes': lambda: '\n' + help_notes('txsign') + 'notes': '\n{}' + }, + 'code': { + 'options': lambda s: s.format( + g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),dn=g.proto.daemon_name, + kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), + kg=g.key_generator, + cu=g.coin), + 'notes': lambda s: s.format( + help_notes('txsign')) + } } infiles = opts.init(opts_data,add_opts=['b16']) diff --git a/mmgen/main_wallet.py b/mmgen/main_wallet.py index cd7ab77f..8aa6daef 100755 --- a/mmgen/main_wallet.py +++ b/mmgen/main_wallet.py @@ -55,12 +55,11 @@ elif invoked_as == 'passchg': else: die(1,"'{}': unrecognized invocation".format(g.prog_name)) -opts_data = lambda: { -# Can't use: share/Opts doesn't know anything about fmt codes -# 'sets': [('hidden_incog_output_params',bool,'out_fmt','hi')], - 'desc': desc.format(pnm=g.proj_name), - 'usage': usage, - 'options': """ +opts_data = { + 'text': { + 'desc': desc.format(pnm=g.proj_name), + 'usage': usage, + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -d, --outdir= d Output files to directory 'd' instead of working dir @@ -90,22 +89,28 @@ opts_data = lambda: { (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}) -S, --stdout Write wallet data to stdout instead of file -v, --verbose Produce more verbose output -""".format( - g=g, - iaction=capfirst(iaction), - oaction=capfirst(oaction), - ), +""", 'notes': """ {n_pw}{n_bw} FMT CODES: + {f} -""".format( - f='\n '.join(SeedSource.format_fmt_codes().splitlines()), - n_pw=help_notes('passwd'), - n_bw=('','\n\n' + help_notes('brainwallet'))[bw_note] - ) +""" + }, + 'code': { + 'options': lambda s: s.format( + iaction=capfirst(iaction), + oaction=capfirst(oaction), + g=g, + ), + 'notes': lambda s: s.format( + f='\n '.join(SeedSource.format_fmt_codes().splitlines()), + n_pw=help_notes('passwd'), + n_bw=('','\n\n' + help_notes('brainwallet'))[bw_note] + ) + } } cmd_args = opts.init(opts_data,opt_filter=opt_filter) diff --git a/mmgen/opts.py b/mmgen/opts.py index 455e403d..cd7a25a7 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -181,10 +181,16 @@ Are you sure you want to continue? if not keypress_confirm(m,default_yes=True): sys.exit(0) -def get_common_opts_data(): - # most, but not all, of these set the corresponding global var +def common_opts_code(s): from mmgen.protocol import CoinProtocol - return """ + return s.format( + pnm=g.proj_name,pn=g.proto.name,dn=g.proto.daemon_name, + cu_dfl=g.coin, + cu_all=' '.join(CoinProtocol.coins) ) + +common_opts_data = { + # most, but not all, of these set the corresponding global var + 'text': """ --, --accept-defaults Accept defaults at all prompts --, --coin=c Choose coin unit. Default: {cu_dfl}. Options: {cu_all} --, --token=t Specify an ERC20 token by address or symbol @@ -203,29 +209,25 @@ def get_common_opts_data(): --, --version Print version information and exit --, --bob Switch to user "Bob" in MMGen regtest setup --, --alice Switch to user "Alice" in MMGen regtest setup - """.format( pnm=g.proj_name,pn=g.proto.name,dn=g.proto.daemon_name, - cu_dfl=g.coin, - cu_all=' '.join(CoinProtocol.coins)) + """, + 'code': common_opts_code +} -def init(opts_f,add_opts=[],opt_filter=None,parse_only=False): +def init(opts_data,add_opts=[],opt_filter=None,parse_only=False): - from mmgen.protocol import CoinProtocol,BitcoinProtocol,init_genonly_altcoins - g.proto = BitcoinProtocol # this must be initialized to something before opts_f is called + opts_data['text']['long_options'] = common_opts_data['text'] - opts_data = opts_f() - opts_data['long_options'] = get_common_opts_data() - - uopts,args,short_opts,long_opts,skipped_opts,do_help = \ - mmgen.share.Opts.parse_opts(sys.argv,opts_data,opt_filter=opt_filter,skip_help=True) + uopts,args,short_opts,long_opts,skipped_opts = \ + mmgen.share.Opts.parse_opts(opts_data,opt_filter=opt_filter,parse_only=parse_only) if parse_only: - return uopts,args,short_opts,long_opts,skipped_opts,do_help + return uopts,args,short_opts,long_opts,skipped_opts if g.debug_opts: opt_preproc_debug(short_opts,long_opts,skipped_opts,uopts,args) # Save this for usage() global usage_txt - usage_txt = opts_data['usage'] + usage_txt = opts_data['text']['usage'] # Transfer uopts into opt, setting program's opts + required opts to None if not set by user for o in ( tuple([s.rstrip('=') for s in long_opts] + add_opts + skipped_opts) @@ -264,6 +266,7 @@ def init(opts_f,add_opts=[],opt_filter=None,parse_only=False): if g.regtest: g.testnet = True # These are equivalent for now + from mmgen.protocol import init_genonly_altcoins,CoinProtocol altcoin_trust_level = init_genonly_altcoins(opt.coin) # g.testnet is set, so we can set g.proto @@ -272,7 +275,7 @@ def init(opts_f,add_opts=[],opt_filter=None,parse_only=False): # global sets proto if g.daemon_data_dir: g.proto.daemon_data_dir = g.daemon_data_dir -# g.proto is set, so we can set g.data_dir + # g.proto is set, so we can set g.data_dir g.data_dir = os.path.normpath(os.path.join(g.data_dir_root,g.proto.data_subdir)) # If user opt is set, convert its type based on value in mmgen.globalvars (g) @@ -295,14 +298,15 @@ def init(opts_f,add_opts=[],opt_filter=None,parse_only=False): opt_postproc_initializations() - if do_help: # print help screen only after global vars are initialized - opts_data = opts_f() - opts_data['long_options'] = get_common_opts_data() + if opts_data['do_help']: # print help screen only after global vars are initialized + if not 'code' in opts_data: + opts_data['code'] = {} + opts_data['code']['long_options'] = common_opts_data['code'] if g.debug_utf8: for k in opts_data: if type(opts_data[k]) == str: opts_data[k] += '-α' - mmgen.share.Opts.parse_opts(sys.argv,opts_data,opt_filter=opt_filter) # exits + mmgen.share.Opts.print_help(opts_data,opt_filter) # exits if g.bob or g.alice: g.testnet = True @@ -329,18 +333,15 @@ def init(opts_f,add_opts=[],opt_filter=None,parse_only=False): my_raw_input('Hit ENTER to continue: ') if g.debug and g.prog_name != 'test.py': - opt.verbose,opt.quiet = True,None + opt.verbose,opt.quiet = (True,None) if g.debug_opts: opt_postproc_debug() - # We don't need this data anymore - del mmgen.share.Opts - del opts_f - for k in ('prog_name','desc','usage','options','notes'): - if k in opts_data: del opts_data[k] - g.altcoin_data_dir = os.path.join(g.data_dir_root,'altcoins') warn_altcoins(altcoin_trust_level) + # We don't need this data anymore + del mmgen.share.Opts, opts_data + return args def opt_is_tx_fee(val,desc): diff --git a/mmgen/share/Opts.py b/mmgen/share/Opts.py index 03142074..ce307420 100755 --- a/mmgen/share/Opts.py +++ b/mmgen/share/Opts.py @@ -27,47 +27,57 @@ def usage(opts_data): print('USAGE: {} {}'.format(opts_data['prog_name'], opts_data['usage'])) sys.exit(2) -def print_help_and_exit(opts_data,longhelp=False): +def print_help(opts_data,opt_filter): + t = opts_data['text'] + c = opts_data['code'] + + # header pn = opts_data['prog_name'] - pn_len = str(len(pn)+2) - out = ' {:<{p}} {}\n'.format(pn.upper()+':',opts_data['desc'].strip(),p=pn_len) - out += ' {:<{p}} {} {}\n'.format('USAGE:',pn,opts_data['usage'].strip(),p=pn_len) - o = opts_data[('options','long_options')[longhelp]].strip() - if 'options_fmt_args' in opts_data: - o = o.format(**opts_data['options_fmt_args']()) - hdr = ('OPTIONS:',' LONG OPTIONS:')[longhelp] - ls = (' ','')[longhelp] - es = ('',' ')[longhelp] - out += '{ls}{}\n{ls}{es}{}'.format(hdr,('\n'+ls).join(o.splitlines()),ls=ls,es=es) - if 'notes' in opts_data and not longhelp: - n = opts_data['notes'] - if isinstance(n, collections.Callable): n = n() - out += '\n ' + '\n '.join(n.rstrip().splitlines()) + out = ' {:<{p}} {}\n'.format(pn.upper()+':',t['desc'].strip(),p=len(pn)+1) + out += ' {:<{p}} {} {}\n'.format('USAGE:',pn,t['usage'].strip(),p=len(pn)+1) + + # options + if opts_data['do_help'] == 'longhelp': + hdr,ls,es = (' LONG OPTIONS:','',' ') + text = t['long_options'].strip() + code = c['long_options'] if 'long_options' in c else None + else: + hdr,ls,es = ('OPTIONS:',' ','') + text = t['options'] + code = c['options'] if 'options' in c else None + + ftext = code(text) if code else text + out += '{ls}{}\n{ls}{es}{}'.format(hdr,('\n'+ls).join(ftext.splitlines()),ls=ls,es=es) + + # notes + if opts_data['do_help'] == 'help' and 'notes' in t: + ftext = c['notes'](t['notes']) if 'notes' in c else t['notes'] + out += '\n ' + '\n '.join(ftext.rstrip().splitlines()) + print(out) sys.exit(0) -def process_opts(argv,opts_data,short_opts,long_opts,skip_help=False): +def process_opts(opts_data,short_opts,long_opts): import os opts_data['prog_name'] = os.path.basename(sys.argv[0]) long_opts = [i.replace('_','-') for i in long_opts] so_str = short_opts.replace('-:','').replace('-','') - try: cl_opts,args = getopt.getopt(argv[1:], so_str, long_opts) + try: cl_opts,args = getopt.getopt(sys.argv[1:], so_str, long_opts) except getopt.GetoptError as err: print(str(err)) sys.exit(2) sopts_list = ':_'.join(['_'.join(list(i)) for i in short_opts.split(':')]).split('_') - opts,skipped_help = {},False + opts = {} + opts_data['do_help'] = False for opt,arg in cl_opts: if opt in ('-h','--help'): - if not skip_help: print_help_and_exit(opts_data) - skipped_help = True + opts_data['do_help'] = 'help' elif opt == '--longhelp': - if not skip_help: print_help_and_exit(opts_data,longhelp=True) - skipped_help = True + opts_data['do_help'] = 'longhelp' elif opt[:2] == '--' and opt[2:] in long_opts: opts[opt[2:].replace('-','_')] = True elif opt[:2] == '--' and opt[2:]+'=' in long_opts: @@ -93,17 +103,18 @@ def process_opts(argv,opts_data,short_opts,long_opts,skip_help=False): else: opts[o_out] = v_out - return opts,args,skipped_help + return opts,args -def parse_opts(argv,opts_data,opt_filter=None,skip_help=False): +def parse_opts(opts_data,opt_filter=None,parse_only=False): import re pat = r'^-([a-zA-Z0-9-]), --([a-zA-Z0-9-]{2,64})(=| )(.+)' od_all = [] - for k in ['options'] + ([],['long_options'])['long_options' in opts_data]: + for k in ('options','long_options'): + if k not in opts_data['text']: continue od,skip = [],True - for l in opts_data[k].strip().splitlines(): + for l in opts_data['text'][k].strip().splitlines(): m = re.match(pat,l) if m: skip = bool(opt_filter) and m.group(1) not in opt_filter @@ -112,15 +123,17 @@ def parse_opts(argv,opts_data,opt_filter=None,skip_help=False): else: if not skip: od[-1][3] += '\n' + l - opts_data[k] = '\n'.join( - ['{:<3} --{} {}'.format( - ('-'+d[0]+',','')[d[0]=='-'],d[1],d[3]) for d in od if d[6] == False] - ) + if not parse_only: + opts_data['text'][k] = '\n'.join( + ['{:<3} --{} {}'.format( + ('-'+d[0]+',','')[d[0]=='-'],d[1],d[3]) for d in od if d[6] == False] + ) od_all += od + short_opts = ''.join([d[0]+d[4] for d in od_all if d[6] == False]) long_opts = [d[1].replace('-','_')+d[5] for d in od_all if d[6] == False] skipped_opts = [d[1].replace('-','_') for d in od_all if d[6] == True] - opts,args,skipped_help = process_opts(argv,opts_data,short_opts,long_opts,skip_help=skip_help) + opts,args = process_opts(opts_data,short_opts,long_opts) - return opts,args,short_opts,long_opts,skipped_opts,skipped_help + return opts,args,short_opts,long_opts,skipped_opts diff --git a/scripts/compute-file-chksum.py b/scripts/compute-file-chksum.py index d04df722..f21fafb0 100755 --- a/scripts/compute-file-chksum.py +++ b/scripts/compute-file-chksum.py @@ -6,13 +6,15 @@ sys.path = [repo_root] + sys.path from mmgen.common import * -opts_data = lambda: { - 'desc': 'Compute checksum for a MMGen data file', - 'usage':'[opts] infile', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Compute checksum for a MMGen data file', + 'usage':'[opts] infile', + 'options': """ -h, --help Print this help message. -i, --include-first-line Include the first line of the file (you probably don't want this) """ + } } cmd_args = opts.init(opts_data) diff --git a/scripts/create-token.py b/scripts/create-token.py index 01db88dd..818d9757 100755 --- a/scripts/create-token.py +++ b/scripts/create-token.py @@ -27,10 +27,11 @@ name = 'MMGen Token' symbol = 'MMT' solc_version_pat = r'0.5.[123]' -opts_data = lambda: { - 'desc': 'Create an ERC20 token contract', - 'usage':'[opts] ', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Create an ERC20 token contract', + 'usage':'[opts] ', + 'options': """ -h, --help Print this help message -o, --outdir= d Specify output directory for *.bin files -d, --decimals=d Number of decimals for the token (default: {d}) @@ -39,7 +40,15 @@ opts_data = lambda: { -s, --symbol= s Token symbol (default: {s}) -S, --stdout Output data in JSON format to stdout instead of files -v, --verbose Produce more verbose output -""".format(d=decimals,n=name,s=symbol,t=supply) +""" + }, + 'code': { + 'options': lambda s: s.format( + d=decimals, + n=name, + s=symbol, + t=supply) + } } cmd_args = opts.init(opts_data) diff --git a/scripts/tx-btc2bch.py b/scripts/tx-btc2bch.py index 60cfc98c..4965f215 100755 --- a/scripts/tx-btc2bch.py +++ b/scripts/tx-btc2bch.py @@ -22,14 +22,16 @@ tx-btc2bch: Convert MMGen transaction files from BTC to BCH format from mmgen.common import * -opts_data = lambda: { - 'desc': """Convert {pnm} transaction files from BTC to BCH format""".format(pnm=g.proj_name), - 'usage':'[opts] [mmgen transaction file]', - 'options': """ +opts_data = { + 'text': { + 'desc': """Convert {pnm} transaction files from BTC to BCH format""".format(pnm=g.proj_name), + 'usage':'[opts] [mmgen transaction file]', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -v, --verbose Produce more verbose output """ + } } cmd_args = opts.init(opts_data) diff --git a/scripts/tx-v2-to-v3.py b/scripts/tx-v2-to-v3.py index 471fad4d..16a05d56 100755 --- a/scripts/tx-v2-to-v3.py +++ b/scripts/tx-v2-to-v3.py @@ -9,15 +9,17 @@ sys.path = [repo_root] + sys.path from mmgen.common import * -opts_data = lambda: { - 'desc': "Convert MMGen transaction file from v2 format to v3 format", - 'usage': "", - 'options': """ +opts_data = { + 'text': { + 'desc': "Convert MMGen transaction file from v2 format to v3 format", + 'usage': "", + 'options': """ -h, --help Print this help message -d, --outdir=d Output files to directory 'd' instead of working dir -q, --quiet Write (and overwrite) files without prompting -S, --stdout Write data to STDOUT instead of file """ + } } cmd_args = opts.init(opts_data) diff --git a/scripts/uninstall-mmgen.py b/scripts/uninstall-mmgen.py index 04f8efb5..d988f927 100755 --- a/scripts/uninstall-mmgen.py +++ b/scripts/uninstall-mmgen.py @@ -40,15 +40,16 @@ modpath_save = sys.modules['mmgen.main'].__spec__.origin from mmgen.common import * -opts_data = lambda: { - 'prog_name': sys.argv[0].split('/')[-1], - 'desc': 'Remove MMGen from your system', - 'usage': '[opts]', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Remove MMGen from your system', + 'usage': '[opts]', + 'options': """ -h, --help Print this help message -l, --list-paths List the directories and files that would be deleted -n, --no-prompt Don't prompt before deleting """ + } } cmd_args = opts.init(opts_data) diff --git a/test/gentest.py b/test/gentest.py index 740901a2..6aac8937 100755 --- a/test/gentest.py +++ b/test/gentest.py @@ -31,10 +31,11 @@ from mmgen.common import * from mmgen.obj import MMGenAddrType rounds = 100 -opts_data = lambda: { - 'desc': 'Test address generation in various ways', - 'usage':'[options] [spec] [rounds | dump file]', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Test address generation in various ways', + 'usage':'[options] [spec] [rounds | dump file]', + 'options': """ -h, --help Print this help message -a, --all Test all supported coins for external generator 'ext' -k, --use-internal-keccak-module Force use of the internal keccak module @@ -68,7 +69,15 @@ EXAMPLES: + pycoin (for supported coins) https://github.com/richardkiss/pycoin + keyconv (for all other coins) https://github.com/exploitagency/vanitygen-plus ('keyconv' generates uncompressed addresses only) -""".format(prog='gentest.py',pnm=g.proj_name,snum=rounds,dn=g.proto.daemon_name) +""" + }, + 'code': { + 'notes': lambda s: s.format( + prog='gentest.py', + pnm=g.proj_name, + snum=rounds, + dn=g.proto.daemon_name) + } } sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:] diff --git a/test/objtest.py b/test/objtest.py index ef78bb0f..39c8996d 100755 --- a/test/objtest.py +++ b/test/objtest.py @@ -30,11 +30,12 @@ from mmgen.common import * from mmgen.obj import * from mmgen.seed import * -opts_data = lambda: { - 'desc': 'Test MMGen data objects', - 'sets': ( ('super_silent', True, 'silent', True), ), - 'usage':'[options] [object]', - 'options': """ +opts_data = { + 'sets': [('super_silent', True, 'silent', True)], + 'text': { + 'desc': 'Test MMGen data objects', + 'usage':'[options] [object]', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -q, --quiet Produce quieter output @@ -42,6 +43,7 @@ opts_data = lambda: { -S, --super-silent Silence all output except for errors -v, --verbose Produce more verbose output """ + } } cmd_args = opts.init(opts_data) diff --git a/test/scrambletest.py b/test/scrambletest.py index 4f11a981..09942051 100755 --- a/test/scrambletest.py +++ b/test/scrambletest.py @@ -30,10 +30,11 @@ os.environ['MMGEN_TEST_SUITE'] = '1' from mmgen.common import * from test.common import init_coverage -opts_data = lambda: { - 'desc': 'Test seed scrambling and addrlist data generation for all supported altcoins', - 'usage':'[options] [command]', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Test seed scrambling and addrlist data generation for all supported altcoins', + 'usage':'[options] [command]', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -C, --coverage Produce code coverage info using trace module @@ -46,6 +47,7 @@ opts_data = lambda: { If no command is given, the whole suite of tests is run. """ + } } cmd_args = opts.init(opts_data) diff --git a/test/test.py b/test/test.py index 9809ab69..c7db3e8b 100755 --- a/test/test.py +++ b/test/test.py @@ -84,10 +84,11 @@ from test.test_py_d.common import * g.quiet = False # if 'quiet' was set in config file, disable here os.environ['MMGEN_QUIET'] = '0' # for this script and spawned scripts -opts_data = lambda: { - 'desc': 'Test suite for the MMGen suite', - 'usage':'[options] [command(s) or metacommand(s)]', - 'options': """ +opts_data = { + 'text': { + 'desc': 'Test suite for the MMGen suite', + 'usage':'[options] [command(s) or metacommand(s)]', + 'options': """ -h, --help Print this help message --, --longhelp Print help message for long options (common options) -B, --bech32 Generate and use Bech32 addresses @@ -122,11 +123,17 @@ opts_data = lambda: { -X, --exit-after=C Exit after command 'C' -y, --segwit Generate and use Segwit addresses -Y, --segwit-random Generate and use a random mix of Segwit and Legacy addrs -""".format(tbc='scripts/traceback_run.py',lf=log_file), - 'notes': """ +""", + 'notes': """ If no command is given, the whole test suite is run. """ + }, + 'code': { + 'options': lambda s: s.format( + tbc='scripts/traceback_run.py', + lf=log_file), + } } data_dir = os.path.join('test','data_dir' + ('','-α')[bool(os.getenv('MMGEN_DEBUG_UTF8'))]) diff --git a/test/tooltest.py b/test/tooltest.py index 1a0bd590..cb6878a2 100755 --- a/test/tooltest.py +++ b/test/tooltest.py @@ -31,10 +31,11 @@ os.environ['MMGEN_TEST_SUITE'] = '1' from mmgen.common import * from test.common import * -opts_data = lambda: { - 'desc': "Test suite for the 'mmgen-tool' utility", - 'usage':'[options] [command]', - 'options': """ +opts_data = { + 'text': { + 'desc': "Test suite for the 'mmgen-tool' utility", + 'usage':'[options] [command]', + 'options': """ -h, --help Print this help message -C, --coverage Produce code coverage info using trace module -d, --debug Produce debugging output (stderr from spawned script) @@ -50,6 +51,7 @@ opts_data = lambda: { If no command is given, the whole suite of tests is run. """ + } } sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:] diff --git a/test/tooltest2.py b/test/tooltest2.py index 9ea58f56..41cee7e0 100755 --- a/test/tooltest2.py +++ b/test/tooltest2.py @@ -40,10 +40,11 @@ from mmgen.seed import is_mnemonic def is_str(s): return type(s) == str -opts_data = lambda: { - 'desc': "Simple test suite for the 'mmgen-tool' utility", - 'usage':'[options] [command]', - 'options': """ +opts_data = { + 'text': { + 'desc': "Simple test suite for the 'mmgen-tool' utility", + 'usage':'[options] [command]', + 'options': """ -h, --help Print this help message -C, --coverage Produce code coverage info using trace module -d, --die-on-missing Abort if no test data found for given command @@ -63,6 +64,7 @@ opts_data = lambda: { If no command is given, the whole suite of tests is run. """ + } } sample_text_hexdump = ( diff --git a/test/unit_tests.py b/test/unit_tests.py index 5a44043f..432e35b3 100755 --- a/test/unit_tests.py +++ b/test/unit_tests.py @@ -30,10 +30,11 @@ os.environ['MMGEN_TEST_SUITE'] = '1' # Import these _after_ prepending repo_root to sys.path from mmgen.common import * -opts_data = lambda: { - 'desc': "Unit tests for the MMGen suite", - 'usage':'[options] [tests]', - 'options': """ +opts_data = { + 'text': { + 'desc': "Unit tests for the MMGen suite", + 'usage':'[options] [tests]', + 'options': """ -h, --help Print this help message -l, --list List available tests -n, --names Print command names instead of descriptions @@ -43,6 +44,7 @@ opts_data = lambda: { 'notes': """ If no test is specified, all available tests are run """ + } } sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]