argparse default option based on another option

argparse, default, python

Solution

Here's another attempt at writing a custom Action class

import argparse
class FooAction(argparse.Action):
    # adapted from documentation
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, values)
        defaultbar = getattr(namespace, 'bar')
        try:
            defaultbar = defaultbar%values
        except TypeError:
            # BAR has already been replaced
            pass
        setattr(namespace, 'bar', defaultbar)

parser = argparse.ArgumentParser()
parser.add_argument("--foo", required=True, action=FooAction)
parser.add_argument("--bar", default="%s_BAR")

args = parser.parse_args(['--foo', 'Foo', '--bar', 'Bar'])
# Namespace(bar='Bar', foo='Foo')
args = parser.parse_args(['--foo', 'Foo'])
# Namespace(bar='Foo_BAR', foo='Foo')
args = parser.parse_args(['--bar', 'Bar', '--foo', 'Foo'])
# Namespace(bar='Bar', foo='Foo')

Note that the class has to know the `dest` of the `--bar` argument. Also I use a `'%s_BAR'` to readily distinguish between a default value, and a non default one. This handles the case where `--bar` appears before `--foo`.

Things that complicate this approach are:

- default values are evaluated at `add_argument` time.

- default values are placed in the Namespace at the start of `parse_args`.

- flagged (optionals) arguments can occur in any order

- the `Action` class is not designed to handle interacting arguments.

- the `bar` action will not be called in the default case.

- the `bar` default could be a function, but something would have to check after `parse_args` whether it needs to be evaluated or not.

While this custom Action does the trick, I still think the `addbar` function in my other answer is a cleaner solution.

Problem

Suppose I have an argparse python script: ``` import argparse parser = argparse.ArgumentParser() parser.add_argument("--foo", required=True) ``` Now I want to add another option --bar, which would default to appending "_BAR" to whatever was specified by --foo argument. My goal: ``` >>> parser.parse_args(['--foo', 'FOO']) >>> Namespace(foo='FOO', bar="FOO_BAR") ``` AND ``` >>> parser.parse_args(['--foo', 'FOO', '--bar', 'BAR']) >>> Namespace(foo='FOO', bar="BAR") ``` I need something like this: ``` parser.add_argument("--bar", default=get_optional_foo + "_BAR") ```

Original source