Reputation: 162
I want to have a set of arguments be passed into a script with an equal amount of inputs
and outputs
arguments. I know that I can parse along the lines of
inputs, outputs = sys.argv[:halfway], sys.argv[halfway:]
taking into account sys.argv[0]
being the name, but I want the helpful features of argparse
.
I also know that I can change the code to parser.add_argument('-i', 'inputs', nargs='+')
so that I can specify my arguments as python testarg.py -i 1 2 -o 3 4
, but I do not want to use that syntax as there is already a precedent of one-input, one-output python testarg.py input output
which I would like to keep by making the syntax python testarg.py inputs[...] outputs[...]
This is the closest I get
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('inputs', nargs='+')
parser.add_argument('outputs', nargs='+')
print(parser.parse_args())
$ python testarg.py 1
usage: testarg.py [-h] input [input ...] output [output ...]
testarg.py: error: the following arguments are required: output
$ python testarg.py 1 2
Namespace(inputs=['1'], outputs=['2'])
$ python testarg.py 1 2 3 4
Namespace(inputs=['1', '2', '3'], outputs=['4'])
I want
Namespace(inputs=['1', '2'], outputs=['3', '4'])
Upvotes: 0
Views: 663
Reputation: 231355
The nargs
are modelled on (and even use) the regex
wildcard quantifiers
In this case:
Namespace(inputs=['1', '2', '3'], outputs=['4'])
one value has been allocated to outputs
(because it is "one-or-more"), and the rest, the "more" goes to inputs
.
Now if you could accept
python prog.py --inputs 1 2 --outputs 3 4
the '+' would work as expected.
But with variable length positionals (or optional followed by positional), there's no way to tell it where the first list ends and second starts.
Of course if you like the argparse
help, you could adjust the lists balance after parsing - e.g move the '3' to the other list. Nothing wrong with tweaking the parsed values. You won't get extra "good boy" points for doing everything in the parser itself.
Upvotes: 1
Reputation: 797
The click library can do this, it supports callback functions that can modify argument values.
import click
def split_args(context, param, value):
num_args = len(value)
if num_args % 2:
raise click.BadParameter(
f"Must provide an even number of arguments, got {num_args} arguments"
)
midpoint = num_args // 2
return value[:midpoint], value[midpoint:]
@click.command()
@click.argument("args", callback=split_args, nargs=-1)
def io(args):
inputs, outputs = args
print("inputs: ", inputs)
print("outputs: ", outputs)
if __name__ == "__main__":
io()
$ python3 testarg.py 1 2 3 4
inputs: ('1', '2')
outputs: ('3', '4')
Upvotes: 2