Configuration loading utilities for Python projects. Load settings from YAML with environment variable substitution, relative path resolution, encrypted passwords, and advanced merge operations.
pip install conf-loadersconf-loaders solves the problem of keeping default settings in Python code while allowing local overrides via YAML files. It provides:
- YAML settings loading with automatic environment variable substitution (
${VAR}or${VAR:-default}) - Relative path resolution — relative paths in YAML are resolved to absolute paths automatically
- Update operations — extend lists, append items, update dicts without replacing the whole structure
- Encrypted passwords — load AES/RSA encrypted secrets from separate YAML files
- Environment variable parsing — parse prefixed environment variables into nested dict structures
# settings.py — default values
DEBUG = False
ALLOWED_HOSTS = ["localhost"]
DATABASES = {
"default": {"ENGINE": "django.db.backends.sqlite3"}
}# config.yaml — local overrides
DEBUG: true
ALLOWED_HOSTS@extend:
- "my-domain.com"
DATABASES@update:
default:
NAME: "/path/to/db.sqlite3"# At the end of settings.py
from conf_loaders.utils.settings_load import load_settings_from_yaml
load_settings_from_yaml("config.yaml", object_to_update=locals())After loading, locals() contains merged values from both sources.
YAML files are pre-processed with varsubst before parsing:
DB_HOST: "${DB_HOST:-localhost}"
DB_PORT: "${DB_PORT:-5432}"String values prefixed with ./ or ../ are resolved to absolute paths using base_dir:
# Relative to the directory passed as base_dir
LOG_PATH: "../../data/logs/app.log"
MODEL_DIR: "./models"The prefix is the marker. A value that merely contains a slash is left untouched, so folder labels and identifiers are never mistaken for paths:
SCRIPT_ROOTS: ["scripts/common/"] # stays as is
MODEL: "soica/qwen3.5-9b" # stays as isSince 0.6.2. Earlier versions resolved any slash-containing value.
To disable resolution for a specific value, prefix it with !:
# Will remain "./relative/path" without resolution
PATTERN: "!./relative/path"Path resolution is recursive — works inside nested dicts and lists.
U can disable all these operations by setting base_dir=....
By default, a YAML key replaces the entire Python value. Use @operation suffixes to merge instead:
ALLOWED_HOSTS@extend:
- "api.example.com"
- "app.example.com"ALLOWED_HOSTS@append: "admin.example.com"DATABASES@update:
default:
HOST: "postgres.internal"Useful when other modules hold references to the list/dict:
# Clears SCRIPT_ROOTS and fills with new values,
# but the list object in memory stays the same
SCRIPT_ROOTS@bind:
- scripts/common/
- scripts/custom/DEBUG@setdefault: falseIf DEBUG is already defined in settings.py, the YAML value is ignored.
| Operation | Effect | Requires existing var |
|---|---|---|
| (no suffix) | Replace entirely | No |
@setdefault |
Set only if undefined | No |
@bind |
Clear + refill, keep object reference | No |
@append |
Append one item to list | Yes |
@try-append |
Append one item, create list if missing | No |
@extend |
Extend list with multiple items | Yes |
@try-extend |
Extend list, create if missing | No |
@update |
Merge dict keys | Yes |
@try-update |
Merge dict keys, create dict if missing | No |
Operations support dot-notation for nested structures:
# Sets DATABASES["default"]["PORT"] = 5433
DATABASES.default.PORT: 5433
# Appends to nested list
DATABASES.default.OPTIONS@append: "sslmode=require"Store sensitive values in a separate encrypted file:
from conf_loaders.utils.settings_load import load_passwords_from_yaml
load_passwords_from_yaml(
passwords_file="secrets.yaml",
secret_key_file=".secret.key",
object_to_update=locals(),
)# secrets.yaml
DB_PASSWORD: "AES$salt$ciphertext"
API_KEY: "AES$salt$ciphertext"Supported algorithms: AES, RSA.
Load prefixed environment variables into settings:
from conf_loaders.utils.settings_load import load_vars_from_env
load_vars_from_env(
object_to_update=locals(),
prefix="MYAPP_", # Only vars starting with MYAPP_
secret_key_file=".secret.key", # Auto-decrypt encrypted values
)With MYAPP_DB_HOST=localhost and MYAPP_DB_PORT_NUMBER=5432 and MYAPP_USE_PROXY_FLAG=1 in environment:
# Results in:
DB_HOST = "localhost"
DB_PORT = 5432
USE_PROXY = TrueDouble underscores create nested dicts: MYAPP_CACHE__REDIS__HOST=localhost → CACHE["REDIS"]["HOST"] = "localhost".
load_settings_from_yaml(
path: str | Path,
object_to_update: MutableMapping[str, Any],
base_dir: str | Path | None = None,
show_update_errors: bool = True,
)- path — path to YAML file
- object_to_update — dict-like object to update, usually
locals() - base_dir — directory for resolving relative paths;
Nonedisables resolution - show_update_errors — whether to print values in error messages
load_passwords_from_yaml(
passwords_file: str | Path,
secret_key_file: str | Path,
object_to_update: MutableMapping[str, Any],
password_sep: str = "$",
) -> Dict[str, Dict[str, str]]Returns a dict of successfully decrypted variables with their algorithms and keys.
load_vars_from_env(
object_to_update: MutableMapping[str, Any],
secret_key_file: str | Path | None = None,
password_sep: str = "$",
prefix: str = "TRANSLATE_",
)save_settings_debug(settings: Mapping[str, Any])If settings contains SAVE_SETTINGS_DEBUG key with a file path, dumps all settings to that file as JSON (for debugging).
conf_loaders.utils.docrender_utils.files provides helpers for common file operations:
read_yaml(path)/write_yaml(path, data)— YAML I/Oread_json(path)/write_json(path, data)— JSON I/Oread_text(path)/write_text(path, text)— Text I/Omkdir(path)/mkdir_of_file(path)/mkparents(path)— Directory creationcopy_file(src, dst)/move_file(src, dst)— File operationstouch(path)/rmdir(path)— Basic file ops
_resolve_variablesrecursion hotfix
save_settings_debug:- if
base_dir=Nonethen it will be extracted fromsettings.BASE_DIR
- if
load_settings_from_yaml:- relative path resolution can be disabled at all (even
!removing) usingbase_dir=... - relative values start from
!provide warning ifbase_dir=None resolve_content_from_env=Falsedisables file content resolving from environment
- relative path resolution can be disabled at all (even
save_settings_debug:base_diroption (like inload_settings_from_yaml) provides supplement path resolution for debugging purposes
- Relative path resolution is now recursive — works inside nested dicts and lists, not just top-level values
- Values starting with
!are excluded from path resolution
- Initial release with YAML loading,
varsubst, update operations, encrypted passwords, and env var parsing