I'd like to support a command line interface where users can declare an arbitrary number of samples, with one or more input files corresponding to each sample. Something like this:
$ myprogram.py \
--foo bar \
--sample1 input1.tsv \
--sample2 input2a.tsv input2b.tsv input2c.tsv \
--sample3 input3-filtered.tsv \
--out output.tsv
The idea is that the option keys will match the pattern --sample(\d+)
, and each key will consume all subsequent arguments as option values until the next -
or --
prefixed flag is encountered. For explicitly declared arguments, this is a common use case that the argparse
module supports with the nargs='+'
option. But since I need to support an arbitrary number of arguments I can't declare them explicitly.
The parse_known_args
command will give me access to all user-supplied arguments, but those not explicitly declared will not be grouped into an indexed data structure. For these I would need to carefully examine the argument list, look ahead to see how many of the subsequent values correspond to the current flag, etc.
Is there any way I can parse these options without having to essentially re-implement large parts of an argument parser (almost) from scratch?
It would be simpler to make that number or key at separate argument value, and collect the related arguments in an nested list.
produces:
There have been questions about collecting general
key:value
pairs. There's nothing inargparse
to directly support that. Various things have been suggested, but all boil down to parsing the pairs yourself.Is it possible to use argparse to capture an arbitrary set of optional arguments?
You have added the complication that the number of arguments per key is variable. That rules out handling '--sample1=input1' as simple strings.
argparse
has extended a well knownPOSIX
commandline standard. But if you want to move beyond that, then be prepared to process the arguments either before (sys.argv) or afterargparse
(theparse_known_args
extras
).As I mentioned in my comment:
Gives:
With the real
sys.argv
you'll probably have to replace theargv.count
with the slightly longer' '.join(sys.argv).count('--sample')
The major downside to this approach is the auto help generation will not cover these fields.
It may well be possible to do the sort of thing that you are looking for with click rather than
argparse
.To quote:
One of the important features of click is the ability to construct sub-commands, (a bit like using git or image magic covert), which should allow you to structure your command line as:
Or even:
Which might be cleaner, in this case your code would have parameters called
--foo
and--out
and functions calledprocess
andcombine
process would be called with the input file(s) specified and combine with no parameters.If you can live with a slightly different syntax, namely:
where the parameter name doesn't contain a number, but still it performs grouping, try this:
It produces a list of lists, ie.
--sample x y --sample 1 2
will produceNamespace(sample=[['x', 'y'], ['1', '2']])