API Reference

The API of upsilonconf consists of mainly two parts. The first part make up the main functionality: configuration objects with a convenient interface. The second part can be seen as the included batteries: functions for conveniently storing and retrieving configuration objects.

This package is very much a work in progress. I have a general idea of what the main interface could look like. The I/O functionality is more of a first draft at this point. This documentation also serves as a tool to guide the interface design. Therefore, I am open to any comments or suggestions to improve the interface.

Configuration Objects

Upsilonconf offers a few different configuration classes. Mutable and immutable configuration types are provided by PlainConfiguration and FrozenConfiguration, respectively. The CarefulConfiguration is a less pythonic configuration that does not allow overwriting values by default.

All of these configuration types share a common interface. This interface is provided by ConfigurationBase. Any class that inherits from this base class enables the features below. For the examples, we will use PlainConfiguration, but these features are available in any of the provided implementations.

>>> from upsilonconf import PlainConfiguration as Config

Constructing Configurations

Configurations objects can be created by the constructor. The configuration entries can be specified by means of keyword arguments.

>>> conf = Config(key="value")
>>> print(conf)
{key: value}

Also a dict (or another mapping) can be directly passed to the constructor. This is possible by the unpacking syntax in Python.

>>> conf = Config(**{"key": "value"})
>>> print(conf)
{key: value}

Alternatively, the from_dict() method can be used. This method allows you to replace patterns in keys, which can be useful to make keys valid attribute names.

>>> conf = Config.from_dict({"a key": "value"}, key_mods={" ": "_"})
>>> print(conf)
{a_key: value}

Any dict (or other mapping type) values will be converted to configuration objects. This makes it easier to create hierarchical configuration object.

>>> conf = Config(sub={"key": "value"})
>>> print(conf)
{sub: {key: value}}

Configuration Attributes

Each value in the configuration is also an attribute in the object. The corresponding key in the configuration is the attribute-name.

>>> conf = Config(key="value")
>>> conf.key
'value'

If a key is not a valid attribute-name in Python, it will not be accessible using the convenient dot-syntax. However, values are still accessible using getattr.

>>> conf = Config(**{"bad key": "value"})
>>> getattr(conf, "bad key")
'value'

Indexing Configurations

Every key in the configuration can also be used as an index for the object. This can be especially useful to avoid more verbose getattr calls.

>>> conf = Config(**{"a key": "value"})
>>> conf["a key"]
'value'

Tuple indices can be used to get values in hierarchical configuration objects.

>>> conf = Config(**{"sub-conf": {"key": "value"}})
>>> conf["sub-conf", "key"]
'value'

It is also possible to use dot-string indices for hierarchical configurations.

>>> conf = Config(**{"sub-conf": {"key": "value"}})
>>> conf["sub-conf.key"]
'value'

Merging Configurations

Configurations can be merged by means of the “or”-operator, |.

>>> conf1 = Config(key1="foo")
>>> conf2 = Config(key2="bar")
>>> print(conf1 | conf2)
{key1: foo, key2: bar}

Merging is not commutative, so the order of configurations is important.

>>> conf1 = Config(key="val", key1="foo")
>>> conf2 = Config(key="value", key2="bar")
>>> print(conf1 | conf2)
{key: value, key1: foo, key2: bar}
>>> print(conf2 | conf1)
{key: val, key2: bar, key1: foo}

Configurations can also be merged with a dict (or other mapping).

>>> conf1 = Config(key1="foo")
>>> conf2 = {"key2": "bar"}
>>> print(conf1 | conf2)
{key1: foo, key2: bar}
>>> print(conf2 | conf1)
{key2: bar, key1: foo}

Converting Configurations

There are different ways to convert configuration objects to a dict again. Non-hierarchical objects can directly be wrapped by the dict constructor.

>>> conf = Config(key="value")
>>> dict(conf)
{'key': 'value'}

This does not work recursively, however, causing issues with hierarchical objects. This is where the to_dict() method can be useful.

>>> conf = Config(sub={"key": "value"})
>>> dict(conf)
{'sub': PlainConfiguration(key='value')}
>>> conf = Config(sub={"key": "value"})
>>> conf.to_dict()
{'sub': {'key': 'value'}}

Additionally, this method makes it possible to convert a hierarchical objects to a non-nested dict.

>>> conf = Config(sub={"key": "value"})
>>> conf.to_dict(flat=True)
{'sub.key': 'value'}

It is also possible to replace patterns in keys, similar to from_dict().

Overview

ConfigurationBase

Interface for configuration objects.

PlainConfiguration

Mutable configuration.

FrozenConfiguration

Immutable configuration.

CarefulConfiguration

Configuration with overwrite protection.

I/O Utilities

Configurations are commonly saved in some file to make them accessible outside of the program. Therefore, upsilonconf provides some convenience function for reading and writing files. These functions are load_config() and save_config(). These functions make use of a simple, but extensible I/O system that is built on top of the io.ConfigIO interface. There is also config_from_cli() to collect configuration values from the CLI.

Convenience Functions

load_config

Load configuration from a file.

save_config

Write a configuration data to disk.

config_from_cli

Construct a configuration from a Command Line Interface.

I/O System

io.ConfigIO

Interface for reading/writing configurations to/from files.

io.JSONIO

IO for reading/writing JSON files.

io.YAMLIO

IO for reading/writing YAML files.

io.TOMLIO

IO for reading/writing TOML files.

io.DirectoryIO

IO for reading/writing configs from a directory.

io.ExtensionIO

IO for selecting IOs based on file extensions.

io.FlexibleIO

IO for selecting IOs based on file extensions.