I need to handle two ways of configuring an application. One is via command line arguments, and the other one is from a JSON file (actually, the JSON file is the result of storing the arguments from a previous run).
I can handle this by merging the two namespace objects, something like this:
cli_args = some_parser.parse_args()
with open(json_file, 'r') as f:
json_args = json.load(f)
all_args = argparse.Namespace()
all_args.__dict__ = {**vars(cli_args), **json_args}
# or all_args.__dict__ = {**json_args, **vars(cli_args)}
The problem is on the last line. If I choose the first version, arguments from the JSON file take precedence. If I choose the second version, arguments from the CLI take precedence.
I would like to have CLI arguments take precedence, but only if they were actually specified. The problem arises when the parser admits default values. In this case, the cli_args
object will be populated with default values, and this will take precedence over JSON arguments.
For a simple example, let me take this parser:
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default='FOO')
parser.add_argument('--bar', default='BAR')
Say I have a JSON file with
{
"foo": "myfoo",
"bar": "mybar"
}
and that I invoke my application with python myapp.py --foo hello
.
I would like to get a namespace object having foo=hello, bar=mybar
. Both ways of merging the arguments will give something different. First, if I give the JSON file precedence I will obtain foo=myfoo, bar=mybar
. If I give the CLI the precedence, I get foo=hello, bar=BAR
.
The problem is that I cannot see a way to distinguish which arguments in the namespace returned from parser.parse_args()
were populated by the user, and which ones were filled in using default settings.
Is there a way to ask argparse which arguments were actually explicitly set on the command line, as opposed to being filled with defaults?