4

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

3
  • Use parse_known_args when you just want act Commented Dec 7, 2019 at 15:13
  • Why do you need to set the required attributes before hand? What's wrong with testing afterwards? Commented Dec 7, 2019 at 16:17
  • IMHO it's far better to use click than argparse. Commented Feb 15, 2021 at 21:06

2 Answers 2

4

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
Sign up to request clarification or add additional context in comments.

Comments

1
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.

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.