WL Config Manager
A flexible configuration manager for Python applications that supports multiple file formats, environment variable overrides, and dot notation access.
Features
- Multiple Formats - YAML, JSON, and INI configuration files
- Dot Notation - Access nested values with
config.server.port
- Environment Variables - Override config values with environment variables
- Default Values - Built-in defaults with easy overrides
- File Search - Automatically find config files in standard locations
- Validation - Ensure required keys are present
- CLI Tool - Command-line interface for config manipulation
- Type Conversion - Automatic type conversion for environment variables
- Live Reload - Reload configuration without restarting
Installation
pip install wl-config-manager
Dependencies
wl_version_manager
pyyaml>=5.1
Quick Start
Basic Usage
from wl_config_manager import ConfigManager
config = ConfigManager(config_path="config.yaml")
port = config.server.port
debug = config.app.debug
database_url = config.get("database.url", default="sqlite:///app.db")
Configuration File Example
app:
name: "My Application"
debug: false
version: "1.0.0"
server:
host: "0.0.0.0"
port: 8080
workers: 4
database:
url: "postgresql://localhost/myapp"
pool_size: 10
echo: false
logging:
level: "INFO"
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
Configuration Loading
Search Paths
If no config path is specified, the manager searches these locations in order:
from wl_config_manager import ConfigManager, DEFAULT_SEARCH_PATHS
config = ConfigManager(search_paths=DEFAULT_SEARCH_PATHS)
config = ConfigManager(search_paths=["/opt/myapp", "/etc/myapp"])
File Formats
The format is auto-detected by file extension, or can be specified:
config = ConfigManager(config_path="settings.json")
config = ConfigManager(config_path="app.ini")
config = ConfigManager(config_path="config.yaml")
config = ConfigManager(config_path="myconfig", format="yaml")
Environment Variable Overrides
Override configuration values using environment variables:
config = ConfigManager(
config_path="config.yaml",
env_prefix="MYAPP_"
)
Environment variable rules:
- Prefix + double underscore (
__
) for nested values
- Values are automatically converted to appropriate types (bool, int, float, string)
- Case-insensitive (environment vars are converted to lowercase)
Default Configuration
Provide default values that are used if not found in config file or environment:
default_config = {
"app": {
"name": "DefaultApp",
"debug": False
},
"server": {
"port": 8080,
"host": "localhost"
}
}
config = ConfigManager(
config_path="config.yaml",
default_config=default_config
)
Validation
Ensure required configuration keys are present:
config = ConfigManager(
config_path="config.yaml",
required_keys=["app.name", "server.port", "database.url"]
)
Accessing Configuration Values
Dot Notation Access
app_name = config.app.name
port = config.server.port
log_level = config.logging.level
smtp_host = config.get("email.smtp.host", default="localhost")
server_config = config.server
server_dict = config.get("server")
Dictionary-like Access
if "debug" in config.app:
print(f"Debug mode: {config.app.debug}")
for section, values in config:
print(f"{section}: {values}")
for key, value in config.items("server"):
print(f"{key} = {value}")
Modifying Configuration
Set Values
config.set("app.version", "2.0.0")
config.set("server.port", 9000)
config.set("new_feature.enabled", True)
config.set("new_feature.options.timeout", 30)
config.update({
"version": "2.0.0",
"debug": True
})
config.update({"port": 9000, "workers": 8}, prefix="server")
Save Configuration
config.save()
config.save("new_config.yaml")
config.save("config.json")
Advanced Usage
Reload Configuration
config.reload()
Create from Dictionary
config_dict = {
"app": {"name": "MyApp"},
"server": {"port": 8080}
}
config = ConfigManager.from_dict(config_dict)
Create from Environment Only
config = ConfigManager.from_env("MYAPP_")
Logging Configuration
config = ConfigManager(
config_path="config.yaml",
log_level=logging.DEBUG
)
from wl_config_manager import setup_file_logging
setup_file_logging(
log_dir="/var/log",
app_name="myapp",
log_level=logging.INFO
)
Command Line Interface
The package includes a CLI tool for managing configurations:
View Configuration
wl_config_manager get config.yaml
wl_config_manager get config.yaml server.port
wl_config_manager get config.yaml app.missing --default="not found"
wl_config_manager get --format=json config.yaml
Modify Configuration
wl_config_manager set config.yaml app.debug true
wl_config_manager set config.yaml server.port 9000
wl_config_manager create --format=yaml new_config.yaml
wl_config_manager create --template=default.yaml --vars='{"app":{"name":"MyApp"}}' config.yaml
Validate Configuration
wl_config_manager validate --required=app.name,server.port config.yaml
Convert Formats
wl_config_manager convert config.ini config.yaml
wl_config_manager convert settings.yaml settings.json
List Configuration
wl_config_manager list config.yaml
wl_config_manager list config.yaml --section=server
Environment Variables
wl_config_manager env MYAPP_ --format=json
Error Handling
The module provides specific exceptions for different error types:
from wl_config_manager import (
ConfigError,
ConfigFileError,
ConfigFormatError,
ConfigValidationError
)
try:
config = ConfigManager(config_path="config.yaml")
except ConfigFileError as e:
print(f"Config file error: {e}")
print(f"File path: {e.file_path}")
except ConfigValidationError as e:
print(f"Validation error: {e}")
print(f"Missing keys: {e.get_missing_keys()}")
Best Practices
-
Use Environment Variables for Secrets
export MYAPP_DATABASE__PASSWORD="secret"
export MYAPP_API__KEY="secret-key"
-
Provide Sensible Defaults
default_config = {
"server": {"port": 8080, "host": "0.0.0.0"},
"logging": {"level": "INFO"}
}
-
Validate Critical Configuration
required_keys = ["database.url", "app.secret_key"]
config = ConfigManager(required_keys=required_keys)
-
Use Type Hints with Config Objects
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from types import SimpleNamespace
def setup_server(server_config: SimpleNamespace):
port = server_config.port
host = server_config.host
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.