Usage

To use LayeredConfig in a project:

from __future__ import print_function
from layeredconfig import LayeredConfig

Also, import any Configuration sources you want to use. It’s common to have one source for code defaults, one configuration file (INI file in this example), one using environment variables as source, and one using command lines:

from layeredconfig import Defaults, INIFile, Environment, Commandline

Each configuration source must be initialized in some way. The Defaults source takes a dict, possibly nested:

from datetime import date, datetime
mydefaults = Defaults({'home': '/tmp/myapp',
                       'name': 'MyApp',
                       'dostuff': False,
                       'times': 4,
                       'duedate': date(2014, 10, 30),
                       'things': ['Huey', 'Dewey', 'Louie'],
                       'submodule': {
                           'retry': False,
                           'lastrun': datetime(2014, 10, 30, 16, 40, 22)
                           }
                       })

A configuration source such as INIFile takes the name of a file. In this example, we use a INI-style file.

myinifile = INIFile("myapp.ini")

Note

LayeredConfig uses the configparser module, which requires that each setting is placed within a section. By default, top-level settings are placed within the [__root__] section.

In this example, we assume that there is a file called myapp.ini within the current directory with the following contents:

[__root__]
home = /usr/home/staffan/.myapp

[submodule]
retry = True
lastrun = 2014-10-31 16:40:22

The Environment source uses environment variables as settings. Since the entire environment is not suitable to use as a configuration, use of this source requires that a prefix is given. Only environment variables starting with this prefix are used. Furthermore, since the name of environment variable typically uses uppercase, they are by default lowercased by this source. This means that, in this example, the value of the environmentvariable MYAPP_HOME will be available as the configuration setting home.

env = {'MYAPP_HOME': 'C:\\Progra~1\\MyApp',
       'MYAPP_SUBMODULE_RETRY': 'True'}
myenv = Environment(env, prefix="MYAPP_")

Finally, the Commandline processes the contents of sys.argv and uses any parameter starting with -- as a setting, such as --home=/Users/staffan/Library/MyApp. Arguments that do not match this (such as positional arguments or short options like -f) are made available through the rest property, to be used with eg. argparse.

mycmdline = Commandline(['-f', '--home=/opt/myapp', '--times=2', '--dostuff'])
rest = mycmdline.rest

Now that we have our config sources all set up, we can create the actual configuration object:

cfg = LayeredConfig(mydefaults,
                    myinifile,
                    myenv,
                    mycmdline)

And we use the attributes on the config object to access the settings:

print("%s starting, home in %s" % (cfg.name, cfg.home))

Precedence

Since several sources may contain a setting, A simple precedence system determines which setting is actually used. In the above example, the printed string is "MyApp starting, home in /opt/myapp". This is because while name was specified only by the mydefaults source, home was specified by source with higher predecence (mycmdline). The order of sources passed to LayeredConfig determines predecence, with the last source having the highest predecence.

Config sources

Apart from the sources used above, there are classes for settings stored in JSON files, YAML files and PList files. Each source can to varying extent be configured with different parameters. See Available sources for further details.

You can also use a single source class multiple times, for example to have one system-wide config file together with a user config file, where settings in the latter override the former.

It’s possible to write your own ConfigSource-based class to read (and possibly write) from any concievable kind of source.

Typing

The values retrieved can have many different types – not just strings.

delay = date.today() - cfg.duedate  # date
if cfg.dostuff: # bool
    for i in range(cfg.times):  # int
        print(", ".join(cfg.things))  # list

If a particular source doesn’t contain intrinsic typing information, other sources can be used to find out what type a particular setting should be. LayeredConfig converts the data automatically.

Note

string are always str objects, (unicode in python 2). They are never bytes objects (str in python 2)

Subsections

It’s possible to divide up settings and group them in subsections.

subcfg = cfg.submodule  
if subcfg.retry:
    print(subcfg.lastrun.isoformat())

Cascading

If a particular setting is not available in a subsection, LayeredConfig can optionally look for the same setting in parent sections.

cfg = LayeredConfig(mydefaults, myinifile, myenv, mycmdline, cascade=True)
subcfg = cfg.submodule
print(subcfg.home)

Modification and persistance

It’s possible to change a setting in a config object. It’s also possible to write out the changed settings to a config source (ie. configuration files) by calling write()

subcfg.lastrun = datetime.now()
LayeredConfig.write(cfg)