I’m trying to write a script that handles a series of arguments. The first part of the argument list must be a series of strings of the form NAME=VALUE
, followed by a further series of strings which don’t have that form. For comparison, think of the argument structure for the Unix env
command: env [NAME=VALUE]... [COMMAND [ARG]...]
My actual script has additional options, so I want to use argparse, and that’s fine. Here’s a massively simplified example:
from argparse import ArgumentParser
p = ArgumentParser()
p.add_argument("arg", nargs="*")
args = p.parse_args()
for i in range(len(args.arg)):
if "=" not in args.arg[i]:
break
assignments, rest = args.arg[:i], args.arg[i:]
print(assignments)
print(rest)
The problem is that the command line help generated by argparse doesn’t explain any of this structure:
❯ py example.py --help
usage: example.py [-h] [arg ...]
positional arguments:
arg
optional arguments:
-h, --help show this help message and exit
What I’d like is more like:
❯ py example.py --help
usage: example.py [-h] [NAME=VAL ...] [ARG...]
positional arguments:
NAME=VAL An assignment of value VAL to NAME
ARG Other stuff
optional arguments:
-h, --help show this help message and exit
I can do that by having two positional arguments,
p.add_argument("arg1", nargs="*", metavar="NAME=VAL", help="An assignment of value VAL to NAME")
p.add_argument("arg2", nargs="*", metavar="ARG", help="Other stuff")
but all of the arguments are picked up by arg1 and arg2 is empty, so the code looks rather clumsy, as the declared arguments don’t match the structure.
Is there a better way of doing this? Either a way to customise the help text (preferably without having to manually handle the help for all of the options I’ve omitted from this example), or even better a way of telling argparse how to detect when to end arg1 and move onto arg2?
(Worst case, I guess I could just name the two positional arguments “args” and “dummy”, and move on…)