0

I have the following code:

import argparse

parser = argparse.ArgumentParser(description='do stuff')

TOP = parser.add_mutually_exclusive_group(required=True)
#
TOP.add_argument('--interactive', action="store_true")
noninteractive = TOP.add_argument_group()
##
noninteractive.add_argument('--required1', required=True, type=float)
noninteractive.add_argument('--required2', required=True, type=float)
##
noninteractive_excl = noninteractive.add_mutually_exclusive_group(required=True)
noninteractive_excl.add_argument('--mutuallyexcl1', default=None, type=float)
noninteractive_excl.add_argument('--mutuallyexcl2', default=None, type=float)

args = parser.parse_args()

My program should work like this:

You can either call it with the option --interactive (which then will ask the user to enter the data step by step) or a group of arguments containing all necessary options for running the program. The group and --interactive shall be mutually exclusive.

The group then has two options which are required and two options that are mutually exclusive again.

My expectation was, that, when called with --interactive, the program would not ask for any other arguments since they are in a group which is part of the first mutually exclusive group. Instead, when I call the program with

python dostuff.py --interactive

I get the following message:

dostuff.py: error: the following arguments are required: --required1, --required2

What did I do wrong and how can I fix this? Many thanks in advance.

EXIT_FAILURE
  • 278
  • 1
  • 13
  • I think it gets thrown off because your mutually exclusive group contains an argument and another group. The recommendation is to use sub-parsers. Maybe this will help: https://stackoverflow.com/a/52865331/843953 – Pranav Hosangadi May 18 '21 at 22:01
  • Subparsers are something that I looked into but if I understood them correctly, that would mean that I had call the function with `dostuff.py interactive --somearg`. Instead, I would like to be able to simply use `dostuff.py --interactive `. – EXIT_FAILURE May 18 '21 at 22:13

1 Answers1

0

You are reading more into the groups than is supported by the docs or the code. argument_group is just a "help" display tool. mutually_exclusive_group is a simple xor, without any nesting.

parser = argparse.ArgumentParser(description='do stuff')

TOP = parser.add_mutually_exclusive_group(required=True)
#
TOP.add_argument('--interactive', action="store_true")

Everything added to noninteractive is also part of the TOP group. None of them can be required.

noninteractive = TOP.add_argument_group()
##
noninteractive.add_argument('--required1', required=True, type=float)
noninteractive.add_argument('--required2', required=True, type=float)
##

These two arguments are required. The fact that they are in the argument_group makes no difference. That's not a logical "any" group.

That includes this group - the xor is flattened.

noninteractive_excl = noninteractive.add_mutually_exclusive_group(required=True)
noninteractive_excl.add_argument('--mutuallyexcl1', default=None, type=float)
noninteractive_excl.add_argument('--mutuallyexcl2', default=None, type=float)

args = parser.parse_args()

mutually_exclusive_group does not cut parsing short. It's not a branching mechanism. It is just a test for occurance. If you provide --iteractive, it will raise an error if you provide any of the other arguments in the group. Your example didn't cause that error, but it still tests for required arguments. In other words, these are just tests; they don't alter parsing order.

The subparsers mechanism does create alternative parsing paths. Only one of the subparsers is used, parsing the remaining arguments according to its own setup.

hpaulj
  • 221,503
  • 14
  • 230
  • 353