Overview

staticconf is a library for loading configuration values from many heterogeneous formats and reading those values. This process is split into two phases.

Configuration Loading

Configuration is read from files or python objects (dict, list, or any object with attributes), flattened, and merged into a container called a staticconf.config.ConfigNamespace. Namespaces are used to group related configuration together.

If configuration is changed frequently, it can also be reloaded easily with very little change to the existing code.

See staticconf.loader for a list of supported file formats and config loading examples.

See staticconf.config.ConfigFacade.load() for examples of building a reloader.

See staticconf.config.ConfigNamespace for more details about namespaces.

Reading configuration values

Once configuration data is loaded into a staticconf.config.ConfigNamespace there are three options for retrieving the configuration values. All of them have a similar set of methods which use validators to ensure you’re getting the type you expect. When a value is missing they will raise staticconf.errors.ConfigurationError unless a default was given. They will raises staticconf.errors.ValidationError if the value in the config fails to validate.

The list of provided validators is staticconf.validation, but you can create custom validators for any type. Functions for reading configuration values are named using a convention based on the validator name. For example the methods for getting a date using staticconf.validation.validate_date() would be:

  • staticconf.read_date()
  • schema.date()
  • staticconf.get_date()

Readers

staticconf.readers

Readers are the most straightforward option. They simply lookup the value in the namespace and return it. There is no caching of the type-coercion.

Schema

staticconf.schema

Schemas are classes where each property is an accessor for a configuration value. The type-coercion is cached on the class. This can be useful if you’re performing more involved coercion (such as converting a large list to a mapping, or building complex types).

Getters

staticconf.getters

Getters have the same properties as schema accessors, but do not use a class. See the module documentation for some limitations with this option.

Example

For this example we’ll use yaml configuration files. Given two files, a application.yaml:

pid: /var/run/app1.pid

storage_paths:
    - /mnt/storage
    - /mnt/nfs

min_date: 2014-12-12

groups:
    users:
        - userone
        - usertwo
    admins:
        - admin

And an overrides.yaml

max_files: 10

groups:
    users:
        - customuser

First load some configuration from a file. This is often done during the “startup” phase of an application, such as after argparse has completed (potentially where one of the command line args is a config filename). For a web application, this might happen during the initialization of the webapp.

import staticconf

app_config = 'application.yaml'
app_custom = 'overrides.yaml'

YamlConfiguration(app_config)
YamlConfiguration(app_custom, optional=True)

Now we’ve loaded up our application config, and overridden it with the data from overrides.yaml. overrides.yaml was optional, so if the file was missing there would be no error.

Next we’ll want to read these values at some point.

import staticconf

pid = staticconf.read_string('pid')

storage_paths = staticconf.read_list_of_string('storage_paths')

# This is the just the list of one user `customuser` since we loaded our
# `overrides.yaml` over the original list
# Also notice the key was flattened, so we use a dotted notation
users = staticconf.read_list_of_string('groups.users')

# Using doted notation allows us to preserve any part of the mapping
# structure, so in this case, the admins from `application.yaml` are
# still there
admins = staticconf.read_list_of_string('groups.admins')

# We can also read other types. In our config this was a string, but we're
# reading a date, so we receive a datetime.date object
min_date = staticconf.read_date('min_date')

See staticconf.config.ConfigFacade for examples of how to reload configuration on changes.

Logging

staticconf logs a message at INFO level when configuration is loaded with the message “Unexpected value in <namespace> configuration: ...”, with the list of unexpected values. Unexpected values are those that haven’t been registered by a staticconf.schema or staticconf.getter.

This message is used to debug configuration errors. If you’d like to disable it you can set the logging level for staticconf.config to WARN.

Reading dicts

By default staticconf flattens all the values it receives from the loaders. There are two ways to get dicts from a loader.

Disable Flatten

You can call loaders with the kwargs flatten=False.

Example:

YamlConfiguration(filename, flatten=False)

The disadvantage with this approach is that the entire config file will preserve its nested structure, so you lose out of the ability to easily merge and override configuration files.

Custom Reader

The second option is to represent a dict structures using lists of values (either a list of pairs or a list of dicts). This list can then be converted into a dict mapping using a custom getter/reader.

Below are some examples on how this is done. The staticconf.readers interface is used as an example, but the same can be done for the staticconf.getters and staticconf.schema interfaces by replacing staticconf.readers.build_reader() with staticconf.getters.build_getter() or staticconf.schema.build_value_type().

Create a reader which translates a list of dicts into a mapping

from staticconf import validation, readers

def build_map_from_key_value(item):
    return item['key'], item['value']

read_mapping = readers.build_reader(
    validation.build_map_type_validator(build_map_from_key_value))

my_mapping = read_mapping('config_key_of_a_list_of_dicts')

Create a reader which translates a list of pairs into a mapping

from staticconf import validation, readers

read_mapping = readers.build_reader(
    validation.build_map_type_validator(tuple))

my_mapping = read_mapping('config_key_of_a_list_of_pairs')

Create a reader from translates a list of complex dicts into a mapping

from staticconf import validation, readers

def build_map_from_dicts(item):
    return item.pop('name'), item

read_mapping = readers.build_reader(
    validation.build_map_type_validator(build_map_from_dicts))

my_mapping = read_mapping('config_key_of_a_list_of_dicts')