Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cause Python's argparse to execute action for default

I am using argparse's action to add various data to a class. I would like to use that action on the default value if that arg is not provided at the command line. Is this possible? Thanks!

like image 944
Max Bileschi Avatar asked Feb 05 '14 17:02

Max Bileschi


People also ask

Is Argparse the default?

The argparse module will automatically allow an option -h or --help that prints a usage string for all the registered options. By default, the type is str , the default value is None , the help string is empty, and metavar is the option in upper case without initial dashes.

What is action in Argparse Python?

action defines how to handle command-line arguments: store it as a constant, append into a list, store a boolean value etc. There are several built-in actions available, plus it's easy to write a custom one.

What is Store_true in Python?

The store_true option automatically creates a default value of False. Likewise, store_false will default to True when the command-line argument is not present.


1 Answers

argparse does not use the action when applying the default. It just uses setattr. It may use the type if the default is a string. But you can invoke the action directly.

Here I use a custom action class borrowed from the documentation. In the first parse_args nothing happens. Then I create a new namespace, and invoke the action on the default. Then I pass that namespace to parse_args. To understand this, you many need to import it into an interactive shell, and examine the attributes of the namespace and action.

# sample custom action from docs
class FooAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        print('Setting: %r %r %r' % (namespace, values, option_string))
        setattr(namespace, self.dest, 'action:'+values)
p = argparse.ArgumentParser()
a1 = p.add_argument('--foo', action=FooAction, default='default')
print 'action:',a1
print p.parse_args([])

ns = argparse.Namespace()
a1(p, ns, a1.default, 'no string') # call action
print p.parse_args([],ns)
print p.parse_args(['--foo','1'],ns)

which produces:

action: FooAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default='default', type=None, choices=None, help=None, metavar=None)
Namespace(foo='default')
Setting: Namespace() 'default' 'no string'
Namespace(foo='action:default')
Setting: Namespace(foo='action:default') '1' '--foo'
Namespace(foo='action:1')

I tailored the output to highlight when the action is being used.


Here's a way of performing a special action on an argument that isn't given on the command line (or given with a value == to the default). It's a simplification of the class given in https://stackoverflow.com/a/24638908/901925.

class Parser1:
    def __init__(self, desc):
        self.parser = argparse.ArgumentParser(description=desc)
        self.actions = []

    def milestone(self, help_='milestone for latest release.', default=None):
        action = self.parser.add_argument('-m', '--milestone', help=help_, default=default)
        self.actions.append(action)
        return self

    def parse(self):
        args = self.parser.parse_args()
        for a in self.actions:
            if getattr(args, a.dest) == a.default:
                print 'Please specify', a.dest
                values = raw_input('>')
                setattr(args, a.dest, values)
        return args

print Parser1('desc').milestone(default='PROMPT').parse()

The prompting is done after parse_args. I don't see any reason to call parse_args again.

like image 190
hpaulj Avatar answered Oct 01 '22 02:10

hpaulj