Configure It All

cfitall (configure it all) is a configuration library for Python applications, loosely inspired by the viper library for Go.

cfitall provides a registry for configuring your application from a series of providers, which are managed by a manager. When requesting a configuration value, data from each provider is merged into the next to resolve the requested value. Providers can be easily written and registered to extend cfitall’s functionality.

Included with the package are a FilesystemProvider and an EnvironmentProvider for reading configuration data from the filesystem or environment variables, respectively.

Quick Start / Example

First, install cfitall from PyPi via your favorite package manager:

pip install 'cfitall>=2.0'

For this example, we will configure a contrived application called myapp, which we will also use as our registry name. The FilesystemProvider and EnvironmentProvider both use this value as a namespace.

Our configuration file will therefore be named myapp.(json|yaml|yml). By default, the FilesystemProvider will search for a matching file first in $HOME/.local/etc/myapp and then in /etc/myapp. Let’s create a configuration file in YAML format and put it in place:

# ~/.local/etc/myapp/myapp.yml

global:
  bar: foo
  things:
    - one
    - two
    - three
  person:
    name: joe
    hair: brown
network:
  port: 9000
  listen: '*'

The EnvironmentProvider also uses the registry name as a namespace, searching environment variables beginning with MYAPP__ for values. Let’s export some environment variables to see this provider in action:

export MYAPP__GLOBAL__NAME="my app from bash"
export MYAPP__GLOBAL__THINGS="[four, five, six]"
export MYAPP__NETWORK__PORT=8080

Next, set up a ConfigurationRegistry for myapp, naming it myapp.

# myapp/__init__.py

from cfitall.registry import ConfigurationRegistry


# create a configuration registry for myapp
config = ConfigurationRegistry('myapp')

# set some default configuration values
config.set_default('global.name', 'my fancy application')
config.set_default('global.foo', 'bar')
config.set_default('network.listen', '127.0.0.1')

# read/update data from enabled providers
config.update()

Now you can use your registry instance to get the configuration data you need. You can access the merged configuration data by its configuration key (dictionary keys separated by .), or you can just grab the entire merged dictionary via the dict property.

# myapp/logic.py

from myapp import config

# prints ['four', 'five', 'six'] because env var overrides config file
print(config.get('global.things'))

# prints 8080 because env var overrides config file
print(config.get_int('network.port'))

# prints * because config file overrides default set by set_default()
print(config.get_str('network.listen'))

# prints 'joe' from myapp.yml because it is only defined there
print(config.get('global.person.name'))

# alternate way to print joe through the config dict property
print(config.dict['global']['person']['name'])

# prints the entire assembled config as dictionary
print(config.dict)

Running logic.py should go something like this:

$ python logic.py
['four', 'five', 'six']
8080
*
joe
joe
{'global': {'name': 'my app from bash', 'foo': 'bar', 'bar': 'foo', 'things': ['four', 'five', 'six'], 'person': {'name': 'joe', 'hair': 'brown'}}, 'network': {'listen': '*', 'port': '8080'}}

Indices and tables