5

I know similar questions have been asked before but I could not really find an exact one (also, I could not understand the 'nargs' or 'subparse' concept and how it would apply to my case)

I want something like:

parser = argparse.ArgumentParser()
parser.add_argument('-mode', choices=['download', 'upload'], required=True)
parser.add_argument('-d', required=True)
args = parser.parse_args()
if args.mode == 'download':
    parser.add_argument('-f', required=True)

args = parser.parse_args()

so the -f argument is required only when the -mode is download otherwise it should not be parsed

2
  • argparse does not have mutually inclusive arguments, which is what you are talking about. You should instead make neither -d nor -f be required, but include in the usage message the fact that either both or neither must be specified, then check to make sure that both -d and -f are specified if either is. Commented Sep 9, 2013 at 22:49
  • 3
    You could use subparsers, so that download becomes a subcommand with it's own set of (required) flags. Commented Sep 9, 2013 at 22:51

2 Answers 2

8

Generally, you should avoid making --options required. In this case, I would suggest the following:

  1. Replace --mode with a subcommand.
  2. Replace -d with a positional argument
  3. Replace -f with a 2nd positional argument for the download command only.

If you really want to, you can keep -d and -f instead of making them positional arguments.

Your calls would look like this:

./script.py upload d-argument
./script.py download d-argument f-argument

or

./script.py upload -d foo
./script.py download -d foo -f bar

The code to implement is a little tedious, but fairly straightforward.

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
upload_parser = subparsers.add_parser('upload')
download_parser = subparsers.add_parser('download')
upload_parser.set_defaults(cmd='upload')
download_parser.set_defaults(cmd='download')
for p in [ upload_parser, download_parser ]:
    p.add_argument("d")
    # p.add_argument("-d", required=True)
download_parser.add_argument("f")
# download_parser.add_argument("-f", required=True)


args = parser.parse_args()
if args.cmd == 'upload':
    # handle upload with args.d
elif args.cmd == 'download':
    # handle download with args.d and args.f

Note the calls to set_defaults used to mark in the final parsed arguments which command was used.

Sign up to request clarification or add additional context in comments.

2 Comments

Great, thanks. Not sure if this happens only for my my python version (3.5), but I need to check if cmd in args in case the user calls the script without cmd.
If your script is called with no positional arguments, argparse itself will raise an error. Otherwise, the first argument will be used for cmd.
6

What about this?

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-mode', choices=['download', 'upload'], required=True)
parser.add_argument('-d', required=True)
parser.add_argument('-f')

args = parser.parse_args()
if args.mode == 'download' and not args.f:
    parser.error('-f argument is required in "download" mode.')

DEMO:

$ python test.py -mode=upload -d 10
$ python test.py -mode=download -d 10
usage: test.py [-h] -mode {download,upload} -d D [-f F]
test.py: error: -f argument is required in "download" mode.
$ python test.py -mode=download -d 10 -f 10

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.