One nuisance with building command line options in argparse
, click
, or any other system is duplicates in the parameter list. When using argparse
, I would call various functions to add a set of arguments to my ArgumentParser
(e.g., for shared output parameters/configuration):
# shared_args.py
def add_output_arguments(parser: ArgumentParser):
"""A function that can be called from multiple
parser.add_argument('--outdir')
parser.add_argument('--filetype', default='csv')
parser.add_argument('--encoding', default='utf8')
# etc.
return parser
# do_something.py
parser = ArgumentParser()
parser.add_argument('--file')
parser = add_output_arguments(parser)
main(**vars(parser.parse_args()))
Treat the above (and below) as pseudocode, but hopefully they gets the idea across.
With click
, however, figuring out how to do this is less clear since everything is built using decorators. A single variable can be used to store an argument or command:
# shared_args.py
outdir = click.option('--outdir')
filetype = click.option('--filetype', default='csv')
encoding = click.option('--encoding', default='utf8')
# do_something.py
@command()
@click.argument('file')
@outdir
@filetype
@encoding
def main(file, outdir, filetype, encoding):
...
Yes, but this still isn’t as good as argparse
‘s solution of calling a function. I want all of these options to always be added, and not forget to add one. They should be treated as a group — all or nothing.
One solution is to place these into a function, with each of these options nested inside another one (i.e., manually transcribing the decorator).
# shared_args.py
outdir = click.option('--outdir')
filetype = click.option('--filetype', default='csv')
encoding = click.option('--encoding', default='utf8')
def output_arguments(func):
return outdir(filetype(encoding(func)))
# do_something.py
@command()
@click.argument('file')
@output_arguments
def main(file, **output_arguments):
result = do_something(file)
build_output(result, **output_arguments)
Now, multiple functions can all be decorated with the same options. More importantly, if I wanted to add an option, I would only need to update the output_arguments
function and build_output
and things would just work.