Configurations
In Central, a configuration is a Config
object which is responsible to fetch the configuration data from an external source.
Central has many type of Config
builtin like FileConfig
, UrlConfig
, EnvironmentConfig
, S3Config
and more.
Central configuration keys are case insensitive.
Central treats keys with None
value as missing keys.
ChainConfig
This configuration groups multiple Config
together to create a single view.
The ChainConfig
does not merge the configurations but instead treats them as overrides so that a key existing in a configuration supersedes the same key in a previous configuration in the chain.
The chain works in reverse order, that means the keys from the last configuration in the chain overrides the previous one.
# main.py
from central.config import ChainConfig, MemoryConfig
defaults = MemoryConfig()
defaults.set('host', 'localhost')
defaults.set('port', 12345)
overrides = MemoryConfig()
overrides.set('host', 'dev.example.com')
overrides.set('timeout', 10)
config = ChainConfig(defaults, overrides)
config.load()
print(config.get('host'))
print(config.get('port'))
print(config.get('timeout'))
python main.py
>> dev.example.com
>> 12345
CommandLineConfig
This configuration loads the configuration data from the command line args.
# main.py
from central.config import CommandLineConfig
config = CommandLineConfig()
config.load()
timeout = config.get('timeout', default=10)
print(timeout)
python main.py timeout=100
>> 100
Dashed argument is also supported.
python main.py --timeout 100
>> 100
Default value is used if argument is missing.
python main.py
>> 10
EnvironmentConfig
This configuration loads the configuration data from the environment variables.
# main.py
from central.config import EnvironmentConfig
config = EnvironmentConfig()
config.load()
timeout = config.get('timeout', default=10)
print(timeout)
export TIMEOUT=100
python main.py
>> 100
Default value is used if environment variable is missing.
python main.py
>> 10
FileConfig
This configuration loads the configuration data from a config file. Central supports JSON, TOML, YAML and INI file formats.
TOML requires the external library toml and you must installed it by yourself (pip install toml)
YAML requires the external library PyYAML and you must installed it by yourself (pip install PyYAML)
The content of the file is determined by the file name extension.
# config.yaml
database:
host: localhost
port: 1234
# main.py
from central.config.file import FileConfig
config = FileConfig('config.yaml')
config.load()
print(config.get('database.host'))
python main.py
>> localhost
Using a custom reader
By default, FileConfig
finds the best reader for the file name using the file name extension, Central has readers for JSON, TOML, JSON and INI.
You can also specific your own reader or you can force one specific reader for a file name.
# app.cfg
database:
host: localhost
port: 1234
# main.py
from central.config.file import FileConfig
from central.readers import YamlReader
config = FileConfig('app.cfg', reader=YamlReader())
config.load()
print(config.get('database.host'))
python main.py
>> localhost
Loading cascaded configuration files
A FileConfig
supports multiple configuration files, that is also called cascaded configuration files.
For example, you want Central to load an environment specific configuration file config.prod.json to override the default config.json. To do that, you can define this key at the end of the config.json file:
{
"@next": "config.${ENV}.json"
}
@next
is a special key to define next file to load. ENV
is a variable that will expand to value returned from environment variables.
Full example:
# config.json
{
"database": {
"host": "localhost",
"port": 1234
},
"@next": "config.${ENV}.json"
}
# config.prod.json
{
"database": {
"host": "prod.db"
}
}
# main.py
from central.config.file import FileConfig
config = FileConfig('config.json')
config.load()
print(config.get('database.host'))
print(config.get('database.port'))
export ENV=prod
python main.py
>> prod.db
>> 1234
MemoryConfig
This configuration allows the user to set custom keys at runtime.
# main.py
from central.config import MemoryConfig
config = MemoryConfig()
config.set('timeout', 100)
timeout = config.get('timeout')
print(timeout)
python main.py
>> 100
A MemoryConfig
in combination with a ChainConfig
can also be used to set default values.
# main.py
from central.config import ChainConfig, FileConfig, MemoryConfig
defaults = MemoryConfig()
defaults.set('host', 'localhost')
defaults.set('port', 12345)
config = ChainConfig(defaults, FileConfig('config.json'))
config.load()
print(config.get('host'))
print(config.get('port'))
# config.json
{
"host": "dev.example.com",
}
python main.py
>> dev.example.com
>> 12345
MergeConfig
This configuration merge multiple Config
into a single configuration data, in case of key collision last-match wins.
It makes a deep merge so configurations with subtrees are also merged.
# base.json
{
"database": {
"host": "localhost",
"port": 1234
}
}
# dev.json
{
"timeout": 100,
"database": {
"host": "dev.example.com"
}
}
Merging both configs, the result will be:
# merged
{
"timeout": 100,
"database": {
"host": "dev.example.com",
"port": 1234
}
}
# main.py
from central.config import MergeConfig
from central.config.file import FileConfig
config = MergeConfig(FileConfig('base.json'), FileConfig('dev.json')
config.load()
print(config.get('timeout'))
print(config.get('database'))
python main.py
>> 100
>> {"database": {"host": "dev.example.com", "port": 1234}}
If your configuration is a simple key/value without subtrees, it is recommended to use ChainConfig
instead of MergeConfig
because ChainConfig
consumes less resources.
PrefixedConfig
This configuration allows to view into another Config
for keys starting with a specified prefix.
# main.py
from central.config import MemoryConfig, PrefixedConfig
config = MemoryConfig(data={'production.timeout': 100})
config = PrefixedConfig('production', config)
timeout = config.get('timeout')
print(timeout)
python main.py
>> 100
There is also a shortcut to create a PrefixedConfig
object based on another Config
object.
# main.py
from central.config import MemoryConfig
config = MemoryConfig(data={'production.timeout': 100}).prefixed('production')
timeout = config.get('timeout')
print(timeout)
python main.py
>> 100
ReloadConfig
This configuration reloads its sub-configuration that is triggered by its scheduler, that is useful when you need to update an application's configuration at runtime and you do not want to restart the application to have a config take effect.
Gone are the days of needing to restart a server to have a config take effect, Central can read an update to a configuration source while running.
Optionally you can provide a function to ReloadConfig
to run each time a change occurs.
from central.config import ReloadConfig
from central.config.file import FileConfig
from central.schedulers import FixedIntervalScheduler
scheduler = FixedIntervalScheduler(60) # 1 minute in seconds
file_config = FileConfig('config.json')
config = ReloadConfig(file_config, scheduler)
config.load()
@config.on_updated
def config_updated():
print('Config updated')
The code above may look a bit verbose, you may prefer to use the shortcut reload_every
, you can use reload_every on any Config
object.
from central.config.file import FileConfig
config = FileConfig('config.json').reload_every(60) # 1 minute in seconds
config.load()
@config.on_updated
def config_updated():
print('Config updated')
UrlConfig
This configuration loads the configuration data from the response of an URL. The format of the content is determined by the Content-Type
header or by a file name extension.
Central supports JSON, TOML, YAML and INI as content formats.
TOML requires the external library toml and you must installed it by yourself (pip install toml)
YAML requires the external library PyYAML and you must installed it by yourself (pip install PyYAML)
from central.config.url import UrlConfig
config = UrlConfig('http://www.example.com/appname/config.json')
config.load()
If the URL does not contain a file name extension, the response must contain a Content-Type
header specifying the format of the content.
UrlConfig
can understand severals Content-Type values, such as: application/json
, application/x-json
, text/json
, text/vnd.json
and so on.
from central.config.url import UrlConfig
config = UrlConfig('http://www.example.com/appname/config')
config.load()
The URL supports string interpolation, that means you can use environment variables in it.
from central.config.url import UrlConfig
config = UrlConfig('http://www.example.com/appname/${ENV}/config')
config.load()
Using a custom reader
By default, UrlConfig
finds the best reader based on the Content-Type
of the response from the URL, Central has readers for JSON, TOML, JSON and INI.
You can also specific your own reader or you can force one specific reader for an URL.
from central.config import UrlConfig
from central.readers import JsonReader
config = UrlConfig('http://example.com/appname/config', reader=JsonReader())
config.load()
Implementing your own Config
If you plan to implement a new Config
that holds keys and values, Central provides a base config class called BaseDataConfig
that handles most of work for you.
from central.config.core import BaseDataConfig
from central.structures import IgnoreCaseDict
class MyConfig(BaseDataConfig):
def load(self):
# you must use an IgnoreCaseDict which is a dict
# but with case insensitive.
data = IgnoreCaseDict()
# here you load the keys/values from
# the external source.
data['key1'] = 'value 1'
...
...
self._data = data