Skip to content

Config

provide.foundation.config

TODO: Add module docstring.

Classes

BaseConfig

Base configuration class with common functionality.

All configuration classes should inherit from this.

Note on Validation

The validate() method is synchronous. Subclasses can override it to add custom validation logic. If async validation is needed, subclasses should implement their own async validation methods.

Functions
__attrs_post_init__
__attrs_post_init__() -> None

Post-initialization hook for subclasses.

Note: validate() is not called automatically to allow subclasses to perform validation at the appropriate time (e.g., after all fields are populated from multiple sources).

Source code in provide/foundation/config/base.py
def __attrs_post_init__(self) -> None:
    """Post-initialization hook for subclasses.

    Note: validate() is not called automatically to allow subclasses
    to perform validation at the appropriate time (e.g., after all
    fields are populated from multiple sources).
    """
__repr__
__repr__() -> str

String representation hiding sensitive fields.

Source code in provide/foundation/config/base.py
def __repr__(self) -> str:
    """String representation hiding sensitive fields."""
    # Get the actual attrs fields
    import attrs

    attr_fields = attrs.fields(self.__class__)

    parts = []
    for attr in attr_fields:
        # Skip internal fields
        if attr.name.startswith("_"):
            continue

        value = getattr(self, attr.name)

        # Hide sensitive values
        if attr.metadata.get("sensitive", False):
            value = "***SENSITIVE***"

        parts.append(f"{attr.name}={value!r}")

    return f"{self.__class__.__name__}({', '.join(parts)})"
clone
clone() -> Self

Create a deep copy of the configuration.

Source code in provide/foundation/config/base.py
def clone(self) -> Self:
    """Create a deep copy of the configuration."""
    cloned = copy.deepcopy(self)
    # Cloned configuration inherits validation state from original
    return cloned
diff
diff(other: BaseConfig) -> dict[str, tuple[Any, Any]]

Compare with another configuration.

Parameters:

Name Type Description Default
other BaseConfig

Configuration to compare with

required

Returns:

Type Description
dict[str, tuple[Any, Any]]

Dictionary of differences (field_name: (self_value, other_value))

Source code in provide/foundation/config/base.py
def diff(self, other: BaseConfig) -> dict[str, tuple[Any, Any]]:
    """Compare with another configuration.

    Args:
        other: Configuration to compare with

    Returns:
        Dictionary of differences (field_name: (self_value, other_value))

    """
    if not isinstance(other, self.__class__):
        raise TypeError(f"Cannot compare {self.__class__.__name__} with {other.__class__.__name__}")

    differences = {}

    for attr in fields(self.__class__):
        self_value = getattr(self, attr.name)
        other_value = getattr(other, attr.name)

        if self_value != other_value:
            differences[attr.name] = (self_value, other_value)

    return differences
from_dict classmethod
from_dict(
    data: ConfigDict,
    source: ConfigSource = ConfigSource.RUNTIME,
) -> Self

Create configuration from dictionary.

Parameters:

Name Type Description Default
data ConfigDict

Configuration data

required
source ConfigSource

Source of the configuration

RUNTIME

Returns:

Type Description
Self

Configuration instance

Raises:

Type Description
ValidationError

If validation fails

Source code in provide/foundation/config/base.py
@classmethod
def from_dict(cls, data: ConfigDict, source: ConfigSource = ConfigSource.RUNTIME) -> Self:
    """Create configuration from dictionary.

    Args:
        data: Configuration data
        source: Source of the configuration

    Returns:
        Configuration instance

    Raises:
        ValidationError: If validation fails

    """
    # Filter data to only include fields defined in the class, excluding private fields
    field_names = {f.name for f in fields(cls) if not f.name.startswith("_")}
    filtered_data = {k: v for k, v in data.items() if k in field_names}

    # Create instance
    instance = cls(**filtered_data)

    # Track sources
    for key in filtered_data:
        instance._source_map[key] = source
        instance._original_values[key] = filtered_data[key]

    # Validate configuration
    instance.validate()

    return instance
get_source
get_source(field_name: str) -> ConfigSource | None

Get the source of a configuration field.

Parameters:

Name Type Description Default
field_name str

Name of the field

required

Returns:

Type Description
ConfigSource | None

Source of the field value or None

Source code in provide/foundation/config/base.py
def get_source(self, field_name: str) -> ConfigSource | None:
    """Get the source of a configuration field.

    Args:
        field_name: Name of the field

    Returns:
        Source of the field value or None

    """
    return self._source_map.get(field_name)
reset_to_defaults
reset_to_defaults() -> None

Reset all fields to their default values.

Source code in provide/foundation/config/base.py
def reset_to_defaults(self) -> None:
    """Reset all fields to their default values."""
    for attr in fields(self.__class__):
        # Skip internal fields
        if attr.name.startswith("_"):
            continue

        if attr.default != NOTHING:
            setattr(self, attr.name, attr.default)
        elif attr.factory != NOTHING:
            # attrs factory is always callable
            setattr(self, attr.name, attr.factory())

    self._source_map.clear()
    self._original_values.clear()
to_dict
to_dict(include_sensitive: bool = False) -> ConfigDict

Convert configuration to dictionary.

Parameters:

Name Type Description Default
include_sensitive bool

Whether to include sensitive fields

False

Returns:

Type Description
ConfigDict

Dictionary representation of the configuration

Source code in provide/foundation/config/base.py
def to_dict(self, include_sensitive: bool = False) -> ConfigDict:
    """Convert configuration to dictionary.

    Args:
        include_sensitive: Whether to include sensitive fields

    Returns:
        Dictionary representation of the configuration

    """
    result = {}

    for attr in fields(self.__class__):
        value = getattr(self, attr.name)

        # Skip sensitive fields if requested
        if not include_sensitive and attr.metadata.get("sensitive", False):
            continue

        # Convert nested configs recursively
        if isinstance(value, BaseConfig):
            value = value.to_dict(include_sensitive)
        elif isinstance(value, dict):
            value = self._convert_dict_values(value, include_sensitive)
        elif isinstance(value, list):
            value = self._convert_list_values(value, include_sensitive)

        result[attr.name] = value

    return result
update
update(
    updates: ConfigDict,
    source: ConfigSource = ConfigSource.RUNTIME,
) -> None

Update configuration with new values.

Parameters:

Name Type Description Default
updates ConfigDict

Dictionary of updates

required
source ConfigSource

Source of the updates

RUNTIME

Raises:

Type Description
ValidationError

If validation fails after update

Source code in provide/foundation/config/base.py
def update(self, updates: ConfigDict, source: ConfigSource = ConfigSource.RUNTIME) -> None:
    """Update configuration with new values.

    Args:
        updates: Dictionary of updates
        source: Source of the updates

    Raises:
        ValidationError: If validation fails after update

    """
    for key, value in updates.items():
        if hasattr(self, key):
            # Only update if new source has higher precedence
            current_source = self._source_map.get(key, ConfigSource.DEFAULT)
            if source >= current_source:
                setattr(self, key, value)
                self._source_map[key] = source
                self._original_values[key] = value

    # Validate configuration after updates
    self.validate()
validate
validate() -> None

Validate the configuration.

This is a synchronous validation method. Override this in subclasses to add custom validation logic. Call this explicitly after creating and populating a configuration instance.

Raises:

Type Description
ValidationError

If validation fails

Source code in provide/foundation/config/base.py
def validate(self) -> None:
    """Validate the configuration.

    This is a synchronous validation method. Override this in subclasses
    to add custom validation logic. Call this explicitly after creating
    and populating a configuration instance.

    Raises:
        ValidationError: If validation fails

    """

ConfigError

ConfigError(
    message: str,
    *,
    config_key: str | None = None,
    config_source: str | None = None,
    **kwargs: Any
)

Bases: FoundationError

Raised when configuration is invalid or cannot be loaded.

Parameters:

Name Type Description Default
message str

Error message describing the configuration issue.

required
config_key str | None

Optional configuration key that caused the error.

None
config_source str | None

Optional source of the configuration (file, env, etc.).

None
**kwargs Any

Additional context passed to FoundationError.

{}

Examples:

>>> raise ConfigurationError("Missing required config")
>>> raise ConfigurationError("Invalid timeout", config_key="timeout")
Source code in provide/foundation/errors/config.py
def __init__(
    self,
    message: str,
    *,
    config_key: str | None = None,
    config_source: str | None = None,
    **kwargs: Any,
) -> None:
    if config_key:
        kwargs.setdefault("context", {})["config.key"] = config_key
    if config_source:
        kwargs.setdefault("context", {})["config.source"] = config_source
    super().__init__(message, **kwargs)

ConfigLoader

Bases: ABC

Abstract base class for configuration loaders.

Built-in implementations: - FileConfigLoader: YAML, JSON, TOML, .env files - RuntimeConfigLoader: Environment variables - DictConfigLoader: In-memory dictionaries

For cloud secret managers (Vault, AWS Secrets, Azure Key Vault), implement custom loaders following this protocol.

Examples: docs/guide/advanced/integration-patterns.md#custom-configuration-sources

Functions
exists abstractmethod
exists() -> bool

Check if the configuration source exists.

Returns:

Type Description
bool

True if source exists

Source code in provide/foundation/config/loader.py
@abstractmethod
def exists(self) -> bool:
    """Check if the configuration source exists.

    Returns:
        True if source exists

    """
load abstractmethod
load(config_class: type[T]) -> T

Load configuration.

Parameters:

Name Type Description Default
config_class type[T]

Configuration class to instantiate

required

Returns:

Type Description
T

Configuration instance

Source code in provide/foundation/config/loader.py
@abstractmethod
def load(self, config_class: type[T]) -> T:
    """Load configuration.

    Args:
        config_class: Configuration class to instantiate

    Returns:
        Configuration instance

    """

ConfigManager

ConfigManager()

Centralized configuration manager.

Manages multiple configuration objects and provides a unified interface.

Initialize configuration manager.

Source code in provide/foundation/config/manager.py
def __init__(self) -> None:
    """Initialize configuration manager."""
    self._configs: dict[str, BaseConfig] = {}
    self._schemas: dict[str, ConfigSchema] = {}
    self._loaders: dict[str, ConfigLoader] = {}
    self._defaults: dict[str, ConfigDict] = {}
Functions
add_loader
add_loader(name: str, loader: ConfigLoader) -> None

Add a loader for a configuration.

Source code in provide/foundation/config/manager.py
def add_loader(self, name: str, loader: ConfigLoader) -> None:
    """Add a loader for a configuration."""
    self._loaders[name] = loader
clear
clear() -> None

Clear all configurations.

Source code in provide/foundation/config/manager.py
def clear(self) -> None:
    """Clear all configurations."""
    self._configs.clear()
    self._schemas.clear()
    self._loaders.clear()
    self._defaults.clear()
export
export(
    name: str, include_sensitive: bool = False
) -> ConfigDict

Export a configuration as dictionary.

Parameters:

Name Type Description Default
name str

Configuration name

required
include_sensitive bool

Whether to include sensitive fields

False

Returns:

Type Description
ConfigDict

Configuration dictionary

Source code in provide/foundation/config/manager.py
def export(self, name: str, include_sensitive: bool = False) -> ConfigDict:
    """Export a configuration as dictionary.

    Args:
        name: Configuration name
        include_sensitive: Whether to include sensitive fields

    Returns:
        Configuration dictionary

    """
    if name not in self._configs:
        raise ValueError(f"Configuration not found: {name}")

    return self._configs[name].to_dict(include_sensitive)
export_all
export_all(
    include_sensitive: bool = False,
) -> dict[str, ConfigDict]

Export all configurations.

Parameters:

Name Type Description Default
include_sensitive bool

Whether to include sensitive fields

False

Returns:

Type Description
dict[str, ConfigDict]

Dictionary of all configurations

Source code in provide/foundation/config/manager.py
def export_all(self, include_sensitive: bool = False) -> dict[str, ConfigDict]:
    """Export all configurations.

    Args:
        include_sensitive: Whether to include sensitive fields

    Returns:
        Dictionary of all configurations

    """
    result = {}
    for name, config in self._configs.items():
        result[name] = config.to_dict(include_sensitive)
    return result
export_to_dict
export_to_dict(
    include_sensitive: bool = False,
) -> dict[str, ConfigDict]

Export all configs to dict. Alias for export_all.

Source code in provide/foundation/config/manager.py
def export_to_dict(self, include_sensitive: bool = False) -> dict[str, ConfigDict]:
    """Export all configs to dict. Alias for export_all."""
    return self.export_all(include_sensitive)
get
get(name: str) -> BaseConfig | None

Get a configuration by name.

Parameters:

Name Type Description Default
name str

Configuration name

required

Returns:

Type Description
BaseConfig | None

Configuration instance or None

Source code in provide/foundation/config/manager.py
def get(self, name: str) -> BaseConfig | None:
    """Get a configuration by name.

    Args:
        name: Configuration name

    Returns:
        Configuration instance or None

    """
    return self._configs.get(name)
get_all
get_all() -> dict[str, BaseConfig]

Get all registered configurations.

Source code in provide/foundation/config/manager.py
def get_all(self) -> dict[str, BaseConfig]:
    """Get all registered configurations."""
    return self._configs.copy()
get_or_create
get_or_create(
    name: str,
    config_class: type[T],
    defaults: ConfigDict | None = None,
) -> T

Get existing config or create new one with defaults.

Source code in provide/foundation/config/manager.py
def get_or_create(self, name: str, config_class: type[T], defaults: ConfigDict | None = None) -> T:
    """Get existing config or create new one with defaults."""
    existing = self.get(name)
    if existing is not None:
        # Type: ignore since we know this is the correct type from how it was registered
        return existing  # type: ignore[return-value]

    # Create new config with defaults
    config = config_class.from_dict(defaults or {})
    self._configs[name] = config
    return config
list_configs
list_configs() -> list[str]

List all registered configurations.

Returns:

Type Description
list[str]

List of configuration names

Source code in provide/foundation/config/manager.py
def list_configs(self) -> list[str]:
    """List all registered configurations.

    Returns:
        List of configuration names

    """
    return list(self._configs.keys())
load
load(
    name: str,
    config_class: type[T],
    loader: ConfigLoader | None = None,
) -> T

Load a configuration.

Parameters:

Name Type Description Default
name str

Configuration name

required
config_class type[T]

Configuration class

required
loader ConfigLoader | None

Optional loader (uses registered if None)

None

Returns:

Type Description
T

Configuration instance

Source code in provide/foundation/config/manager.py
def load(self, name: str, config_class: type[T], loader: ConfigLoader | None = None) -> T:
    """Load a configuration.

    Args:
        name: Configuration name
        config_class: Configuration class
        loader: Optional loader (uses registered if None)

    Returns:
        Configuration instance

    """
    # Use provided loader or registered one
    if loader is None:
        loader = self._loaders.get(name)
        if loader is None:
            raise ValueError(f"No loader registered for configuration: {name}")

    # Load configuration
    config = loader.load(config_class)

    # Apply defaults if available
    if name in self._defaults:
        defaults_dict = self._defaults[name]
        for key, value in defaults_dict.items():
            if not hasattr(config, key) or getattr(config, key) is None:
                setattr(config, key, value)

    # Validate against schema if available
    if name in self._schemas:
        schema = self._schemas[name]
        config_dict = config.to_dict(include_sensitive=True)
        schema.validate(config_dict)

    # Store configuration
    self._configs[name] = config

    return config
load_from_dict
load_from_dict(
    name: str, config_class: type[T], data: ConfigDict
) -> T

Load config from dictionary.

Source code in provide/foundation/config/manager.py
def load_from_dict(self, name: str, config_class: type[T], data: ConfigDict) -> T:
    """Load config from dictionary."""
    config = config_class.from_dict(data)
    self._configs[name] = config
    return config
register
register(
    name: str,
    config: BaseConfig | None = None,
    schema: ConfigSchema | None = None,
    loader: ConfigLoader | None = None,
    defaults: ConfigDict | None = None,
) -> None

Register a configuration.

Parameters:

Name Type Description Default
name str

Configuration name

required
config BaseConfig | None

Configuration instance

None
schema ConfigSchema | None

Configuration schema

None
loader ConfigLoader | None

Configuration loader

None
defaults ConfigDict | None

Default configuration values

None
Source code in provide/foundation/config/manager.py
def register(
    self,
    name: str,
    config: BaseConfig | None = None,
    schema: ConfigSchema | None = None,
    loader: ConfigLoader | None = None,
    defaults: ConfigDict | None = None,
) -> None:
    """Register a configuration.

    Args:
        name: Configuration name
        config: Configuration instance
        schema: Configuration schema
        loader: Configuration loader
        defaults: Default configuration values

    """
    if config is not None:
        self._configs[name] = config

    if schema is not None:
        self._schemas[name] = schema

    if loader is not None:
        self._loaders[name] = loader

    if defaults is not None:
        self._defaults[name] = defaults
reload
reload(name: str) -> BaseConfig

Reload a configuration.

Parameters:

Name Type Description Default
name str

Configuration name

required

Returns:

Type Description
BaseConfig

Reloaded configuration instance

Source code in provide/foundation/config/manager.py
def reload(self, name: str) -> BaseConfig:
    """Reload a configuration.

    Args:
        name: Configuration name

    Returns:
        Reloaded configuration instance

    """
    if name not in self._configs:
        raise ValueError(f"Configuration not found: {name}")

    config = self._configs[name]
    loader = self._loaders.get(name)

    if loader is None:
        raise ValueError(f"No loader registered for configuration: {name}")

    # Reload from loader
    new_config = loader.load(config.__class__)

    # Apply defaults
    if name in self._defaults:
        defaults_dict = self._defaults[name]
        for key, value in defaults_dict.items():
            if not hasattr(new_config, key) or getattr(new_config, key) is None:
                setattr(new_config, key, value)

    # Validate
    if name in self._schemas:
        schema = self._schemas[name]
        config_dict = new_config.to_dict(include_sensitive=True)
        schema.validate(config_dict)

    # Update stored configuration
    self._configs[name] = new_config

    return new_config
remove
remove(name: str) -> None

Remove a configuration. Alias for unregister.

Source code in provide/foundation/config/manager.py
def remove(self, name: str) -> None:
    """Remove a configuration. Alias for unregister."""
    self.unregister(name)
reset
reset(name: str) -> None

Reset a configuration to defaults.

Parameters:

Name Type Description Default
name str

Configuration name

required
Source code in provide/foundation/config/manager.py
def reset(self, name: str) -> None:
    """Reset a configuration to defaults.

    Args:
        name: Configuration name

    """
    if name not in self._configs:
        raise ValueError(f"Configuration not found: {name}")

    config = self._configs[name]
    config.reset_to_defaults()

    # Apply registered defaults
    if name in self._defaults:
        config.update(self._defaults[name], ConfigSource.DEFAULT)
set
set(name: str, config: BaseConfig) -> None

Set a configuration.

Parameters:

Name Type Description Default
name str

Configuration name

required
config BaseConfig

Configuration instance

required
Source code in provide/foundation/config/manager.py
def set(self, name: str, config: BaseConfig) -> None:
    """Set a configuration.

    Args:
        name: Configuration name
        config: Configuration instance

    """
    self._configs[name] = config
unregister
unregister(name: str) -> None

Unregister a configuration.

Parameters:

Name Type Description Default
name str

Configuration name

required
Source code in provide/foundation/config/manager.py
def unregister(self, name: str) -> None:
    """Unregister a configuration.

    Args:
        name: Configuration name

    """
    self._configs.pop(name, None)
    self._schemas.pop(name, None)
    self._loaders.pop(name, None)
    self._defaults.pop(name, None)
update
update(
    name: str,
    updates: ConfigDict,
    source: ConfigSource = ConfigSource.RUNTIME,
) -> None

Update a configuration.

Parameters:

Name Type Description Default
name str

Configuration name

required
updates ConfigDict

Configuration updates

required
source ConfigSource

Source of updates

RUNTIME
Source code in provide/foundation/config/manager.py
def update(
    self,
    name: str,
    updates: ConfigDict,
    source: ConfigSource = ConfigSource.RUNTIME,
) -> None:
    """Update a configuration.

    Args:
        name: Configuration name
        updates: Configuration updates
        source: Source of updates

    """
    if name not in self._configs:
        raise ValueError(f"Configuration not found: {name}")

    config = self._configs[name]

    # Validate updates against schema if available
    if name in self._schemas:
        schema = self._schemas[name]
        # Validate only the updated fields
        for key, value in updates.items():
            if key in schema._field_map:
                schema._field_map[key].validate(value)

    # Apply updates
    config.update(updates, source)
validate_all
validate_all() -> None

Validate all configurations.

Source code in provide/foundation/config/manager.py
def validate_all(self) -> None:
    """Validate all configurations."""
    for name, config in self._configs.items():
        if hasattr(config, "validate"):
            config.validate()
        if name in self._schemas:
            schema = self._schemas[name]
            config_dict = config.to_dict(include_sensitive=True)
            if hasattr(schema, "validate"):
                schema.validate(config_dict)

ConfigSchema

ConfigSchema(fields: list[SchemaField] | None = None)

Schema definition for configuration classes.

Initialize configuration schema.

Parameters:

Name Type Description Default
fields list[SchemaField] | None

List of schema fields

None
Source code in provide/foundation/config/schema.py
def __init__(self, fields: list[SchemaField] | None = None) -> None:
    """Initialize configuration schema.

    Args:
        fields: List of schema fields

    """
    self.fields = fields or []
    self._field_map = {field.name: field for field in self.fields}
Functions
add_field
add_field(field: SchemaField) -> None

Add a field to the schema.

Source code in provide/foundation/config/schema.py
def add_field(self, field: SchemaField) -> None:
    """Add a field to the schema."""
    self.fields.append(field)
    self._field_map[field.name] = field
apply_defaults
apply_defaults(data: ConfigDict) -> ConfigDict

Apply default values to configuration data.

Parameters:

Name Type Description Default
data ConfigDict

Configuration data

required

Returns:

Type Description
ConfigDict

Data with defaults applied

Source code in provide/foundation/config/schema.py
def apply_defaults(self, data: ConfigDict) -> ConfigDict:
    """Apply default values to configuration data.

    Args:
        data: Configuration data

    Returns:
        Data with defaults applied

    """
    result = data.copy()

    for field in self.fields:
        if field.name not in result and field.default is not None:
            result[field.name] = field.default

    return result
filter_extra_fields
filter_extra_fields(data: ConfigDict) -> ConfigDict

Remove fields not defined in schema.

Parameters:

Name Type Description Default
data ConfigDict

Configuration data

required

Returns:

Type Description
ConfigDict

Filtered data

Source code in provide/foundation/config/schema.py
def filter_extra_fields(self, data: ConfigDict) -> ConfigDict:
    """Remove fields not defined in schema.

    Args:
        data: Configuration data

    Returns:
        Filtered data

    """
    return {k: v for k, v in data.items() if k in self._field_map}
from_config_class classmethod
from_config_class(
    config_class: type[BaseConfig],
) -> ConfigSchema

Generate schema from configuration class.

Parameters:

Name Type Description Default
config_class type[BaseConfig]

Configuration class

required

Returns:

Type Description
ConfigSchema

Generated schema

Source code in provide/foundation/config/schema.py
@classmethod
def from_config_class(cls, config_class: type[BaseConfig]) -> ConfigSchema:
    """Generate schema from configuration class.

    Args:
        config_class: Configuration class

    Returns:
        Generated schema

    """
    schema_fields = []

    for attr in fields(config_class):
        schema_field = cls._attr_to_schema_field(attr)
        schema_fields.append(schema_field)

    return cls(schema_fields)
validate
validate(data: ConfigDict) -> None

Validate configuration data against schema.

Parameters:

Name Type Description Default
data ConfigDict

Configuration data to validate

required

Raises:

Type Description
ConfigValidationError

If validation fails

Source code in provide/foundation/config/schema.py
def validate(self, data: ConfigDict) -> None:
    """Validate configuration data against schema.

    Args:
        data: Configuration data to validate

    Raises:
        ConfigValidationError: If validation fails

    """
    # Check required fields
    for field in self.fields:
        if field.required and field.name not in data:
            raise ConfigValidationError("Required field missing", field=field.name)

    # Validate each field
    for key, value in data.items():
        if key in self._field_map:
            self._field_map[key].validate(value)

ConfigSource

Bases: Enum

Sources for configuration values with precedence order.

Functions
__eq__
__eq__(other: object) -> bool

Enable == comparison for precedence.

Source code in provide/foundation/config/types.py
def __eq__(self, other: object) -> bool:
    """Enable == comparison for precedence."""
    if not isinstance(other, ConfigSource):
        return NotImplemented
    return self.value == other.value
__ge__
__ge__(other: object) -> bool

Enable >= comparison for precedence.

Source code in provide/foundation/config/types.py
def __ge__(self, other: object) -> bool:
    """Enable >= comparison for precedence."""
    if not isinstance(other, ConfigSource):
        return NotImplemented
    return self.value >= other.value
__gt__
__gt__(other: object) -> bool

Enable > comparison for precedence.

Source code in provide/foundation/config/types.py
def __gt__(self, other: object) -> bool:
    """Enable > comparison for precedence."""
    if not isinstance(other, ConfigSource):
        return NotImplemented
    return self.value > other.value
__le__
__le__(other: object) -> bool

Enable <= comparison for precedence.

Source code in provide/foundation/config/types.py
def __le__(self, other: object) -> bool:
    """Enable <= comparison for precedence."""
    if not isinstance(other, ConfigSource):
        return NotImplemented
    return self.value <= other.value
__lt__
__lt__(other: object) -> bool

Enable comparison for precedence.

Source code in provide/foundation/config/types.py
def __lt__(self, other: object) -> bool:
    """Enable comparison for precedence."""
    if not isinstance(other, ConfigSource):
        return NotImplemented
    return self.value < other.value

ConfigValidationError

ConfigValidationError(
    message: str,
    *,
    field: str | None = None,
    value: Any = None,
    rule: str | None = None,
    **kwargs: Any
)

Bases: FoundationError

Raised when data validation fails.

Parameters:

Name Type Description Default
message str

Validation error message.

required
field str | None

Optional field name that failed validation.

None
value Any

Optional invalid value.

None
rule str | None

Optional validation rule that failed.

None
**kwargs Any

Additional context passed to FoundationError.

{}

Examples:

>>> raise ValidationError("Invalid email format")
>>> raise ValidationError("Value out of range", field="age", value=-1)
Source code in provide/foundation/errors/config.py
def __init__(
    self,
    message: str,
    *,
    field: str | None = None,
    value: Any = None,
    rule: str | None = None,
    **kwargs: Any,
) -> None:
    if field:
        kwargs.setdefault("context", {})["validation.field"] = field
    if value is not None:
        kwargs.setdefault("context", {})["validation.value"] = str(value)
    if rule:
        kwargs.setdefault("context", {})["validation.rule"] = rule
    super().__init__(message, **kwargs)

DictConfigLoader

DictConfigLoader(
    data: ConfigDict,
    source: ConfigSource = ConfigSource.RUNTIME,
)

Bases: ConfigLoader

Load configuration from a dictionary.

Initialize dictionary configuration loader.

Parameters:

Name Type Description Default
data ConfigDict

Configuration data

required
source ConfigSource

Source of the configuration

RUNTIME
Source code in provide/foundation/config/loader.py
def __init__(self, data: ConfigDict, source: ConfigSource = ConfigSource.RUNTIME) -> None:
    """Initialize dictionary configuration loader.

    Args:
        data: Configuration data
        source: Source of the configuration

    """
    self.data = data
    self.source = source
Functions
exists
exists() -> bool

Check if configuration data exists.

Source code in provide/foundation/config/loader.py
def exists(self) -> bool:
    """Check if configuration data exists."""
    return self.data is not None
load
load(config_class: type[T]) -> T

Load configuration from dictionary.

Source code in provide/foundation/config/loader.py
def load(self, config_class: type[T]) -> T:
    """Load configuration from dictionary."""
    return config_class.from_dict(self.data, source=self.source)

FileConfigLoader

FileConfigLoader(
    path: str | Path,
    format: ConfigFormat | None = None,
    encoding: str = "utf-8",
)

Bases: ConfigLoader

Load configuration from files.

Initialize file configuration loader.

Parameters:

Name Type Description Default
path str | Path

Path to configuration file

required
format ConfigFormat | None

File format (auto-detected if None)

None
encoding str

File encoding

'utf-8'
Source code in provide/foundation/config/loader.py
def __init__(
    self,
    path: str | Path,
    format: ConfigFormat | None = None,
    encoding: str = "utf-8",
) -> None:
    """Initialize file configuration loader.

    Args:
        path: Path to configuration file
        format: File format (auto-detected if None)
        encoding: File encoding

    """
    self.path = Path(path)
    self.encoding = encoding

    if format is None:
        format = ConfigFormat.from_extension(str(self.path))
        if format is None:
            raise ConfigurationError(
                f"Cannot determine format for file: {self.path}",
                code="CONFIG_FORMAT_UNKNOWN",
                path=str(self.path),
            )

    self.format = format
Functions
exists
exists() -> bool

Check if configuration file exists.

Source code in provide/foundation/config/loader.py
def exists(self) -> bool:
    """Check if configuration file exists."""
    return self.path.exists()
load
load(config_class: type[T]) -> T

Load configuration from file.

Source code in provide/foundation/config/loader.py
@resilient(
    context_provider=lambda: {"loader": FileConfigLoader},
    error_mapper=lambda e: ConfigurationError(
        f"Failed to load configuration: {e}",
        code="CONFIG_LOAD_ERROR",
        cause=e,
    )
    if not isinstance(e, ConfigurationError | NotFoundError)
    else e,
)
def load(self, config_class: type[T]) -> T:
    """Load configuration from file."""
    from provide.foundation.logger.setup.coordinator import (
        create_foundation_internal_logger,
    )
    from provide.foundation.utils.timing import timed_block

    setup_logger = create_foundation_internal_logger()

    with timed_block(setup_logger, f"Load config from {self.path.name}"):
        if not self.exists():
            raise NotFoundError(
                f"Configuration file not found: {self.path}",
                code="CONFIG_FILE_NOT_FOUND",
                path=str(self.path),
            )

        data = self._read_file()
        return config_class.from_dict(data, source=ConfigSource.FILE)

MultiSourceLoader

MultiSourceLoader(*loaders: ConfigLoader)

Bases: ConfigLoader

Load configuration from multiple sources with precedence.

Initialize multi-source configuration loader.

Parameters:

Name Type Description Default
*loaders ConfigLoader

Configuration loaders in order of precedence (later overrides earlier)

()
Source code in provide/foundation/config/loader.py
def __init__(self, *loaders: ConfigLoader) -> None:
    """Initialize multi-source configuration loader.

    Args:
        *loaders: Configuration loaders in order of precedence (later overrides earlier)

    """
    self.loaders = loaders
Functions
exists
exists() -> bool

Check if any configuration source exists.

Source code in provide/foundation/config/loader.py
def exists(self) -> bool:
    """Check if any configuration source exists."""
    return any(loader.exists() for loader in self.loaders)
load
load(config_class: type[T]) -> T

Load and merge configuration from multiple sources.

Source code in provide/foundation/config/loader.py
def load(self, config_class: type[T]) -> T:
    """Load and merge configuration from multiple sources."""
    if not self.exists():
        raise ValueError("No configuration sources available")

    config = None

    for loader in self.loaders:
        if loader.exists():
            if config is None:
                config = loader.load(config_class)
            else:
                # Load and merge
                new_config = loader.load(config_class)
                new_dict = new_config.to_dict(include_sensitive=True)
                # Update each field with its proper source
                for key, value in new_dict.items():
                    source = new_config.get_source(key)
                    if source is not None:
                        config.update({key: value}, source=source)

    if config is None:
        raise ValueError("Failed to load configuration from any source")
    return config

RuntimeConfig

Bases: BaseConfig

Configuration that can be loaded from environment variables.

Functions
from_env classmethod
from_env(
    prefix: str = "",
    delimiter: str = "_",
    case_sensitive: bool = False,
) -> Self

Load configuration from environment variables synchronously.

Parameters:

Name Type Description Default
prefix str

Prefix for all environment variables

''
delimiter str

Delimiter between prefix and field name

'_'
case_sensitive bool

Whether variable names are case-sensitive

False

Returns:

Type Description
Self

Configuration instance

Source code in provide/foundation/config/env.py
@classmethod
def from_env(
    cls,
    prefix: str = "",
    delimiter: str = "_",
    case_sensitive: bool = False,
) -> Self:
    """Load configuration from environment variables synchronously.

    Args:
        prefix: Prefix for all environment variables
        delimiter: Delimiter between prefix and field name
        case_sensitive: Whether variable names are case-sensitive

    Returns:
        Configuration instance

    """
    data = {}

    for attr in fields(cls):
        # Determine environment variable name
        env_var = attr.metadata.get("env_var")

        if not env_var:
            # Build from prefix and field name
            field_prefix = attr.metadata.get("env_prefix", prefix)
            field_name = attr.name.upper() if not case_sensitive else attr.name

            env_var = f"{field_prefix}{delimiter}{field_name}" if field_prefix else field_name

        # Get value from environment
        raw_value = os.environ.get(env_var)

        if raw_value is not None:
            value = raw_value
            # Check if it's a file-based secret
            if value.startswith("file://"):
                # Read synchronously
                file_path = value[7:]
                from provide.foundation.file.safe import safe_read_text

                try:
                    value = safe_read_text(file_path, default="").strip()
                    if not value:
                        raise ValueError(f"Secret file is empty: {file_path}")
                except Exception as e:
                    raise ValueError(f"Failed to read secret from file '{file_path}': {e}") from e

            # Apply parser if specified
            parser = attr.metadata.get("env_parser")

            if parser:
                try:
                    value = parser(value)
                except Exception as e:
                    raise ValueError(f"Failed to parse {env_var}: {e}") from e
            else:
                # Try to infer parser from type
                from provide.foundation.parsers import auto_parse

                value = auto_parse(attr, value)

            data[attr.name] = value

    return cls.from_dict(data, source=ConfigSource.ENV)
to_env_dict
to_env_dict(
    prefix: str = "", delimiter: str = "_"
) -> dict[str, str]

Convert configuration to environment variable dictionary.

Parameters:

Name Type Description Default
prefix str

Prefix for all environment variables

''
delimiter str

Delimiter between prefix and field name

'_'

Returns:

Type Description
dict[str, str]

Dictionary of environment variables

Source code in provide/foundation/config/env.py
def to_env_dict(self, prefix: str = "", delimiter: str = "_") -> dict[str, str]:
    """Convert configuration to environment variable dictionary.

    Args:
        prefix: Prefix for all environment variables
        delimiter: Delimiter between prefix and field name

    Returns:
        Dictionary of environment variables

    """
    env_dict = {}

    for attr in fields(self.__class__):
        value = getattr(self, attr.name)

        # Skip None values
        if value is None:
            continue

        # Determine environment variable name
        env_var = attr.metadata.get("env_var")

        if not env_var:
            field_prefix = attr.metadata.get("env_prefix", prefix)
            field_name = attr.name.upper()

            env_var = f"{field_prefix}{delimiter}{field_name}" if field_prefix else field_name

        # Convert value to string
        if isinstance(value, bool):
            str_value = "true" if value else "false"
        elif isinstance(value, list):
            str_value = ",".join(str(item) for item in value)
        elif isinstance(value, dict):
            str_value = ",".join(f"{k}={v}" for k, v in value.items())
        else:
            str_value = str(value)

        env_dict[env_var] = str_value

    return env_dict

SchemaField

Schema definition for a configuration field.

Functions
validate
validate(value: Any) -> None

Validate a value against this schema field.

Parameters:

Name Type Description Default
value Any

Value to validate

required

Raises:

Type Description
ConfigValidationError

If validation fails

Source code in provide/foundation/config/schema.py
def validate(self, value: Any) -> None:
    """Validate a value against this schema field.

    Args:
        value: Value to validate

    Raises:
        ConfigValidationError: If validation fails

    """
    # Check required
    self._validate_required(value)

    # Skip further validation for None values
    if value is None:
        return

    # Run all validations
    self._validate_type(value)
    self._validate_choices(value)
    self._validate_range(value)
    self._validate_pattern(value)
    self._validate_custom(value)

Functions

env_field

env_field(
    env_var: str | None = None,
    env_prefix: str | None = None,
    parser: Callable[[str], Any] | None = None,
    **kwargs: Any
) -> Any

Create a field that can be loaded from environment variables.

Parameters:

Name Type Description Default
env_var str | None

Explicit environment variable name

None
env_prefix str | None

Prefix for environment variable

None
parser Callable[[str], Any] | None

Custom parser function

None
**kwargs Any

Additional field arguments

{}

Returns:

Type Description
Any

Field descriptor

Source code in provide/foundation/config/env.py
def env_field(
    env_var: str | None = None,
    env_prefix: str | None = None,
    parser: Callable[[str], Any] | None = None,
    **kwargs: Any,
) -> Any:
    """Create a field that can be loaded from environment variables.

    Args:
        env_var: Explicit environment variable name
        env_prefix: Prefix for environment variable
        parser: Custom parser function
        **kwargs: Additional field arguments

    Returns:
        Field descriptor

    """
    metadata = kwargs.pop("metadata", {})

    if env_var:
        metadata["env_var"] = env_var
    if env_prefix:
        metadata["env_prefix"] = env_prefix
    if parser:
        metadata["env_parser"] = parser

    return field(metadata=metadata, **kwargs)

field

field(
    *,
    default: Any = NOTHING,
    factory: Callable[[], Any] | None = None,
    validator: (
        Callable[[Any, Attribute[Any], Any], None] | None
    ) = None,
    converter: Callable[[Any], Any] | None = None,
    metadata: dict[str, Any] | None = None,
    description: str | None = None,
    env_var: str | None = None,
    env_prefix: str | None = None,
    sensitive: bool = False,
    **kwargs: Any
) -> Any

Enhanced attrs field with configuration-specific metadata.

Parameters:

Name Type Description Default
default Any

Default value for the field

NOTHING
factory Callable[[], Any] | None

Factory function to generate default value

None
validator Callable[[Any, Attribute[Any], Any], None] | None

Validation function

None
converter Callable[[Any], Any] | None

Conversion function

None
metadata dict[str, Any] | None

Additional metadata

None
description str | None

Human-readable description

None
env_var str | None

Environment variable name override

None
env_prefix str | None

Prefix for environment variable

None
sensitive bool

Whether this field contains sensitive data

False
**kwargs Any

Additional attrs field arguments

{}
Source code in provide/foundation/config/base.py
def field(
    *,
    default: Any = NOTHING,
    factory: Callable[[], Any] | None = None,
    validator: Callable[[Any, Attribute[Any], Any], None] | None = None,
    converter: Callable[[Any], Any] | None = None,
    metadata: dict[str, Any] | None = None,
    description: str | None = None,
    env_var: str | None = None,
    env_prefix: str | None = None,
    sensitive: bool = False,
    **kwargs: Any,
) -> Any:
    """Enhanced attrs field with configuration-specific metadata.

    Args:
        default: Default value for the field
        factory: Factory function to generate default value
        validator: Validation function
        converter: Conversion function
        metadata: Additional metadata
        description: Human-readable description
        env_var: Environment variable name override
        env_prefix: Prefix for environment variable
        sensitive: Whether this field contains sensitive data
        **kwargs: Additional attrs field arguments

    """
    config_metadata = metadata or {}

    # Add configuration-specific metadata
    if description:
        config_metadata["description"] = description
    if env_var:
        config_metadata["env_var"] = env_var
    if env_prefix:
        config_metadata["env_prefix"] = env_prefix
    if sensitive:
        config_metadata["sensitive"] = sensitive

    # Handle factory vs default
    if factory is not None:
        return attrs_field(
            factory=factory,
            validator=validator,
            converter=converter,
            metadata=config_metadata,
            **kwargs,
        )
    return attrs_field(
        default=default,
        validator=validator,
        converter=converter,
        metadata=config_metadata,
        **kwargs,
    )

get_config

get_config(name: str) -> BaseConfig | None

Get a configuration from the global manager.

Parameters:

Name Type Description Default
name str

Configuration name

required

Returns:

Type Description
BaseConfig | None

Configuration instance or None

Source code in provide/foundation/config/manager.py
def get_config(name: str) -> BaseConfig | None:
    """Get a configuration from the global manager.

    Args:
        name: Configuration name

    Returns:
        Configuration instance or None

    """
    return _manager.get(name)

get_env

get_env(
    var_name: str,
    default: str | None = None,
    required: bool = False,
    secret_file: bool = True,
) -> str | None

Get environment variable value with optional file-based secret support.

Parameters:

Name Type Description Default
var_name str

Environment variable name

required
default str | None

Default value if not found

None
required bool

Whether the variable is required

False
secret_file bool

Whether to support file:// prefix for secrets

True

Returns:

Type Description
str | None

Environment variable value or default

Raises:

Type Description
ValueError

If required and not found

Source code in provide/foundation/config/env.py
def get_env(
    var_name: str,
    default: str | None = None,
    required: bool = False,
    secret_file: bool = True,
) -> str | None:
    """Get environment variable value with optional file-based secret support.

    Args:
        var_name: Environment variable name
        default: Default value if not found
        required: Whether the variable is required
        secret_file: Whether to support file:// prefix for secrets

    Returns:
        Environment variable value or default

    Raises:
        ValueError: If required and not found

    """
    value = os.environ.get(var_name)

    if value is None:
        if required:
            raise ValueError(f"Required environment variable '{var_name}' not found")
        return default

    # Handle file-based secrets synchronously
    if secret_file and value.startswith("file://"):
        file_path = value[7:]  # Remove "file://" prefix
        from provide.foundation.file.safe import safe_read_text

        try:
            value = safe_read_text(file_path, default="").strip()
            if not value:
                raise ValueError(f"Secret file is empty: {file_path}")
        except Exception as e:
            raise ValueError(f"Failed to read secret from file '{file_path}': {e}") from e

    return value

load_config

load_config(
    name: str,
    config_class: type[T],
    loader: ConfigLoader | None = None,
) -> T

Load a configuration using the global manager.

Parameters:

Name Type Description Default
name str

Configuration name

required
config_class type[T]

Configuration class

required
loader ConfigLoader | None

Optional loader

None

Returns:

Type Description
T

Configuration instance

Source code in provide/foundation/config/manager.py
def load_config(name: str, config_class: type[T], loader: ConfigLoader | None = None) -> T:
    """Load a configuration using the global manager.

    Args:
        name: Configuration name
        config_class: Configuration class
        loader: Optional loader

    Returns:
        Configuration instance

    """
    return _manager.load(name, config_class, loader)

parse_bool

parse_bool(value: Any, strict: bool = False) -> bool

Parse a boolean value from string or other types.

Accepts: true/false, yes/no, 1/0, on/off (case-insensitive)

Parameters:

Name Type Description Default
value Any

Value to parse as boolean

required
strict bool

If True, only accept bool or string types (raise TypeError otherwise)

False

Returns:

Type Description
bool

Boolean value

Raises:

Type Description
TypeError

If strict=True and value is not bool or string, or if value is not bool/str

ValueError

If value cannot be parsed as boolean

Source code in provide/foundation/parsers/primitives.py
def parse_bool(value: Any, strict: bool = False) -> bool:
    """Parse a boolean value from string or other types.

    Accepts: true/false, yes/no, 1/0, on/off (case-insensitive)

    Args:
        value: Value to parse as boolean
        strict: If True, only accept bool or string types (raise TypeError otherwise)

    Returns:
        Boolean value

    Raises:
        TypeError: If strict=True and value is not bool or string, or if value is not bool/str
        ValueError: If value cannot be parsed as boolean

    """
    if strict and not isinstance(value, (bool, str)):
        raise TypeError(f"Cannot convert {type(value).__name__} to bool: {value!r}")

    return parse_bool_strict(value)

parse_bool_extended

parse_bool_extended(value: str | bool) -> bool

Parse boolean from string with lenient/forgiving interpretation.

This is the lenient boolean parser - designed for user-facing configuration where we want to be forgiving of various inputs. Any unrecognized string defaults to False rather than raising an error.

Use Cases: - Environment variables set by end users - Feature flags that should default to "off" if misconfigured - Optional telemetry settings where failure should not break the system

Recognized True Values: true, yes, 1, on (case-insensitive) Recognized False Values: false, no, 0, off (case-insensitive) Default Behavior: Any other string → False (no error)

Parameters:

Name Type Description Default
value str | bool

Boolean string representation or actual bool

required

Returns:

Type Description
bool

Boolean value (defaults to False for unrecognized strings)

Examples:

>>> parse_bool_extended("yes")  # True
>>> parse_bool_extended("FALSE")  # False
>>> parse_bool_extended("invalid")  # False (no error)
>>> parse_bool_extended(True)  # True
Source code in provide/foundation/parsers/primitives.py
def parse_bool_extended(value: str | bool) -> bool:
    """Parse boolean from string with lenient/forgiving interpretation.

    This is the **lenient** boolean parser - designed for user-facing configuration
    where we want to be forgiving of various inputs. Any unrecognized string
    defaults to False rather than raising an error.

    **Use Cases:**
    - Environment variables set by end users
    - Feature flags that should default to "off" if misconfigured
    - Optional telemetry settings where failure should not break the system

    **Recognized True Values:** true, yes, 1, on (case-insensitive)
    **Recognized False Values:** false, no, 0, off (case-insensitive)
    **Default Behavior:** Any other string → False (no error)

    Args:
        value: Boolean string representation or actual bool

    Returns:
        Boolean value (defaults to False for unrecognized strings)

    Examples:
        >>> parse_bool_extended("yes")  # True
        >>> parse_bool_extended("FALSE")  # False
        >>> parse_bool_extended("invalid")  # False (no error)
        >>> parse_bool_extended(True)  # True

    """
    # If already a bool, return as-is
    if isinstance(value, bool):
        return value

    # Convert to string and parse
    value_lower = str(value).lower().strip()
    # Only return True for explicit true values, everything else is False
    return value_lower in ("true", "yes", "1", "on")

parse_comma_list

parse_comma_list(value: str) -> list[str]

Parse comma-separated list of strings.

Parameters:

Name Type Description Default
value str

Comma-separated string

required

Returns:

Type Description
list[str]

List of trimmed non-empty strings

Source code in provide/foundation/parsers/collections.py
def parse_comma_list(value: str) -> list[str]:
    """Parse comma-separated list of strings.

    Args:
        value: Comma-separated string

    Returns:
        List of trimmed non-empty strings

    """
    if not value or not value.strip():
        return []

    return [item.strip() for item in value.split(",") if item.strip()]

parse_console_formatter

parse_console_formatter(value: str) -> ConsoleFormatterStr

Parse and validate console formatter string.

Parameters:

Name Type Description Default
value str

Formatter string (case-insensitive)

required

Returns:

Type Description
ConsoleFormatterStr

Valid formatter string in lowercase

Raises:

Type Description
ValueError

If the formatter is invalid

Source code in provide/foundation/parsers/telemetry.py
def parse_console_formatter(value: str) -> ConsoleFormatterStr:
    """Parse and validate console formatter string.

    Args:
        value: Formatter string (case-insensitive)

    Returns:
        Valid formatter string in lowercase

    Raises:
        ValueError: If the formatter is invalid

    """
    formatter = value.lower()
    if formatter not in _VALID_FORMATTER_TUPLE:
        raise ValueError(
            _format_invalid_value_error(
                "console_formatter",
                value,
                valid_options=list(_VALID_FORMATTER_TUPLE),
            ),
        )
    return cast("ConsoleFormatterStr", formatter)

parse_dict

parse_dict(
    value: str | dict[str, str],
    item_separator: str = ",",
    key_separator: str = "=",
    strip: bool = True,
) -> dict[str, str]

Parse a dictionary from a string.

Format: "key1=value1,key2=value2"

Parameters:

Name Type Description Default
value str | dict[str, str]

String or dict to parse

required
item_separator str

Separator between items

','
key_separator str

Separator between key and value

'='
strip bool

Whether to strip whitespace

True

Returns:

Type Description
dict[str, str]

Dictionary of string keys and values

Raises:

Type Description
ValueError

If format is invalid

Source code in provide/foundation/parsers/collections.py
def parse_dict(
    value: str | dict[str, str],
    item_separator: str = ",",
    key_separator: str = "=",
    strip: bool = True,
) -> dict[str, str]:
    """Parse a dictionary from a string.

    Format: "key1=value1,key2=value2"

    Args:
        value: String or dict to parse
        item_separator: Separator between items
        key_separator: Separator between key and value
        strip: Whether to strip whitespace

    Returns:
        Dictionary of string keys and values

    Raises:
        ValueError: If format is invalid

    """
    if isinstance(value, dict):
        return value

    if not value:
        return {}

    result = {}
    items = value.split(item_separator)

    for item in items:
        if not item:
            continue

        if key_separator not in item:
            raise ValueError(f"Invalid dict format: '{item}' missing '{key_separator}'")

        key, val = item.split(key_separator, 1)

        if strip:
            key = key.strip()
            val = val.strip()

        result[key] = val

    return result

parse_float_with_validation

parse_float_with_validation(
    value: str,
    min_val: float | None = None,
    max_val: float | None = None,
) -> float

Parse float with optional range validation.

Parameters:

Name Type Description Default
value str

String representation of float

required
min_val float | None

Minimum allowed value (inclusive)

None
max_val float | None

Maximum allowed value (inclusive)

None

Returns:

Type Description
float

Parsed float value

Raises:

Type Description
ValueError

If value is not a valid float or out of range

Source code in provide/foundation/parsers/primitives.py
def parse_float_with_validation(
    value: str,
    min_val: float | None = None,
    max_val: float | None = None,
) -> float:
    """Parse float with optional range validation.

    Args:
        value: String representation of float
        min_val: Minimum allowed value (inclusive)
        max_val: Maximum allowed value (inclusive)

    Returns:
        Parsed float value

    Raises:
        ValueError: If value is not a valid float or out of range

    """
    try:
        result = float(value)
    except (ValueError, TypeError) as e:
        raise ValueError(
            _format_invalid_value_error("float", value, expected_type="float"),
        ) from e

    if min_val is not None and result < min_val:
        raise ValueError(
            _format_validation_error("float", result, f"must be >= {min_val}"),
        )

    if max_val is not None and result > max_val:
        raise ValueError(
            _format_validation_error("float", result, f"must be <= {max_val}"),
        )

    return result

parse_headers

parse_headers(
    value: str | dict[str, str],
) -> dict[str, str]

Parse HTTP headers from string format.

Format Requirements: - Comma-separated key=value pairs: "key1=value1,key2=value2" - Header names and values are trimmed of whitespace - Empty header names are ignored - Each pair must contain exactly one '=' separator - Invalid pairs are silently skipped

Examples: >>> parse_headers("Authorization=Bearer token,Content-Type=application/json")

>>> parse_headers("X-API-Key=secret123")  # Single header
{'X-API-Key': 'secret123'}

>>> parse_headers("valid=ok,invalid-no-equals,another=good")  # Partial success
{'valid': 'ok', 'another': 'good'}

>>> parse_headers("empty-value=")  # Empty values allowed
{'empty-value': ''}

Parameters:

Name Type Description Default
value str | dict[str, str]

Comma-separated key=value pairs for HTTP headers, or dict if already parsed

required

Returns:

Type Description
dict[str, str]

Dictionary of header name-value pairs.

dict[str, str]

Invalid entries are silently ignored.

Note

This parser is lenient by design - invalid header pairs are skipped rather than raising errors to allow partial configuration success in production environments.

Source code in provide/foundation/parsers/structured.py
def parse_headers(value: str | dict[str, str]) -> dict[str, str]:
    """Parse HTTP headers from string format.

    **Format Requirements:**
    - Comma-separated key=value pairs: "key1=value1,key2=value2"
    - Header names and values are trimmed of whitespace
    - Empty header names are ignored
    - Each pair must contain exactly one '=' separator
    - Invalid pairs are silently skipped

    **Examples:**
        >>> parse_headers("Authorization=Bearer token,Content-Type=application/json")
        {'Authorization': 'Bearer token', 'Content-Type': 'application/json'}

        >>> parse_headers("X-API-Key=secret123")  # Single header
        {'X-API-Key': 'secret123'}

        >>> parse_headers("valid=ok,invalid-no-equals,another=good")  # Partial success
        {'valid': 'ok', 'another': 'good'}

        >>> parse_headers("empty-value=")  # Empty values allowed
        {'empty-value': ''}

    Args:
        value: Comma-separated key=value pairs for HTTP headers, or dict if already parsed

    Returns:
        Dictionary of header name-value pairs.
        Invalid entries are silently ignored.

    Note:
        This parser is lenient by design - invalid header pairs are skipped rather than
        raising errors to allow partial configuration success in production environments.

    """
    # If already a dict (from factory default), return as-is
    if isinstance(value, dict):
        return value

    if not value or not value.strip():
        return {}

    result = {}
    for pair in value.split(","):
        pair = pair.strip()
        if not pair:
            continue

        if "=" not in pair:
            # Skip invalid entries
            continue

        key, val = pair.split("=", 1)
        key = key.strip()
        val = val.strip()

        if key:
            result[key] = val

    return result

parse_json_dict

parse_json_dict(value: str) -> dict[str, Any]

Parse JSON string into dictionary.

Parameters:

Name Type Description Default
value str

JSON string

required

Returns:

Type Description
dict[str, Any]

Parsed dictionary

Raises:

Type Description
ValueError

If JSON is invalid

Source code in provide/foundation/parsers/primitives.py
def parse_json_dict(value: str) -> dict[str, Any]:
    """Parse JSON string into dictionary.

    Args:
        value: JSON string

    Returns:
        Parsed dictionary

    Raises:
        ValueError: If JSON is invalid

    """
    if not value or not value.strip():
        return {}

    try:
        result = json_loads(value)
        if not isinstance(result, dict):
            raise ValueError(
                _format_invalid_value_error(
                    "json_dict",
                    type(result).__name__,
                    expected_type="JSON object",
                ),
            )
        return result
    except Exception as e:
        raise ValueError(
            _format_invalid_value_error("json_dict", value, expected_type="valid JSON"),
        ) from e

parse_json_list

parse_json_list(value: str) -> list[Any]

Parse JSON string into list.

Parameters:

Name Type Description Default
value str

JSON string

required

Returns:

Type Description
list[Any]

Parsed list

Raises:

Type Description
ValueError

If JSON is invalid

Source code in provide/foundation/parsers/primitives.py
def parse_json_list(value: str) -> list[Any]:
    """Parse JSON string into list.

    Args:
        value: JSON string

    Returns:
        Parsed list

    Raises:
        ValueError: If JSON is invalid

    """
    if not value or not value.strip():
        return []

    try:
        result = json_loads(value)
        if not isinstance(result, list):
            raise ValueError(
                _format_invalid_value_error(
                    "json_list",
                    type(result).__name__,
                    expected_type="JSON array",
                ),
            )
        return result
    except Exception as e:
        raise ValueError(
            _format_invalid_value_error("json_list", value, expected_type="valid JSON"),
        ) from e

parse_list

parse_list(
    value: str | list[str],
    separator: str = ",",
    strip: bool = True,
) -> list[str]

Parse a list from a string.

Parameters:

Name Type Description Default
value str | list[str]

String or list to parse

required
separator str

Separator character

','
strip bool

Whether to strip whitespace from items

True

Returns:

Type Description
list[str]

List of strings

Source code in provide/foundation/parsers/collections.py
def parse_list(
    value: str | list[str],
    separator: str = ",",
    strip: bool = True,
) -> list[str]:
    """Parse a list from a string.

    Args:
        value: String or list to parse
        separator: Separator character
        strip: Whether to strip whitespace from items

    Returns:
        List of strings

    """
    if isinstance(value, list):
        return value

    if not value:
        return []

    items = value.split(separator)

    if strip:
        items = [item.strip() for item in items]

    return items

parse_log_level

parse_log_level(value: str) -> LogLevelStr

Parse and validate log level string.

Parameters:

Name Type Description Default
value str

Log level string (case-insensitive)

required

Returns:

Type Description
LogLevelStr

Valid log level string in uppercase

Raises:

Type Description
ValueError

If the log level is invalid

Source code in provide/foundation/parsers/telemetry.py
def parse_log_level(value: str) -> LogLevelStr:
    """Parse and validate log level string.

    Args:
        value: Log level string (case-insensitive)

    Returns:
        Valid log level string in uppercase

    Raises:
        ValueError: If the log level is invalid

    """
    level = value.upper()
    if level not in _VALID_LOG_LEVEL_TUPLE:
        raise ValueError(
            _format_invalid_value_error(
                "log_level",
                value,
                valid_options=list(_VALID_LOG_LEVEL_TUPLE),
            ),
        )
    return cast("LogLevelStr", level)

parse_module_levels

parse_module_levels(
    value: str | dict[str, str],
) -> dict[str, LogLevelStr]

Parse module-specific log levels from string format.

Format Requirements: - String format: "module1:LEVEL,module2:LEVEL" (comma-separated pairs) - Dict format: Already parsed dictionary (validated and returned) - Log levels must be valid: TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL - Module names are trimmed of whitespace - Invalid log levels are silently ignored

Examples: >>> parse_module_levels("auth.service:DEBUG,database:ERROR")

>>> parse_module_levels("api:INFO")  # Single module
{'api': 'INFO'}

>>> parse_module_levels({"web": "warning"})  # Dict input (case normalized)
{'web': 'WARNING'}

>>> parse_module_levels("api:INFO,bad:INVALID,db:ERROR")  # Partial success
{'api': 'INFO', 'db': 'ERROR'}

Parameters:

Name Type Description Default
value str | dict[str, str]

Comma-separated module:level pairs or pre-parsed dict

required

Returns:

Type Description
dict[str, LogLevelStr]

Dictionary mapping module names to validated log level strings.

dict[str, LogLevelStr]

Invalid entries are silently ignored.

Note

This parser is lenient by design - invalid log levels are skipped rather than raising errors to allow partial configuration success in production environments.

Source code in provide/foundation/parsers/structured.py
def parse_module_levels(value: str | dict[str, str]) -> dict[str, LogLevelStr]:
    """Parse module-specific log levels from string format.

    **Format Requirements:**
    - String format: "module1:LEVEL,module2:LEVEL" (comma-separated pairs)
    - Dict format: Already parsed dictionary (validated and returned)
    - Log levels must be valid: TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL
    - Module names are trimmed of whitespace
    - Invalid log levels are silently ignored

    **Examples:**
        >>> parse_module_levels("auth.service:DEBUG,database:ERROR")
        {'auth.service': 'DEBUG', 'database': 'ERROR'}

        >>> parse_module_levels("api:INFO")  # Single module
        {'api': 'INFO'}

        >>> parse_module_levels({"web": "warning"})  # Dict input (case normalized)
        {'web': 'WARNING'}

        >>> parse_module_levels("api:INFO,bad:INVALID,db:ERROR")  # Partial success
        {'api': 'INFO', 'db': 'ERROR'}

    Args:
        value: Comma-separated module:level pairs or pre-parsed dict

    Returns:
        Dictionary mapping module names to validated log level strings.
        Invalid entries are silently ignored.

    Note:
        This parser is lenient by design - invalid log levels are skipped rather than
        raising errors to allow partial configuration success in production environments.

    """
    # If already a dict, validate and return
    if isinstance(value, dict):
        result = {}
        for module, level in value.items():
            try:
                result[module] = parse_log_level(level)
            except ValueError:
                # Skip invalid levels silently
                continue
        return result

    if not value or not value.strip():
        return {}

    result = {}
    for pair in value.split(","):
        pair = pair.strip()
        if not pair:
            continue

        if ":" not in pair:
            # Skip invalid entries silently
            continue

        module, level = pair.split(":", 1)
        module = module.strip()
        level = level.strip()

        if module:
            try:
                result[module] = parse_log_level(level)
            except ValueError:
                # Skip invalid log levels silently
                continue

    return result

parse_rate_limits

parse_rate_limits(
    value: str,
) -> dict[str, tuple[float, float]]

Parse per-logger rate limits from string format.

Format Requirements: - Comma-separated triplets: "logger1:rate:capacity,logger2:rate:capacity" - Rate and capacity must be valid float numbers - Logger names are trimmed of whitespace - Empty logger names are ignored - Invalid entries are silently skipped to allow partial success

Examples: >>> parse_rate_limits("api:10.0:100.0,worker:5.0:50.0")

>>> parse_rate_limits("db:1.5:25.0")  # Single entry
{'db': (1.5, 25.0)}

>>> parse_rate_limits("api:10:100,invalid:bad,worker:5:50")  # Partial success
{'api': (10.0, 100.0), 'worker': (5.0, 50.0)}

Parameters:

Name Type Description Default
value str

Comma-separated logger:rate:capacity triplets

required

Returns:

Type Description
dict[str, tuple[float, float]]

Dictionary mapping logger names to (rate, capacity) tuples.

dict[str, tuple[float, float]]

Invalid entries are silently ignored.

Note

This parser is lenient by design - invalid entries are skipped rather than raising errors to allow partial configuration success in production environments.

Source code in provide/foundation/parsers/structured.py
def parse_rate_limits(value: str) -> dict[str, tuple[float, float]]:
    """Parse per-logger rate limits from string format.

    **Format Requirements:**
    - Comma-separated triplets: "logger1:rate:capacity,logger2:rate:capacity"
    - Rate and capacity must be valid float numbers
    - Logger names are trimmed of whitespace
    - Empty logger names are ignored
    - Invalid entries are silently skipped to allow partial success

    **Examples:**
        >>> parse_rate_limits("api:10.0:100.0,worker:5.0:50.0")
        {'api': (10.0, 100.0), 'worker': (5.0, 50.0)}

        >>> parse_rate_limits("db:1.5:25.0")  # Single entry
        {'db': (1.5, 25.0)}

        >>> parse_rate_limits("api:10:100,invalid:bad,worker:5:50")  # Partial success
        {'api': (10.0, 100.0), 'worker': (5.0, 50.0)}

    Args:
        value: Comma-separated logger:rate:capacity triplets

    Returns:
        Dictionary mapping logger names to (rate, capacity) tuples.
        Invalid entries are silently ignored.

    Note:
        This parser is lenient by design - invalid entries are skipped rather than
        raising errors to allow partial configuration success in production environments.

    """
    if not value or not value.strip():
        return {}

    result = {}
    for triplet in value.split(","):
        triplet = triplet.strip()
        if not triplet:
            continue

        parts = triplet.split(":")
        if len(parts) != 3:
            # Skip invalid entries silently
            continue

        logger_name, rate_str, capacity_str = parts
        logger_name = logger_name.strip()

        if not logger_name:
            continue

        try:
            rate = float(rate_str.strip())
            capacity = float(capacity_str.strip())
            result[logger_name] = (rate, capacity)
        except (ValueError, TypeError):
            # Skip invalid numeric values silently
            continue

    return result

parse_sample_rate

parse_sample_rate(value: str) -> float

Parse sampling rate (0.0 to 1.0).

Parameters:

Name Type Description Default
value str

String representation of sampling rate

required

Returns:

Type Description
float

Float between 0.0 and 1.0

Raises:

Type Description
ValueError

If value is not valid or out of range

Source code in provide/foundation/parsers/primitives.py
def parse_sample_rate(value: str) -> float:
    """Parse sampling rate (0.0 to 1.0).

    Args:
        value: String representation of sampling rate

    Returns:
        Float between 0.0 and 1.0

    Raises:
        ValueError: If value is not valid or out of range

    """
    return parse_float_with_validation(value, min_val=0.0, max_val=1.0)

register_config

register_config(
    name: str,
    config: BaseConfig | None = None,
    schema: ConfigSchema | None = None,
    loader: ConfigLoader | None = None,
    defaults: ConfigDict | None = None,
) -> None

Register a configuration with the global manager.

Parameters:

Name Type Description Default
name str

Configuration name

required
config BaseConfig | None

Configuration instance

None
schema ConfigSchema | None

Configuration schema

None
loader ConfigLoader | None

Configuration loader

None
defaults ConfigDict | None

Default configuration values

None
Source code in provide/foundation/config/manager.py
def register_config(
    name: str,
    config: BaseConfig | None = None,
    schema: ConfigSchema | None = None,
    loader: ConfigLoader | None = None,
    defaults: ConfigDict | None = None,
) -> None:
    """Register a configuration with the global manager.

    Args:
        name: Configuration name
        config: Configuration instance
        schema: Configuration schema
        loader: Configuration loader
        defaults: Default configuration values

    """
    _manager.register(name, config, schema, loader, defaults)

set_config

set_config(name: str, config: BaseConfig) -> None

Set a configuration in the global manager.

Parameters:

Name Type Description Default
name str

Configuration name

required
config BaseConfig

Configuration instance

required
Source code in provide/foundation/config/manager.py
def set_config(name: str, config: BaseConfig) -> None:
    """Set a configuration in the global manager.

    Args:
        name: Configuration name
        config: Configuration instance

    """
    _manager.set(name, config)

validate_choice

validate_choice(
    choices: list[Any],
) -> Callable[[Any, Any, Any], None]

Create a validator that ensures value is one of the given choices.

Parameters:

Name Type Description Default
choices list[Any]

List of valid choices

required

Returns:

Type Description
Callable[[Any, Any, Any], None]

Validator function for use with attrs

Source code in provide/foundation/config/validators.py
def validate_choice(choices: list[Any]) -> Callable[[Any, Any, Any], None]:
    """Create a validator that ensures value is one of the given choices.

    Args:
        choices: List of valid choices

    Returns:
        Validator function for use with attrs

    """

    def validator(instance: Any, attribute: Any, value: Any) -> None:
        if value not in choices:
            # Import ValidationError locally to avoid circular imports
            from provide.foundation.errors.config import ValidationError

            raise ValidationError(
                f"Invalid value '{value}' for {attribute.name}. Must be one of: {choices!r}",
            )

    return validator

validate_log_level

validate_log_level(
    instance: Any, attribute: Any, value: str
) -> None

Validate that a log level is valid.

Source code in provide/foundation/config/validators.py
def validate_log_level(instance: Any, attribute: Any, value: str) -> None:
    """Validate that a log level is valid."""
    # Import ValidationError locally to avoid circular imports
    from provide.foundation.errors.config import ValidationError

    if value not in _VALID_LOG_LEVEL_TUPLE:
        raise ValidationError(
            _format_invalid_value_error(
                attribute.name,
                value,
                valid_options=list(_VALID_LOG_LEVEL_TUPLE),
            ),
        )

validate_non_negative

validate_non_negative(
    instance: Any, attribute: Any, value: float
) -> None

Validate that a value is non-negative.

Source code in provide/foundation/config/validators.py
def validate_non_negative(instance: Any, attribute: Any, value: float) -> None:
    """Validate that a value is non-negative."""
    # Import ValidationError locally to avoid circular imports
    from provide.foundation.errors.config import ValidationError

    # Check if value is numeric
    if not isinstance(value, (int, float)):
        raise ValidationError(
            f"Value must be a number, got {type(value).__name__}",
        )

    if value < 0:
        raise ValidationError(
            _format_validation_error(attribute.name, value, "must be non-negative"),
        )

validate_overflow_policy

validate_overflow_policy(
    instance: Any, attribute: Any, value: str
) -> None

Validate rate limit overflow policy.

Source code in provide/foundation/config/validators.py
def validate_overflow_policy(instance: Any, attribute: Any, value: str) -> None:
    """Validate rate limit overflow policy."""
    # Import ValidationError locally to avoid circular imports
    from provide.foundation.errors.config import ValidationError

    if value not in _VALID_OVERFLOW_POLICY_TUPLE:
        raise ValidationError(
            _format_invalid_value_error(
                attribute.name,
                value,
                valid_options=list(_VALID_OVERFLOW_POLICY_TUPLE),
            ),
        )

validate_port

validate_port(
    instance: Any, attribute: Any, value: int
) -> None

Validate that a port number is valid.

Source code in provide/foundation/config/validators.py
def validate_port(instance: Any, attribute: Any, value: int) -> None:
    """Validate that a port number is valid."""
    # Import ValidationError locally to avoid circular imports
    from provide.foundation.errors.config import ValidationError

    if not 1 <= value <= 65535:
        raise ValidationError(
            _format_validation_error(attribute.name, value, "must be between 1 and 65535"),
        )

validate_positive

validate_positive(
    instance: Any, attribute: Any, value: float
) -> None

Validate that a value is positive.

Source code in provide/foundation/config/validators.py
def validate_positive(instance: Any, attribute: Any, value: float) -> None:
    """Validate that a value is positive."""
    # Import ValidationError locally to avoid circular imports
    from provide.foundation.errors.config import ValidationError

    # Check if value is numeric
    if not isinstance(value, (int, float)):
        raise ValidationError(
            f"Value must be a number, got {type(value).__name__}",
        )

    if value <= 0:
        raise ValidationError(
            _format_validation_error(attribute.name, value, "must be positive"),
        )

validate_range

validate_range(
    min_val: float, max_val: float
) -> Callable[[Any, Any, Any], None]

Create a validator that ensures value is within the given numeric range.

Parameters:

Name Type Description Default
min_val float

Minimum allowed value (inclusive)

required
max_val float

Maximum allowed value (inclusive)

required

Returns:

Type Description
Callable[[Any, Any, Any], None]

Validator function for use with attrs

Source code in provide/foundation/config/validators.py
def validate_range(min_val: float, max_val: float) -> Callable[[Any, Any, Any], None]:
    """Create a validator that ensures value is within the given numeric range.

    Args:
        min_val: Minimum allowed value (inclusive)
        max_val: Maximum allowed value (inclusive)

    Returns:
        Validator function for use with attrs

    """

    def validator(instance: Any, attribute: Any, value: Any) -> None:
        # Import ValidationError locally to avoid circular imports
        from provide.foundation.errors.config import ValidationError

        # Check if value is numeric
        if not isinstance(value, (int, float)):
            raise ValidationError(
                f"Value must be a number, got {type(value).__name__}",
            )

        if not (min_val <= value <= max_val):
            raise ValidationError(
                f"Value must be between {min_val} and {max_val}, got {value}",
            )

    return validator

validate_sample_rate

validate_sample_rate(
    instance: Any, attribute: Any, value: float
) -> None

Validate that a sample rate is between 0.0 and 1.0.

Source code in provide/foundation/config/validators.py
def validate_sample_rate(instance: Any, attribute: Any, value: float) -> None:
    """Validate that a sample rate is between 0.0 and 1.0."""
    # Import ValidationError locally to avoid circular imports
    from provide.foundation.errors.config import ValidationError

    if not 0.0 <= value <= 1.0:
        raise ValidationError(
            _format_validation_error(attribute.name, value, "must be between 0.0 and 1.0"),
        )

validate_schema

validate_schema(
    config: BaseConfig, schema: ConfigSchema
) -> None

Validate configuration instance against schema.

Parameters:

Name Type Description Default
config BaseConfig

Configuration instance

required
schema ConfigSchema

Schema to validate against

required

Raises:

Type Description
ConfigValidationError

If validation fails

Source code in provide/foundation/config/schema.py
def validate_schema(config: BaseConfig, schema: ConfigSchema) -> None:
    """Validate configuration instance against schema.

    Args:
        config: Configuration instance
        schema: Schema to validate against

    Raises:
        ConfigValidationError: If validation fails

    """
    data = config.to_dict(include_sensitive=True)
    schema.validate(data)