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