Here's a short summary of what I am trying to achieve:
So in an ideal world I'd like to run my script in the following ways, they are all valid use cases:
python myscript.py file1 file2 file3 "key" 10
python myscript.py file1 file2 "key" 10 12
python myscript.py file1 "key" 10 12
python myscript.py file1 "key" 10
Given the use case I tried:
parser = argparse.ArgumentParser()
parser.add_argument("files", nargs='+', help="input files")
parser.add_argument("gene", help="gene of interest")
parser.add_argument("pos", nargs='+', type=int, help="position(s) to analyze")
""" Validate pos """
if len(args.pos) > 2:
    sys.exit("Positions argument needs to be a single integer or two integers denoting a range!")
...
# in some other function
if len(args.pos) == 1:
    key = row[SIND][args.pos[0]]
elif len(args.pos) == 2:
    key = row[SIND][args.pos[0] : args.pos[1]]
else:
    print(args.pos, len(args.pos))
    sys.exit("args.pos assertion failed!")
It works when there is only one integer but not when I send two integers to analyze a range instead. In the latter case the "key" is interpreted as a file as well thus I get FileNotFoundError: [Errno 2] No such file or directory: 'IGHV4-39'.
Question1: Is it possible to flag or indicate positional arguments, so that I can tell argparse when the files parameter ends and gene starts? I don't want to make them optional arguments, since the logic of the script is incomplete if either of the three parameters is omitted. 
Question2: Is it otherwise helpful to divide the pos parameter into two; pos which takes a single integer, and range which takes two integers, then make them somehow exclusive?
Any ideas?
"Is it possible to flag ... positional arguments?" Well, if you did that, then they wouldn't be positional arguments anymore! Since after all positional arguments are defined by being in position, not by being flagged.
I think all you really need is the required option, and not to try and be too clever using positional arguments. Is your aim to create an amazingly clever interface that magically works out what it is you want regardless of input, or an interface that works predictably and produces good error messages? 
I can be sarcastic on this point because I have spent far too much time in the past doing the first instead of just doing the second.
So why not this:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--files', required=True, nargs='+')
parser.add_argument('--genes', required=True, nargs='+')
parser.add_argument(
    '--pos', required=True, nargs='+', type=int, help="position(s) to analyze")
opts = parser.parse_args()
print(dir(parser))
print('files: %s, genes: %s, poses: %s' % (
    opts.files, opts.genes, opts.pos))
So:
$ python test.py
usage: test.py [-h] --files FILES [FILES ...] --genes GENES [GENES ...] --pos
           POS [POS ...]
test.py: error: the following arguments are required: --files, --genes, --pos
$ python test.py --files file1 file2 --genes xzy --pos 2 1
files: ['file1', 'file2'], genes: ['xzy'], poses: [2, 1]
Aren't you essentially asking "what is the best interface here"? Surely that's something you need to decide. My advice: keep it simple. Why not do:
parser.add_argument('--start', '-s', required=True, nargs=1, type=int)
parser.add_argument('--end', '-e', nargs=1, type=int)
opts = parser.parse_args()
if opts.end is None:
    opts.end = opts.start + 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With