Argparse - Custom Action With No Argument?

For anyone who finds this in 2020: If you attempt to pass nargs=0 in the latest argparse package for python3, it will error with:

ValueError: nargs for store actions must be != 0; if you have nothing to store, actions such as store true or store const may be more appropriate

The solution you're probably looking for is to pass action="store_const" and const=True

Example:

parser = argparse.ArgumentParser
parser.add_argument('-s', '--start', help='Start script or whatever', action="store_const", const=True)

This will set the value for parser.start equal to "True" when the -s parameter is passed to main/the rest of the script.


As mgilson suggested nargs=0 does the trick. Since it's intrinsic to your action, I'd put it inside the action:

class StartAction(argparse.Action):
    def __init__(self, nargs=0, **kw):
        super().__init__(nargs=nargs, **kw)
    def __call__(self, parser, namespace, values, option_string=None):
        print "Hello"

start.add_argument('-s', '--start', action=StartAction)

And you have your desired behaviour without the redundancy of having to add nargs=0 to every add_argument() call. Neat if you have multiple arguments working in the same way.

However users could still override the default of nargs=0 which is silly for your use case as demonstrated in the question. So I'd enforce it:

class StartAction(argparse.Action):
    def __init__(self, nargs=0, **kw):
        if nargs != 0:
            raise ValueError('nargs for StartAction must be 0; it is '
                             'just a flag.')
        super().__init__(nargs=nargs, **kw)
    def __call__(self, parser, namespace, values, option_string=None):
        print "Hello"

I wrote this example after consulting the above and trying out different methods. Here is a class which implements the "Uncount" acount, which is the opposite of the 'count' action.

import argparse
class Uncount(argparse.Action):
    def __init__(self, option_strings, dest, nargs=0, choices=None, const=None, **kwargs):
        if nargs != 0:
            raise ValueError("no arguments to this parameter are allowed")
        if const is not None:
            raise ValueError("this parameter does not permit constants")
        if choices is not None:
            raise ValueError("no choices to this parameter are allowed")
        super(Uncount, self).__init__(option_strings=option_strings, nargs=nargs, dest=dest,**kwargs)
    def __call__(self, parser, namespace, values, option_string=None):
        value = getattr(namespace, self.dest, 0)
        setattr(namespace, self.dest, value-1)

opts={}
argparser = argparse.ArgumentParser(description='Report on other teams')
argparser.add_argument(
    '-v','--verbose',dest='verbose',default=0,action='count',
    help='increase log level',
)
argparser.add_argument(
    '-q','--quiet',dest='verbose',default=0,action=Uncount,
    help='increase log level',
)

Examples:

>>> argparser.parse_args('-q -q'.split())
Namespace(verbose=-2)
>>> argparser.parse_args('-q -v'.split())
Namespace(verbose=0)
>>> argparser.parse_args('-q -v -v'.split())
Namespace(verbose=1)

Try adding nargs=0 to your start.add_argument:

start.add_argument('-s', '--start', action=StartAction, nargs=0)