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.