user2998514
user2998514

Reputation: 55

How to Check for and raise an error for no command line argument

I have an argparsing program:

import argparse

def main():
    p = argparse.ArgumentParser()
    help_str = {'rule': 'Blah blah blah and stuff.'}
    p.add_argument('-r', '--rule', action="store", type=str, dest='rule', help=help_str['rule'])
    p.set_defaults(rule='string')
    args = p.parse_args()

Therefore, my command line input:

username@compname:~$python progname.py -r/--rule 'something'

I am trying to figure out how I could just input:

username@compname:~$python progname.py -r/--rule

and have my own error message come up:

print '\n    Error: no input value for rule:'
print '    resetting rule to default value.'
args.rule = 'string'

after this, the rule value should print out as 'string'

I am only slightly fluent in Python, sorry. I have tried using try/except blocks and even sys.argv[2] (though I may have done something wrong.) This is the error that keeps popping up with all of my solutions (not very familiar with errors outside Type and Value):

progname.py: error: argument -r/--rule: expected one argument

Any help appreciated.

Upvotes: 3

Views: 2813

Answers (1)

hpaulj
hpaulj

Reputation: 231475

p = argparse.ArgumentParser()
p.add_argument('-r', '--rule', nargs='?', default='string')
args = p.parse_args()
print args

with nargs='?' produces Namespace(rule='string') if called without argument. With -r, args is Namespace(rule=None). And Namespace(rule='something') with -r something.

Lets add a few lines of code

if args.rule is None:
    print '\n    Error: no input value for rule:'
    print '    resetting rule to default value.'
    args.rule = 'string'
print args

Now the output with '-r' (or --rule) is:

Namespace(rule=None)

    Error: no input value for rule:
    resetting rule to default value.
Namespace(rule='string')

the other cases are the same.

If I drop the default; p.add_argument('-r', '--rule', nargs='?'), the no argument case also produces this custom 'error message', since the default (in argparse) is None.

It's possible to add custom error checking with a custom type or action, but I think testing for None after using argparse is simpler, and easier to understand.

I'd suggest changing this error message to a warning. An Error usually terminates the program; a warning prints the message and continues.


Here's a solution when nargs=2 (or something else that's fixed). It's not a trivial change, since it involves redefining the error method (documented near the end of the argparse docs), and then capturing the error that it produces. The error method cannot access the Namespace (args), nor can it continue parse_args. So it cannot handle any other arguments if there is problem with the rule argument.

class MyParser(argparse.ArgumentParser):
    def error(self, message):
        if 'rule' in message:
            message = 'wrong number of input values for rule'
            raise argparse.ArgumentError(None,message)
            # cannot access namespace or continue parsing
        else:
            super(MyParser, self).error(message)

p = MyParser()
p.add_argument('-r', '--rule', nargs=2)
try:
    args = p.parse_args()
except argparse.ArgumentError as e:
    print e
    args = argparse.Namespace(rule='default')
    # create a namespace with this default value
print args

Keep in mind that nargs has 2 purposes: - raise an error if a wrong number of argument strings is given - allocate multiple argument strings among multiple Actions (the thing created by add_argument).

The 2nd is especially evident if you have, for example, several positionals, taking 1,2 and * arguments respectively. It is a key novel feature of argparse, at least relative to earlier Python parsers. While it possible to tweak it, it is hard to completely redefine it.

If the arguments allow it, you could use nargs='*' instead of the '?', and issue the warning if the number of strings is not '2' (or whatever).

Upvotes: 1

Related Questions