Skip to content

Instantly share code, notes, and snippets.

@AdrienHorgnies
Last active April 12, 2019 23:40
Show Gist options
  • Save AdrienHorgnies/738025ed60c9499d6aad10cbd9be0642 to your computer and use it in GitHub Desktop.
Save AdrienHorgnies/738025ed60c9499d6aad10cbd9be0642 to your computer and use it in GitHub Desktop.
a wrapper around argparse to handle configuration file

confargparse

Extends argparse.ArgumentParser to make it able to take in values from a configuration file.

Beyond the added functionality it doesn't not modify the behaviour of argparse.ArgumentParser and can thus be used as a drop in replacement.

Why

You may want to provide values either to the CLI or by configuration file. And you don't want to check yourself if each value not provided to the CLI is present in the configuration file. And you also don't want the CLI to shout an error because it hasn't been provided an option when it is present in the configuration file.

How

It inserts values from the configuration files before CLI provided values into sys.argv. The last provided value overrules the previous one. Thus if you provide a value which is present in the configuration file, the configuration file value won't be used.

Getting start

  1. Provide the configuration as a dict to the constructor:
with open("application.yml", "r") as config_file:
  config = yaml.load(config_file)

parser = ConfArgParser(description="Cli prototype", config=config)
  1. Link the corresponding item to the option:
parser.add_argument("--host", required=True, type=str, conf_key="mysql.host")
parser.add_argument("--user", required=True, type=str, conf_key="mysql.user")
  1. If a value is present in the configuration file and is linked to an option, it is added to sys.argv:
args = parser.parse_args()
print(args)

Assuming the configuration file defines mysql.host: localhost and mysql.user: me the call cli --host 127.0.0.1 becomes cli --host localhost --user me --host 127.0.0.1 which is equivalent to cli --user me --host 127.0.0.1. And it outputs: Namespace(host='127.0.0.1', user='me')

mysql:
host: 127.0.0.1
user: root
#!/usr/bin/env python3
from confargparse import ConfArgParser
import yaml
if __name__ == "__main__":
with open("application.yml", "r") as config_file:
config = yaml.load(config_file)
parser = ConfArgParser(description="Cli prototype", config=config)
parser.add_argument("--host", required=True, type=str, conf_key="mysql.host")
parser.add_argument("-u", "--user", required=True, type=str, conf_key="mysql.user")
args = parser.parse_args()
print(args)
import sys
from argparse import ArgumentParser
class ConfArgParser(ArgumentParser):
def __init__(self, *args, config=None, **kwargs):
self.__config = config if config is not None else dict()
self.__key_opts = dict()
super(ConfArgParser, self).__init__(*args, **kwargs)
def add_argument(self, *args, conf_key=None, **kwargs):
if conf_key is not None:
self.__key_opts[conf_key] = args[0]
return super(ConfArgParser, self).add_argument(*args, **kwargs)
def parse_args(self, **kwargs):
config_as_opts = []
for key, opt in self.__key_opts.items():
conf_value = self.__resolve_conf(key.split("."), self.__config)
if conf_value is not None:
config_as_opts.append(opt)
config_as_opts.append(str(conf_value))
sys.argv = sys.argv[0:1] + config_as_opts + sys.argv[1:]
return super(ConfArgParser, self).parse_args(**kwargs)
def __resolve_conf(self, key_frags, node):
if key_frags[0] not in node:
return None
elif len(key_frags) == 1:
return node[key_frags[0]]
else:
return self.__resolve_conf(key_frags[1:], node[key_frags[0]])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment