1

Given a small parser:

from argparse import ArgumentError, ArgumentParser
p = ArgumentParser()
p.add_argument('x', choices=['1', '2'])
p.add_argument('--y', required=False)
p.parse_args('1 --y 2'.split()) # this would work
p.parse_args('1'.split()) # this would work also

However, I want to ensure that if x=='1', then --y would get a value. My implementation was this:

args = p.parse_args('1 --y 2'.split())
if args.x=='1' and not args.y:
     raise ArgumentError('blah')

Is there an inner argparse way to do this? it looks much better when argparse throws a parsing error

Even better, I want the --y arg will be required when x=='1' and not allowed when x=='2'

CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
  • 2
    Your problem could be cast as subparsers. That's the only builtin mechanism for making one set of arguments depend on the value of another. Otherwise post-parsing testing is the easiest approach. The parser doesn't have to do everything! – hpaulj Apr 28 '19 at 15:57
  • See related/possible duplicate: [Conditional argparse with choice option](https://stackoverflow.com/q/55595539/1782792). You can use the `error` method of the parser to trigger an error. – jdehesa May 16 '19 at 13:36

1 Answers1

0

‘I want to ensure that if x == '1', then --y would get a value’

You could use this:

from argparse import ArgumentParser
from sys import argv

p = ArgumentParser(prog='MyParser')

yIsRequired = (argv[1] == '1') # True if x == '1' else False

p.add_argument('x')            # Positional argument
p.add_argument(
    '--y',                     # Optional argument
    required=yIsRequired,
)

p = p.parse_args()

It's all tested. This code:

  • Checks sys.argv for x.

  • **If x == '1' then yIsRequired is set to True

  • y's required parameter is set to the value of yIsRequired

‘Even better, I want the --y arg will be required when x=='1' and not allowed when x=='2'’

Here you go:

from argparse import ArgumentParser
import sys

p = ArgumentParser(prog='Demo')

yIsRequired = (sys.argv[1] == '1')

p.add_argument('x')
p.add_argument(
    '--y',
    required=yIsRequired,
)

if sys.argv[1] == '2' and '--y' in sys.argv:                    # If x == 2 and y has a value
    sys.stderr.write('Demo: error: argument --y not allowed\n') # Write error to standard error output
    sys.exit(1)                                                 # Exit with non-zero exit code

p = p.parse_args()

Conclusion:

  • sys.argv contains all arguments passed to your script (it is a list)
  • You can use sys.argv to check whether arguments have been passed (boolean logic)
  • See this question and this for help on removing/disabling arguments. There are quite a few answers, but many are worth reading.
Benj
  • 736
  • 7
  • 21
  • 1
    Mixing argv is not ideal. I prefer a way that uses only argparse methods. Using the way i posted and replacing the raise with argparse.error would give much nicer results – CIsForCookies May 17 '19 at 04:16
  • @CIsForCookies It seems then like you have solved your problem! Btw, if an argument is missed out etc an ArgumentError is not displayed to the user in the command line. – Benj May 17 '19 at 08:24