While investigating support for compound keys I came to the conclusion that the best way to support them was to rewrite. I’m not a fan of the impulsive “it’s broken we have to rewrite it” clan, but sometimes it’s a necessary thing to do. The previous code didn’t handle dependency cycles gracefully, and the type handling was verbose. I believe I’ve fixed these issues now, and 0.5 will be the baseline for future work.

0.5 wasn’t just about better implementation of the existing features. It also introduces some new features like the compound keys mentioned earlier. One of the features I’ve added is proper support for __getattr__. The previous support was limited to top-level keys, but the keys can now descend the configuration to any level.

config_json = """
{
    "person": {
        "name": "John",
        "address": {
            "town": "Dublin"
        }
    }
}
"""
config = Config(config_json, restricted=False)

# in 0.4 you could do config.person['name']
"John" == config.person.name

# you can also use __getitem__
"Dublin" == config['person']['address']['town']

0.5 supports the use of compound variable keys to reflect the __getattr__ functionality.

...
    "nick": "${person.name} - ${person.address.town}"
...
"John - Dublin" == config.nick

Expression support is now explicit, to give more control to the configuration author. Expressions can be nested.

{% raw %}
config_json = """ 
{
    "a": 10,
    "b": 20,
    "c": "{{ {{ ${a} * 5 }} + ${b} }}"
}
"""
config = Config(config_json)
10 == config['a']
20 == config['b']
70 == config['c']
{% endraw %}

Detection of variable dependency cycles is more complete, and I haven’t managed to sneak anything past it yet (I haven’t been trying any harder than the unit tests though). The check is performed up-front when parsing the configuration.

config_json = """
{
    "a": "${c}",
    "b": {
        "d": {
            "e": "${a}"
        }
    },
    "c": "${b}"
}
"""
try:
    config = Config(config_json)
except KeyError:
    """Cycle detected"""

That’s about it I think. The release is available on PyPI and GitHub.