Skip to main content

YAML-based configuration module

Project description

Simple YAML-based configuration module, does what it says in the name.

There are generally MUCH more advanced and well-maintained modules for similar purpose, please see “Links” section below for a list with some of these.

Much simplier alternative can be (Python 3):

from collections import ChainMap

class DeepChainMap(ChainMap):
        def __init__(self, *maps):
                super(DeepChainMap, self).__init__(*filter(None, maps))
        def __getattr__(self, k):
                k_maps = list()
                for m in self.maps:
                        v = m.get(k)
                        if isinstance(v, dict): k_maps.append(v)
                        elif v is not None: return v
                return DeepChainMap(*k_maps)

import yaml
cli_opts = dict(connection=dict(port=6789))
file_conf_a, file_conf_b = None, yaml.safe_load('connection: {host: myhost, port: null}')
defaults = dict(connection=dict(host='localhost', port=1234, proto='tcp'))

conf = DeepChainMap(cli_opts, file_conf_a, file_conf_b, defaults)
print(, conf.connection.port, conf.connection.proto)
# Should print "myhost 6789 tcp", with changes to underlying maps propagating to "conf"


Basic syntax

Idea is the same as with yaml.safe_load() (yaml.load() was used before 14.06.5, see #2 for rationale behind the change) to load YAML configuration file like this one:

    # twisted endpoint syntax, see twisted.internet.endpoints.html#clientFromString
    nickname: testbot
      maxDelay: 30
  xattr_emulation: /tmp/xattr.db

But when you use resulting nested-dicts in code, consider the difference between config['core']['connection']['reconnect']['maxDelay'] and config.core.connection.reconnect.maxDelay.

Python dicts support only the first syntax, this module supports both. Assigning values through attributes is also possible.

Recursive updates (inheritance)

I find it useful to have default parameters specified in the same format as any configurable overrides to them - simple yaml file.

So consider this use-case:

import lya
cfg = lya.AttrDict.from_yaml('default.yaml')
for path in sys.argv[1:]: cfg.update_yaml(path)

(there is also AttrDict.update_dict method for recursive updates from dict)

With default configuration file from the previous section shipped along with the package as “default.yaml”, you can have simple override like:


And above code will result in the following config (which will be dumped as nicely-formatted yaml, as presented below):

    nickname: testbot
      maxDelay: 30
  xattr_emulation: /tmp/xattr.db


Similar to the above, but reversed, so result presented above can be produced by taking some arbitrary configuration (AttrDict) and rebasing it on top of some other (base) config:

import lya
base = lya.AttrDict.from_yaml('default.yaml')
for path in sys.argv[1:]:
  print 'Config:', path

Useful to fill-in default values for similar configuration parts (e.g. configuration for each module or component).

Key ordering

Keys in python dictionaries are unordered and by default, yaml module loses any ordering of keys in yaml dicts as well.

Strictly speaking, this is correct processing of YAML, but for most cases it is inconvenient when instead of clear section like this one:

    name: '#bot-central'
    server: testserver
  important_filter: '^important:'
  announce: '#important-news'
  debug_filter: '\(debug message\)'
  feedback: botmaster

…you have to resort to putting all the keys that need ordering into a list just to preserve ordering.

Especially annoying if you have to access these sections by key afterwards (and they should be unique) or you need to override some of the sections later, so list wrapper becomes completely artificial as it have to be converted into OrderedDict anyway.

YAML files, parsed from AttrDict.from_yaml and AttrDict.update_yaml methods have key ordering preserved, and AttrDict objects are based on OrderedDict objects, which provide all the features of dict and preserve ordering during the iteration like lists do.

There’s no downside to it - both ordered dicts and lists can be used as usual, if that’s more desirable.


Sometimes it’s useful to have nested configuration (like presented above) to be represented as flat list of key-value pairs.

Example usage can be storage of the configuration tree in a simple k-v database (like berkdb) or comparison of configuration objects - ordered flat lists can be easily processed by the “diff” command, tested for equality or hashed.

That is easy to do via AttrDict.flatten method, producing (from config above) a list like this one:

[ (('core', 'connection', 'endpoint'), ''),
  (('core', 'connection', 'nickname'), 'testbot'),
  (('core', 'connection', 'reconnect', 'maxDelay'), 30),
  (('core', 'xattr_emulation'), '/tmp/xattr.db') ]

Resulting list contains 2-value tuples - key tuple, containing the full path of the value and the value object itself.

A note on name clashes

Methods of AttrDict object itself, like ones listed above can clash with keys in the config file itself, in which case attribute access to config values is not possible, i.e.:

>>> a = lya.AttrDict(keys=1)
>>> a.keys
<bound method AttrDict.keys of AttrDict([('keys', 1)])>
>>> a['keys']

It’s kinda-deliberate that such basic methods (like the ones from built-in dict and listed above) are accessible by as usual attributes, though a bit inconsistent.

With any kind of dynamic keys, just use access by key, not by attr.

Lists and tuples inside AttrDicts

These two types (and their subclasses) are handled specially, transforming dict values inside to AttrDicts, and wrapping all these into same sequence type.

I.e. loading this YAML:

  - module: icmp
  - module: tcp
    filter: port 80
  - module: udp

Will produce AttrDict with a list of AttrDict’s inside, so that e.g. data.parsers[1].filter would work afterwards.

But flattening that won’t flatten lists, sets, tuples or anything but the dicts inside, and AttrDict.update() won’t “merge” these types in any way, just override previous ones for same key/path.

This is done for consistency and simplicity (same type for any subtree), but see github-issue-6 for more rationale behind it.

More stuff

Some extra data-mangling methods are available via AttrDict._ proxy object (that allows access to all other methods as well, e.g. a._.pop(k)).

  • AttrDict._.apply(func, items=False, update=True)

    Apply a function (f(v) or f(k, v) if “items” is set) to all values (on any level, depth-first), modifying them in-place if “update” is set.

  • AttrDict._.apply_flat(func, update=True)

    Same as “apply” above, but passes tuple of keys forming a path to each value (e.g. ('a', 'b', 'c') for value in dict(a=dict(b=dict(c=1)))) to f(k, v).

  • AttrDict._.filter(func, items=False)

    Same as “apply” above, but will remove values if filter function returns falsy value, leaving them unchanged otherwise.


import sys, lya

if len(sys.argv) == 1:
  print('Usage: {} [ config.yaml ... ]', file=sys.stderr)

cfg = lya.AttrDict.from_yaml(sys.argv[1])
for path in sys.argv[2:]: cfg.update_yaml(path)



It’s a regular package for Python 2.7+ and Python 3.0+ (though probably not well-tested there).

Best way to install it (from PyPI) would be to use pip:

% pip install layered-yaml-attrdict-config

If you don’t have it, use:

% easy_install pip
% pip install layered-yaml-attrdict-config

Alternatively (see also, pip install guide and python packaging tutorial):

% curl | python
% pip install layered-yaml-attrdict-config

Current-git version can be installed like this:

% pip install 'git+'

Note that to install stuff in system-wide PATH and site-packages, elevated privileges are often required. Use install --user, ~/.pydistutils.cfg or virtualenv to do unprivileged installs into custom paths.

Module uses PyYAML for processing of the actual YAML files, but can work without it, as long as you use any methods with “yaml” in their name, i.e. creating and using AttrDict objects like a regular dicts.

Project details

Release history Release notifications

This version
History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Filename, size & hash SHA256 hash help File type Python version Upload date
layered-yaml-attrdict-config-16.1.0.tar.gz (8.8 kB) Copy SHA256 hash SHA256 Source None Jan 19, 2016

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging CloudAMQP CloudAMQP RabbitMQ AWS AWS Cloud computing Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page