Modelsettings is a straight-forward, easy to use python application settings manager that includes ini file, environment variable, and command-line parameter support.
The necessary settings variables are declared in a yaml model, which is then used to parse an .ini file, environment variables, and finally command-line arguments.
In addition to reading settings from the three sources, modelsettings also includes sample configuration generator support for:
- command-line
- environment variables (e.g., export statements)
- ini file
- docker run
- docker compose
- kubernetes
- drone plugins
Modelsettings looks for a model_settings.yml in the current working directory. A simple model_settings.yml file might look like this:
env_prefix: CF
model:
cream:
choices:
- True
- False
default: False
example: True
help: Would you like cream in your coffee?
required: True
sugar:
choices:
- True
- False
default: True
example: True
help: Would you like sugar in your coffee?
required: True
size:
choices:
- 10
- 12
- 16
default: 12
example: 16
help: What size cup would you like in ounces?
required: True
env_prefix is used as a prefix for the environment variables, this helps avoid name collision when running multiple python applications in the same shell.
model is a dictionary of required settings values.
In your application, simply import modelsettings.
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install modelsettings
app.py
from modelsettings import settings
def main():
output = f"You ordered a {settings.SIZE} oz. cup of coffee"
modifiers = []
if settings.CREAM: modifiers.append("cream")
if settings.SUGAR: modifiers.append("sugar")
if modifiers:
output += " with " + " and ".join(modifiers)
output += "."
print(output)
if __name__ == "__main__":
main()
All settings are converted to uppercase and available as settings.XXXXX
Argparse help is generated from the model:
$ python app.py --help
usage: app.py [-h]
[--generate {command,docker-run,docker-compose,ini,env,kubernetes,readme,drone-plugin}]
[--settings SETTINGS] [--cream {True,False}]
[--sugar {True,False}] [--size {10,12,16}]
optional arguments:
-h, --help show this help message and exit
--generate {command,docker-run,docker-compose,ini,env,kubernetes,readme,drone-plugin}
Generate a template
--settings SETTINGS Specify a settings file. (ie settings.dev)
--cream {True,False} Would you like cream in your coffee? e.g., True
--sugar {True,False} Would you like sugar in your coffee? e.g., True
--size {10,12,16} What size cup would you like in ounces? e.g., 16
A command-line parameter of generate is added to the application which, when used, will generate sample settings in a number of formats.
$ python app.py --generate env
export CF_CREAM=True
export CF_SIZE=16
export CF_SUGAR=True
Markdown can be generated which includes all the available generate formats.
$ python app.py --generate readme >> README.md
The .ini file is read first, then the environment variables, then the command-line parameters.
The application will now support loading settings from a settings.ini file.
$ cat settings.ini
[settings]
cream=True
size=16
sugar=True
$ python app.py
You ordered a 16 oz. cup of coffee with cream and sugar.
An alternate settings file can be specified with the command line, this is useful during development of the application.
$ cat settings.dev
[settings]
cream=True
size=10
sugar=False
$ python app.py --settings settings.dev
You ordered a 10 oz. cup of coffee with cream.
$ python app.py --settings settings.dev --size 16
You ordered a 16 oz. cup of coffee with cream.
All settings can be stored as environment variables. The environment variables should be prefaced with the env_prefix from the model_settings.yml file and capitalized.
$ export CF_CREAM=False
$ export CF_SIZE=12
$ export CF_SUGAR=True
$ python app.py
You ordered a 12 oz. cup of coffee with sugar.
$ export CF_CREAM=True
$ python app.py
You ordered a 12 oz. cup of coffee with cream and sugar.
Command line parameters take precedence over .ini files and environment variables.
$ python app.py --size 10 --sugar False --cream False
You ordered a 10 oz. cup of coffee.
$ python app.py --size 12 --sugar True --cream False
You ordered a 12 oz. cup of coffee with sugar.
$ python app.py --size 16 --sugar True --cream True
You ordered a 16 oz. cup of coffee with cream and sugar.
- The model support 5 basic python types:
boolintfloatstringlistdict
The type is derived from the example given, and the settings variable is cast to that type.
In the example below, each supported type is shown with a corresponding yaml native example.
example is therefore a required property for every entry in the model.
bool:
choices:
- True
- False
default: False
help: This is an integer setting
required: False
example: True
integer:
choices:
- 10
- 20
default: 60
help: This is an integer setting
required: False
example: 30
float:
default: 60.5
help: This is an integer setting
required: False
example: 30.5
string:
default: string
help: This is a string setting
required: False
example: string
dictionary:
default:
key: value
help: This is a dict setting
required: False
example:
key: value
list:
default:
- item1
- item2
help: This is a list setting
required: False
example:
- item1
- item2
Dictionary and lists should be represented as json in .ini files and environment variables:
$ more complex.yml
env_prefix: RM
model:
dictionary:
default:
key: value
help: This is a dict setting
required: False
example:
key: value
list:
default:
- item1
- item2
help: This is a list setting
required: False
example:
- item1
- item2
$ export MODEL_SETTINGS=complex.yml
$ python app.py --generate env
export RM_DICTIONARY='{"key": "value"}'
export RM_LIST='["item1", "item2"]'
$ python app.py --generate ini
[settings]
dictionary={"key": "value"}
list=["item1", "item2"]
Because all settings are supported as environment variables, transitioning a python application to a docker container is simple.
Dockerfile
FROM python:3.6.4-alpine
RUN apk add -U \
ca-certificates \
&& rm -rf /var/cache/apk/* \
&& pip install --no-cache-dir --upgrade pip
WORKDIR /usr/src/app/
ADD model_settings.yml .
ADD app.py .
ADD requirements.txt .
RUN pip install -r requirements.txt
ENTRYPOINT ["python", "app.py"]
Build
$ docker build -t coffee .
<...>
Successfully built 74938ac5902a
Successfully tagged coffee:latest
Generate
$ python app.py --generate docker-run
docker run -it \
-e CF_CREAM=True \
-e CF_SIZE=16 \
-e CF_SUGAR=True \
<container-name>
Run
$ docker run -it \
-e CF_CREAM=True \
-e CF_SIZE=16 \
-e CF_SUGAR=True \
coffee
You ordered a 16 oz. cup of coffee with cream and sugar.
$ docker run -it \
-e CF_CREAM=False \
-e CF_SIZE=12 \
-e CF_SUGAR=True \
coffee
You ordered a 12 oz. cup of coffee with sugar.