Config API

This module provides a structure for a configurable application. You can create a customized Config object passing any ConfigSource you need (like JSONSource and ENVSource) and a config schema.

Here’s an example of a simple app that uses this module:

schema.json:

{
    "string": {"doc": "String config", "format": "string", "default": "DEFAULT"},
    "integer": {"doc": "Integer config", "format": "int", "default": 1},
    "float": {"doc": "Float config", "format": "float", "default": 1.1},
    "list": {"doc": "List config", "format": "list", "default": ["a", "b", "c"]},
    "bool": {"doc": "Bool config", "format": "bool", "default": true},
    "nested.string": {"doc": "String config", "format": "string", "default": "DEFAULT"},
}

/etc/myapp/config.json:

{
    "float": 2.2,
    "string": "loaded from json",
    "nested": {
        "string": "loaded from json"
    }
}

/etc/myapp/.env:

MYAPP_CONF_LIST="e,n,v"
MYAPP_CONF_BOOL="False"
MYAPP_CONF_NESTED_STRING="loaded from env"

app.py:

import json
from pathlib import Path
from projectutils.config import Config, ENVSource, JSONSource


# Setup includes loading the schema
# and defining some params used in sources.
with open("schema.json", "r") as fp:
    schema = json.load(fp)

envs_prefix = "MYAPP_CONF_"
configs_root = Path("/etc/myapp")

# Source definition dictates precedence.
# In this case ENV values will override JSON values.
sources = [
    JSONSource(configs_root / "config.json"),
    ENVSource(envs_prefix, configs_root),
]

# Load config
config = Config(schema, sources)


config.get("integer")
# 1

config.get("float")
# 2.2

config.get("bool")
# False

config.get("list")
# ['e', 'n', 'v']

config.get("nested.string")
# 'loaded from env'

config.get("string")
# 'loaded from json'

config.get("nested")
# {'nested': 'loaded from env'}
class projectutils.config.Config(schema: projectutils.config.ConfigSchema, sources: Optional[list[projectutils.config.ConfigSource]] = None)

Readonly config interface. Loads and merges config data from multiple sources.

get(path: str) int | float | str | bool | list | dict

Returns value for config path looking at, in order:

  • Data loaded from sources

  • Schema defaults

If path is not an exact match (e.g a.b when a.b.c and a.b.d exists) a deep tree containing all child values will be returned.

NameError is raised if path is not found.

class projectutils.config.ConfigSchema(data: dict)

Stores all available configs and their attributes (as ConfigDef s).

classmethod from_json_file(file: str | pathlib.Path) projectutils.config.ConfigSchema
keys() Iterable[str]

Returns all config names.

items() Iterable[tuple[str, projectutils.config.ConfigDef]]

Returns iterable of all configs in the schema.

defaults() dict

Returns deep tree of default configs.

class projectutils.config.ConfigSource

Base class for config sources.

All ConfigSource’s must implement _load_data() returning a 1-dimensional mapping of path: value.

items()
class projectutils.config.ConfigDef(path: str, doc: str, format: str, default: Any)

Stores Config Definition attributes for the ConfigSchema.

projectutils.config.FORMATTERS = {'bool': <function _bool_formatter>, 'float': <class 'float'>, 'int': <class 'int'>, 'list': <function _list_formatter>, 'string': <class 'str'>}

All available formaters.

class projectutils.config.JSONSource(file: pathlib.Path)

Loads configs from a JSON file.

The JSON data does not need to be flat. For example,

{"parent": {"child": "value"}}

is the same as

{"parent.child": "value"}
class projectutils.config.ENVSource(prefix: str, root: pathlib.Path, envs: Optional[list[str]] = None)

Loads configs from environment variables.

Also loads envvars from .env files in root.

Always tries to load .env. Additionally tries to loads .env.{name} files for all names in envs.

Only environment variables that start with prefix will be loaded.

class projectutils.config.LCDict

Lowercase dictionary for case-insensitive config names.

projectutils.config.flatten(dict_: dict, base_path: str = '') dict

Flattens a multi dimensional mapping into a 1-dimensional dict.

>>> flatten({"a": {"b": {"c": 1}, "d": 2}, "e": 3})
{'a.b.c': 1, 'a.d': 2, 'e': 3}
projectutils.config.closest_formatter(format: str)

Returns a formatter recommendation for error messages.

projectutils.config.get_formatter(format: str)

Returns a formatter given it’s name.

exception projectutils.config.InvalidConfigValue
exception projectutils.config.ConfigNotDefined