Config Util¶
oqtopus_util.config provides utility functions for loading YAML configuration files
with environment-variable substitution, masking sensitive values, and applying logging
configuration.
Functions¶
load_config¶
load_config(config_path) reads a YAML file and returns a Python dict.
Before the file is parsed by PyYAML, every ${VAR} and ${VAR, default} placeholder
is replaced at the string level, allowing YAML to apply its own type-casting rules.
| Placeholder | Environment variable set | Result |
|---|---|---|
${VAR} |
Yes | Raw value of VAR (string; YAML casts the surrounding context) |
${VAR} |
No | null (YAML None) |
${VAR, default} |
Yes | Raw value of VAR |
${VAR, default} |
No | default — YAML type-casts it (e.g. 10 → int, false → bool) |
${VAR, "default"} |
Yes | Raw value of VAR |
${VAR, "default"} |
No | "default" — YAML treats it as a string (quotes suppress type-casting) |
${VAR, 'default'} |
Yes | Raw value of VAR |
${VAR, 'default'} |
No | 'default' — same as double-quoted form |
Quoting the default value allows } to appear inside it. Without quotes, } would be
interpreted as the closing delimiter of the ${...} expression.
String values that start with ~ are expanded to absolute home-directory paths
(equivalent to Path(value).expanduser()).
Example config.yaml¶
server:
host: ${HOST, localhost}
port: ${PORT, 8080}
debug: ${DEBUG, false}
data_dir: ~/oqtopus/data
template: ${TEMPLATE, "/base/{key}/sub"}
import os
from oqtopus_util.config import load_config
os.environ["HOST"] = "example.com"
cfg = load_config("config/config.yaml")
# cfg["server"]["host"] == "example.com"
# cfg["server"]["port"] == 8080 (int, cast by YAML)
# cfg["server"]["debug"] is False (bool, cast by YAML)
# cfg["server"]["data_dir"] == "/home/user/oqtopus/data"
# cfg["server"]["template"] == "/base/{key}/sub" (str, quotes suppress casting)
mask_sensitive_info¶
mask_sensitive_info(config) returns a copy of the configuration dictionary where
sensitive values are replaced with "***MASKED***".
A key is treated as sensitive when its name (case-insensitive) contains any of the following substrings:
| Substring | Example keys that match |
|---|---|
key |
api_key, openai_api_key |
password |
password, db_password |
secret |
secret, client_secret |
token |
token, access_token |
The function processes nested dictionaries recursively, so sensitive values at any depth are masked.
Note
This function is designed for small configuration dicts loaded at startup and is not intended for large data sets.
Example: mask_sensitive_info¶
from oqtopus_util.config import mask_sensitive_info
config = {
"database": {
"host": "localhost",
"db_password": "s3cr3t",
},
"openai_api_key": "abc123",
"access_token": "tok456",
}
safe = mask_sensitive_info(config)
# safe["database"]["db_password"] == "***MASKED***"
# safe["openai_api_key"] == "***MASKED***"
# safe["access_token"] == "***MASKED***"
# safe["database"]["host"] == "localhost"
setup_logging¶
setup_logging(logging_cfg) applies a logging configuration dictionary by calling
logging.config.dictConfig.
The logging_cfg argument must be a Python dict.
A TypeError is raised if a non-dict value is passed.
Example: setup_logging¶
from oqtopus_util.config import load_config, setup_logging
logging_cfg = load_config("config/logging.yaml")
setup_logging(logging_cfg)
Example logging.yaml¶
version: 1
formatters:
default:
format: "%(asctime)s %(levelname)s %(message)s"
handlers:
console:
class: logging.StreamHandler
formatter: default
stream: ext://sys.stdout
root:
level: INFO
handlers: [console]
disable_existing_loggers: false
Complete Example¶
import logging
from oqtopus_util.config import load_config, mask_sensitive_info, setup_logging
# Load main configuration
config = load_config("config/config.yaml")
# Set up logging first
logging_cfg = load_config("config/logging.yaml")
setup_logging(logging_cfg)
logger = logging.getLogger(__name__)
# Log configuration without exposing secrets
safe_cfg = mask_sensitive_info(config)
logger.info("Loaded configuration: %s", safe_cfg)