Created
March 6, 2020 08:43
-
-
Save tuxuser/60461a3ab1b8ee2b5551121312f3e99f to your computer and use it in GitHub Desktop.
ArgumentParser with subcommands, common arguments and logging setup
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 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