Advanced Application Framework for Python with a focus on Command Line Interfaces
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The configuration system provides comprehensive configuration file handling with support for multiple formats, default values, and hierarchical configuration merging. It integrates seamlessly with argument parsing for unified application configuration.
Base interface for configuration management handlers that defines the contract for configuration operations.
class ConfigHandler:
"""
Configuration handler interface for managing application configuration.
Provides methods for parsing configuration files, merging configuration
data, and accessing configuration values in a structured way.
"""
def parse_file(self, file_path: str) -> bool:
"""
Parse and load a configuration file.
Args:
file_path: Path to configuration file to parse
Returns:
True if file was successfully parsed, False otherwise
"""
def merge(self, dict_obj: Dict[str, Any]) -> None:
"""
Merge a dictionary into the configuration.
Args:
dict_obj: Dictionary to merge into configuration
"""
def get(self, section: str, key: str) -> Any:
"""
Get a configuration value.
Args:
section: Configuration section name
key: Configuration key name
Returns:
Configuration value
Raises:
KeyError: If section or key not found
"""
def get_section_dict(self, section: str) -> Dict[str, Any]:
"""
Get an entire configuration section as a dictionary.
Args:
section: Section name to retrieve
Returns:
Dictionary containing all key-value pairs in the section
Raises:
KeyError: If section not found
"""
def get_sections(self) -> List[str]:
"""
Get list of all configuration sections.
Returns:
List of section names
"""
def add_section(self, section: str) -> None:
"""
Add a new configuration section.
Args:
section: Section name to add
"""
def has_section(self, section: str) -> bool:
"""
Check if a configuration section exists.
Args:
section: Section name to check
Returns:
True if section exists, False otherwise
"""
def keys(self, section: str) -> List[str]:
"""
Get list of keys in a configuration section.
Args:
section: Section name
Returns:
List of key names in the section
Raises:
KeyError: If section not found
"""
def set(self, section: str, key: str, value: Any) -> None:
"""
Set a configuration value.
Args:
section: Configuration section name
key: Configuration key name
value: Value to set
"""Utility functions for working with configuration defaults and initialization.
def init_defaults(*sections: str) -> Dict[str, Any]:
"""
Create a standard dictionary for application configuration defaults.
Creates a nested dictionary structure with the specified sections.
This is commonly used for setting up application configuration defaults.
Args:
*sections: Section names to create in the defaults dictionary
Returns:
Dictionary with nested sections for configuration defaults
Example:
config = init_defaults('myapp', 'database', 'logging')
# Returns: {'myapp': {}, 'database': {}, 'logging': {}}
"""from cement import App, init_defaults
# Initialize configuration defaults
CONFIG = init_defaults('myapp')
CONFIG['myapp']['debug'] = False
CONFIG['myapp']['log_level'] = 'INFO'
CONFIG['myapp']['max_connections'] = 100
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
config_files = [
'/etc/myapp.conf',
'~/.myapp.conf',
'./myapp.conf'
]
with MyApp() as app:
app.setup()
# Access configuration values
debug = app.config.get('myapp', 'debug')
log_level = app.config.get('myapp', 'log_level')
max_conn = app.config.get('myapp', 'max_connections')
print(f"Debug: {debug}")
print(f"Log Level: {log_level}")
print(f"Max Connections: {max_conn}")# /etc/myapp.conf
[myapp]
debug = false
log_level = INFO
max_connections = 100
[database]
host = localhost
port = 5432
name = myapp_db
user = myapp_user
password = secret123
[logging]
file = /var/log/myapp.log
rotate = true
max_size = 10MB# ~/.myapp.yaml
myapp:
debug: false
log_level: INFO
max_connections: 100
database:
host: localhost
port: 5432
name: myapp_db
user: myapp_user
password: secret123
logging:
file: /var/log/myapp.log
rotate: true
max_size: 10MBfrom cement import App, init_defaults
# Initialize with multiple sections
CONFIG = init_defaults('myapp', 'database', 'logging', 'cache')
# Set defaults for each section
CONFIG['myapp']['debug'] = False
CONFIG['myapp']['version'] = '1.0.0'
CONFIG['database']['host'] = 'localhost'
CONFIG['database']['port'] = 5432
CONFIG['database']['timeout'] = 30
CONFIG['logging']['level'] = 'INFO'
CONFIG['logging']['file'] = None
CONFIG['logging']['format'] = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
CONFIG['cache']['enabled'] = True
CONFIG['cache']['ttl'] = 3600
CONFIG['cache']['max_size'] = 1000
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
config_files = ['./myapp.conf']
with MyApp() as app:
app.setup()
# Access different sections
db_config = app.config.get_section_dict('database')
print(f"Database config: {db_config}")
# Check if sections exist
if app.config.has_section('cache'):
cache_enabled = app.config.get('cache', 'enabled')
print(f"Cache enabled: {cache_enabled}")
# List all sections
sections = app.config.get_sections()
print(f"Available sections: {sections}")from cement import App, Controller, ex, init_defaults
CONFIG = init_defaults('myapp')
class ConfigController(Controller):
class Meta:
label = 'config'
stacked_on = 'base'
stacked_type = 'nested'
@ex(
help='show configuration values',
arguments=[
(['--section'], {'help': 'show specific section only'})
]
)
def show(self):
"""Display current configuration."""
if self.app.pargs.section:
section = self.app.pargs.section
if self.app.config.has_section(section):
config_dict = self.app.config.get_section_dict(section)
print(f"[{section}]")
for key, value in config_dict.items():
print(f"{key} = {value}")
else:
print(f"Section '{section}' not found")
else:
# Show all sections
for section in self.app.config.get_sections():
print(f"[{section}]")
config_dict = self.app.config.get_section_dict(section)
for key, value in config_dict.items():
print(f"{key} = {value}")
print()
@ex(
help='set configuration value',
arguments=[
(['section'], {'help': 'configuration section'}),
(['key'], {'help': 'configuration key'}),
(['value'], {'help': 'configuration value'})
]
)
def set(self):
"""Set a configuration value."""
section = self.app.pargs.section
key = self.app.pargs.key
value = self.app.pargs.value
# Create section if it doesn't exist
if not self.app.config.has_section(section):
self.app.config.add_section(section)
self.app.config.set(section, key, value)
print(f"Set {section}.{key} = {value}")
@ex(
help='get configuration value',
arguments=[
(['section'], {'help': 'configuration section'}),
(['key'], {'help': 'configuration key'})
]
)
def get(self):
"""Get a configuration value."""
section = self.app.pargs.section
key = self.app.pargs.key
try:
value = self.app.config.get(section, key)
print(f"{section}.{key} = {value}")
except KeyError:
print(f"Configuration key '{section}.{key}' not found")
class BaseController(Controller):
class Meta:
label = 'base'
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
config_defaults = CONFIG
handlers = [
BaseController,
ConfigController
]
with MyApp() as app:
app.run()
# Usage:
# myapp config show
# myapp config show --section database
# myapp config set database host localhost
# myapp config get database hostimport os
from cement import App, init_defaults
CONFIG = init_defaults('myapp')
# Set defaults that can be overridden by environment variables
CONFIG['myapp']['debug'] = os.getenv('MYAPP_DEBUG', 'false').lower() == 'true'
CONFIG['myapp']['log_level'] = os.getenv('MYAPP_LOG_LEVEL', 'INFO')
CONFIG['myapp']['database_url'] = os.getenv('DATABASE_URL', 'sqlite:///app.db')
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
def setup(self):
super().setup()
# Override with environment variables after config file loading
env_overrides = {}
if 'MYAPP_DEBUG' in os.environ:
env_overrides['debug'] = os.getenv('MYAPP_DEBUG').lower() == 'true'
if 'MYAPP_LOG_LEVEL' in os.environ:
env_overrides['log_level'] = os.getenv('MYAPP_LOG_LEVEL')
if 'DATABASE_URL' in os.environ:
env_overrides['database_url'] = os.getenv('DATABASE_URL')
# Merge environment overrides
if env_overrides:
self.config.merge({'myapp': env_overrides})
with MyApp() as app:
app.setup()
debug = app.config.get('myapp', 'debug')
log_level = app.config.get('myapp', 'log_level')
db_url = app.config.get('myapp', 'database_url')
print(f"Configuration loaded:")
print(f" Debug: {debug}")
print(f" Log Level: {log_level}")
print(f" Database URL: {db_url}")from cement import App, init_defaults
CONFIG = init_defaults('myapp', 'database')
CONFIG['myapp']['name'] = 'MyApplication'
CONFIG['myapp']['version'] = '1.0.0'
CONFIG['database']['host'] = 'localhost'
CONFIG['database']['port'] = 5432
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
config_files = ['./myapp.conf']
def validate_config(self):
"""Validate configuration after loading."""
# Check required configuration values
required_keys = [
('myapp', 'name'),
('myapp', 'version'),
('database', 'host'),
('database', 'port')
]
for section, key in required_keys:
try:
value = self.config.get(section, key)
if value is None or value == '':
raise ValueError(f"Required configuration {section}.{key} is missing or empty")
except KeyError:
raise ValueError(f"Required configuration {section}.{key} is missing")
# Validate data types and ranges
port = self.config.get('database', 'port')
if not isinstance(port, int) or port < 1 or port > 65535:
raise ValueError(f"Database port must be an integer between 1 and 65535, got: {port}")
print("Configuration validation passed")
def setup(self):
super().setup()
self.validate_config()
with MyApp() as app:
app.setup()
app.run()from cement import App, init_defaults
import os
CONFIG = init_defaults('myapp')
CONFIG['myapp']['environment'] = 'development'
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
def setup(self):
super().setup()
# Load configuration files in priority order
config_files = [
'/etc/myapp/config.conf', # System-wide config
'/etc/myapp/myapp.conf', # System-wide app config
os.path.expanduser('~/.myapp.conf'), # User config
'./myapp.conf', # Local config
]
# Add environment-specific config
env = self.config.get('myapp', 'environment')
env_config = f'./config/{env}.conf'
config_files.append(env_config)
# Load each config file (later files override earlier ones)
for config_file in config_files:
if os.path.exists(config_file):
print(f"Loading config: {config_file}")
self.config.parse_file(config_file)
with MyApp() as app:
app.setup()
# Final configuration reflects the hierarchy
environment = app.config.get('myapp', 'environment')
print(f"Running in {environment} environment")
app.run()Install with Tessl CLI
npx tessl i tessl/pypi-cement