1

I am trying to create a simple python with argparse flags. There are 2 arguments A and B and in case in aargument A the parameter that was entered was A it will ask for the 3rd parameter C.

  • if in the first parameter A wasn't submitted it won't need to enter 3rd parameter My code:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("A", help="A")
parser.add_argument("B", help="B")


args = parser.parse_args()

print("A:{} B:{}     ".format(
    args.A,
    args.B,
))

if args.a =="A":
    parser.add_argument("C", help="C")
    print(args.C)
David
  • 8,113
  • 2
  • 17
  • 36
  • Are these arguments positionals (as in your example) or optionals (as in the answer)? Whether they are 'required' or not is specified in different ways. And how they handle order also differs. In either case I'd suggest making `C` not-required, and testing after parsing. That's usually simpler than trying to do something fancy with in the parser itself. – hpaulj Nov 17 '20 at 18:11

2 Answers2

2

You could do the following:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--A", help="A")
parser.add_argument("--B", help="B")

opts, rem_args = parser.parse_known_args()
if opts.A:
    parser.add_argument("--C", required=True,type=int, help='C')
    # use options and namespace from first parsing
    parser.parse_args(rem_args, namespace = opts)

In this case whenever the argument --A is passed it will require also --C.

You can refer to the docs to understand parser.parse_known_args().

Notice the --C won't appear in the help messege.

Example:

  1. so.py --B 1 - Works
  2. so.py --A 1 --B 1 - Don't Work
  3. so.py --A 1 --B 1 --C 1 - Works
  4. so.py --A 1 --C 1 - Works
  5. so.py --B 1 --C 1 - Works, but it's a don't care case so avoid it in the code logic.
David
  • 8,113
  • 2
  • 17
  • 36
  • but I need a case that if --A argument that was passed is equal to "A" it will require the argument of --C – python maniac Nov 17 '20 at 11:32
  • I mean it's also ok to have 3 argsparse from the beginning, I just want that the 3rd one will be required just in case the first argument is equal to something ("A" or so). – python maniac Nov 17 '20 at 11:44
  • @pythonmaniac so just implement it as logic in the code. You can condition the existence of an argument based on the value passed to other argument. – David Nov 17 '20 at 12:35
  • That's what i was trying to do in my code but with no success. – python maniac Nov 17 '20 at 13:19
  • The help text that this generates doesn't include `--C`. The only way to know that `--C` is required is to not provide it when providing `--A`. This may be better implemented using [`sub-commands`](https://docs.python.org/3/library/argparse.html#sub-commands) – Alex Nov 17 '20 at 14:04
1

In an ipython session:

In [1]: import argparse
In [2]: p = argparse.ArgumentParser()
In [3]: p.add_argument('A');
In [4]: p.add_argument('B');
In [5]: p.add_argument('C', nargs='?');    # optional positional

In [6]: p.print_help()
usage: ipython3 [-h] A B [C]

positional arguments:
  A
  B
  C

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

If only 2 are given, the last is default None:

In [7]: p.parse_args('foo bar'.split())
Out[7]: Namespace(A='foo', B='bar', C=None)
In [8]: p.parse_args('foo bar baz'.split())
Out[8]: Namespace(A='foo', B='bar', C='baz')

We can test args.A and raise an error is C is not given (its default):

In [9]: args = p.parse_args('A bar baz'.split())
In [10]: if args.A == 'A' and args.C is None:
    ...:     p.error('C is required')
    ...: 
In [11]: args = p.parse_args('A bar'.split())
In [12]: if args.A == 'A' and args.C is None:
    ...:     p.error('C is required')
    ...: 
usage: ipython3 [-h] A B [C]
ipython3: error: C is required

We could do something similar with optionals/flagged arguments.

While it's possible to do the required testing with a 2 stage parser, I think testing after parsing is simpler.

===

In [16]: p = argparse.ArgumentParser()
In [17]: p.add_argument('--A', required=True);
In [18]: p.add_argument('--B', required=True);
In [19]: p.add_argument('--C');
In [20]: p.print_help()
usage: ipython3 [-h] --A A --B B [--C C]

optional arguments:
  -h, --help  show this help message and exit
  --A A
  --B B
  --C C
In [21]: args = p.parse_args('--A foo --B bar'.split())
In [22]: args
Out[22]: Namespace(A='foo', B='bar', C=None)
hpaulj
  • 221,503
  • 14
  • 230
  • 353