Last active
May 2, 2019 17:36
-
-
Save wware/2616e291389e1d7aedd60db3392d14c0 to your computer and use it in GitHub Desktop.
A little command infrastructure thing to simplify using argparse.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import logging | |
import os | |
import pdb | |
import re | |
import subprocess | |
_cmds = {} | |
_args = [] | |
dir_path = os.path.dirname(os.path.realpath(__file__)) | |
logging.basicConfig( | |
format='%(asctime)-15s %(levelname)s ' + | |
'%(filename)s:%(lineno)d %(message)s', | |
level=logging.INFO | |
) | |
def command(func): | |
_cmds[func.__name__] = func | |
return func | |
def add_argument(*args, **kwargs): | |
_args.append((args, kwargs)) | |
class MyHelpFormatter(argparse.HelpFormatter): | |
"""Blend RawDescriptionHelpFormatter with ArgumentDefaultsHelpFormatter | |
""" | |
def _fill_text(self, text, width, indent): | |
return ''.join([indent + line for line in text.splitlines(True)]) | |
def _get_help_string(self, action): | |
help = action.help | |
if '%(default)' not in action.help: | |
if action.default is not argparse.SUPPRESS: | |
defaulting_nargs = [argparse.OPTIONAL, argparse.ZERO_OR_MORE] | |
if action.option_strings or action.nargs in defaulting_nargs: | |
help += ' (default: %(default)s)' | |
return help | |
def parse_options(script, docstring='', argv=None, | |
prep_opts=None, got_commands=True): | |
class MockOptions(object): | |
def __getattr__(self, key): | |
return None | |
if prep_opts is not None: | |
opts = MockOptions() | |
prep_opts(opts) | |
return opts | |
def mk_cmd_info(cmd): | |
r = " " + cmd.__name__ + " : " | |
if cmd.__doc__ is not None: | |
doc = re.sub(r"@.*", "", cmd.__doc__) | |
r += doc.strip() | |
else: | |
r += "no documentation available" | |
return r | |
cmds = _cmds.values() | |
cmds.sort(key=lambda _f: _f.__name__) | |
cmd_info = "\nCommands:\n" + "\n".join([mk_cmd_info(f) for f in cmds]) | |
class HelpAction(argparse.Action): | |
def __call__(self, parser, namespace, values, option_string=None): | |
file = None | |
try: | |
p = subprocess.Popen("which more", shell=True, | |
stdout=subprocess.PIPE) | |
_more, _ = p.communicate() | |
assert len(_more) > 0 | |
class MoreFile(object): | |
def write(self, x): | |
p = subprocess.Popen(_more, shell=True, | |
stdin=subprocess.PIPE) | |
p.communicate(x) | |
file = MoreFile() | |
except (OSError, ValueError): | |
pass | |
parser.print_help(file=file) | |
parser.exit() | |
prog = os.path.basename(script) | |
parser = argparse.ArgumentParser( | |
prog=prog, | |
formatter_class=MyHelpFormatter, | |
description=docstring.format(prog), epilog=cmd_info, | |
add_help=False | |
) | |
parser.add_argument( | |
'-h', '--help', nargs=0, action=HelpAction, | |
help="Display this help" | |
) | |
for args, kwargs in _args: | |
parser.add_argument(*args, **kwargs) | |
if got_commands: | |
parser.add_argument( | |
'commands', | |
help='commands to run', nargs='*', default=[], | |
choices=_cmds.keys() | |
) | |
opts = parser.parse_args(argv) | |
logging.getLogger().setLevel(logging.DEBUG if opts.debug else logging.INFO) | |
if opts.pdb: | |
pdb.set_trace() | |
return opts | |
def step_thru_commands(options): | |
for cmd in options.commands: | |
_cmds[cmd](options) | |
add_argument('-D', '--debug', action='store_true', | |
help='turn on debug-level logging') | |
add_argument('-p', '--pdb', action='store_true', | |
help='run this script in the PDB debugger') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
The file-level docstring becomes part of the "--help" output. | |
""" | |
import logging | |
from commands import command, step_thru_commands, parse_options, add_argument | |
add_argument('-v', '--verbose', | |
action='store_true', | |
help='crank up the verbosity') | |
@command | |
def foo(options): | |
""" | |
print the string "foo" | |
""" | |
logging.debug( | |
"gonna print " + | |
("every single charcter of " if options.verbose else "") + | |
"'foo'" | |
) | |
print("foo") | |
@command | |
def bar(options): | |
""" | |
print the string "bar" | |
""" | |
logging.debug( | |
"gonna print " + | |
("every single charcter of " if options.verbose else "") + | |
"'bar'" | |
) | |
print("bar") | |
def main(): | |
options = parse_options(__file__, __doc__) | |
step_thru_commands(options) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment