# 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')