Skip to content

Instantly share code, notes, and snippets.

@tuxuser
Created March 6, 2020 08:43
Show Gist options
  • Save tuxuser/60461a3ab1b8ee2b5551121312f3e99f to your computer and use it in GitHub Desktop.
Save tuxuser/60461a3ab1b8ee2b5551121312f3e99f to your computer and use it in GitHub Desktop.
ArgumentParser with subcommands, common arguments and logging setup
import logging
import argparse
from logging.handlers import RotatingFileHandler
"""
argparse_subcommands.py
# Argparse with subcommands
$ python3 argparse_subcommands.py -h
usage: argparse_subcommands.py [-h] {cmd_no_args,cmd} ...
Miner managment tool
positional arguments:
{cmd_no_args,cmd} Available commands
cmd_no_args Command with no args
cmd Command taking args
optional arguments:
-h, --help show this help message and exit
$ python3 argparse_subcommands.py cmd -h
usage: argparse_subcommands.py cmd [-h] [--logfile LOGFILE] [-v] [--optional OPTIONAL] some_arg
positional arguments:
some_arg Some required argument
optional arguments:
-h, --help show this help message and exit
--logfile LOGFILE Path for logfile
-v, --verbose Set logging level ( -v: INFO, -vv: DEBUG)
--optional OPTIONAL, -o OPTIONAL
Optional arg
$ python3 argparse_subcommands.py cmd bla -vvv
[2020-03-06 09:34:22,461] INFO: Set Loglevel: DEBUG
[2020-03-06 09:34:22,461] DEBUG: Parsed args: Namespace(command='cmd', logfile=None, optional='whatever', some_arg='bla', verbose=3)
[2020-03-06 09:34:22,461] DEBUG: Command: cmd
[2020-03-06 09:34:22,461] DEBUG: Optional argument: whatever
[2020-03-06 09:34:22,462] DEBUG: Some arg: bla
$ python3 argparse_subcommands.py cmd_no_args -vvv
[2020-03-06 09:35:35,091] INFO: Set Loglevel: DEBUG
[2020-03-06 09:35:35,092] DEBUG: Parsed args: Namespace(command='cmd_no_args', logfile=None, verbose=3)
[2020-03-06 09:35:35,092] DEBUG: Command: cmd_no_args
# Define console_scripts in setup.py
entry_points={
'console_scripts': [
'cli-main=root_namespace.scripts.argparse_subcommands:main',
'cli-cmd=root_namespace.scripts.argparse_subcommands:cmd',
'cli-cmd-no-args=root_namespace.scripts.argparse_subcommands:cmd_no_args',
]
}
"""
LOG_FMT = '[%(asctime)s] %(levelname)s: %(message)s'
LOGGER = logging.getLogger(__name__)
def handle_logging_setup(args):
"""
Determine log level, logfile and special DEBUG_INCL_PACKETS
via cmdline arguments.
Args:
args: ArgumentParser `Namespace`
Returns:
None
"""
levels = [logging.WARNING, logging.INFO, logging.DEBUG]
# Output level capped to number of levels
log_level = levels[min(len(levels) - 1, args.verbose)]
logging.basicConfig(level=log_level, format=LOG_FMT)
logging.root.info('Set Loglevel: {0}'
.format(logging.getLevelName(log_level)))
if args.logfile:
logging.root.info('Set Logfile path: {0}'.format(args.logfile))
file_handler = RotatingFileHandler(args.logfile, backupCount=2)
file_handler.setLevel(log_level)
file_handler.setFormatter(logging.Formatter(LOG_FMT))
logging.root.addHandler(file_handler)
class Commands(object):
"""
Available commands for CLI
"""
CmdNoArgs = 'cmd_no_args'
Cmd = 'cmd'
def parse_arguments(args=None):
"""
Parse arguments with argparse.ArgumentParser
Returns:
Namespace: Parsed arguments
Raises:
Exception: On generic failure
"""
parser = argparse.ArgumentParser(description='Miner managment tool')
"""Common arguments for logging"""
logging_args = argparse.ArgumentParser(add_help=False)
logging_args.add_argument(
'--logfile',
help="Path for logfile")
logging_args.add_argument(
'-v', '--verbose', action='count', default=0,
help='Set logging level\n'
'( -v: INFO,\n'
' -vv: DEBUG)')
"""
Define commands
"""
# parser.add_subparsers(help='Available commands', dest='command', required=True)
subparsers = parser.add_subparsers(help='Available commands')
# Setting dest and required separately for py3.6 compat
subparsers.dest = 'command'
subparsers.required = True
"""Command - No Args"""
subparsers.add_parser(Commands.CmdNoArgs, help='Command with no args', parents=[logging_args])
"""Command"""
cmd = subparsers.add_parser(Commands.Cmd, help='Command taking args', parents=[logging_args])
cmd.add_argument('some_arg', type=str, help='Some required argument')
cmd.add_argument('--optional', '-o', type=str, help='Optional arg', default='whatever')
return parser.parse_args(args)
def main(command=None):
if command:
# Take passed command and append actual cmdline
cmdline_arguments = sys.argv[1:]
cmdline_arguments.insert(0, command)
else:
cmdline_arguments = None
args = parse_arguments(cmdline_arguments)
handle_logging_setup(args)
LOGGER.debug('Parsed args: {0}'.format(args))
LOGGER.debug('Command: {0}'.format(args.command))
if args.command == Commands.Cmd:
LOGGER.debug('Optional argument: {0}'.format(args.optional))
LOGGER.debug('Some arg: {0}'.format(args.some_arg))
def cmd_no_args():
main(Commands.CmdNoArgs)
def cmd():
main(Commands.Cmd)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment