Search code examples
pythondefaultargparse

argparse default option based on another option


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")

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.