0

How do I setup a argparse.ArgumentParser() to get an optional argument (-p bar) that needs an [optional] positional argument (foo) ?

The program should accept:

  • prog.py
  • prog.py foo
  • prog.py foo -p bar

But it should not accept:

  • prog.py -p bar

This is my code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse

def setupArgParser():
  desc="tun0graf"
  parser=argparse.ArgumentParser(description=desc)
  parser.add_argument('logfilename',  nargs='?', help='name of a logfile')
  parser.add_argument('-s', dest='snapname', nargs=1,
               help='create snapshot of logfilename and save it as snapname.png')
  return(parser.parse_args())


arg=setupArgParser()

print "logfilename", arg.logfilename
print "snapname", arg.snapname
dede
  • 706
  • 9
  • 19
  • Where is your attempt, and what exactly is wrong with it? This isn't a code-writing service. – jonrsharpe Jun 14 '14 at 10:36
  • 1
    Sounds like you want to support a [sub-parser](https://docs.python.org/2/library/argparse.html#sub-commands). – Martijn Pieters Jun 14 '14 at 10:44
  • sub-parser wont work, because all my arguments are optional. And these sub-parser sub-commands are fixed text. For me it seems that argparse does not support [arg1 [-p arg2]] and the same for [arg1] | [arg1 -p arg2]. – dede Jun 14 '14 at 13:02
  • Due to a bug, in the latest release, subparsers are optional. But for 2.7 they are required. Until my UsageGroup goes in, you need to write your test to be run after parse_args. – hpaulj Jun 14 '14 at 13:17

1 Answers1

1

This modification to your function should do the trick:

def setupArgParser():
    desc="tun0graf"
    usage = '%(prog)s [-h] [logfilename [-s SNAPNAME]]'
    parser=argparse.ArgumentParser(description=desc, usage=usage)
    parser.add_argument('logfilename',  nargs='?', help='name of a logfile')
    parser.add_argument('-s', dest='snapname', nargs=1,
               help='create snapshot of logfilename and save it as snapname.png')
    args = parser.parse_args()
    # test
    if args.logfilename is None and args.snapname is not None:
        parser.error('SNAPNAME can only be used with logfilename')
    return args

I am testing for the interaction of the 2 arguments after parsing. And I am using a custom usage. With this, giving a -s name without a filename produces:

usage: stack24219021.py [-h] [logfilename [-s SNAPNAME]]
stack24219021.py: error: SNAPNAME can only be used with logfilename

Without the custom usage, we get:

usage: stack24219021.py [-h] [-s SNAPNAME] [logfilename]

That's because the parser knows nothing about the interaction of the 2 arguments, and it normally puts positional arguments after the optionals (the ones with -).


This does look like a case for subparsers, except that you want logfilename to be optional. As discussed in the recent https://stackoverflow.com/a/24211480/901925 in Py2.7 this is not possible. In the latest Py3 release subparsers are optional, but that's a bug that will be changed (though it may still be possible to flag them as optional).

There is talk about extending the Mutually_exclusive_group mechanism to allow more general tests like yours. Your example would be a good test case. But the test I wrote above is simpler than anything that can be done with the proposed groups.

Community
  • 1
  • 1
hpaulj
  • 221,503
  • 14
  • 230
  • 353