rob
rob

Reputation: 173

python argparse if argument selected then another argument required =True

Is there a way to make an argument required to be true if another specific argument choice is present otherwise argument required is false?

For example the following code if argument -act choice select is 'copy' then the argument dp required is true otherwise required is false:

import argparse

ap = argparse.ArgumentParser()

ap.add_argument("-act", "--act", required=True, choices=['tidy','copy'],type=str.lower,
                help="Action Type")

ap.add_argument("-sp", "--sp", required=True,
                help="Source Path")

args = vars(ap.parse_args())

if args["act"] == 'copy':
    ap.add_argument("-dp", "--dp", required=True,
                help="Destination Path")
else:
    ap.add_argument("-dp", "--dp", required=False,
                help="Destination Path")

args = vars(ap.parse_args())

### Tidy Function
def tidy():
    print("tidy Function")

### Copy Function
def copy():
    print("copy Function")


### Call Functions

if args["act"] == 'tidy':
    tidy()
if args["act"] == 'copy':
    copy()

I am currently getting an error unrecognized arguments: -dp with the above code.

The expected result would be to move on to call function. Thanks

Upvotes: 4

Views: 4455

Answers (2)

hpaulj
hpaulj

Reputation: 231375

ap.add_argument("-dp", "--dp", help="Destination Path")
args = parser.parse_args()
if args.copy is in ['copy']:
    if args.dp is None:
        parser.error('With copy, a dp value is required')

Since a user can't set a value to None, is None is a good test for arguments that haven't been used.

The parser has a list of defined arguments, parser._actions. Each has a required attribute. During parsing it maintains a set of seen-actions. At the end of parsing it just checks this set against the actions for which required is True, and issues the error message if there are any.

I would argue that testing after parsing is simpler than trying to set the required parameter before hand.

An alternative is to provide dp with a reasonable default. Then you won't care whether the user provides a value or not.

I can imagine defining a custom Action class for copy that would set the required attribute of dp, but overall that would be more work.

Upvotes: 1

Alex
Alex

Reputation: 7045

I would use ArgumentParser.add_subparsers to define the action type {tidy, copy} and give the command specific arguments. Using a base parser with parents allows you to define arguments that are shared by both (or all) your sub-commands.

import argparse


parser = argparse.ArgumentParser(
    prog='PROG', 
    epilog="See '<command> --help' to read about a specific sub-command."
)

base_parser = argparse.ArgumentParser(add_help=False)
base_parser.add_argument("--sp", required=True, help="source")

subparsers = parser.add_subparsers(dest='act', help='Sub-commands')

A_parser = subparsers.add_parser('copy', help='copy command', parents=[base_parser])
A_parser.add_argument('--dp', required=True, help="dest, required")

B_parser = subparsers.add_parser('tidy', help='tidy command', parents=[base_parser])
B_parser.add_argument('--dp', required=False, help="dest, optional")

args = parser.parse_args()

if args.act == 'copy':
    pass
elif args.act == 'tidy':
    pass

print(args)

Which produces the following help pages, note that instead of needing to use -act the command is given as a positional parameter.

~ python args.py -h
usage: PROG [-h] {tidy,copy} ...

positional arguments:
  {tidy,copy}  Sub-commands
    tidy       tidy command
    copy       copy command

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

See '<command> --help' to read about a specific sub-command.
~ python args.py copy -h
usage: PROG copy [-h] --sp SP [--dp DP]

optional arguments:
  -h, --help  show this help message and exit
  --sp SP     source
  --dp DP     dest, optional
~ python args.py tidy -h
usage: PROG tidy [-h] --sp SP --dp DP

optional arguments:
  -h, --help  show this help message and exit
  --sp SP     source
  --dp DP     dest, required

Upvotes: 4

Related Questions