Painless YAML configuration library for Python applications with validation, type checking, and multi-source data merging
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Dictionary-like interface for querying configuration data with support for nested access, type conversion, and validation. ConfigView provides the primary API for accessing configuration values with transparent type checking and error handling.
Base class for configuration queries that provides dictionary-like access with validation and type conversion capabilities.
class ConfigView:
def get(self, template=REQUIRED):
"""
Get configuration value with optional validation template.
Parameters:
- template: Type or Template object for validation/conversion
Returns:
Validated and converted configuration value
Raises:
- NotFoundError: If value is missing and required
- ConfigTypeError: If value doesn't match expected type
- ConfigValueError: If value is invalid
"""
def exists(self):
"""
Check if this configuration key exists in any source.
Returns:
bool: True if key exists, False otherwise
"""
def first(self):
"""
Get the first available value and its source.
Returns:
tuple: (value, source) pair
Raises:
- NotFoundError: If no value is found
"""
def keys(self):
"""
Get all available keys from all sources for this view.
Returns:
list: Available configuration keys
Raises:
- ConfigTypeError: If this view is not a dictionary
"""
def items(self):
"""
Get key-value pairs as (key, subview) tuples.
Returns:
list: Tuples of (key, ConfigView) pairs
"""
def values(self):
"""
Get all values as ConfigView objects.
Returns:
list: ConfigView objects for each value
"""
def sequence(self):
"""
Iterate over this view as a list.
Returns:
generator: ConfigView objects for each list item
Raises:
- ConfigTypeError: If this view is not a list
"""
def set(self, value):
"""
Override this configuration value with highest priority.
Parameters:
- value: New configuration value
"""
def add(self, value):
"""
Add a default value with lowest priority.
Parameters:
- value: Default configuration value
"""
def set_args(self, namespace, dots=False):
"""
Overlay parsed command-line arguments onto configuration.
Parameters:
- namespace: argparse.Namespace or dict with argument values
- dots (bool): Split keys on dots to create nested structure
"""
def all_contents(self):
"""
Get all values from all sources for this view.
Returns:
generator: (value, source) tuples for all matching values
"""
def flatten(self, redact=False):
"""
Generate flat dictionary representation of configuration.
Parameters:
- redact (bool): Replace sensitive values with redaction placeholder
Returns:
dict: Flattened configuration with dot-separated keys
"""
def set_redaction(self, path, flag):
"""
Mark a configuration path as sensitive for redaction.
Parameters:
- path (str): Configuration path (e.g., 'database.password')
- flag (bool): Whether to redact this path
"""
def get_redactions(self):
"""
Get dictionary of redaction flags for configuration paths.
Returns:
dict: Mapping of paths to redaction flags
"""
def resolve(self):
"""
Generate all (value, source) pairs for this view from all sources.
Returns:
generator: (value, ConfigSource) tuples in priority order
"""
def root(self):
"""
Get the root ConfigView for this view hierarchy.
Returns:
RootView: Root view containing all configuration sources
"""
def __iter__(self):
"""
Iterate over configuration keys or sequence items.
Returns:
generator: Keys for mappings or ConfigView objects for sequences
"""
def __contains__(self, key):
"""
Check if key exists in configuration.
Parameters:
- key: Key to check for existence
Returns:
bool: True if key exists in any source
"""
def __getitem__(self, key):
"""
Access nested configuration via subscript notation.
Parameters:
- key: Dictionary key or list index
Returns:
Subview: ConfigView for the nested configuration item
"""
def __setitem__(self, key, value):
"""
Set nested configuration value via subscript notation.
Parameters:
- key: Dictionary key or list index
- value: Value to set
"""
def __str__(self):
"""
Get string representation of configuration value.
Returns:
str: String representation of the first available value
"""
def __bool__(self):
"""
Check if this configuration view has any values.
Returns:
bool: True if any sources contain values for this view
"""Convenience methods for common type conversions with built-in validation.
class ConfigView:
def as_str(self):
"""
Get value as string.
Returns:
str: String representation of value
"""
def as_str_expanded(self):
"""
Get value as string with environment variable expansion.
Returns:
str: String with $VAR and ${VAR} expanded
"""
def as_filename(self):
"""
Get value as validated filename with path resolution.
Returns:
str: Absolute path to file
"""
def as_path(self):
"""
Get value as pathlib.Path object.
Returns:
pathlib.Path: Path object for the file
"""
def as_number(self):
"""
Get value as numeric type (int or float).
Returns:
int or float: Numeric value
"""
def as_choice(self, choices):
"""
Get value validated against list of choices.
Parameters:
- choices: Sequence of valid values or Enum class
Returns:
Validated choice value
"""
def as_str_seq(self, split=True):
"""
Get value as list of strings.
Parameters:
- split (bool): Split single strings on whitespace
Returns:
list: List of string values
"""
def as_pairs(self, default_value=None):
"""
Get value as list of key-value pairs.
Parameters:
- default_value: Default value for keys without explicit values
Returns:
list: List of (key, value) tuples
"""Accessing nested configuration data through subscript notation and attribute-style access.
class Subview(ConfigView):
def __init__(self, parent, key):
"""
Create a subview for accessing nested configuration data.
Parameters:
- parent (ConfigView): Parent view object
- key: Dictionary key or list index for access
"""import confuse
config = confuse.Configuration('myapp')
# Dictionary-style access
database_host = config['database']['host'].get(str)
port = config['database']['port'].get(int)
# Check if key exists
if config['debug'].exists():
debug_mode = config['debug'].get(bool)
# Get with default using template
timeout = config['timeout'].get(confuse.Number(30.0))import confuse
config = confuse.Configuration('myapp')
# Basic type conversions
name = config['app_name'].as_str()
count = config['worker_count'].as_number()
config_file = config['config_path'].as_filename()
output_dir = config['output_dir'].as_path()
# String expansion with environment variables
expanded_path = config['data_path'].as_str_expanded() # Expands $HOME/data
# Choice validation
log_level = config['log_level'].as_choice(['DEBUG', 'INFO', 'WARNING', 'ERROR'])
# List of strings
plugins = config['plugins'].as_str_seq() # ['plugin1', 'plugin2']
keywords = config['keywords'].as_str_seq(split=True) # 'foo bar' -> ['foo', 'bar']import confuse
config = confuse.Configuration('myapp')
# Iterate over dictionary keys
for key in config['servers']:
server_config = config['servers'][key]
print(f"Server {key}: {server_config['host'].as_str()}")
# Get all keys and values
server_keys = config['servers'].keys()
server_views = config['servers'].values()
# Iterate over list items
for server_view in config['server_list'].sequence():
hostname = server_view['hostname'].as_str()
port = server_view['port'].as_number()
print(f"Server: {hostname}:{port}")
# List iteration with index access
servers = config['servers']
for i, server_view in enumerate(servers):
print(f"Server {i}: {server_view.get(str)}")import confuse
import argparse
config = confuse.Configuration('myapp')
# Parse command-line arguments
parser = argparse.ArgumentParser()
parser.add_argument('--host', default='localhost')
parser.add_argument('--port', type=int, default=8080)
parser.add_argument('--database-url', dest='database.url')
parser.add_argument('--verbose', action='store_true')
args = parser.parse_args()
# Overlay arguments onto configuration
config.set_args(args, dots=True) # Split 'database.url' into nested structure
# Access merged configuration
host = config['host'].as_str() # From args or config file
port = config['port'].as_number() # From args or config file
db_url = config['database']['url'].as_str() # From --database-urlimport confuse
config = confuse.Configuration('myapp')
try:
# This will raise NotFoundError if 'required_setting' is missing
value = config['required_setting'].get(str)
except confuse.NotFoundError:
print("Required setting is missing from configuration")
try:
# This will raise ConfigTypeError if value is not an integer
count = config['item_count'].get(int)
except confuse.ConfigTypeError as e:
print(f"Invalid type for item_count: {e}")
try:
# This will raise ConfigValueError for invalid values
level = config['log_level'].as_choice(['DEBUG', 'INFO', 'WARNING', 'ERROR'])
except confuse.ConfigValueError as e:
print(f"Invalid log level: {e}")import confuse
config = confuse.Configuration('myapp')
# Working with nested dictionaries
database_config = config['database']
if database_config.exists():
# Get individual values
host = database_config['host'].as_str()
port = database_config['port'].as_number()
ssl_enabled = database_config['ssl'].get(bool)
# Check for optional nested values
if database_config['connection_pool'].exists():
max_connections = database_config['connection_pool']['max_size'].get(int)
# Working with lists of dictionaries
for server in config['servers'].sequence():
hostname = server['hostname'].as_str()
options = server['options'].as_str_seq()
print(f"Server {hostname} with options: {options}")
# Key-value pair processing
pairs = config['environment'].as_pairs()
for key, value in pairs:
print(f"Environment: {key}={value}")import confuse
config = confuse.Configuration('myapp')
# Set values at runtime
config['debug'] = True
config.set({'logging': {'level': 'DEBUG', 'format': 'detailed'}})
# Add default values (lowest priority)
config.add({'timeout': 30, 'retries': 3})
# Set nested values
config['database']['host'] = 'production.db.example.com'
# Check current configuration state
all_keys = list(config.keys())
has_database = 'database' in config
database_exists = config['database'].exists()import confuse
config = confuse.Configuration('myapp')
config.set({'database': {'password': 'secret123', 'host': 'localhost'}})
# Mark sensitive paths for redaction
config.set_redaction('database.password', True)
config.set_redaction('api_key', True)
# Get flat configuration with redaction
flat_config = config.flatten(redact=True)
print(flat_config) # {'database.password': 'REDACTED', 'database.host': 'localhost'}
# Get redaction settings
redactions = config.get_redactions()
print(redactions) # {'database.password': True, 'api_key': True}
# Iterate over all configuration values and sources
for value, source in config.all_contents():
print(f"Value: {value}, Source: {source.filename}")Install with Tessl CLI
npx tessl i tessl/pypi-confuse