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, as well as etcd stores. 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.
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 if the cascade option is set.
cfg = LayeredConfig(mydefaults, myinifile, myenv, mycmdline, cascade=True)
subcfg = cfg.submodule
print(subcfg.home) # prints '/opt/myapp', from Commandline source root section