Skip to content

Instantly share code, notes, and snippets.

@apua
Last active March 11, 2023 11:07
Show Gist options
  • Save apua/88cbaf336ebe200f7223fcf0ef2f9e52 to your computer and use it in GitHub Desktop.
Save apua/88cbaf336ebe200f7223fcf0ef2f9e52 to your computer and use it in GitHub Desktop.
A snippet that make Python package integrate argument parsers of its sub packages
# file path: `mod/__main__.py`
import argparse, importlib, inspect, sys
prog = sys.argv[0]
if prog.endswith('__main__.py'):
prog = f'python -m {__package__}'
else:
prog = prog.rsplit('/',1)[-1]
parser = argparse.ArgumentParser(prog=prog, description='A toolbox collecting features')
subparsers = parser.add_subparsers(metavar='{subcommand}', dest='subcommand', required=True)
submains = {}
for alias, modpath in [('tool_1', '.feature')]:
mod = importlib.import_module(modpath, __package__)
desc = inspect.signature(mod.parse_args).parameters['parser'].default.description
subparser = subparsers.add_parser(alias, description=desc, help=desc)
subparser.parse_args = lambda:None
mod.parse_args(subparser)
submains[alias] = mod.main
args = parser.parse_args()
main = submains[args.subcommand]
main(args)
$ toolbox
usage: toolbox [-h] {subcommand} ...
toolbox: error: the following arguments are required: {subcommand}
$ python -m mod
usage: python -m mod [-h] {subcommand} ...
python -m mod: error: the following arguments are required: {subcommand}
$ toolbox -h
usage: toolbox [-h] {subcommand} ...

A toolbox collecting features

positional arguments:
  {subcommand}
    tool_1      A feature helping to do something

optional arguments:
  -h, --help    show this help message and exit
$ python -m mod -h
usage: python -m mod [-h] {subcommand} ...

A toolbox collecting features

positional arguments:
  {subcommand}
    tool_1      A feature helping to do something

optional arguments:
  -h, --help    show this help message and exit
$ toolbox tool_1 -h
usage: toolbox tool_1 [-h] arg

A feature helping to do something

positional arguments:
  arg         arg for the feature

optional arguments:
  -h, --help  show this help message and exit
$ python -m mod tool_1 -h
usage: python -m mod tool_1 [-h] arg

A feature helping to do something

positional arguments:
  arg         arg for the feature

optional arguments:
  -h, --help  show this help message and exit
$ python -m mod.feature -h
usage: python -m mod.feature [-h] arg

A feature helping to do something

positional arguments:
  arg         arg for the feature

optional arguments:
  -h, --help  show this help message and exit
$ toolbox tool_1 arg
start running with args: Namespace(subcommand='tool_1', arg='arg')
$ python -m mod tool_1 arg
start running with args: Namespace(subcommand='tool_1', arg='arg')
$ python -m mod.feature arg
start running with args: Namespace(arg='arg')
# file path: `mod/feature.py`
def parse_args(parser=__import__('argparse').ArgumentParser(
prog=f'python -m {__package__}.{__file__.rsplit("/",1)[-1].split(".")[0]}',
description='A feature helping to do something',
)):
parser.add_argument('arg', help='arg for the feature')
return parser.parse_args()
def main(args):
print(f'start running with args: {args}')
if __name__ == '__main__':
main(parse_args())
#!/usr/bin/env python
# Writing it in shell script like below is improper because of
# unable to get called name::
#
# #!/bin/sh
# python -m mod "$@"
#
# Instead, make command itself to be a Python script.
# Run main of `mod`
from mod import __main__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment