CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-confuse

Painless YAML configuration library for Python applications with validation, type checking, and multi-source data merging

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

templates.mddocs/

Type Validation

Comprehensive template system for validating and converting configuration values with built-in templates for common types and support for custom validation logic. Templates provide type safety and automatic conversion between YAML representations and Python objects.

Capabilities

Base Template Class

The foundation template class that all validation templates inherit from, providing the core validation and conversion interface.

class Template:
    def __init__(self, default=REQUIRED):
        """
        Create a template with optional default value.
        
        Parameters:
        - default: Default value to return when configuration value is missing.
                  Use REQUIRED (default) to raise exception for missing values.
        """
    
    def value(self, view, template=None):
        """
        Get validated value from a ConfigView.
        
        Parameters:
        - view (ConfigView): Configuration view to validate
        - template (Template, optional): Parent template context
        
        Returns:
        Validated and converted value
        
        Raises:
        - NotFoundError: If value is missing and required
        - ConfigValueError: If value is invalid
        """
    
    def convert(self, value, view):
        """
        Convert raw configuration value to target type.
        
        Parameters:
        - value: Raw value from configuration source
        - view (ConfigView): Configuration view context
        
        Returns:
        Converted value
        """
    
    def get_default_value(self, key_name='default'):
        """
        Get the default value for this template.
        
        Returns:
        Default value or raises NotFoundError if required
        """

Basic Type Templates

Templates for primitive types with automatic conversion and validation.

class Integer(Template):
    """Template for integer values with float rounding."""
    
class Number(Template):
    """Template for numeric values (int or float)."""
    
class String(Template):
    def __init__(self, default=REQUIRED, pattern=None, expand_vars=False):
        """
        Template for string values with optional pattern matching and variable expansion.
        
        Parameters:
        - default: Default string value
        - pattern (str, optional): Regular expression pattern to validate against
        - expand_vars (bool): Whether to expand environment variables ($VAR, ${VAR})
        """

class TypeTemplate(Template):
    def __init__(self, typ, default=REQUIRED):
        """
        Template for validating Python type instances.
        
        Parameters:
        - typ (type): Python type to validate against
        - default: Default value of the specified type
        """

Choice and Option Templates

Templates for validating values against predefined choices or multiple possible templates.

class Choice(Template):
    def __init__(self, choices, default=REQUIRED):
        """
        Template for values from predefined choices.
        
        Parameters:
        - choices: Sequence of valid values, dict mapping, or Enum class
        - default: Default choice value
        """

class OneOf(Template):
    def __init__(self, allowed, default=REQUIRED):
        """
        Template accepting values matching any of multiple sub-templates.
        
        Parameters:
        - allowed: List of Template objects or convertible values
        - default: Default value
        """

class Optional(Template):
    def __init__(self, subtemplate, default=None, allow_missing=True):
        """
        Template making sub-templates optional with null/missing value handling.
        
        Parameters:
        - subtemplate: Template to apply when value is present and not null
        - default: Value to return for null/missing values
        - allow_missing (bool): Whether to allow missing values
        """

Container Templates

Templates for validating collections and nested data structures.

class MappingTemplate(Template):
    def __init__(self, mapping):
        """
        Template for dictionary validation with typed values.
        
        Parameters:
        - mapping (dict): Dictionary specifying value templates for each key
        """

class Sequence(Template):
    def __init__(self, subtemplate):
        """
        Template for list validation with uniform item types.
        
        Parameters:
        - subtemplate: Template to apply to each list item
        """

class MappingValues(Template):
    def __init__(self, subtemplate):
        """
        Template for mappings with variable keys but typed values.
        
        Parameters:
        - subtemplate: Template to apply to all mapping values
        """

class StrSeq(Template):
    def __init__(self, split=True, default=REQUIRED):
        """
        Template for lists of strings with optional whitespace splitting.
        
        Parameters:
        - split (bool): Whether to split single strings on whitespace
        - default: Default list value
        """

class Pairs(StrSeq):
    def __init__(self, default_value=None):
        """
        Template for ordered key-value pairs from various input formats.
        
        Parameters:
        - default_value: Default value for keys without explicit values
        """

File Path Templates

Templates for validating and resolving file paths with platform-aware handling.

class Filename(Template):
    def __init__(self, default=REQUIRED, cwd=None, relative_to=None, 
                 in_app_dir=False, in_source_dir=False):
        """
        Template for filename validation with path resolution.
        
        Parameters:
        - default: Default filename
        - cwd (str, optional): Working directory for relative paths
        - relative_to (str, optional): Key name for sibling path resolution
        - in_app_dir (bool): Resolve relative to application config directory
        - in_source_dir (bool): Resolve relative to configuration file directory
        """

class Path(Filename):
    """Template for pathlib.Path objects with same resolution as Filename."""

Utility Functions and Classes

Helper functions and classes for working with templates and validation.

def as_template(value):
    """
    Convert shorthand Python values to Template objects.
    
    Parameters:
    - value: Python type, value, or existing Template
    
    Returns:
    Template: Corresponding Template object
    """

class AttrDict(dict):
    """
    Dictionary subclass with attribute-style access.
    
    Enables dot notation: config.database.host instead of config['database']['host']
    """
    def __getattr__(self, key): ...
    def __setattr__(self, key, value): ...

Usage Examples

Basic Type Validation

import confuse

config = confuse.Configuration('myapp')

# Simple type templates
port = config['port'].get(confuse.Integer())
timeout = config['timeout'].get(confuse.Number(30.0))  # Default: 30.0
name = config['app_name'].get(confuse.String())

# Type shorthand (automatically converted to templates)
port = config['port'].get(int)  # Same as Integer()
timeout = config['timeout'].get(30.0)  # Same as Number(30.0)
name = config['app_name'].get(str)  # Same as String()

String Templates with Patterns

import confuse

config = confuse.Configuration('myapp')

# String with regex pattern validation
email_template = confuse.String(pattern=r'^[^@]+@[^@]+\.[^@]+$')
email = config['admin_email'].get(email_template)

# String with environment variable expansion
path_template = confuse.String(expand_vars=True)
data_path = config['data_path'].get(path_template)  # Expands $HOME/data

Choice Templates

import confuse
from enum import Enum

config = confuse.Configuration('myapp')

# Choice from list
log_level = config['log_level'].get(confuse.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR']))

# Choice from dict (maps keys to values)
env_mapping = {'dev': 'development', 'prod': 'production'}
environment = config['env'].get(confuse.Choice(env_mapping))

# Choice from Enum
class LogLevel(Enum):
    DEBUG = 'debug'
    INFO = 'info' 
    WARNING = 'warning'
    ERROR = 'error'

log_level = config['log_level'].get(confuse.Choice(LogLevel))

Complex Data Structure Validation

import confuse

config = confuse.Configuration('myapp')

# Dictionary template with typed values
database_template = {
    'host': str,
    'port': confuse.Integer(5432),
    'username': str,
    'password': str,
    'ssl': bool,
    'timeout': confuse.Number(30.0),
    'options': confuse.StrSeq(),
}

# Get validated configuration as AttrDict
db_config = config['database'].get(database_template)
print(f"Connecting to {db_config.host}:{db_config.port}")
print(f"SSL enabled: {db_config.ssl}")
print(f"Options: {db_config.options}")

# List of dictionaries
server_template = confuse.Sequence({
    'hostname': str,
    'port': confuse.Integer(),
    'enabled': bool,
    'tags': confuse.StrSeq(),
})

servers = config['servers'].get(server_template)
for server in servers:
    print(f"Server: {server.hostname}:{server.port} (enabled: {server.enabled})")

File Path Validation

import confuse

config = confuse.Configuration('myapp')

# Basic filename template
config_file = config['config_file'].get(confuse.Filename())

# Filename relative to application config directory
log_file = config['log_file'].get(confuse.Filename(in_app_dir=True))

# Filename relative to configuration source file
data_file = config['data_file'].get(confuse.Filename(in_source_dir=True))

# Filename with custom working directory
output_file = config['output'].get(confuse.Filename(cwd='/tmp'))

# Filename relative to another configuration value
template = {
    'base_dir': confuse.Filename(),
    'data_file': confuse.Filename(relative_to='base_dir'),
}
paths = config.get(template)
print(f"Data file: {paths.data_file}")  # Resolved relative to base_dir

Optional and Default Values

import confuse

config = confuse.Configuration('myapp')

# Optional template with default
debug_mode = config['debug'].get(confuse.Optional(bool, default=False))

# Template with REQUIRED (raises exception if missing)
api_key = config['api_key'].get(confuse.String())  # Must be present

# Template with default value
max_workers = config['max_workers'].get(confuse.Integer(4))

# Optional nested configuration
cache_template = confuse.Optional({
    'enabled': bool,
    'ttl': confuse.Integer(3600),
    'backend': confuse.Choice(['memory', 'redis']),
})
cache_config = config['cache'].get(cache_template)
if cache_config:
    print(f"Cache enabled: {cache_config.enabled}")

Multiple Template Options

import confuse

config = confuse.Configuration('myapp')

# Value can be boolean or string
import_mode = config['import_mode'].get(confuse.OneOf([bool, 'ask', 'skip']))

# Value can be filename or URL
source_template = confuse.OneOf([
    confuse.Filename(),
    confuse.String(pattern=r'^https?://'),
])
data_source = config['data_source'].get(source_template)

# Multiple validation options with different defaults
retry_template = confuse.OneOf([
    confuse.Integer(),  # Number of retries
    bool,  # True/False for default retry count
    confuse.String(pattern=r'^(always|never)$'),  # Special string values
])
retry_config = config['retry'].get(retry_template)

String Sequences and Pairs

import confuse

config = confuse.Configuration('myapp')

# List of strings (splits single string on whitespace)
plugins = config['plugins'].get(confuse.StrSeq())
# YAML: plugins: "plugin1 plugin2 plugin3" -> ['plugin1', 'plugin2', 'plugin3']
# YAML: plugins: [plugin1, plugin2, plugin3] -> ['plugin1', 'plugin2', 'plugin3']

# List of strings without splitting
tags = config['tags'].get(confuse.StrSeq(split=False))
# YAML: tags: "development staging" -> ['development staging']

# Key-value pairs
env_vars = config['environment'].get(confuse.Pairs())
# YAML:
# environment:
#   - DATABASE_URL: postgres://localhost/db
#   - [API_KEY, secret123]
#   - DEBUG  # Uses default_value
# Result: [('DATABASE_URL', 'postgres://localhost/db'), ('API_KEY', 'secret123'), ('DEBUG', None)]

# Pairs with default value
flags = config['flags'].get(confuse.Pairs(default_value=True))
# Result: [('DEBUG', True)] for YAML: flags: [DEBUG]

Custom Template Creation

import confuse

class EmailTemplate(confuse.Template):
    def convert(self, value, view):
        if not isinstance(value, str):
            self.fail('must be a string', view, True)
        
        if '@' not in value:
            self.fail('must be a valid email address', view)
        
        return value.lower()  # Normalize to lowercase

config = confuse.Configuration('myapp')
admin_email = config['admin_email'].get(EmailTemplate())

Install with Tessl CLI

npx tessl i tessl/pypi-confuse

docs

configuration.md

index.md

sources.md

templates.md

views.md

tile.json