0

I have some trouble while working with the argparse module for Python v2.7. Basically, what I have is a script that works with 5 mandatory arguments :

  1. url
  2. method
  3. login
  4. password
  5. output

An example of the syntax would look like this :

script.py -w/--url [URL] -m/--method [METHOD] -l/--login [LOGIN] -p/--password [PASSWORD] -o/--output [OUTPUT]

What I'd like to do is this :

  • add an optional argument -t/--test
  • its behavior would be that, based on the url used with the -w/--url argument, it would bypass completely the -m/--method, -l/--login and -p/--password arguments but, for it to work, I need to tell argparse to stop processing arguments if -t/--test is provided (but only with -w/--url).

Is this behavior even possible? I tried to play with argparse sub-commands but it seems to be (at least to my small knowledge) a bit overkill.

NB: Here is my original code :

# Description : parses script arguments
# Argument(s) : all
# Return value : all arguments values
def testArgs():
    parser = argparse.ArgumentParser(description='Foo', formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('-w','--url', help='URL', required=True)
    parser.add_argument('-t','--test', help='Test command', action='store_true')
    parser.add_argument('-m','--method', help='METHOD', required=True)
    parser.add_argument('-u','--login_name', help='LOGIN', required=True)
    parser.add_argument('-p','--login_password', help='PASSWORD', required=True)
    parser.add_argument('-o','--output_format', help='OUTPUT', required=True, choices=['json', 'yaml', 'python'], default='json')
    args = parser.parse_args()
    return args

EDIT : After a lot of testing, I have managed the following :

def testArgs():
    parser = argparse.ArgumentParser(description='DESCRIPTION')
    subparsers = parser.add_subparsers()
    p_list = subparsers.add_parser('test', help='List all available methods')
    p_list.add_argument('-w', help='URL', required=True)
    p_list.add_argument('-t', help='Test', action='store_true', required=True)
    p_cmd = subparsers.add_parser('cmd', help='Executes command')
    p_cmd.add_argument('-w', help='URL', required=True)
    p_cmd.add_argument('-m', help='Method', required=True)
    p_cmd.add_argument('-l', help='Login', required=True)
    p_cmd.add_argument('-p', help='Password', required=True)
    p_cmd.add_argument('-o', help='Output', required=True)
    args = parser.parse_args()
    return args

Which exhibits the following behavior :

$ python testArgparse.py -h
usage: testArgeparse.py [-h] {test,cmd} ...

DESCRIPTION

positional arguments:
  {test,cmd}
    test      Lists all available methods
    cmd       Executes command

optional arguments:
  -h, --help  show this help message and exit

But to access help on the others arguments, I need to do the following :

$ python testArgparse.py test -h
usage: testArgparse.py test [-h] -w W -t

optional arguments:
  -h, --help  show this help message and exit
  -w W        URL
  -t          Test


$ python testArgparse.py cmd -h
usage: testArgparse.py cmd [-h] -w W -m M -l L -p P -o O

optional arguments:
  -h, --help  show this help message and exit
  -w W        URL
  -m M        Method
  -l L        Login
  -p P        Password
  -o O        Output

I'd like to be able to, at least, display help about all arguments without having to use --help for both test and cmd arguments.

Ideally, what I'd like is this behavior :

$ python testArgparse.py [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
5
  • 2
    Just ignore the other arguments when you see -t/--test Commented Jan 12, 2014 at 3:43
  • Added original code to first comment. Commented Jan 12, 2014 at 5:14
  • You should check out mutually exclusive groups. You can make it so that one argument or the other is required, but not both. You might have to do some trickery to have 1 or 3 groups, though. Commented Jan 12, 2014 at 5:45
  • So if '-t', the other arguments ('mupo') are no longer required. You don't want to just ignore them. Is that right? For a start, I'd drop the 'required' flags, give them meaningful defaults, and test after parse_args for the presence or absence of necessary values. Commented Jan 12, 2014 at 18:21
  • Added the result from more testing on my side to original post. Commented Jan 12, 2014 at 19:34

1 Answer 1

1

required=True with store_true does not make sense. The default is False, but if it is required the returned value will always be True.

Since only -w is required unconditionally, I would drop the required parameter on everything else. Then test for the required values after parse_args. I can still issue an argparse error with usage at that time. In other words, do my own testing rather than try something fancy in argparse.

def testArgs():
    usage = '[-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]'
    parser = argparse.ArgumentParser(description='DESCRIPTION',usage=usage)
    parser.add_argument('-w', help='URL', required=True)
    parser.add_argument('-t', help='Test', action='store_true')
    parser.add_argument('-m', help='Method')
    parser.add_argument('-l', help='Login')
    parser.add_argument('-p', help='Password')
    parser.add_argument('-o', help='Output')
    args = parser.parse_args()

    # sample test, streamline and refine to suit your needs
    # this assumes the default for all these args is None
    if not args.t: # '-t' in in argv
        if any([args.m is None, args.l is None, args.p is None, args.o is None]):
            parser.error('m,l,p,o are all required')
    return args

1216:~/mypy$ python2.7 stack21070971.py
usage: [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
stack21070971.py: error: argument -w is required

1213:~/mypy$ python2.7 stack21070971.py -w url
usage: [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
stack21070971.py: error: m,l,p,o are all required

1213:~/mypy$ python2.7 stack21070971.py -w url -t
Namespace(l=None, m=None, o=None, p=None, t=True, w='url')

1214:~/mypy$ python2.7 stack21070971.py -w url -m mmm
usage: [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
stack21070971.py: error: m,l,p,o are all required
...

1215:~/mypy$ python2.7 stack21070971.py -w url -m mmm -l uuu -p ppp -o ooo
Namespace(l='uuu', m='mmm', o='ooo', p='ppp', t=False, w='url')
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks again, hpaulj. I ended up using your suggestion and test the content of args. I was told to stop using sys.argv to check for argument and use instead argparse but it seems there are still some cases where you have to work around the issue.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.