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¶
Interface for configuration objects. |
|
Mutable configuration. |
|
Immutable configuration. |
|
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 configuration from a file. |
|
Write a configuration data to disk. |
|
Construct a configuration from a Command Line Interface. |
I/O System¶
Interface for reading/writing configurations to/from files. |
|
IO for reading/writing JSON files. |
|
IO for reading/writing YAML files. |
|
IO for reading/writing TOML files. |
|
IO for reading/writing configs from a directory. |
|
IO for selecting IOs based on file extensions. |
|
IO for selecting IOs based on file extensions. |