load_configuration()¶
- audeer.load_configuration(default_config_file, user_configs=None, *, env_prefix=None, types=None, validate=None, tracking=None)[source]¶
Load configuration from files and environment variables.
Configuration values are collected from, in order of increasing precedence:
default_config_file, e.g. the configuration file shipped with a packageuser_configs, applied in the given order (a later entry overrides an earlier one)environment variables, if
env_prefixis given
Configuration files or mappings are deep-merged: nested mappings are merged key by key, so a section in a user file only overrides the keys it defines and keeps the remaining keys from the default. Any non-mapping value (including a list) replaces the previous value as a whole.
Environment variables are matched against the upper-cased configuration keys, prefixed with
env_prefixand an underscore, e.g. the keycache_rootis overridden by<env_prefix>_CACHE_ROOT. Keys inside nested mappings are addressed by joining the levels with__, e.g.model.deviceis overridden by<env_prefix>_MODEL__DEVICE. A whole nested mapping can instead be replaced by a single variable holding a JSON object, e.g.<env_prefix>_MODEL='{"device": "cpu"}'; the object replaces the mapping as a whole (keys it omits are dropped) and may introduce keys not present in the files. Each value of the object must match the type of the default value it replaces (an integer is accepted for a float default), otherwise aValueErroris raised; introduced keys and keys whose default isNonekeep their JSON type. Nested variables are applied afterwards and therefore take precedence, e.g.<env_prefix>_MODEL__DEVICEoverridesdevicefrom<env_prefix>_MODEL. The value of an environment variable is converted to the type of the corresponding default value:strvalues are used as they are,int/floatvalues are cast, andlist/dictvalues are parsed as JSON. Aboolvalue isTruefor"1","true","yes","on"(case insensitive) andFalsefor any other value; a boolean is therefore never rejected, whereasint,floatand JSON conversions raise aValueErroron invalid input. A default value of any other type (e.g. a date parsed from YAML) cannot be overridden and raises aValueErroras well. Only keys already present in the merged configuration, whether from a file or a mapping, can be overridden by environment variables. Only string keys are matched; a non-string key (e.g. a numeric YAML key) is left untouched.A default value of
Nonecarries no type, so an environment variable would be kept as a string. Usetypesto declare the intended type for such keys, or to override the inferred type of any key.typesmirrors the (possibly nested) structure of the configuration; an entry that does not match a configuration key raises aValueError.Missing or empty configuration files are skipped.
Reading configuration files requires
pyyaml, which is installed when depending onaudeer[yaml].- Parameters:
default_config_file (
str) – path to default configuration file. The file does not have to existuser_configs (
str|Mapping|Sequence[str|Mapping] |None) – path(s) to user configuration file(s), or already parsed mapping(s), applied in the given order. Files do not have to exist, and a given mapping is not modifiedenv_prefix (
str|None) – prefix of environment variables used to override configuration values. IfNone, environment variables are ignoredtypes (
Mapping|None) – mapping that declares the type of configuration values, mirroring the (possibly nested) configuration structure. Used to cast environment variable overrides for keys whose default value isNone, or to override the type inferred from the default value. Supported types arebool,int,float,str,list,dictvalidate (
Callable[[dict],None] |None) – callable that receives the merged configuration dictionary and raises an error if it is invalid. It is applied once, after files and environment variables are mergedtracking (
MutableMapping|None) – mutable mapping that records, for each configuration key, which layer set its effective value:f"file:{path}"fordefault_config_fileor auser_configsentry that provided a file,"mapping[<i>]"for theuser_configsentry at index<i>that provided an already parsed mapping (indices count every entry of the sequence, file or mapping alike), orf"env:{name}"naming the exact environment variable (e.g."env:PKG_MODEL__DEVICE") for an environment variable override. A whole-section JSON environment variable labels every key it sets; a more specific nested variable re-labels only the key it overrides. Entries are added totrackingin place. Reuse the same mapping across several calls to accumulate their entries. IfNone, no tracking is performed
- Return type:
- Returns:
merged configuration dictionary
- Raises:
ImportError – if
pyyamlis not installed, and a configuration file existsValueError – if a configuration file does not contain a mapping of key-value pairs
ValueError – if an environment variable cannot be converted to the type of the corresponding default value, or that type is not supported
ValueError – if a type declared in
typesis not a class, or not one of the supported typesValueError – if
types, or atypesentry for a nested section, is not a mappingValueError – if a
typesentry does not match any configuration keyValueError – if
trackingis not a mutable mapping
Examples
>>> import tempfile >>> config_file = audeer.path(tempfile.mkdtemp(), "config.yaml") >>> with open(config_file, "w") as file: ... _ = file.write("cache_root: ~/cache\n") >>> audeer.load_configuration(config_file) {'cache_root': '~/cache'}
A user configuration can also be given as an already parsed mapping.
>>> config_file = audeer.path(tempfile.mkdtemp(), "config.yaml") >>> with open(config_file, "w") as file: ... _ = file.write("model:\n device: cpu\n lora: false\n") >>> audeer.load_configuration(config_file, {"model": {"device": "cuda"}}) {'model': {'device': 'cuda', 'lora': False}}
A key that defaults to
Nonehas no inferred type, so declare it intypes.>>> import os >>> config_file = audeer.path(tempfile.mkdtemp(), "config.yaml") >>> with open(config_file, "w") as file: ... _ = file.write("hosts: null\n") >>> os.environ["APP_HOSTS"] = '["host1", "host2"]' >>> audeer.load_configuration( ... config_file, env_prefix="APP", types={"hosts": list} ... ) {'hosts': ['host1', 'host2']} >>> del os.environ["APP_HOSTS"]
trackingrecords which layer set each key’s effective value.>>> config_file = audeer.path(tempfile.mkdtemp(), "config.yaml") >>> with open(config_file, "w") as file: ... _ = file.write("model:\n device: cpu\n lora: false\n") >>> os.environ["PKG_MODEL__DEVICE"] = "cuda" >>> tracking = {} >>> audeer.load_configuration(config_file, env_prefix="PKG", tracking=tracking) {'model': {'device': 'cuda', 'lora': False}} >>> tracking {'model': {'device': 'env:PKG_MODEL__DEVICE', 'lora': 'file:...config.yaml'}} >>> del os.environ["PKG_MODEL__DEVICE"]