Last active
May 30, 2025 05:46
-
-
Save amarao/36327a6f77b86b90c2bca72ba03c9d3a to your computer and use it in GitHub Desktop.
Example of argparse with subparsers for python
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 | |
import argparse | |
def main(command_line=None): | |
parser = argparse.ArgumentParser('Blame Praise app') | |
parser.add_argument( | |
'--debug', | |
action='store_true', | |
help='Print debug info' | |
) | |
subparsers = parser.add_subparsers(dest='command') | |
blame = subparsers.add_parser('blame', help='blame people') | |
blame.add_argument( | |
'--dry-run', | |
help='do not blame, just pretend', | |
action='store_true' | |
) | |
blame.add_argument('name', nargs='+', help='name(s) to blame') | |
praise = subparsers.add_parser('praise', help='praise someone') | |
praise.add_argument('name', help='name of person to praise') | |
praise.add_argument( | |
'reason', | |
help='what to praise for (optional)', | |
default="no reason", | |
nargs='?' | |
) | |
args = parser.parse_args(command_line) | |
if args.debug: | |
print("debug: " + str(args)) | |
if args.command == 'blame': | |
if args.dry_run: | |
print("Not for real") | |
print("blaming " + ", ".join(args.name)) | |
elif args.command == 'praise': | |
print('praising ' + args.name + ' for ' + args.reason) | |
if __name__ == '__main__': | |
main() |
It can be useful to define a utility function to avoid duplication when defining subparsers.
#!/usr/bin/env python
import logging
import sys
from pathlib import Path
import argparse
from argparse import ArgumentParser, Namespace
from typing import Callable
logger = logging.getLogger(__name__)
def run_alpha(input_path: Path) -> int:
logger.info(f"Running alpha for {input_path}")
return 0
def run_beta(src: Path, dest: Path) -> int:
logger.info(f"Running alpha with {src=} {dest=}")
return 0
def _to_parser_alpha(p: ArgumentParser) -> ArgumentParser:
p.add_argument("-i", "--input", type=Path, required=True)
return p
def _to_parser_beta(p: ArgumentParser) -> ArgumentParser:
p.add_argument("-s", "--src", type=Path, required=True)
p.add_argument("-d", "--dest", type=Path, required=True)
return p
def get_parser() -> ArgumentParser:
p = ArgumentParser(prog="example.py")
sp = p.add_subparsers()
def _add(
name: str,
add_opts: Callable[[ArgumentParser], ArgumentParser],
func: Callable[[Namespace], int],
help_: str,
) -> argparse.ArgumentParser:
px = sp.add_parser(name, help=help_)
add_opts(px)
px.set_defaults(func=func)
return px
_add("alpha", _to_parser_alpha, lambda ns: run_alpha(ns.input), "Running alpha")
_add("beta", _to_parser_beta, lambda ns: run_beta(ns.src, ns.dest), "Run beta")
p.add_argument("--version", action="version", version="0.1.0")
return p
def main(argv: list[str]) -> int:
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
p = get_parser()
pargs = p.parse_args(argv)
return pargs.func(pargs)
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
You can also get shell autocomplete functionality by adding shtab
.
https://github.com/iterative/shtab
Support can added by:
import stab
# Add in get_parser()
shtab.add_argument_to(p)
Yielding --help
:
python shtab_test.py --help
usage: example.py [-h] [--version] [--print-completion {bash,zsh,tcsh}] {alpha,beta} ...
positional arguments:
{alpha,beta}
alpha Running alpha
beta Run beta
options:
-h, --help show this help message and exit
--version show program's version number and exit
--print-completion {bash,zsh,tcsh}
print shell completion script
Then emitting the bash/zsh completion to stdout by:
python example.py --print-completion zsh
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Good evening @fireattack
Maybe something more elegant like: