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.